mirror of
https://github.com/torvalds/linux.git
synced 2024-12-08 03:52:16 +00:00
f49f43371b
For the CRC PMIC we end up with multiple IRQ domains with the same fwnode. One for the irqchip which demultiplexes the actual PMIC interrupt into interrupts for the various cells (known as the level 1 interrupts); And 2 more for the irqchips which are part of the crystal_cove_gpio and crystal_cove_charger cells. This leads to the following error being printed when CONFIG_GENERIC_IRQ_DEBUGFS is enabled: debugfs: File '\_SB.I2C7.PMIC' in directory 'domains' already present! Set the bus token of the main IRQ domain to DOMAIN_BUS_NEXUS to avoid this error, this also allows irq_find_matching_fwspec() to find the right domain if necessary. Note all 3 domain registering drivers need to set the IRQ domain bus token. This is necessary because the IRQ domain code defaults to creating the debugfs dir with just the fwnode name and then renames it when the bus token is set. So each one starts with the same default name and all 3 must be given a different name to avoid problems when one of the other drivers loads and starts with the same default name. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Lee Jones <lee.jones@linaro.org> Link: https://lore.kernel.org/r/20211225115509.94891-4-hdegoede@redhat.com
161 lines
3.9 KiB
C
161 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Intel SoC PMIC MFD Driver
|
|
*
|
|
* Copyright (C) 2013, 2014 Intel Corporation. All rights reserved.
|
|
*
|
|
* Author: Yang, Bin <bin.yang@intel.com>
|
|
* Author: Zhu, Lejun <lejun.zhu@linux.intel.com>
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/intel_soc_pmic.h>
|
|
#include <linux/platform_data/x86/soc.h>
|
|
#include <linux/pwm.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#include "intel_soc_pmic_core.h"
|
|
|
|
/* PWM consumed by the Intel GFX */
|
|
static struct pwm_lookup crc_pwm_lookup[] = {
|
|
PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL),
|
|
};
|
|
|
|
static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
|
|
const struct i2c_device_id *i2c_id)
|
|
{
|
|
struct device *dev = &i2c->dev;
|
|
struct intel_soc_pmic_config *config;
|
|
struct intel_soc_pmic *pmic;
|
|
int ret;
|
|
|
|
if (soc_intel_is_byt())
|
|
config = &intel_soc_pmic_config_byt_crc;
|
|
else
|
|
config = &intel_soc_pmic_config_cht_crc;
|
|
|
|
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
|
|
if (!pmic)
|
|
return -ENOMEM;
|
|
|
|
dev_set_drvdata(dev, pmic);
|
|
|
|
pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config);
|
|
if (IS_ERR(pmic->regmap))
|
|
return PTR_ERR(pmic->regmap);
|
|
|
|
pmic->irq = i2c->irq;
|
|
|
|
ret = regmap_add_irq_chip(pmic->regmap, pmic->irq,
|
|
config->irq_flags | IRQF_ONESHOT,
|
|
0, config->irq_chip,
|
|
&pmic->irq_chip_data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = enable_irq_wake(pmic->irq);
|
|
if (ret)
|
|
dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret);
|
|
|
|
/* Add lookup table for crc-pwm */
|
|
pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
|
|
|
|
/* To distuingish this domain from the GPIO/charger's irqchip domains */
|
|
irq_domain_update_bus_token(regmap_irq_get_domain(pmic->irq_chip_data),
|
|
DOMAIN_BUS_NEXUS);
|
|
|
|
ret = mfd_add_devices(dev, -1, config->cell_dev,
|
|
config->n_cell_devs, NULL, 0,
|
|
regmap_irq_get_domain(pmic->irq_chip_data));
|
|
if (ret)
|
|
goto err_del_irq_chip;
|
|
|
|
return 0;
|
|
|
|
err_del_irq_chip:
|
|
regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
|
|
return ret;
|
|
}
|
|
|
|
static int intel_soc_pmic_i2c_remove(struct i2c_client *i2c)
|
|
{
|
|
struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev);
|
|
|
|
regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
|
|
|
|
/* remove crc-pwm lookup table */
|
|
pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
|
|
|
|
mfd_remove_devices(&i2c->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void intel_soc_pmic_shutdown(struct i2c_client *i2c)
|
|
{
|
|
struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev);
|
|
|
|
disable_irq(pmic->irq);
|
|
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_PM_SLEEP)
|
|
static int intel_soc_pmic_suspend(struct device *dev)
|
|
{
|
|
struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
|
|
|
|
disable_irq(pmic->irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int intel_soc_pmic_resume(struct device *dev)
|
|
{
|
|
struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
|
|
|
|
enable_irq(pmic->irq);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend,
|
|
intel_soc_pmic_resume);
|
|
|
|
static const struct i2c_device_id intel_soc_pmic_i2c_id[] = {
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, intel_soc_pmic_i2c_id);
|
|
|
|
#if defined(CONFIG_ACPI)
|
|
static const struct acpi_device_id intel_soc_pmic_acpi_match[] = {
|
|
{ "INT33FD" },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, intel_soc_pmic_acpi_match);
|
|
#endif
|
|
|
|
static struct i2c_driver intel_soc_pmic_i2c_driver = {
|
|
.driver = {
|
|
.name = "intel_soc_pmic_i2c",
|
|
.pm = &intel_soc_pmic_pm_ops,
|
|
.acpi_match_table = ACPI_PTR(intel_soc_pmic_acpi_match),
|
|
},
|
|
.probe = intel_soc_pmic_i2c_probe,
|
|
.remove = intel_soc_pmic_i2c_remove,
|
|
.id_table = intel_soc_pmic_i2c_id,
|
|
.shutdown = intel_soc_pmic_shutdown,
|
|
};
|
|
|
|
module_i2c_driver(intel_soc_pmic_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>");
|
|
MODULE_AUTHOR("Zhu, Lejun <lejun.zhu@linux.intel.com>");
|