From: Haibo Chen <haibo.chen@xxxxxxx> ADP5585 support multi function, include expander GPIO, pwm, keypad controller. Signed-off-by: Haibo Chen <haibo.chen@xxxxxxx> Reviewed-by: Jun Li <jun.li@xxxxxxx> Signed-off-by: Frank Li <Frank.Li@xxxxxxx> --- drivers/mfd/Kconfig | 9 +++ drivers/mfd/Makefile | 1 + drivers/mfd/adp5585.c | 134 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/adp5585.h | 100 +++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index bc8be2e593b6b..62a967ee8ae1c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -20,6 +20,15 @@ config MFD_CS5535 This is the core driver for CS5535/CS5536 MFD functions. This is necessary for using the board's GPIO and MFGPT functionality. +config MFD_ADP5585 + tristate "ADI ADP5585 core functions" + select MFD_CORE + depends on I2C && OF + + help + This is ADI adp5585 core support, implement the support for + communication through i2c bus. + config MFD_ALTERA_A10SR bool "Altera Arria10 DevKit System Resource chip" depends on ARCH_INTEL_SOCFPGA && SPI_MASTER=y && OF diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 02b651cd75352..2a9f91e81af83 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -193,6 +193,7 @@ obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o +obj-$(CONFIG_MFD_ADP5585) += adp5585.o obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c new file mode 100644 index 0000000000000..52cc0d38bf2c3 --- /dev/null +++ b/drivers/mfd/adp5585.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* ADP5585 IO Expander, Key controller core driver. + * + * Copyright 2024 NXP + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/adp5585.h> + +static const struct mfd_cell adp5585_devs[] = { + { + .name = "adp5585-gpio", + .of_compatible = "adp5585-gpio", + }, + { + .name = "adp5585-pwm", + .of_compatible = "adp5585-pwm", + }, +}; + +static int adp5585_i2c_read_reg(struct adp5585_dev *adp5585, u8 reg, u8 *val) +{ + struct i2c_client *i2c = adp5585->i2c_client; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 1; + xfer[1].buf = val; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + return 0; + + dev_err(&i2c->dev, "Failed to read reg 0x%02x, ret is %d\n", reg, ret); + + return ret >= 0 ? -EIO : ret; +} + +static int adp5585_i2c_write_reg(struct adp5585_dev *adp5585, u8 reg, u8 val) +{ + struct i2c_client *i2c = adp5585->i2c_client; + u8 msg[2]; + int ret; + + msg[0] = reg; + msg[1] = val; + ret = i2c_master_send(i2c, msg, 2); + if (ret == 2) + return 0; + + dev_err(&i2c->dev, "Failed to write reg 0x%02x, ret is %d\n", reg, ret); + + return ret >= 0 ? -EIO : ret; +} + +static int adp5585_i2c_probe(struct i2c_client *i2c) +{ + struct adp5585_dev *adp; + u8 reg; + int ret; + + adp = devm_kzalloc(&i2c->dev, sizeof(struct adp5585_dev), GFP_KERNEL); + if (!adp) + return -ENOMEM; + + i2c_set_clientdata(i2c, adp); + adp->dev = &i2c->dev; + adp->i2c_client = i2c; + adp->read_reg = adp5585_i2c_read_reg; + adp->write_reg = adp5585_i2c_write_reg; + + ret = adp5585_i2c_read_reg(adp, ADP5585_ID, ®); + if (ret) + return ret; + + return devm_mfd_add_devices(adp->dev, PLATFORM_DEVID_AUTO, + adp5585_devs, ARRAY_SIZE(adp5585_devs), + NULL, 0, NULL); +} + +static const struct i2c_device_id adp5585_i2c_id[] = { + { "adp5585", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, adp5585_i2c_id); + +static const struct of_device_id adp5585_of_match[] = { + {.compatible = "adi,adp5585", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, adp5585_of_match); + +static struct i2c_driver adp5585_i2c_driver = { + .driver = { + .name = "adp5585", + .of_match_table = of_match_ptr(adp5585_of_match), + }, + .probe = adp5585_i2c_probe, + .id_table = adp5585_i2c_id, +}; + +static int __init adp5585_i2c_init(void) +{ + return i2c_add_driver(&adp5585_i2c_driver); +} + +/* init early so consumer devices can complete system boot */ +subsys_initcall(adp5585_i2c_init); + +static void __exit adp5585_i2c_exit(void) +{ + i2c_del_driver(&adp5585_i2c_driver); +} +module_exit(adp5585_i2c_exit); + +MODULE_DESCRIPTION("ADP5585 core driver"); +MODULE_AUTHOR("Haibo Chen <haibo.chen@xxxxxxx>"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h new file mode 100644 index 0000000000000..58a9f84c9a75a --- /dev/null +++ b/include/linux/mfd/adp5585.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Analog Devices ADP5585 I/O Expander, keypad controller, + * PWM contorller. + * + * Copyright 2022 NXP + */ + +#ifndef __ADP5585_H_ +#define __ADP5585_H_ + +#define ADP5585_ID 0x00 +#define ADP5585_INT_STATUS 0x01 +#define ADP5585_STATUS 0x02 +#define ADP5585_FIFO_1 0x03 +#define ADP5585_FIFO_2 0x04 +#define ADP5585_FIFO_3 0x05 +#define ADP5585_FIFO_4 0x06 +#define ADP5585_FIFO_5 0x07 +#define ADP5585_FIFO_6 0x08 +#define ADP5585_FIFO_7 0x09 +#define ADP5585_FIFO_8 0x0A +#define ADP5585_FIFO_9 0x0B +#define ADP5585_FIFO_10 0x0C +#define ADP5585_FIFO_11 0x0D +#define ADP5585_FIFO_12 0x0E +#define ADP5585_FIFO_13 0x0F +#define ADP5585_FIFO_14 0x10 +#define ADP5585_FIFO_15 0x11 +#define ADP5585_FIFO_16 0x12 +#define ADP5585_GPI_INT_STAT_A 0x13 +#define ADP5585_GPI_INT_STAT_B 0x14 +#define ADP5585_GPI_STATUS_A 0x15 +#define ADP5585_GPI_STATUS_B 0x16 +#define ADP5585_RPULL_CONFIG_A 0x17 +#define ADP5585_RPULL_CONFIG_B 0x18 +#define ADP5585_RPULL_CONFIG_C 0x19 +#define ADP5585_RPULL_CONFIG_D 0x1A +#define ADP5585_GPI_INT_LEVEL_A 0x1B +#define ADP5585_GPI_INT_LEVEL_B 0x1C +#define ADP5585_GPI_EVENT_EN_A 0x1D +#define ADP5585_GPI_EVENT_EN_B 0x1E +#define ADP5585_GPI_INTERRUPT_EN_A 0x1F +#define ADP5585_GPI_INTERRUPT_EN_B 0x20 +#define ADP5585_DEBOUNCE_DIS_A 0x21 +#define ADP5585_DEBOUNCE_DIS_B 0x22 +#define ADP5585_GPO_DATA_OUT_A 0x23 +#define ADP5585_GPO_DATA_OUT_B 0x24 +#define ADP5585_GPO_OUT_MODE_A 0x25 +#define ADP5585_GPO_OUT_MODE_B 0x26 +#define ADP5585_GPIO_DIRECTION_A 0x27 +#define ADP5585_GPIO_DIRECTION_B 0x28 +#define ADP5585_RESET1_EVENT_A 0x29 +#define ADP5585_RESET1_EVENT_B 0x2A +#define ADP5585_RESET1_EVENT_C 0x2B +#define ADP5585_RESET2_EVENT_A 0x2C +#define ADP5585_RESET2_EVENT_B 0x2D +#define ADP5585_RESET_CFG 0x2E +#define ADP5585_PWM_OFFT_LOW 0x2F +#define ADP5585_PWM_OFFT_HIGH 0x30 +#define ADP5585_PWM_ONT_LOW 0x31 +#define ADP5585_PWM_ONT_HIGH 0x32 +#define ADP5585_PWM_CFG 0x33 +#define ADP5585_LOGIC_CFG 0x34 +#define ADP5585_LOGIC_FF_CFG 0x35 +#define ADP5585_LOGIC_INT_EVENT_EN 0x36 +#define ADP5585_POLL_PTIME_CFG 0x37 +#define ADP5585_PIN_CONFIG_A 0x38 +#define ADP5585_PIN_CONFIG_B 0x39 +#define ADP5585_PIN_CONFIG_C 0x3A +#define ADP5585_GENERAL_CFG 0x3B +#define ADP5585_INT_EN 0x3C + +/* ID Register */ +#define ADP5585_DEVICE_ID_MASK 0xF +#define ADP5585_MAN_ID_MASK 0xF +#define ADP5585_MAN_ID_SHIFT 4 +#define ADP5585_MAN_ID 0x02 + +#define ADP5585_PWM_CFG_EN 0x1 +#define ADP5585_PWM_CFG_MODE 0x2 +#define ADP5585_PIN_CONFIG_R3_PWM 0x8 +#define ADP5585_PIN_CONFIG_R3_MASK 0xC +#define ADP5585_GENERAL_CFG_OSC_EN 0x80 + +#define ADP5585_REG_MASK 0xFF + +#define ADP5585_BANK(offs) ((offs) > 4) +#define ADP5585_BIT(offs) ((offs) > 4 ? \ + 1u << ((offs) - 5) : 1u << (offs)) + +struct adp5585_dev { + struct device *dev; + struct i2c_client *i2c_client; + + int (*read_reg)(struct adp5585_dev *adp5585, u8 reg, u8 *val); + int (*write_reg)(struct adp5585_dev *adp5585, u8 reg, u8 val); +}; + +#endif -- 2.34.1