Merge branches 'ib-mfd-x86-usb-watchdog-5.8', 'ib-mfd-power-rtc-5.8', 'ib-mfd-iio-power-5.8' and 'ib-mfd-hwmon-5.8' into ibs-for-mfd-merged

Цей коміт міститься в:
Lee Jones
2020-05-26 10:50:51 +01:00
32 змінених файлів з 2963 додано та 62 видалено

Переглянути файл

@@ -407,6 +407,21 @@ config MFD_EXYNOS_LPASS
Select this option to enable support for Samsung Exynos Low Power
Audio Subsystem.
config MFD_GATEWORKS_GSC
tristate "Gateworks System Controller"
depends on (I2C && OF)
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
Enable support for the Gateworks System Controller (GSC) found
on Gateworks Single Board Computers supporting system functions
such as push-button monitor, multiple ADC's for voltage and
temperature monitoring, fan controller and watchdog monitor.
This driver provides common support for accessing the device.
Additional drivers must be enabled in order to use the
functionality of the device.
config MFD_MC13XXX
tristate
depends on (SPI_MASTER || I2C)
@@ -434,6 +449,15 @@ config MFD_MC13XXX_I2C
help
Select this if your MC13xxx is connected via an I2C bus.
config MFD_MP2629
tristate "Monolithic Power Systems MP2629 ADC and Battery charger"
depends on I2C
select REGMAP_I2C
help
Select this option to enable support for Monolithic Power Systems
battery charger. This provides ADC, thermal and battery charger power
management functions.
config MFD_MXS_LRADC
tristate "Freescale i.MX23/i.MX28 LRADC"
depends on ARCH_MXS || COMPILE_TEST

Переглянути файл

@@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
@@ -170,6 +171,8 @@ obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
obj-$(CONFIG_MFD_MP2629) += mp2629.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
@@ -240,7 +243,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o
obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC) += intel_soc_pmic_chtwc.o
obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o
mt6397-objs := mt6397-core.o mt6397-irq.o
mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o
obj-$(CONFIG_MFD_MT6397) += mt6397.o
obj-$(CONFIG_INTEL_SOC_PMIC_MRFLD) += intel_soc_pmic_mrfld.o

277
drivers/mfd/gateworks-gsc.c Звичайний файл
Переглянути файл

@@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0
/*
* The Gateworks System Controller (GSC) is a multi-function
* device designed for use in Gateworks Single Board Computers.
* The control interface is I2C, with an interrupt. The device supports
* system functions such as push-button monitoring, multiple ADC's for
* voltage and temperature monitoring, fan controller and watchdog monitor.
*
* Copyright (C) 2020 Gateworks Corporation
*/
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/gsc.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <asm/unaligned.h>
/*
* The GSC suffers from an errata where occasionally during
* ADC cycles the chip can NAK I2C transactions. To ensure we have reliable
* register access we place retries around register access.
*/
#define I2C_RETRIES 3
int gsc_write(void *context, unsigned int reg, unsigned int val)
{
struct i2c_client *client = context;
int retry, ret;
for (retry = 0; retry < I2C_RETRIES; retry++) {
ret = i2c_smbus_write_byte_data(client, reg, val);
/*
* -EAGAIN returned when the i2c host controller is busy
* -EIO returned when i2c device is busy
*/
if (ret != -EAGAIN && ret != -EIO)
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(gsc_write);
int gsc_read(void *context, unsigned int reg, unsigned int *val)
{
struct i2c_client *client = context;
int retry, ret;
for (retry = 0; retry < I2C_RETRIES; retry++) {
ret = i2c_smbus_read_byte_data(client, reg);
/*
* -EAGAIN returned when the i2c host controller is busy
* -EIO returned when i2c device is busy
*/
if (ret != -EAGAIN && ret != -EIO)
break;
}
*val = ret & 0xff;
return 0;
}
EXPORT_SYMBOL_GPL(gsc_read);
/*
* gsc_powerdown - API to use GSC to power down board for a specific time
*
* secs - number of seconds to remain powered off
*/
static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs)
{
int ret;
unsigned char regs[4];
dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n",
secs);
put_unaligned_le32(secs, regs);
ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4);
if (ret)
return ret;
ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
BIT(GSC_CTRL_1_SLEEP_ADD),
BIT(GSC_CTRL_1_SLEEP_ADD));
if (ret)
return ret;
ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
BIT(GSC_CTRL_1_SLEEP_ENABLE),
BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
BIT(GSC_CTRL_1_SLEEP_ENABLE));
return ret;
}
static ssize_t gsc_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct gsc_dev *gsc = dev_get_drvdata(dev);
const char *name = attr->attr.name;
int rz = 0;
if (strcasecmp(name, "fw_version") == 0)
rz = sprintf(buf, "%d\n", gsc->fwver);
else if (strcasecmp(name, "fw_crc") == 0)
rz = sprintf(buf, "0x%04x\n", gsc->fwcrc);
else
dev_err(dev, "invalid command: '%s'\n", name);
return rz;
}
static ssize_t gsc_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct gsc_dev *gsc = dev_get_drvdata(dev);
const char *name = attr->attr.name;
long value;
if (strcasecmp(name, "powerdown") == 0) {
if (kstrtol(buf, 0, &value) == 0)
gsc_powerdown(gsc, value);
} else {
dev_err(dev, "invalid command: '%s\n", name);
}
return count;
}
static struct device_attribute attr_fwver =
__ATTR(fw_version, 0440, gsc_show, NULL);
static struct device_attribute attr_fwcrc =
__ATTR(fw_crc, 0440, gsc_show, NULL);
static struct device_attribute attr_pwrdown =
__ATTR(powerdown, 0220, NULL, gsc_store);
static struct attribute *gsc_attrs[] = {
&attr_fwver.attr,
&attr_fwcrc.attr,
&attr_pwrdown.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = gsc_attrs,
};
static const struct of_device_id gsc_of_match[] = {
{ .compatible = "gw,gsc", },
{ }
};
MODULE_DEVICE_TABLE(of, gsc_of_match);
static struct regmap_bus gsc_regmap_bus = {
.reg_read = gsc_read,
.reg_write = gsc_write,
};
static const struct regmap_config gsc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_NONE,
.max_register = GSC_WP,
};
static const struct regmap_irq gsc_irqs[] = {
REGMAP_IRQ_REG(GSC_IRQ_PB, 0, BIT(GSC_IRQ_PB)),
REGMAP_IRQ_REG(GSC_IRQ_KEY_ERASED, 0, BIT(GSC_IRQ_KEY_ERASED)),
REGMAP_IRQ_REG(GSC_IRQ_EEPROM_WP, 0, BIT(GSC_IRQ_EEPROM_WP)),
REGMAP_IRQ_REG(GSC_IRQ_RESV, 0, BIT(GSC_IRQ_RESV)),
REGMAP_IRQ_REG(GSC_IRQ_GPIO, 0, BIT(GSC_IRQ_GPIO)),
REGMAP_IRQ_REG(GSC_IRQ_TAMPER, 0, BIT(GSC_IRQ_TAMPER)),
REGMAP_IRQ_REG(GSC_IRQ_WDT_TIMEOUT, 0, BIT(GSC_IRQ_WDT_TIMEOUT)),
REGMAP_IRQ_REG(GSC_IRQ_SWITCH_HOLD, 0, BIT(GSC_IRQ_SWITCH_HOLD)),
};
static const struct regmap_irq_chip gsc_irq_chip = {
.name = "gateworks-gsc",
.irqs = gsc_irqs,
.num_irqs = ARRAY_SIZE(gsc_irqs),
.num_regs = 1,
.status_base = GSC_IRQ_STATUS,
.mask_base = GSC_IRQ_ENABLE,
.mask_invert = true,
.ack_base = GSC_IRQ_STATUS,
.ack_invert = true,
};
static int gsc_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct gsc_dev *gsc;
struct regmap_irq_chip_data *irq_data;
int ret;
unsigned int reg;
gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL);
if (!gsc)
return -ENOMEM;
gsc->dev = &client->dev;
gsc->i2c = client;
i2c_set_clientdata(client, gsc);
gsc->regmap = devm_regmap_init(dev, &gsc_regmap_bus, client,
&gsc_regmap_config);
if (IS_ERR(gsc->regmap))
return PTR_ERR(gsc->regmap);
if (regmap_read(gsc->regmap, GSC_FW_VER, &reg))
return -EIO;
gsc->fwver = reg;
regmap_read(gsc->regmap, GSC_FW_CRC, &reg);
gsc->fwcrc = reg;
regmap_read(gsc->regmap, GSC_FW_CRC + 1, &reg);
gsc->fwcrc |= reg << 8;
gsc->i2c_hwmon = devm_i2c_new_dummy_device(dev, client->adapter,
GSC_HWMON);
if (IS_ERR(gsc->i2c_hwmon)) {
dev_err(dev, "Failed to allocate I2C device for HWMON\n");
return PTR_ERR(gsc->i2c_hwmon);
}
ret = devm_regmap_add_irq_chip(dev, gsc->regmap, client->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&gsc_irq_chip, &irq_data);
if (ret)
return ret;
dev_info(dev, "Gateworks System Controller v%d: fw 0x%04x\n",
gsc->fwver, gsc->fwcrc);
ret = sysfs_create_group(&dev->kobj, &attr_group);
if (ret)
dev_err(dev, "failed to create sysfs attrs\n");
ret = devm_of_platform_populate(dev);
if (ret) {
sysfs_remove_group(&dev->kobj, &attr_group);
return ret;
}
return 0;
}
static int gsc_remove(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &attr_group);
return 0;
}
static struct i2c_driver gsc_driver = {
.driver = {
.name = "gateworks-gsc",
.of_match_table = gsc_of_match,
},
.probe_new = gsc_probe,
.remove = gsc_remove,
};
module_i2c_driver(gsc_driver);
MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
MODULE_DESCRIPTION("I2C Core interface for GSC");
MODULE_LICENSE("GPL v2");

79
drivers/mfd/mp2629.c Звичайний файл
Переглянути файл

@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* MP2629 parent driver for ADC and battery charger
*
* Copyright 2020 Monolithic Power Systems, Inc
*
* Author: Saravanan Sekar <sravanhome@gmail.com>
*/
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mp2629.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static const struct mfd_cell mp2629_cell[] = {
{
.name = "mp2629_adc",
.of_compatible = "mps,mp2629_adc",
},
{
.name = "mp2629_charger",
.of_compatible = "mps,mp2629_charger",
}
};
static const struct regmap_config mp2629_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x17,
};
static int mp2629_probe(struct i2c_client *client)
{
struct mp2629_data *ddata;
int ret;
ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
ddata->dev = &client->dev;
i2c_set_clientdata(client, ddata);
ddata->regmap = devm_regmap_init_i2c(client, &mp2629_regmap_config);
if (IS_ERR(ddata->regmap)) {
dev_err(ddata->dev, "Failed to allocate regmap\n");
return PTR_ERR(ddata->regmap);
}
ret = devm_mfd_add_devices(ddata->dev, PLATFORM_DEVID_AUTO, mp2629_cell,
ARRAY_SIZE(mp2629_cell), NULL, 0, NULL);
if (ret)
dev_err(ddata->dev, "Failed to register sub-devices %d\n", ret);
return ret;
}
static const struct of_device_id mp2629_of_match[] = {
{ .compatible = "mps,mp2629"},
{ }
};
MODULE_DEVICE_TABLE(of, mp2629_of_match);
static struct i2c_driver mp2629_driver = {
.driver = {
.name = "mp2629",
.of_match_table = mp2629_of_match,
},
.probe_new = mp2629_probe,
};
module_i2c_driver(mp2629_driver);
MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
MODULE_DESCRIPTION("MP2629 Battery charger parent driver");
MODULE_LICENSE("GPL");

235
drivers/mfd/mt6358-irq.c Звичайний файл
Переглянути файл

@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2020 MediaTek Inc.
#include <linux/interrupt.h>
#include <linux/mfd/mt6358/core.h>
#include <linux/mfd/mt6358/registers.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
static struct irq_top_t mt6358_ints[] = {
MT6358_TOP_GEN(BUCK),
MT6358_TOP_GEN(LDO),
MT6358_TOP_GEN(PSC),
MT6358_TOP_GEN(SCK),
MT6358_TOP_GEN(BM),
MT6358_TOP_GEN(HK),
MT6358_TOP_GEN(AUD),
MT6358_TOP_GEN(MISC),
};
static void pmic_irq_enable(struct irq_data *data)
{
unsigned int hwirq = irqd_to_hwirq(data);
struct mt6397_chip *chip = irq_data_get_irq_chip_data(data);
struct pmic_irq_data *irqd = chip->irq_data;
irqd->enable_hwirq[hwirq] = true;
}
static void pmic_irq_disable(struct irq_data *data)
{
unsigned int hwirq = irqd_to_hwirq(data);
struct mt6397_chip *chip = irq_data_get_irq_chip_data(data);
struct pmic_irq_data *irqd = chip->irq_data;
irqd->enable_hwirq[hwirq] = false;
}
static void pmic_irq_lock(struct irq_data *data)
{
struct mt6397_chip *chip = irq_data_get_irq_chip_data(data);
mutex_lock(&chip->irqlock);
}
static void pmic_irq_sync_unlock(struct irq_data *data)
{
unsigned int i, top_gp, gp_offset, en_reg, int_regs, shift;
struct mt6397_chip *chip = irq_data_get_irq_chip_data(data);
struct pmic_irq_data *irqd = chip->irq_data;
for (i = 0; i < irqd->num_pmic_irqs; i++) {
if (irqd->enable_hwirq[i] == irqd->cache_hwirq[i])
continue;
/* Find out the IRQ group */
top_gp = 0;
while ((top_gp + 1) < irqd->num_top &&
i >= mt6358_ints[top_gp + 1].hwirq_base)
top_gp++;
/* Find the IRQ registers */
gp_offset = i - mt6358_ints[top_gp].hwirq_base;
int_regs = gp_offset / MT6358_REG_WIDTH;
shift = gp_offset % MT6358_REG_WIDTH;
en_reg = mt6358_ints[top_gp].en_reg +
(mt6358_ints[top_gp].en_reg_shift * int_regs);
regmap_update_bits(chip->regmap, en_reg, BIT(shift),
irqd->enable_hwirq[i] << shift);
irqd->cache_hwirq[i] = irqd->enable_hwirq[i];
}
mutex_unlock(&chip->irqlock);
}
static struct irq_chip mt6358_irq_chip = {
.name = "mt6358-irq",
.flags = IRQCHIP_SKIP_SET_WAKE,
.irq_enable = pmic_irq_enable,
.irq_disable = pmic_irq_disable,
.irq_bus_lock = pmic_irq_lock,
.irq_bus_sync_unlock = pmic_irq_sync_unlock,
};
static void mt6358_irq_sp_handler(struct mt6397_chip *chip,
unsigned int top_gp)
{
unsigned int irq_status, sta_reg, status;
unsigned int hwirq, virq;
int i, j, ret;
for (i = 0; i < mt6358_ints[top_gp].num_int_regs; i++) {
sta_reg = mt6358_ints[top_gp].sta_reg +
mt6358_ints[top_gp].sta_reg_shift * i;
ret = regmap_read(chip->regmap, sta_reg, &irq_status);
if (ret) {
dev_err(chip->dev,
"Failed to read IRQ status, ret=%d\n", ret);
return;
}
if (!irq_status)
continue;
status = irq_status;
do {
j = __ffs(status);
hwirq = mt6358_ints[top_gp].hwirq_base +
MT6358_REG_WIDTH * i + j;
virq = irq_find_mapping(chip->irq_domain, hwirq);
if (virq)
handle_nested_irq(virq);
status &= ~BIT(j);
} while (status);
regmap_write(chip->regmap, sta_reg, irq_status);
}
}
static irqreturn_t mt6358_irq_handler(int irq, void *data)
{
struct mt6397_chip *chip = data;
struct pmic_irq_data *mt6358_irq_data = chip->irq_data;
unsigned int bit, i, top_irq_status = 0;
int ret;
ret = regmap_read(chip->regmap,
mt6358_irq_data->top_int_status_reg,
&top_irq_status);
if (ret) {
dev_err(chip->dev,
"Failed to read status from the device, ret=%d\n", ret);
return IRQ_NONE;
}
for (i = 0; i < mt6358_irq_data->num_top; i++) {
bit = BIT(mt6358_ints[i].top_offset);
if (top_irq_status & bit) {
mt6358_irq_sp_handler(chip, i);
top_irq_status &= ~bit;
if (!top_irq_status)
break;
}
}
return IRQ_HANDLED;
}
static int pmic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
struct mt6397_chip *mt6397 = d->host_data;
irq_set_chip_data(irq, mt6397);
irq_set_chip_and_handler(irq, &mt6358_irq_chip, handle_level_irq);
irq_set_nested_thread(irq, 1);
irq_set_noprobe(irq);
return 0;
}
static const struct irq_domain_ops mt6358_irq_domain_ops = {
.map = pmic_irq_domain_map,
.xlate = irq_domain_xlate_twocell,
};
int mt6358_irq_init(struct mt6397_chip *chip)
{
int i, j, ret;
struct pmic_irq_data *irqd;
irqd = devm_kzalloc(chip->dev, sizeof(*irqd), GFP_KERNEL);
if (!irqd)
return -ENOMEM;
chip->irq_data = irqd;
mutex_init(&chip->irqlock);
irqd->top_int_status_reg = MT6358_TOP_INT_STATUS0;
irqd->num_pmic_irqs = MT6358_IRQ_NR;
irqd->num_top = ARRAY_SIZE(mt6358_ints);
irqd->enable_hwirq = devm_kcalloc(chip->dev,
irqd->num_pmic_irqs,
sizeof(*irqd->enable_hwirq),
GFP_KERNEL);
if (!irqd->enable_hwirq)
return -ENOMEM;
irqd->cache_hwirq = devm_kcalloc(chip->dev,
irqd->num_pmic_irqs,
sizeof(*irqd->cache_hwirq),
GFP_KERNEL);
if (!irqd->cache_hwirq)
return -ENOMEM;
/* Disable all interrupts for initializing */
for (i = 0; i < irqd->num_top; i++) {
for (j = 0; j < mt6358_ints[i].num_int_regs; j++)
regmap_write(chip->regmap,
mt6358_ints[i].en_reg +
mt6358_ints[i].en_reg_shift * j, 0);
}
chip->irq_domain = irq_domain_add_linear(chip->dev->of_node,
irqd->num_pmic_irqs,
&mt6358_irq_domain_ops, chip);
if (!chip->irq_domain) {
dev_err(chip->dev, "Could not create IRQ domain\n");
return -ENODEV;
}
ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL,
mt6358_irq_handler, IRQF_ONESHOT,
mt6358_irq_chip.name, chip);
if (ret) {
dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n",
chip->irq, ret);
return ret;
}
enable_irq_wake(chip->irq);
return ret;
}

Переглянути файл

@@ -12,13 +12,18 @@
#include <linux/regmap.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mt6323/core.h>
#include <linux/mfd/mt6358/core.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6323/registers.h>
#include <linux/mfd/mt6358/registers.h>
#include <linux/mfd/mt6397/registers.h>
#define MT6323_RTC_BASE 0x8000
#define MT6323_RTC_SIZE 0x40
#define MT6358_RTC_BASE 0x0588
#define MT6358_RTC_SIZE 0x3c
#define MT6397_RTC_BASE 0xe000
#define MT6397_RTC_SIZE 0x3e
@@ -30,6 +35,11 @@ static const struct resource mt6323_rtc_resources[] = {
DEFINE_RES_IRQ(MT6323_IRQ_STATUS_RTC),
};
static const struct resource mt6358_rtc_resources[] = {
DEFINE_RES_MEM(MT6358_RTC_BASE, MT6358_RTC_SIZE),
DEFINE_RES_IRQ(MT6358_IRQ_RTC),
};
static const struct resource mt6397_rtc_resources[] = {
DEFINE_RES_MEM(MT6397_RTC_BASE, MT6397_RTC_SIZE),
DEFINE_RES_IRQ(MT6397_IRQ_RTC),
@@ -74,6 +84,21 @@ static const struct mfd_cell mt6323_devs[] = {
},
};
static const struct mfd_cell mt6358_devs[] = {
{
.name = "mt6358-regulator",
.of_compatible = "mediatek,mt6358-regulator"
}, {
.name = "mt6358-rtc",
.num_resources = ARRAY_SIZE(mt6358_rtc_resources),
.resources = mt6358_rtc_resources,
.of_compatible = "mediatek,mt6358-rtc",
}, {
.name = "mt6358-sound",
.of_compatible = "mediatek,mt6358-sound"
},
};
static const struct mfd_cell mt6397_devs[] = {
{
.name = "mt6397-rtc",
@@ -100,54 +125,42 @@ static const struct mfd_cell mt6397_devs[] = {
}
};
#ifdef CONFIG_PM_SLEEP
static int mt6397_irq_suspend(struct device *dev)
{
struct mt6397_chip *chip = dev_get_drvdata(dev);
regmap_write(chip->regmap, chip->int_con[0], chip->wake_mask[0]);
regmap_write(chip->regmap, chip->int_con[1], chip->wake_mask[1]);
enable_irq_wake(chip->irq);
return 0;
}
static int mt6397_irq_resume(struct device *dev)
{
struct mt6397_chip *chip = dev_get_drvdata(dev);
regmap_write(chip->regmap, chip->int_con[0], chip->irq_masks_cur[0]);
regmap_write(chip->regmap, chip->int_con[1], chip->irq_masks_cur[1]);
disable_irq_wake(chip->irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend,
mt6397_irq_resume);
struct chip_data {
u32 cid_addr;
u32 cid_shift;
const struct mfd_cell *cells;
int cell_size;
int (*irq_init)(struct mt6397_chip *chip);
};
static const struct chip_data mt6323_core = {
.cid_addr = MT6323_CID,
.cid_shift = 0,
.cells = mt6323_devs,
.cell_size = ARRAY_SIZE(mt6323_devs),
.irq_init = mt6397_irq_init,
};
static const struct chip_data mt6358_core = {
.cid_addr = MT6358_SWCID,
.cid_shift = 8,
.cells = mt6358_devs,
.cell_size = ARRAY_SIZE(mt6358_devs),
.irq_init = mt6358_irq_init,
};
static const struct chip_data mt6397_core = {
.cid_addr = MT6397_CID,
.cid_shift = 0,
.cells = mt6397_devs,
.cell_size = ARRAY_SIZE(mt6397_devs),
.irq_init = mt6397_irq_init,
};
static int mt6397_probe(struct platform_device *pdev)
{
int ret;
unsigned int id;
unsigned int id = 0;
struct mt6397_chip *pmic;
const struct chip_data *pmic_core;
@@ -183,29 +196,13 @@ static int mt6397_probe(struct platform_device *pdev)
if (pmic->irq <= 0)
return pmic->irq;
ret = mt6397_irq_init(pmic);
ret = pmic_core->irq_init(pmic);
if (ret)
return ret;
switch (pmic->chip_id) {
case MT6323_CHIP_ID:
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
mt6323_devs, ARRAY_SIZE(mt6323_devs),
NULL, 0, pmic->irq_domain);
break;
case MT6391_CHIP_ID:
case MT6397_CHIP_ID:
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
mt6397_devs, ARRAY_SIZE(mt6397_devs),
NULL, 0, pmic->irq_domain);
break;
default:
dev_err(&pdev->dev, "unsupported chip: %d\n", pmic->chip_id);
return -ENODEV;
}
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
pmic_core->cells, pmic_core->cell_size,
NULL, 0, pmic->irq_domain);
if (ret) {
irq_domain_remove(pmic->irq_domain);
dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
@@ -218,6 +215,9 @@ static const struct of_device_id mt6397_of_match[] = {
{
.compatible = "mediatek,mt6323",
.data = &mt6323_core,
}, {
.compatible = "mediatek,mt6358",
.data = &mt6358_core,
}, {
.compatible = "mediatek,mt6397",
.data = &mt6397_core,
@@ -238,7 +238,6 @@ static struct platform_driver mt6397_driver = {
.driver = {
.name = "mt6397",
.of_match_table = of_match_ptr(mt6397_of_match),
.pm = &mt6397_pm_ops,
},
.id_table = mt6397_id,
};

Переглянути файл

@@ -9,6 +9,7 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/mfd/mt6323/core.h>
#include <linux/mfd/mt6323/registers.h>
#include <linux/mfd/mt6397/core.h>
@@ -81,7 +82,7 @@ static struct irq_chip mt6397_irq_chip = {
static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
int irqbase)
{
unsigned int status;
unsigned int status = 0;
int i, irq, ret;
ret = regmap_read(mt6397->regmap, reg, &status);
@@ -128,6 +129,36 @@ static const struct irq_domain_ops mt6397_irq_domain_ops = {
.map = mt6397_irq_domain_map,
};
static int mt6397_irq_pm_notifier(struct notifier_block *notifier,
unsigned long pm_event, void *unused)
{
struct mt6397_chip *chip =
container_of(notifier, struct mt6397_chip, pm_nb);
switch (pm_event) {
case PM_SUSPEND_PREPARE:
regmap_write(chip->regmap,
chip->int_con[0], chip->wake_mask[0]);
regmap_write(chip->regmap,
chip->int_con[1], chip->wake_mask[1]);
enable_irq_wake(chip->irq);
break;
case PM_POST_SUSPEND:
regmap_write(chip->regmap,
chip->int_con[0], chip->irq_masks_cur[0]);
regmap_write(chip->regmap,
chip->int_con[1], chip->irq_masks_cur[1]);
disable_irq_wake(chip->irq);
break;
default:
break;
}
return NOTIFY_DONE;
}
int mt6397_irq_init(struct mt6397_chip *chip)
{
int ret;
@@ -159,6 +190,7 @@ int mt6397_irq_init(struct mt6397_chip *chip)
regmap_write(chip->regmap, chip->int_con[0], 0x0);
regmap_write(chip->regmap, chip->int_con[1], 0x0);
chip->pm_nb.notifier_call = mt6397_irq_pm_notifier;
chip->irq_domain = irq_domain_add_linear(chip->dev->of_node,
MT6397_IRQ_NR,
&mt6397_irq_domain_ops,
@@ -177,5 +209,6 @@ int mt6397_irq_init(struct mt6397_chip *chip)
return ret;
}
register_pm_notifier(&chip->pm_nb);
return 0;
}