diff options
Diffstat (limited to 'drivers/mfd')
| -rw-r--r-- | drivers/mfd/Kconfig | 32 | ||||
| -rw-r--r-- | drivers/mfd/Makefile | 3 | ||||
| -rw-r--r-- | drivers/mfd/altera-sysmgr.c | 2 | ||||
| -rw-r--r-- | drivers/mfd/bcm2835-pm.c | 1 | ||||
| -rw-r--r-- | drivers/mfd/da9055-core.c | 2 | ||||
| -rw-r--r-- | drivers/mfd/da9063-i2c.c | 3 | ||||
| -rw-r--r-- | drivers/mfd/ls2k-bmc-core.c | 14 | ||||
| -rw-r--r-- | drivers/mfd/macsmc.c | 6 | ||||
| -rw-r--r-- | drivers/mfd/max77620.c | 15 | ||||
| -rw-r--r-- | drivers/mfd/mt6358-irq.c | 1 | ||||
| -rw-r--r-- | drivers/mfd/mt6397-irq.c | 1 | ||||
| -rw-r--r-- | drivers/mfd/pf1550.c | 367 | ||||
| -rw-r--r-- | drivers/mfd/qnap-mcu.c | 80 | ||||
| -rw-r--r-- | drivers/mfd/rohm-bd718x7.c | 9 | ||||
| -rw-r--r-- | drivers/mfd/sec-acpm.c | 23 | ||||
| -rw-r--r-- | drivers/mfd/sec-irq.c | 73 | ||||
| -rw-r--r-- | drivers/mfd/simple-mfd-i2c.c | 18 | ||||
| -rw-r--r-- | drivers/mfd/syscon.c | 2 | ||||
| -rw-r--r-- | drivers/mfd/tqmx86.c | 8 | ||||
| -rw-r--r-- | drivers/mfd/wl1273-core.c | 262 |
20 files changed, 591 insertions, 331 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6cec1858947b..aace5766b38a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -45,7 +45,8 @@ config MFD_ALTERA_A10SR config MFD_ALTERA_SYSMGR bool "Altera SOCFPGA System Manager" - depends on ARCH_INTEL_SOCFPGA && OF + depends on ARCH_INTEL_SOCFPGA || COMPILE_TEST + depends on OF select MFD_SYSCON help Select this to get System Manager support for all Altera branded @@ -605,6 +606,22 @@ config MFD_MX25_TSADC i.MX25 processors. They consist of a conversion queue for general purpose ADC and a queue for Touchscreens. +config MFD_PF1550 + tristate "NXP PF1550 PMIC Support" + depends on I2C=y && OF + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to add support for NXP PF1550. This is a companion Power + Management IC with regulators, onkey, and charger control on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + + This driver can also be built as a module and if so will be called + pf1550. + config MFD_HI6421_PMIC tristate "HiSilicon Hi6421 PMU/Codec IC" depends on OF @@ -879,7 +896,7 @@ config MFD_88PM886_PMIC config MFD_MAX5970 tristate "Maxim 5970/5978 power switch and monitor" - depends on I2C && OF + depends on I2C select MFD_SIMPLE_MFD_I2C help This driver controls a Maxim 5970/5978 switch via I2C bus. @@ -1258,7 +1275,6 @@ config MFD_SPACEMIT_P1 tristate "SpacemiT P1 PMIC" depends on ARCH_SPACEMIT || COMPILE_TEST depends on I2C - select I2C_K1 select MFD_SIMPLE_MFD_I2C help This option supports the I2C-based SpacemiT P1 PMIC, which @@ -1986,16 +2002,6 @@ config MENELAUS and other features that are often used in portable devices like cell phones and PDAs. -config MFD_WL1273_CORE - tristate "TI WL1273 FM radio" - depends on I2C - select MFD_CORE - default n - help - This is the core driver for the TI WL1273 FM radio. This MFD - driver connects the radio-wl1273 V4L2 module and the wl1273 - audio codec. - config MFD_LM3533 tristate "TI/National Semiconductor LM3533 Lighting Power chip" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 865e9f12faff..e75e8045c28a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -122,6 +122,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o +obj-$(CONFIG_MFD_PF1550) += pf1550.o + obj-$(CONFIG_MFD_NCT6694) += nct6694.o obj-$(CONFIG_MFD_CORE) += mfd-core.o @@ -205,7 +207,6 @@ obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o obj-$(CONFIG_MFD_VX855) += vx855.o -obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c index fb5f988e61f3..90c6902d537d 100644 --- a/drivers/mfd/altera-sysmgr.c +++ b/drivers/mfd/altera-sysmgr.c @@ -117,6 +117,8 @@ struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np, sysmgr = dev_get_drvdata(dev); + put_device(dev); + return sysmgr->regmap; } EXPORT_SYMBOL_GPL(altr_sysmgr_regmap_lookup_by_phandle); diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c index 3cb2b9423121..8bed59816e82 100644 --- a/drivers/mfd/bcm2835-pm.c +++ b/drivers/mfd/bcm2835-pm.c @@ -108,6 +108,7 @@ static const struct of_device_id bcm2835_pm_of_match[] = { { .compatible = "brcm,bcm2835-pm-wdt", }, { .compatible = "brcm,bcm2835-pm", }, { .compatible = "brcm,bcm2711-pm", }, + { .compatible = "brcm,bcm2712-pm", }, {}, }; MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c index 1f727ef60d63..158590ad37d4 100644 --- a/drivers/mfd/da9055-core.c +++ b/drivers/mfd/da9055-core.c @@ -387,7 +387,7 @@ int da9055_device_init(struct da9055 *da9055) return 0; err: - mfd_remove_devices(da9055->dev); + regmap_del_irq_chip(da9055->chip_irq, da9055->irq_data); return ret; } diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 1ec9ab56442d..a803b7440f09 100644 --- a/drivers/mfd/da9063-i2c.c +++ b/drivers/mfd/da9063-i2c.c @@ -469,6 +469,9 @@ static int da9063_i2c_probe(struct i2c_client *i2c) } } + /* Reserve our unused second address so userspace won't interfere */ + devm_i2c_new_dummy_device(&i2c->dev, i2c->adapter, i2c->addr + 1); + return da9063_device_init(da9063, i2c->irq); } diff --git a/drivers/mfd/ls2k-bmc-core.c b/drivers/mfd/ls2k-bmc-core.c index e162b3c7c9f8..e42f1de9e641 100644 --- a/drivers/mfd/ls2k-bmc-core.c +++ b/drivers/mfd/ls2k-bmc-core.c @@ -265,7 +265,7 @@ static int ls2k_bmc_recover_pci_data(void *data) if (!ls2k_bmc_bar0_addr_is_set(parent)) break; mdelay(1); - }; + } if (i == 0) return false; @@ -469,7 +469,7 @@ static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id) return ret; ddata = devm_kzalloc(&dev->dev, sizeof(*ddata), GFP_KERNEL); - if (IS_ERR(ddata)) { + if (!ddata) { ret = -ENOMEM; goto disable_pci; } @@ -495,9 +495,13 @@ static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id) goto disable_pci; } - return devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, - ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells), - &dev->resource[0], 0, NULL); + ret = devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, + ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells), + &dev->resource[0], 0, NULL); + if (ret) + goto disable_pci; + + return 0; disable_pci: pci_disable_device(dev); diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c index e6cdae221f1d..e3893e255ce5 100644 --- a/drivers/mfd/macsmc.c +++ b/drivers/mfd/macsmc.c @@ -173,7 +173,7 @@ int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size) } EXPORT_SYMBOL(apple_smc_read); -int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size) +int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size) { guard(mutex)(&smc->mutex); @@ -181,7 +181,7 @@ int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size) } EXPORT_SYMBOL(apple_smc_write); -int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize, +int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize, void *rbuf, size_t rsize) { guard(mutex)(&smc->mutex); @@ -239,7 +239,7 @@ int apple_smc_enter_atomic(struct apple_smc *smc) } EXPORT_SYMBOL(apple_smc_enter_atomic); -int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size) +int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size) { guard(spinlock_irqsave)(&smc->lock); u8 result; diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index 21d2ab3db254..3af2974b3023 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -254,7 +254,7 @@ static int max77620_irq_global_unmask(void *irq_drv_data) return ret; } -static struct regmap_irq_chip max77620_top_irq_chip = { +static const struct regmap_irq_chip max77620_top_irq_chip = { .name = "max77620-top", .irqs = max77620_top_irqs, .num_irqs = ARRAY_SIZE(max77620_top_irqs), @@ -498,6 +498,7 @@ static int max77620_probe(struct i2c_client *client) const struct i2c_device_id *id = i2c_client_get_device_id(client); const struct regmap_config *rmap_config; struct max77620_chip *chip; + struct regmap_irq_chip *chip_desc; const struct mfd_cell *mfd_cells; int n_mfd_cells; bool pm_off; @@ -508,6 +509,14 @@ static int max77620_probe(struct i2c_client *client) return -ENOMEM; i2c_set_clientdata(client, chip); + + chip_desc = devm_kmemdup(&client->dev, &max77620_top_irq_chip, + sizeof(max77620_top_irq_chip), + GFP_KERNEL); + if (!chip_desc) + return -ENOMEM; + chip_desc->irq_drv_data = chip; + chip->dev = &client->dev; chip->chip_irq = client->irq; chip->chip_id = (enum max77620_chip_id)id->driver_data; @@ -544,11 +553,9 @@ static int max77620_probe(struct i2c_client *client) if (ret < 0) return ret; - max77620_top_irq_chip.irq_drv_data = chip; ret = devm_regmap_add_irq_chip(chip->dev, chip->rmap, client->irq, IRQF_ONESHOT | IRQF_SHARED, 0, - &max77620_top_irq_chip, - &chip->top_irq_data); + chip_desc, &chip->top_irq_data); if (ret < 0) { dev_err(chip->dev, "Failed to add regmap irq: %d\n", ret); return ret; diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c index f467b00d2366..74cf20843044 100644 --- a/drivers/mfd/mt6358-irq.c +++ b/drivers/mfd/mt6358-irq.c @@ -285,6 +285,7 @@ int mt6358_irq_init(struct mt6397_chip *chip) if (ret) { dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n", chip->irq, ret); + irq_domain_remove(chip->irq_domain); return ret; } diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c index 0e463026c5a9..5d2e5459f744 100644 --- a/drivers/mfd/mt6397-irq.c +++ b/drivers/mfd/mt6397-irq.c @@ -229,6 +229,7 @@ int mt6397_irq_init(struct mt6397_chip *chip) if (ret) { dev_err(chip->dev, "failed to register irq=%d; err: %d\n", chip->irq, ret); + irq_domain_remove(chip->irq_domain); return ret; } diff --git a/drivers/mfd/pf1550.c b/drivers/mfd/pf1550.c new file mode 100644 index 000000000000..c4f567c05564 --- /dev/null +++ b/drivers/mfd/pf1550.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Core driver for the PF1550 + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Robin Gong <yibin.gong@freescale.com> + * + * Portions Copyright (c) 2025 Savoir-faire Linux Inc. + * Samuel Kayode <samuel.kayode@savoirfairelinux.com> + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/pf1550.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +static const struct regmap_config pf1550_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PF1550_PMIC_REG_END, +}; + +static const struct regmap_irq pf1550_irqs[] = { + REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG), + REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR), + REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY), +}; + +static const struct regmap_irq_chip pf1550_irq_chip = { + .name = "pf1550", + .status_base = PF1550_PMIC_REG_INT_CATEGORY, + .init_ack_masked = 1, + .num_regs = 1, + .irqs = pf1550_irqs, + .num_irqs = ARRAY_SIZE(pf1550_irqs), +}; + +static const struct regmap_irq pf1550_regulator_irqs[] = { + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110), + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125), +}; + +static const struct regmap_irq_chip pf1550_regulator_irq_chip = { + .name = "pf1550-regulator", + .status_base = PF1550_PMIC_REG_SW_INT_STAT0, + .ack_base = PF1550_PMIC_REG_SW_INT_STAT0, + .mask_base = PF1550_PMIC_REG_SW_INT_MASK0, + .use_ack = 1, + .init_ack_masked = 1, + .num_regs = 25, + .irqs = pf1550_regulator_irqs, + .num_irqs = ARRAY_SIZE(pf1550_regulator_irqs), +}; + +static const struct resource regulator_resources[] = { + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110), + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125), +}; + +static const struct regmap_irq pf1550_onkey_irqs[] = { + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI), + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI), + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI), + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI), + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI), + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI), +}; + +static const struct regmap_irq_chip pf1550_onkey_irq_chip = { + .name = "pf1550-onkey", + .status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0, + .ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0, + .mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0, + .use_ack = 1, + .init_ack_masked = 1, + .num_regs = 1, + .irqs = pf1550_onkey_irqs, + .num_irqs = ARRAY_SIZE(pf1550_onkey_irqs), +}; + +static const struct resource onkey_resources[] = { + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI), + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI), + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI), + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI), + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI), + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI), +}; + +static const struct regmap_irq pf1550_charger_irqs[] = { + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI), + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI), + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI), + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI), + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI), +}; + +static const struct regmap_irq_chip pf1550_charger_irq_chip = { + .name = "pf1550-charger", + .status_base = PF1550_CHARG_REG_CHG_INT, + .ack_base = PF1550_CHARG_REG_CHG_INT, + .mask_base = PF1550_CHARG_REG_CHG_INT_MASK, + .use_ack = 1, + .init_ack_masked = 1, + .num_regs = 1, + .irqs = pf1550_charger_irqs, + .num_irqs = ARRAY_SIZE(pf1550_charger_irqs), +}; + +static const struct resource charger_resources[] = { + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI), + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI), + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI), + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI), + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI), +}; + +static const struct mfd_cell pf1550_regulator_cell = { + .name = "pf1550-regulator", + .num_resources = ARRAY_SIZE(regulator_resources), + .resources = regulator_resources, +}; + +static const struct mfd_cell pf1550_onkey_cell = { + .name = "pf1550-onkey", + .num_resources = ARRAY_SIZE(onkey_resources), + .resources = onkey_resources, +}; + +static const struct mfd_cell pf1550_charger_cell = { + .name = "pf1550-charger", + .num_resources = ARRAY_SIZE(charger_resources), + .resources = charger_resources, +}; + +/* + * The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a + * configuration of the PMIC in a One-Time Programmable (OTP) memory. + * This memory is accessed indirectly by writing valid keys to specific + * registers of the PMIC. To read the OTP memory after writing the valid keys, + * the OTP register address to be read is written to pf1550 register 0xc4 and + * its value read from pf1550 register 0xc5. + */ +static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index, + unsigned int *val) +{ + int ret = 0; + + ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY, PF1550_OTP_PMIC_KEY); + if (ret) + goto read_err; + + ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2, PF1550_OTP_CHGR_KEY); + if (ret) + goto read_err; + + ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3, PF1550_OTP_TEST_KEY); + if (ret) + goto read_err; + + ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index); + if (ret) + goto read_err; + + ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val); + if (ret) + goto read_err; + + return 0; + +read_err: + return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n", index); +} + +static int pf1550_i2c_probe(struct i2c_client *i2c) +{ + const struct mfd_cell *regulator = &pf1550_regulator_cell; + const struct mfd_cell *charger = &pf1550_charger_cell; + const struct mfd_cell *onkey = &pf1550_onkey_cell; + unsigned int reg_data = 0, otp_data = 0; + struct pf1550_ddata *pf1550; + struct irq_domain *domain; + int irq, ret = 0; + + pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL); + if (!pf1550) + return -ENOMEM; + + i2c_set_clientdata(i2c, pf1550); + pf1550->dev = &i2c->dev; + pf1550->irq = i2c->irq; + + pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config); + if (IS_ERR(pf1550->regmap)) + return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap), + "failed to allocate register map\n"); + + ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, ®_data); + if (ret < 0) + return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n"); + if (reg_data != PF1550_DEVICE_ID) + return dev_err_probe(pf1550->dev, -ENODEV, "invalid device ID: 0x%02x\n", reg_data); + + /* Regulator DVS for SW2 */ + ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data); + if (ret) + return ret; + + /* When clear, DVS should be enabled */ + if (!(otp_data & OTP_SW2_DVS_ENB)) + pf1550->dvs2_enable = true; + + /* Regulator DVS for SW1 */ + ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data); + if (ret) + return ret; + + if (!(otp_data & OTP_SW1_DVS_ENB)) + pf1550->dvs1_enable = true; + + /* Add top level interrupts */ + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, + 0, &pf1550_irq_chip, + &pf1550->irq_data); + if (ret) + return ret; + + /* Add regulator */ + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR); + if (irq < 0) + return dev_err_probe(pf1550->dev, irq, + "Failed to get parent vIRQ(%d) for chip %s\n", + PF1550_IRQ_REGULATOR, pf1550_irq_chip.name); + + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &pf1550_regulator_irq_chip, + &pf1550->irq_data_regulator); + if (ret) + return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n", + pf1550_regulator_irq_chip.name); + + domain = regmap_irq_get_domain(pf1550->irq_data_regulator); + + ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator, 1, NULL, 0, domain); + if (ret) + return ret; + + /* Add onkey */ + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY); + if (irq < 0) + return dev_err_probe(pf1550->dev, irq, + "Failed to get parent vIRQ(%d) for chip %s\n", + PF1550_IRQ_ONKEY, pf1550_irq_chip.name); + + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &pf1550_onkey_irq_chip, + &pf1550->irq_data_onkey); + if (ret) + return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n", + pf1550_onkey_irq_chip.name); + + domain = regmap_irq_get_domain(pf1550->irq_data_onkey); + + ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1, NULL, 0, domain); + if (ret) + return ret; + + /* Add battery charger */ + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG); + if (irq < 0) + return dev_err_probe(pf1550->dev, irq, + "Failed to get parent vIRQ(%d) for chip %s\n", + PF1550_IRQ_CHG, pf1550_irq_chip.name); + + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &pf1550_charger_irq_chip, + &pf1550->irq_data_charger); + if (ret) + return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n", + pf1550_charger_irq_chip.name); + + domain = regmap_irq_get_domain(pf1550->irq_data_charger); + + return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger, 1, NULL, 0, domain); +} + +static int pf1550_suspend(struct device *dev) +{ + struct pf1550_ddata *pf1550 = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + enable_irq_wake(pf1550->irq); + disable_irq(pf1550->irq); + } + + return 0; +} + +static int pf1550_resume(struct device *dev) +{ + struct pf1550_ddata *pf1550 = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + disable_irq_wake(pf1550->irq); + enable_irq(pf1550->irq); + } + + return 0; +} +static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume); + +static const struct i2c_device_id pf1550_i2c_id[] = { + { "pf1550" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id); + +static const struct of_device_id pf1550_dt_match[] = { + { .compatible = "nxp,pf1550" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pf1550_dt_match); + +static struct i2c_driver pf1550_i2c_driver = { + .driver = { + .name = "pf1550", + .pm = pm_sleep_ptr(&pf1550_pm), + .of_match_table = pf1550_dt_match, + }, + .probe = pf1550_i2c_probe, + .id_table = pf1550_i2c_id, +}; +module_i2c_driver(pf1550_i2c_driver); + +MODULE_DESCRIPTION("NXP PF1550 core driver"); +MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/qnap-mcu.c b/drivers/mfd/qnap-mcu.c index 4ec1f4cf902f..f81c69f22254 100644 --- a/drivers/mfd/qnap-mcu.c +++ b/drivers/mfd/qnap-mcu.c @@ -19,6 +19,7 @@ /* The longest command found so far is 5 bytes long */ #define QNAP_MCU_MAX_CMD_SIZE 5 #define QNAP_MCU_MAX_DATA_SIZE 36 +#define QNAP_MCU_ERROR_SIZE 2 #define QNAP_MCU_CHECKSUM_SIZE 1 #define QNAP_MCU_RX_BUFFER_SIZE \ @@ -78,6 +79,13 @@ static u8 qnap_mcu_csum(const u8 *buf, size_t size) return csum; } +static bool qnap_mcu_verify_checksum(const u8 *buf, size_t size) +{ + u8 crc = qnap_mcu_csum(buf, size - QNAP_MCU_CHECKSUM_SIZE); + + return crc == buf[size - QNAP_MCU_CHECKSUM_SIZE]; +} + static int qnap_mcu_write(struct qnap_mcu *mcu, const u8 *data, u8 data_size) { unsigned char tx[QNAP_MCU_TX_BUFFER_SIZE]; @@ -96,6 +104,48 @@ static int qnap_mcu_write(struct qnap_mcu *mcu, const u8 *data, u8 data_size) return serdev_device_write(mcu->serdev, tx, length, HZ); } +static bool qnap_mcu_is_error_msg(size_t size) +{ + return (size == QNAP_MCU_ERROR_SIZE + QNAP_MCU_CHECKSUM_SIZE); +} + +static bool qnap_mcu_reply_is_generic_error(unsigned char *buf, size_t size) +{ + if (!qnap_mcu_is_error_msg(size)) + return false; + + if (buf[0] == '@' && buf[1] == '9') + return true; + + return false; +} + +static bool qnap_mcu_reply_is_checksum_error(unsigned char *buf, size_t size) +{ + if (!qnap_mcu_is_error_msg(size)) + return false; + + if (buf[0] == '@' && buf[1] == '8') + return true; + + return false; +} + +static bool qnap_mcu_reply_is_any_error(struct qnap_mcu *mcu, unsigned char *buf, size_t size) +{ + if (qnap_mcu_reply_is_generic_error(buf, size)) { + dev_err(&mcu->serdev->dev, "Controller sent generic error response\n"); + return true; + } + + if (qnap_mcu_reply_is_checksum_error(buf, size)) { + dev_err(&mcu->serdev->dev, "Controller received invalid checksum for the command\n"); + return true; + } + + return false; +} + static size_t qnap_mcu_receive_buf(struct serdev_device *serdev, const u8 *buf, size_t size) { struct device *dev = &serdev->dev; @@ -130,6 +180,24 @@ static size_t qnap_mcu_receive_buf(struct serdev_device *serdev, const u8 *buf, } /* + * We received everything the uart had to offer for now. + * This could mean that either the uart will send more in a 2nd + * receive run, or that the MCU cut the reply short because it + * sent an error code instead of the expected reply. + * + * So check if the received data has the correct size for an error + * reply and if it matches, is an actual error code. + */ + if (qnap_mcu_is_error_msg(reply->received) && + qnap_mcu_verify_checksum(reply->data, reply->received) && + qnap_mcu_reply_is_any_error(mcu, reply->data, reply->received)) { + /* The reply was an error code, we're done */ + reply->length = 0; + + complete(&reply->done); + } + + /* * The only way to get out of the above loop and end up here * is through consuming all of the supplied data, so here we * report that we processed it all. @@ -150,7 +218,6 @@ int qnap_mcu_exec(struct qnap_mcu *mcu, size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE; struct qnap_mcu_reply *reply = &mcu->reply; int ret = 0; - u8 crc; if (length > sizeof(rx)) { dev_err(&mcu->serdev->dev, "expected data too big for receive buffer"); @@ -175,12 +242,14 @@ int qnap_mcu_exec(struct qnap_mcu *mcu, return -ETIMEDOUT; } - crc = qnap_mcu_csum(rx, reply_data_size); - if (crc != rx[reply_data_size]) { - dev_err(&mcu->serdev->dev, "Invalid Checksum received\n"); - return -EIO; + if (!qnap_mcu_verify_checksum(rx, reply->received)) { + dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n"); + return -EPROTO; } + if (qnap_mcu_reply_is_any_error(mcu, rx, reply->received)) + return -EPROTO; + memcpy(reply_data, rx, reply_data_size); return 0; @@ -264,6 +333,7 @@ static const struct qnap_mcu_variant qnap_ts433_mcu = { }; static struct mfd_cell qnap_mcu_cells[] = { + { .name = "qnap-mcu-eeprom", }, { .name = "qnap-mcu-input", }, { .name = "qnap-mcu-leds", }, { .name = "qnap-mcu-hwmon", } diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c index 25e494a93d48..ff714fd4f54d 100644 --- a/drivers/mfd/rohm-bd718x7.c +++ b/drivers/mfd/rohm-bd718x7.c @@ -72,14 +72,13 @@ static const struct regmap_irq_chip bd718xx_irq_chip = { .init_ack_masked = true, }; -static const struct regmap_range pmic_status_range = { - .range_min = BD718XX_REG_IRQ, - .range_max = BD718XX_REG_POW_STATE, +static const struct regmap_range pmic_status_range[] = { + regmap_reg_range(BD718XX_REG_IRQ, BD718XX_REG_POW_STATE), }; static const struct regmap_access_table volatile_regs = { - .yes_ranges = &pmic_status_range, - .n_yes_ranges = 1, + .yes_ranges = &pmic_status_range[0], + .n_yes_ranges = ARRAY_SIZE(pmic_status_range), }; static const struct regmap_config bd718xx_regmap_config = { diff --git a/drivers/mfd/sec-acpm.c b/drivers/mfd/sec-acpm.c index 8b31c816d65b..36622069a788 100644 --- a/drivers/mfd/sec-acpm.c +++ b/drivers/mfd/sec-acpm.c @@ -325,11 +325,6 @@ static struct regmap *sec_pmic_acpm_regmap_init(struct device *dev, return regmap; } -static void sec_pmic_acpm_mask_common_irqs(void *regmap_common) -{ - regmap_write(regmap_common, S2MPG10_COMMON_INT_MASK, S2MPG10_COMMON_INT_SRC); -} - static int sec_pmic_acpm_probe(struct platform_device *pdev) { struct regmap *regmap_common, *regmap_pmic, *regmap; @@ -360,15 +355,10 @@ static int sec_pmic_acpm_probe(struct platform_device *pdev) shared_ctx->speedy_channel = pdata->speedy_channel; regmap_common = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_COMMON, - pdata->regmap_cfg_common, false); + pdata->regmap_cfg_common, true); if (IS_ERR(regmap_common)) return PTR_ERR(regmap_common); - /* Mask all interrupts from 'common' block, until successful init */ - ret = regmap_write(regmap_common, S2MPG10_COMMON_INT_MASK, S2MPG10_COMMON_INT_SRC); - if (ret) - return dev_err_probe(dev, ret, "failed to mask common block interrupts\n"); - regmap_pmic = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_PMIC, pdata->regmap_cfg_pmic, false); if (IS_ERR(regmap_pmic)) @@ -391,17 +381,6 @@ static int sec_pmic_acpm_probe(struct platform_device *pdev) if (device_property_read_bool(dev, "wakeup-source")) devm_device_init_wakeup(dev); - /* Unmask PMIC interrupt from 'common' block, now that everything is in place. */ - ret = regmap_clear_bits(regmap_common, S2MPG10_COMMON_INT_MASK, - S2MPG10_COMMON_INT_SRC_PMIC); - if (ret) - return dev_err_probe(dev, ret, "failed to unmask PMIC interrupt\n"); - - /* Mask all interrupts from 'common' block on shutdown */ - ret = devm_add_action_or_reset(dev, sec_pmic_acpm_mask_common_irqs, regmap_common); - if (ret) - return ret; - return 0; } diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index c5c80b1ba104..74ac70002d1f 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -20,6 +20,12 @@ #include "sec-core.h" static const struct regmap_irq s2mpg10_irqs[] = { + REGMAP_IRQ_REG(S2MPG10_COMMON_IRQ_PMIC, 0, S2MPG10_COMMON_INT_SRC_PMIC), + /* No documentation or other reference for remaining bits */ + REGMAP_IRQ_REG(S2MPG10_COMMON_IRQ_UNUSED, 0, GENMASK(7, 1)), +}; + +static const struct regmap_irq s2mpg10_pmic_irqs[] = { REGMAP_IRQ_REG(S2MPG10_IRQ_PWRONF, 0, S2MPG10_IRQ_PWRONF_MASK), REGMAP_IRQ_REG(S2MPG10_IRQ_PWRONR, 0, S2MPG10_IRQ_PWRONR_MASK), REGMAP_IRQ_REG(S2MPG10_IRQ_JIGONBF, 0, S2MPG10_IRQ_JIGONBF_MASK), @@ -183,11 +189,20 @@ static const struct regmap_irq s5m8767_irqs[] = { /* All S2MPG10 interrupt sources are read-only and don't require clearing */ static const struct regmap_irq_chip s2mpg10_irq_chip = { .name = "s2mpg10", + .status_base = S2MPG10_COMMON_INT, + .mask_base = S2MPG10_COMMON_INT_MASK, + .num_regs = 1, .irqs = s2mpg10_irqs, .num_irqs = ARRAY_SIZE(s2mpg10_irqs), - .num_regs = 6, +}; + +static const struct regmap_irq_chip s2mpg10_irq_chip_pmic = { + .name = "s2mpg10-pmic", .status_base = S2MPG10_PMIC_INT1, .mask_base = S2MPG10_PMIC_INT1M, + .num_regs = 6, + .irqs = s2mpg10_pmic_irqs, + .num_irqs = ARRAY_SIZE(s2mpg10_pmic_irqs), }; static const struct regmap_irq_chip s2mps11_irq_chip = { @@ -253,6 +268,59 @@ static const struct regmap_irq_chip s5m8767_irq_chip = { .ack_base = S5M8767_REG_INT1, }; +static int s2mpg1x_add_chained_irq_chip(struct device *dev, struct regmap *regmap, int pirq, + struct regmap_irq_chip_data *parent, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) +{ + int irq, ret; + + irq = regmap_irq_get_virq(parent, pirq); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", pirq, + chip->name); + + ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT | IRQF_SHARED, 0, chip, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name); + + return 0; +} + +static int sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic) +{ + const struct regmap_irq_chip *irq_chip, *chained_irq_chip; + struct regmap_irq_chip_data *irq_data; + struct regmap *regmap_common; + int chained_pirq; + int ret; + + switch (sec_pmic->device_type) { + case S2MPG10: + irq_chip = &s2mpg10_irq_chip; + chained_irq_chip = &s2mpg10_irq_chip_pmic; + chained_pirq = S2MPG10_COMMON_IRQ_PMIC; + break; + default: + return dev_err_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n", + sec_pmic->device_type); + } + + regmap_common = dev_get_regmap(sec_pmic->dev, "common"); + if (!regmap_common) + return dev_err_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n", + sec_pmic->device_type); + + ret = devm_regmap_add_irq_chip(sec_pmic->dev, regmap_common, sec_pmic->irq, IRQF_ONESHOT, 0, + irq_chip, &irq_data); + if (ret) + return dev_err_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n", + irq_chip->name); + + return s2mpg1x_add_chained_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic, chained_pirq, + irq_data, chained_irq_chip, &sec_pmic->irq_data); +} + int sec_irq_init(struct sec_pmic_dev *sec_pmic) { const struct regmap_irq_chip *sec_irq_chip; @@ -268,8 +336,7 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) sec_irq_chip = &s2mps14_irq_chip; break; case S2MPG10: - sec_irq_chip = &s2mpg10_irq_chip; - break; + return sec_irq_init_s2mpg1x(sec_pmic); case S2MPS11X: sec_irq_chip = &s2mps11_irq_chip; break; diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c index 0a607a1e3ca1..8b751d8e3b5a 100644 --- a/drivers/mfd/simple-mfd-i2c.c +++ b/drivers/mfd/simple-mfd-i2c.c @@ -15,12 +15,18 @@ * will be subsequently registered. */ +#include <linux/array_size.h> +#include <linux/dev_printk.h> +#include <linux/err.h> #include <linux/i2c.h> -#include <linux/kernel.h> #include <linux/mfd/core.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> +#include <linux/stddef.h> #include "simple-mfd-i2c.h" @@ -114,11 +120,11 @@ static const struct of_device_id simple_mfd_i2c_of_match[] = { { .compatible = "fsl,lx2160aqds-fpga" }, { .compatible = "fsl,lx2160ardb-fpga" }, { .compatible = "kontron,sl28cpld" }, - { .compatible = "maxim,max5970", .data = &maxim_max5970}, - { .compatible = "maxim,max5978", .data = &maxim_max5970}, - { .compatible = "maxim,max77705-battery", .data = &maxim_mon_max77705}, - { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a}, - { .compatible = "spacemit,p1", .data = &spacemit_p1, }, + { .compatible = "maxim,max5970", .data = &maxim_max5970 }, + { .compatible = "maxim,max5978", .data = &maxim_max5970 }, + { .compatible = "maxim,max77705-battery", .data = &maxim_mon_max77705 }, + { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a }, + { .compatible = "spacemit,p1", .data = &spacemit_p1 }, {} }; MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match); diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index ae71a2710bed..e5d5def594f6 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -183,7 +183,7 @@ static struct regmap *device_node_get_regmap(struct device_node *np, if (create_regmap) syscon = of_syscon_register(np, check_res); else - syscon = ERR_PTR(-EINVAL); + syscon = ERR_PTR(-EPROBE_DEFER); } mutex_unlock(&syscon_list_lock); diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c index 1cba3b67b0fb..1c2fe3f91238 100644 --- a/drivers/mfd/tqmx86.c +++ b/drivers/mfd/tqmx86.c @@ -43,6 +43,8 @@ #define TQMX86_REG_BOARD_ID_E40C2 15 #define TQMX86_REG_BOARD_ID_130UC 16 #define TQMX86_REG_BOARD_ID_E41S 19 +#define TQMX86_REG_BOARD_ID_CU1_HPCM 24 +#define TQMX86_REG_BOARD_ID_CU2_HPCM 25 #define TQMX86_REG_BOARD_REV 0x01 #define TQMX86_REG_IO_EXT_INT 0x06 #define TQMX86_REG_IO_EXT_INT_NONE 0 @@ -165,6 +167,10 @@ static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc) return "TQMx130UC"; case TQMX86_REG_BOARD_ID_E41S: return "TQMxE41S"; + case TQMX86_REG_BOARD_ID_CU1_HPCM: + return "TQMxCU1-HPCM"; + case TQMX86_REG_BOARD_ID_CU2_HPCM: + return "TQMxCU2-HPCM"; default: return "Unknown"; } @@ -185,6 +191,8 @@ static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id) case TQMX86_REG_BOARD_ID_E40C2: case TQMX86_REG_BOARD_ID_130UC: case TQMX86_REG_BOARD_ID_E41S: + case TQMX86_REG_BOARD_ID_CU1_HPCM: + case TQMX86_REG_BOARD_ID_CU2_HPCM: return 24000; case TQMX86_REG_BOARD_ID_E39MS: case TQMX86_REG_BOARD_ID_E39C1: diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c deleted file mode 100644 index 2f185e93318e..000000000000 --- a/drivers/mfd/wl1273-core.c +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * MFD driver for wl1273 FM radio and audio codec submodules. - * - * Copyright (C) 2011 Nokia Corporation - * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com> - */ - -#include <linux/mfd/wl1273-core.h> -#include <linux/slab.h> -#include <linux/module.h> - -#define DRIVER_DESC "WL1273 FM Radio Core" - -static const struct i2c_device_id wl1273_driver_id_table[] = { - { WL1273_FM_DRIVER_NAME }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table); - -static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value) -{ - struct i2c_client *client = core->client; - u8 b[2]; - int r; - - r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b); - if (r != 2) { - dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg); - return -EREMOTEIO; - } - - *value = (u16)b[0] << 8 | b[1]; - - return 0; -} - -static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param) -{ - struct i2c_client *client = core->client; - u8 buf[] = { (param >> 8) & 0xff, param & 0xff }; - int r; - - r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf); - if (r) { - dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd); - return r; - } - - return 0; -} - -static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len) -{ - struct i2c_client *client = core->client; - struct i2c_msg msg; - int r; - - msg.addr = client->addr; - msg.flags = 0; - msg.buf = data; - msg.len = len; - - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - dev_err(&client->dev, "%s: write error.\n", __func__); - return -EREMOTEIO; - } - - return 0; -} - -/** - * wl1273_fm_set_audio() - Set audio mode. - * @core: A pointer to the device struct. - * @new_mode: The new audio mode. - * - * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG. - */ -static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode) -{ - int r = 0; - - if (core->mode == WL1273_MODE_OFF || - core->mode == WL1273_MODE_SUSPENDED) - return -EPERM; - - if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) { - r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET, - WL1273_PCM_DEF_MODE); - if (r) - goto out; - - r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, - core->i2s_mode); - if (r) - goto out; - - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, - WL1273_AUDIO_ENABLE_I2S); - if (r) - goto out; - - } else if (core->mode == WL1273_MODE_RX && - new_mode == WL1273_AUDIO_ANALOG) { - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, - WL1273_AUDIO_ENABLE_ANALOG); - if (r) - goto out; - - } else if (core->mode == WL1273_MODE_TX && - new_mode == WL1273_AUDIO_DIGITAL) { - r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, - core->i2s_mode); - if (r) - goto out; - - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, - WL1273_AUDIO_IO_SET_I2S); - if (r) - goto out; - - } else if (core->mode == WL1273_MODE_TX && - new_mode == WL1273_AUDIO_ANALOG) { - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, - WL1273_AUDIO_IO_SET_ANALOG); - if (r) - goto out; - } - - core->audio_mode = new_mode; -out: - return r; -} - -/** - * wl1273_fm_set_volume() - Set volume. - * @core: A pointer to the device struct. - * @volume: The new volume value. - */ -static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume) -{ - int r; - - if (volume > WL1273_MAX_VOLUME) - return -EINVAL; - - if (core->volume == volume) - return 0; - - r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume); - if (r) - return r; - - core->volume = volume; - return 0; -} - -static int wl1273_core_probe(struct i2c_client *client) -{ - struct wl1273_fm_platform_data *pdata = dev_get_platdata(&client->dev); - struct wl1273_core *core; - struct mfd_cell *cell; - int children = 0; - int r = 0; - - dev_dbg(&client->dev, "%s\n", __func__); - - if (!pdata) { - dev_err(&client->dev, "No platform data.\n"); - return -EINVAL; - } - - if (!(pdata->children & WL1273_RADIO_CHILD)) { - dev_err(&client->dev, "Cannot function without radio child.\n"); - return -EINVAL; - } - - core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL); - if (!core) - return -ENOMEM; - - core->pdata = pdata; - core->client = client; - mutex_init(&core->lock); - - i2c_set_clientdata(client, core); - - dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__); - - cell = &core->cells[children]; - cell->name = "wl1273_fm_radio"; - cell->platform_data = &core; - cell->pdata_size = sizeof(core); - children++; - - core->read = wl1273_fm_read_reg; - core->write = wl1273_fm_write_cmd; - core->write_data = wl1273_fm_write_data; - core->set_audio = wl1273_fm_set_audio; - core->set_volume = wl1273_fm_set_volume; - - if (pdata->children & WL1273_CODEC_CHILD) { - cell = &core->cells[children]; - - dev_dbg(&client->dev, "%s: Have codec.\n", __func__); - cell->name = "wl1273-codec"; - cell->platform_data = &core; - cell->pdata_size = sizeof(core); - children++; - } - - dev_dbg(&client->dev, "%s: number of children: %d.\n", - __func__, children); - - r = devm_mfd_add_devices(&client->dev, -1, core->cells, - children, NULL, 0, NULL); - if (r) - goto err; - - return 0; - -err: - pdata->free_resources(); - - dev_dbg(&client->dev, "%s\n", __func__); - - return r; -} - -static struct i2c_driver wl1273_core_driver = { - .driver = { - .name = WL1273_FM_DRIVER_NAME, - }, - .probe = wl1273_core_probe, - .id_table = wl1273_driver_id_table, -}; - -static int __init wl1273_core_init(void) -{ - int r; - - r = i2c_add_driver(&wl1273_core_driver); - if (r) { - pr_err(WL1273_FM_DRIVER_NAME - ": driver registration failed\n"); - return r; - } - - return r; -} - -static void __exit wl1273_core_exit(void) -{ - i2c_del_driver(&wl1273_core_driver); -} -late_initcall(wl1273_core_init); -module_exit(wl1273_core_exit); - -MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>"); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); |
