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
Цей коміт міститься в:
@@ -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
Звичайний файл
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, ®))
|
||||
return -EIO;
|
||||
gsc->fwver = reg;
|
||||
|
||||
regmap_read(gsc->regmap, GSC_FW_CRC, ®);
|
||||
gsc->fwcrc = reg;
|
||||
regmap_read(gsc->regmap, GSC_FW_CRC + 1, ®);
|
||||
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
Звичайний файл
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
Звичайний файл
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;
|
||||
}
|
||||
|
Посилання в новій задачі
Заблокувати користувача