From bae5a4acef67db8870e33d9258223c9dcd64b5d1 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jan 2022 22:10:03 +1000 Subject: mfd: simple-mfd-i2c: Add a Kconfig name Add a Kconfig name to the "Simple Multi-Functional Device support (I2C)" device so that it can be enabled via menuconfig. Signed-off-by: Alistair Francis Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ba0b3eb131f1..e0d2fcb10a0c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1188,7 +1188,7 @@ config MFD_SI476X_CORE module will be called si476x-core. config MFD_SIMPLE_MFD_I2C - tristate + tristate "Simple Multi-Functional Device support (I2C)" depends on I2C select MFD_CORE select REGMAP_I2C -- cgit v1.2.3-71-gd317 From 5913eb45d036288babb67e97b210233537ef7b90 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jan 2022 22:10:04 +1000 Subject: mfd: simple-mfd-i2c: Enable support for the silergy,sy7636a Signed-off-by: Alistair Francis Signed-off-by: Lee Jones --- drivers/mfd/simple-mfd-i2c.c | 11 +++++++++++ include/linux/mfd/sy7636a.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 include/linux/mfd/sy7636a.h (limited to 'drivers') diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c index 51536691ad9d..f4c8fc3ee463 100644 --- a/drivers/mfd/simple-mfd-i2c.c +++ b/drivers/mfd/simple-mfd-i2c.c @@ -62,8 +62,19 @@ static int simple_mfd_i2c_probe(struct i2c_client *i2c) return ret; } +static const struct mfd_cell sy7636a_cells[] = { + { .name = "sy7636a-regulator", }, + { .name = "sy7636a-temperature", }, +}; + +static const struct simple_mfd_data silergy_sy7636a = { + .mfd_cell = sy7636a_cells, + .mfd_cell_size = ARRAY_SIZE(sy7636a_cells), +}; + static const struct of_device_id simple_mfd_i2c_of_match[] = { { .compatible = "kontron,sl28cpld" }, + { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a}, {} }; MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match); diff --git a/include/linux/mfd/sy7636a.h b/include/linux/mfd/sy7636a.h new file mode 100644 index 000000000000..22f03b2f851e --- /dev/null +++ b/include/linux/mfd/sy7636a.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Functions to access SY3686A power management chip. + * + * Copyright (C) 2021 reMarkable AS - http://www.remarkable.com/ + */ + +#ifndef __MFD_SY7636A_H +#define __MFD_SY7636A_H + +#define SY7636A_REG_OPERATION_MODE_CRL 0x00 +/* It is set if a gpio is used to control the regulator */ +#define SY7636A_OPERATION_MODE_CRL_VCOMCTL BIT(6) +#define SY7636A_OPERATION_MODE_CRL_ONOFF BIT(7) +#define SY7636A_REG_VCOM_ADJUST_CTRL_L 0x01 +#define SY7636A_REG_VCOM_ADJUST_CTRL_H 0x02 +#define SY7636A_REG_VCOM_ADJUST_CTRL_MASK 0x01ff +#define SY7636A_REG_VLDO_VOLTAGE_ADJULST_CTRL 0x03 +#define SY7636A_REG_POWER_ON_DELAY_TIME 0x06 +#define SY7636A_REG_FAULT_FLAG 0x07 +#define SY7636A_FAULT_FLAG_PG BIT(0) +#define SY7636A_REG_TERMISTOR_READOUT 0x08 + +#define SY7636A_REG_MAX 0x08 + +#define VCOM_ADJUST_CTRL_MASK 0x1ff +// Used to shift the high byte +#define VCOM_ADJUST_CTRL_SHIFT 8 +// Used to scale from VCOM_ADJUST_CTRL to mv +#define VCOM_ADJUST_CTRL_SCAL 10000 + +#define FAULT_FLAG_SHIFT 1 + +#endif /* __LINUX_MFD_SY7636A_H */ -- cgit v1.2.3-71-gd317 From 947d0cce70ae37b89295040165404b1da3994c10 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jan 2022 22:10:05 +1000 Subject: regulator: sy7636a: Remove requirement on sy7636a mfd Signed-off-by: Alistair Francis Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/regulator/Kconfig | 1 - drivers/regulator/sy7636a-regulator.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 1c35fed20d34..25ae5f087ff9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1208,7 +1208,6 @@ config REGULATOR_STW481X_VMMC config REGULATOR_SY7636A tristate "Silergy SY7636A voltage regulator" - depends on MFD_SY7636A help This driver supports Silergy SY3686A voltage regulator. diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c index 22fddf868e4c..29fc27c2cda0 100644 --- a/drivers/regulator/sy7636a-regulator.c +++ b/drivers/regulator/sy7636a-regulator.c @@ -7,11 +7,14 @@ // Authors: Lars Ivar Miljeteig // Alistair Francis +#include +#include +#include #include #include +#include +#include #include -#include -#include struct sy7636a_data { struct regmap *regmap; -- cgit v1.2.3-71-gd317 From de34a4053250781404779b567b58dd97af689ce0 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jan 2022 22:10:06 +1000 Subject: hwmon: sy7636a: Add temperature driver for sy7636a This is a multi-function device to interface with the sy7636a EPD PMIC chip from Silergy. Signed-off-by: Alistair Francis Acked-by: Guenter Roeck Signed-off-by: Lee Jones --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/sy7636a-hwmon.rst | 26 +++++++++ drivers/hwmon/Kconfig | 9 +++ drivers/hwmon/Makefile | 1 + drivers/hwmon/sy7636a-hwmon.c | 106 ++++++++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 Documentation/hwmon/sy7636a-hwmon.rst create mode 100644 drivers/hwmon/sy7636a-hwmon.c (limited to 'drivers') diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index df20022c741f..6e0906ef5d25 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -185,6 +185,7 @@ Hardware Monitoring Kernel Drivers smsc47m1 sparx5-temp stpddc60 + sy7636a-hwmon tc654 tc74 thmc50 diff --git a/Documentation/hwmon/sy7636a-hwmon.rst b/Documentation/hwmon/sy7636a-hwmon.rst new file mode 100644 index 000000000000..5612079397d5 --- /dev/null +++ b/Documentation/hwmon/sy7636a-hwmon.rst @@ -0,0 +1,26 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver sy7636a-hwmon +========================= + +Supported chips: + + * Silergy SY7636A PMIC + + +Description +----------- + +This driver adds hardware temperature reading support for +the Silergy SY7636A PMIC. + +The following sensors are supported + + * Temperature + - SoC on-die temperature in milli-degree C + +sysfs-Interface +--------------- + +temp0_input + - SoC on-die temperature (milli-degree C) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8df25f1079ba..aa5785e657a4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1672,6 +1672,15 @@ config SENSORS_SIS5595 This driver can also be built as a module. If so, the module will be called sis5595. +config SENSORS_SY7636A + tristate "Silergy SY7636A" + help + If you say yes here you get support for the thermistor readout of + the Silergy SY7636A PMIC. + + This driver can also be built as a module. If so, the module + will be called sy7636a-hwmon. + config SENSORS_DME1737 tristate "SMSC DME1737, SCH311x and compatibles" depends on I2C && !PPC diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 185f946d698b..fe54a3dfdb03 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -186,6 +186,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o +obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o obj-$(CONFIG_SENSORS_TC74) += tc74.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o diff --git a/drivers/hwmon/sy7636a-hwmon.c b/drivers/hwmon/sy7636a-hwmon.c new file mode 100644 index 000000000000..6dd9c2a0f0e0 --- /dev/null +++ b/drivers/hwmon/sy7636a-hwmon.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functions to access SY3686A power management chip temperature + * + * Copyright (C) 2021 reMarkable AS - http://www.remarkable.com/ + * + * Authors: Lars Ivar Miljeteig + * Alistair Francis + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int sy7636a_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct regmap *regmap = dev_get_drvdata(dev); + int ret, reg_val; + + ret = regmap_read(regmap, + SY7636A_REG_TERMISTOR_READOUT, ®_val); + if (ret) + return ret; + + *temp = reg_val * 1000; + + return 0; +} + +static umode_t sy7636a_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + if (attr != hwmon_temp_input) + return 0; + + return 0444; +} + +static const struct hwmon_ops sy7636a_hwmon_ops = { + .is_visible = sy7636a_is_visible, + .read = sy7636a_read, +}; + +static const struct hwmon_channel_info *sy7636a_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_chip_info sy7636a_chip_info = { + .ops = &sy7636a_hwmon_ops, + .info = sy7636a_info, +}; + +static int sy7636a_sensor_probe(struct platform_device *pdev) +{ + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); + struct regulator *regulator; + struct device *hwmon_dev; + int err; + + if (!regmap) + return -EPROBE_DEFER; + + regulator = devm_regulator_get(&pdev->dev, "vcom"); + if (IS_ERR(regulator)) + return PTR_ERR(regulator); + + err = regulator_enable(regulator); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "sy7636a_temperature", regmap, + &sy7636a_chip_info, NULL); + + if (IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + dev_err(&pdev->dev, "Unable to register hwmon device, returned %d\n", err); + return err; + } + + return 0; +} + +static struct platform_driver sy7636a_sensor_driver = { + .probe = sy7636a_sensor_probe, + .driver = { + .name = "sy7636a-temperature", + }, +}; +module_platform_driver(sy7636a_sensor_driver); + +MODULE_DESCRIPTION("SY7636A sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-71-gd317 From 1de785a58035031f81a43c42f355fa1db510dc36 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 23 Jan 2022 13:01:05 -0600 Subject: mfd: iqs62x: Provide device revision to sub-devices Individual sub-devices may elect to make decisions based on the specific revision of silicon encountered at probe. This data is already read from the device, but is not retained. Pass this data on to the sub-devices by adding the software and hardware numbers (registers 0x01 and 0x02, respectively) to the iqs62x_core struct. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 6 +++--- include/linux/mfd/iqs62x.h | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 9805cf191245..575ab67e243d 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -898,7 +898,6 @@ static int iqs62x_probe(struct i2c_client *client) struct iqs62x_info info; unsigned int val; int ret, i, j; - u8 sw_num = 0; const char *fw_name = NULL; iqs62x = devm_kzalloc(&client->dev, sizeof(*iqs62x), GFP_KERNEL); @@ -949,7 +948,8 @@ static int iqs62x_probe(struct i2c_client *client) if (info.sw_num < iqs62x->dev_desc->sw_num) continue; - sw_num = info.sw_num; + iqs62x->sw_num = info.sw_num; + iqs62x->hw_num = info.hw_num; /* * Read each of the device's designated calibration registers, @@ -985,7 +985,7 @@ static int iqs62x_probe(struct i2c_client *client) return -EINVAL; } - if (!sw_num) { + if (!iqs62x->sw_num) { dev_err(&client->dev, "Unrecognized software number: 0x%02X\n", info.sw_num); return -EINVAL; diff --git a/include/linux/mfd/iqs62x.h b/include/linux/mfd/iqs62x.h index 5ced55eae11b..ffc86010af74 100644 --- a/include/linux/mfd/iqs62x.h +++ b/include/linux/mfd/iqs62x.h @@ -14,6 +14,11 @@ #define IQS624_PROD_NUM 0x43 #define IQS625_PROD_NUM 0x4E +#define IQS620_HW_NUM_V0 0x82 +#define IQS620_HW_NUM_V1 IQS620_HW_NUM_V0 +#define IQS620_HW_NUM_V2 IQS620_HW_NUM_V0 +#define IQS620_HW_NUM_V3 0x92 + #define IQS621_ALS_FLAGS 0x16 #define IQS622_ALS_FLAGS 0x14 @@ -129,6 +134,8 @@ struct iqs62x_core { struct completion fw_done; enum iqs62x_ui_sel ui_sel; unsigned long event_cache; + u8 sw_num; + u8 hw_num; }; extern const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS]; -- cgit v1.2.3-71-gd317 From 9e444dbf278c604110762b25d8e0b4e9f1d27dbc Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 23 Jan 2022 13:01:06 -0600 Subject: iio: temperature: iqs620at-temp: Add support for V3 silicon The vendor introduced an updated revision of IQS620A(T) silicon which is backwards-compatible with previous revisions, the only exception being the offset used to derive temperature. Enable this new revision by returning the appropriate offset as a function of the hardware number provided by the parent MFD. Signed-off-by: Jeff LaBundy Signed-off-by: Lee Jones --- drivers/iio/temperature/iqs620at-temp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/temperature/iqs620at-temp.c b/drivers/iio/temperature/iqs620at-temp.c index fe126e1fb783..e2f878d57af7 100644 --- a/drivers/iio/temperature/iqs620at-temp.c +++ b/drivers/iio/temperature/iqs620at-temp.c @@ -17,6 +17,7 @@ #define IQS620_TEMP_SCALE 1000 #define IQS620_TEMP_OFFSET (-100) +#define IQS620_TEMP_OFFSET_V3 (-40) static int iqs620_temp_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -41,7 +42,8 @@ static int iqs620_temp_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - *val = IQS620_TEMP_OFFSET; + *val = iqs62x->hw_num < IQS620_HW_NUM_V3 ? IQS620_TEMP_OFFSET + : IQS620_TEMP_OFFSET_V3; return IIO_VAL_INT; default: -- cgit v1.2.3-71-gd317 From 72c356c2cf4066c355cc69ca7127ba70df717d93 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:01 +0100 Subject: rtc: max77686: Convert comments to kernel-doc format Convert the comments documenting this struct to kernel-doc format for standardization and readability. Signed-off-by: Luca Ceresoli Reviewed-by: Krzysztof Kozlowski Acked-by: Alexandre Belloni Reviewed-by: Randy Dunlap Signed-off-by: Lee Jones --- drivers/rtc/rtc-max77686.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index eae7cb9faf1e..bac52cdea97d 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -61,24 +61,27 @@ enum { RTC_NR_TIME }; +/** + * struct max77686_rtc_driver_data - model-specific configuration + * @delay: Minimum usecs needed for a RTC update + * @mask: Mask used to read RTC registers value + * @map: Registers offset to I2C addresses map + * @alarm_enable_reg: Has a separate alarm enable register? + * @rtc_i2c_addr: I2C address for RTC block + * @rtc_irq_from_platform: RTC interrupt via platform resource + * @alarm_pending_status_reg: Pending alarm status register + * @rtc_irq_chip: RTC IRQ CHIP for regmap + * @regmap_config: regmap configuration for the chip + */ struct max77686_rtc_driver_data { - /* Minimum usecs needed for a RTC update */ unsigned long delay; - /* Mask used to read RTC registers value */ u8 mask; - /* Registers offset to I2C addresses map */ const unsigned int *map; - /* Has a separate alarm enable register? */ bool alarm_enable_reg; - /* I2C address for RTC block */ int rtc_i2c_addr; - /* RTC interrupt via platform resource */ bool rtc_irq_from_platform; - /* Pending alarm status register */ int alarm_pending_status_reg; - /* RTC IRQ CHIP for regmap */ const struct regmap_irq_chip *rtc_irq_chip; - /* regmap configuration for the chip */ const struct regmap_config *regmap_config; }; -- cgit v1.2.3-71-gd317 From a26d84633c2ba3c41fd7692986fb2231c9228c4e Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:02 +0100 Subject: rtc: max77686: Rename day-of-month defines RTC_DATE and REG_RTC_DATE are used for the registers holding the day of month. Rename these constants to mean what they mean. Signed-off-by: Luca Ceresoli Reviewed-by: Krzysztof Kozlowski Acked-by: Alexandre Belloni Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 2 +- drivers/rtc/rtc-max77686.c | 16 ++++++++-------- include/linux/mfd/max77686-private.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index f9e12ab2bc75..2ac64277fb84 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -87,7 +87,7 @@ static bool max77802_rtc_is_volatile_reg(struct device *dev, unsigned int reg) reg == MAX77802_RTC_WEEKDAY || reg == MAX77802_RTC_MONTH || reg == MAX77802_RTC_YEAR || - reg == MAX77802_RTC_DATE); + reg == MAX77802_RTC_MONTHDAY); } static bool max77802_is_volatile_reg(struct device *dev, unsigned int reg) diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index bac52cdea97d..7e765207f28e 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -57,7 +57,7 @@ enum { RTC_WEEKDAY, RTC_MONTH, RTC_YEAR, - RTC_DATE, + RTC_MONTHDAY, RTC_NR_TIME }; @@ -119,7 +119,7 @@ enum max77686_rtc_reg_offset { REG_RTC_WEEKDAY, REG_RTC_MONTH, REG_RTC_YEAR, - REG_RTC_DATE, + REG_RTC_MONTHDAY, REG_ALARM1_SEC, REG_ALARM1_MIN, REG_ALARM1_HOUR, @@ -150,7 +150,7 @@ static const unsigned int max77686_map[REG_RTC_END] = { [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY, [REG_RTC_MONTH] = MAX77686_RTC_MONTH, [REG_RTC_YEAR] = MAX77686_RTC_YEAR, - [REG_RTC_DATE] = MAX77686_RTC_DATE, + [REG_RTC_MONTHDAY] = MAX77686_RTC_MONTHDAY, [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC, [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN, [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR, @@ -233,7 +233,7 @@ static const unsigned int max77802_map[REG_RTC_END] = { [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY, [REG_RTC_MONTH] = MAX77802_RTC_MONTH, [REG_RTC_YEAR] = MAX77802_RTC_YEAR, - [REG_RTC_DATE] = MAX77802_RTC_DATE, + [REG_RTC_MONTHDAY] = MAX77802_RTC_MONTHDAY, [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC, [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN, [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR, @@ -288,7 +288,7 @@ static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, /* Only a single bit is set in data[], so fls() would be equivalent */ tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1; - tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mday = data[RTC_MONTHDAY] & 0x1f; tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; tm->tm_year = data[RTC_YEAR] & mask; tm->tm_yday = 0; @@ -309,7 +309,7 @@ static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data, data[RTC_MIN] = tm->tm_min; data[RTC_HOUR] = tm->tm_hour; data[RTC_WEEKDAY] = 1 << tm->tm_wday; - data[RTC_DATE] = tm->tm_mday; + data[RTC_MONTHDAY] = tm->tm_mday; data[RTC_MONTH] = tm->tm_mon + 1; if (info->drv_data->alarm_enable_reg) { @@ -565,8 +565,8 @@ static int max77686_rtc_start_alarm(struct max77686_rtc_info *info) data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); if (data[RTC_YEAR] & info->drv_data->mask) data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); - if (data[RTC_DATE] & 0x1f) - data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_MONTHDAY] & 0x1f) + data[RTC_MONTHDAY] |= (1 << ALARM_ENABLE_SHIFT); ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], data, ARRAY_SIZE(data)); diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index b1482b3cf353..3acceeedbaba 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -152,7 +152,7 @@ enum max77686_rtc_reg { MAX77686_RTC_WEEKDAY = 0x0A, MAX77686_RTC_MONTH = 0x0B, MAX77686_RTC_YEAR = 0x0C, - MAX77686_RTC_DATE = 0x0D, + MAX77686_RTC_MONTHDAY = 0x0D, MAX77686_ALARM1_SEC = 0x0E, MAX77686_ALARM1_MIN = 0x0F, MAX77686_ALARM1_HOUR = 0x10, @@ -352,7 +352,7 @@ enum max77802_rtc_reg { MAX77802_RTC_WEEKDAY = 0xCA, MAX77802_RTC_MONTH = 0xCB, MAX77802_RTC_YEAR = 0xCC, - MAX77802_RTC_DATE = 0xCD, + MAX77802_RTC_MONTHDAY = 0xCD, MAX77802_RTC_AE1 = 0xCE, MAX77802_ALARM1_SEC = 0xCF, MAX77802_ALARM1_MIN = 0xD0, -- cgit v1.2.3-71-gd317 From 3e2e90fefc9bc6f81a477d35db5e3be1bcb9e62d Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:03 +0100 Subject: rtc: max77686: Remove unused code to read in 12-hour mode The MAX77714 RTC chip is explicitly set to 24-hour mode in max77686_rtc_probe() -> max77686_rtc_init_reg() and never changed back to 12-hour mode. Accordingly info->rtc_24hr_mode is set to 1 in the same place and never modified later, so it is de facto a constant. Yet there is code to read 12-hour time, which is unreachable. Remove the unused variable, the unreachable code to manage 12-hour mode and the defines that become unused due to the above changes. Signed-off-by: Luca Ceresoli Reviewed-by: Krzysztof Kozlowski Acked-by: Alexandre Belloni Signed-off-by: Lee Jones --- drivers/rtc/rtc-max77686.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 7e765207f28e..5c64d08c0732 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -34,9 +34,6 @@ #define RTC_UDR_MASK BIT(RTC_UDR_SHIFT) #define RTC_RBUDR_SHIFT 4 #define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT) -/* RTC Hour register */ -#define HOUR_PM_SHIFT 6 -#define HOUR_PM_MASK BIT(HOUR_PM_SHIFT) /* RTC Alarm Enable */ #define ALARM_ENABLE_SHIFT 7 #define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT) @@ -99,7 +96,6 @@ struct max77686_rtc_info { int rtc_irq; int virq; - int rtc_24hr_mode; }; enum MAX77686_RTC_OP { @@ -278,13 +274,7 @@ static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, tm->tm_sec = data[RTC_SEC] & mask; tm->tm_min = data[RTC_MIN] & mask; - if (info->rtc_24hr_mode) { - tm->tm_hour = data[RTC_HOUR] & 0x1f; - } else { - tm->tm_hour = data[RTC_HOUR] & 0x0f; - if (data[RTC_HOUR] & HOUR_PM_MASK) - tm->tm_hour += 12; - } + tm->tm_hour = data[RTC_HOUR] & 0x1f; /* Only a single bit is set in data[], so fls() would be equivalent */ tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1; @@ -662,8 +652,6 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info) data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); - info->rtc_24hr_mode = 1; - ret = regmap_bulk_write(info->rtc_regmap, info->drv_data->map[REG_RTC_CONTROLM], data, ARRAY_SIZE(data)); -- cgit v1.2.3-71-gd317 From 60b050ff3a60273d56b4017d0f45d5ca71aae5ad Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:05 +0100 Subject: mfd: max77714: Add driver for Maxim MAX77714 PMIC Add a simple driver for the Maxim MAX77714 PMIC, supporting RTC and watchdog only. Signed-off-by: Luca Ceresoli Reviewed-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- MAINTAINERS | 2 + drivers/mfd/Kconfig | 14 ++++ drivers/mfd/Makefile | 1 + drivers/mfd/max77714.c | 152 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/max77714.h | 60 +++++++++++++++++ 5 files changed, 229 insertions(+) create mode 100644 drivers/mfd/max77714.c create mode 100644 include/linux/mfd/max77714.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 4cb0e77b47ef..6b7c58e7ce84 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11695,6 +11695,8 @@ MAXIM MAX77714 PMIC MFD DRIVER M: Luca Ceresoli S: Maintained F: Documentation/devicetree/bindings/mfd/maxim,max77714.yaml +F: drivers/mfd/max77714.c +F: include/linux/mfd/max77714.h MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER M: Javier Martinez Canillas diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ba0b3eb131f1..036ddf68c814 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -849,6 +849,20 @@ config MFD_MAX77693 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77714 + tristate "Maxim Semiconductor MAX77714 PMIC Support" + depends on I2C + depends on OF || COMPILE_TEST + select MFD_CORE + select REGMAP_I2C + help + Say yes here to add support for Maxim Semiconductor MAX77714. + This is a Power Management IC with 4 buck regulators, 9 + low-dropout regulators, 8 GPIOs, RTC, watchdog etc. This driver + provides common support for accessing the device; additional + drivers must be enabled in order to use each functionality of the + device. + config MFD_MAX77843 bool "Maxim Semiconductor MAX77843 PMIC Support" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index df1ecc4a4c95..eea70cb9f9b9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -162,6 +162,7 @@ obj-$(CONFIG_MFD_MAX77620) += max77620.o obj-$(CONFIG_MFD_MAX77650) += max77650.o obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o +obj-$(CONFIG_MFD_MAX77714) += max77714.o obj-$(CONFIG_MFD_MAX77843) += max77843.o obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o diff --git a/drivers/mfd/max77714.c b/drivers/mfd/max77714.c new file mode 100644 index 000000000000..d1e4247800d2 --- /dev/null +++ b/drivers/mfd/max77714.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Maxim MAX77714 Core Driver + * + * Copyright (C) 2022 Luca Ceresoli + * Author: Luca Ceresoli + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell max77714_cells[] = { + { .name = "max77714-watchdog" }, + { .name = "max77714-rtc" }, +}; + +static const struct regmap_range max77714_readable_ranges[] = { + regmap_reg_range(MAX77714_INT_TOP, MAX77714_INT_TOP), + regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), + regmap_reg_range(MAX77714_32K_STATUS, MAX77714_32K_CONFIG), + regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), +}; + +static const struct regmap_range max77714_writable_ranges[] = { + regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), + regmap_reg_range(MAX77714_32K_CONFIG, MAX77714_32K_CONFIG), + regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), +}; + +static const struct regmap_access_table max77714_readable_table = { + .yes_ranges = max77714_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(max77714_readable_ranges), +}; + +static const struct regmap_access_table max77714_writable_table = { + .yes_ranges = max77714_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(max77714_writable_ranges), +}; + +static const struct regmap_config max77714_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77714_CNFG2_ONOFF, + .rd_table = &max77714_readable_table, + .wr_table = &max77714_writable_table, +}; + +static const struct regmap_irq max77714_top_irqs[] = { + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_ONOFF, 0, MAX77714_INT_TOP_ONOFF), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_RTC, 0, MAX77714_INT_TOP_RTC), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GPIO, 0, MAX77714_INT_TOP_GPIO), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_LDO, 0, MAX77714_INT_TOP_LDO), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_SD, 0, MAX77714_INT_TOP_SD), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GLBL, 0, MAX77714_INT_TOP_GLBL), +}; + +static const struct regmap_irq_chip max77714_irq_chip = { + .name = "max77714-pmic", + .status_base = MAX77714_INT_TOP, + .mask_base = MAX77714_INT_TOPM, + .num_regs = 1, + .irqs = max77714_top_irqs, + .num_irqs = ARRAY_SIZE(max77714_top_irqs), +}; + +/* + * MAX77714 initially uses the internal, low precision oscillator. Enable + * the external oscillator by setting the XOSC_RETRY bit. If the external + * oscillator is not OK (probably not installed) this has no effect. + */ +static int max77714_setup_xosc(struct device *dev, struct regmap *regmap) +{ + /* Internal Crystal Load Capacitance, indexed by value of 32KLOAD bits */ + static const unsigned int load_cap[4] = {0, 10, 12, 22}; /* pF */ + unsigned int load_cap_idx; + unsigned int status; + int err; + + err = regmap_update_bits(regmap, MAX77714_32K_CONFIG, + MAX77714_32K_CONFIG_XOSC_RETRY, + MAX77714_32K_CONFIG_XOSC_RETRY); + if (err) + return dev_err_probe(dev, err, "Failed to configure the external oscillator\n"); + + err = regmap_read(regmap, MAX77714_32K_STATUS, &status); + if (err) + return dev_err_probe(dev, err, "Failed to read external oscillator status\n"); + + load_cap_idx = (status >> MAX77714_32K_STATUS_32KLOAD_SHF) + & MAX77714_32K_STATUS_32KLOAD_MSK; + + dev_info(dev, "Using %s oscillator, %d pF load cap\n", + status & MAX77714_32K_STATUS_32KSOURCE ? "internal" : "external", + load_cap[load_cap_idx]); + + return 0; +} + +static int max77714_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + int err; + + regmap = devm_regmap_init_i2c(client, &max77714_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialise regmap\n"); + + err = max77714_setup_xosc(dev, regmap); + if (err) + return err; + + err = devm_regmap_add_irq_chip(dev, regmap, client->irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77714_irq_chip, &irq_data); + if (err) + return dev_err_probe(dev, err, "Failed to add PMIC IRQ chip\n"); + + err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + max77714_cells, ARRAY_SIZE(max77714_cells), + NULL, 0, NULL); + if (err) + return dev_err_probe(dev, err, "Failed to register child devices\n"); + + return 0; +} + +static const struct of_device_id max77714_dt_match[] = { + { .compatible = "maxim,max77714" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max77714_dt_match); + +static struct i2c_driver max77714_driver = { + .driver = { + .name = "max77714", + .of_match_table = max77714_dt_match, + }, + .probe_new = max77714_probe, +}; +module_i2c_driver(max77714_driver); + +MODULE_DESCRIPTION("Maxim MAX77714 MFD core driver"); +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77714.h b/include/linux/mfd/max77714.h new file mode 100644 index 000000000000..a970dc455426 --- /dev/null +++ b/include/linux/mfd/max77714.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Maxim MAX77714 Register and data structures definition. + * + * Copyright (C) 2022 Luca Ceresoli + * Author: Luca Ceresoli + */ + +#ifndef __LINUX_MFD_MAX77714_H_ +#define __LINUX_MFD_MAX77714_H_ + +#include + +#define MAX77714_INT_TOP 0x00 +#define MAX77714_INT_TOPM 0x07 /* Datasheet says "read only", but it is RW */ + +#define MAX77714_INT_TOP_ONOFF BIT(1) +#define MAX77714_INT_TOP_RTC BIT(3) +#define MAX77714_INT_TOP_GPIO BIT(4) +#define MAX77714_INT_TOP_LDO BIT(5) +#define MAX77714_INT_TOP_SD BIT(6) +#define MAX77714_INT_TOP_GLBL BIT(7) + +#define MAX77714_32K_STATUS 0x30 +#define MAX77714_32K_STATUS_SIOSCOK BIT(5) +#define MAX77714_32K_STATUS_XOSCOK BIT(4) +#define MAX77714_32K_STATUS_32KSOURCE BIT(3) +#define MAX77714_32K_STATUS_32KLOAD_MSK 0x3 +#define MAX77714_32K_STATUS_32KLOAD_SHF 1 +#define MAX77714_32K_STATUS_CRYSTAL_CFG BIT(0) + +#define MAX77714_32K_CONFIG 0x31 +#define MAX77714_32K_CONFIG_XOSC_RETRY BIT(4) + +#define MAX77714_CNFG_GLBL2 0x91 +#define MAX77714_WDTEN BIT(2) +#define MAX77714_WDTSLPC BIT(3) +#define MAX77714_TWD_MASK 0x3 +#define MAX77714_TWD_2s 0x0 +#define MAX77714_TWD_16s 0x1 +#define MAX77714_TWD_64s 0x2 +#define MAX77714_TWD_128s 0x3 + +#define MAX77714_CNFG_GLBL3 0x92 +#define MAX77714_WDTC BIT(0) + +#define MAX77714_CNFG2_ONOFF 0x94 +#define MAX77714_WD_RST_WK BIT(5) + +/* Interrupts */ +enum { + MAX77714_IRQ_TOP_ONOFF, + MAX77714_IRQ_TOP_RTC, /* Real-time clock */ + MAX77714_IRQ_TOP_GPIO, /* GPIOs */ + MAX77714_IRQ_TOP_LDO, /* Low-dropout regulators */ + MAX77714_IRQ_TOP_SD, /* Step-down regulators */ + MAX77714_IRQ_TOP_GLBL, /* "Global resources": Low-Battery, overtemp... */ +}; + +#endif /* __LINUX_MFD_MAX77714_H_ */ -- cgit v1.2.3-71-gd317 From 254099d8fafcd80e11a3246f6ff62af816a2b566 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:06 +0100 Subject: watchdog: max77620: Add support for the max77714 variant The MAX77714 is a MFD chip whose watchdog has the same programming procedures as the MAX77620 watchdog, but most register offsets and bit masks are different, as well as some features. Support the MAX77714 watchdog by adding a variant description table holding the differences. All the features implemented by this driver are available on the MAX77714 except for the lack of a WDTOFFC bit. Instead of using a "HAS_*" flag we handle this by holding in the cnfg_glbl2_cfg_bits struct field the bits (i.e. the features) to enable in the CNFG_GLBL2 register. These bits differ among the two models. This implementation allows to avoid any conditional code, keeping the execution flow unchanged. Signed-off-by: Luca Ceresoli Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/watchdog/Kconfig | 2 +- drivers/watchdog/max77620_wdt.c | 80 ++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c8fa79da23b3..5373ecdec58e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -678,7 +678,7 @@ config MAX63XX_WATCHDOG config MAX77620_WATCHDOG tristate "Maxim Max77620 Watchdog Timer" - depends on MFD_MAX77620 || COMPILE_TEST + depends on MFD_MAX77620 || MFD_MAX77714 || COMPILE_TEST select WATCHDOG_CORE help This is the driver for the Max77620 watchdog timer. diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c index be6a53c30002..cd321c7e0d59 100644 --- a/drivers/watchdog/max77620_wdt.c +++ b/drivers/watchdog/max77620_wdt.c @@ -3,8 +3,10 @@ * Maxim MAX77620 Watchdog Driver * * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2022 Luca Ceresoli * * Author: Laxman Dewangan + * Author: Luca Ceresoli */ #include @@ -13,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -20,17 +23,57 @@ static bool nowayout = WATCHDOG_NOWAYOUT; +/** + * struct max77620_variant - Data specific to a chip variant + * @wdt_info: watchdog descriptor + * @reg_onoff_cnfg2: ONOFF_CNFG2 register offset + * @reg_cnfg_glbl2: CNFG_GLBL2 register offset + * @reg_cnfg_glbl3: CNFG_GLBL3 register offset + * @wdtc_mask: WDTC bit mask in CNFG_GLBL3 (=bits to update to ping the watchdog) + * @bit_wd_rst_wk: WD_RST_WK bit offset within ONOFF_CNFG2 + * @cnfg_glbl2_cfg_bits: configuration bits to enable in CNFG_GLBL2 register + */ +struct max77620_variant { + u8 reg_onoff_cnfg2; + u8 reg_cnfg_glbl2; + u8 reg_cnfg_glbl3; + u8 wdtc_mask; + u8 bit_wd_rst_wk; + u8 cnfg_glbl2_cfg_bits; +}; + struct max77620_wdt { struct device *dev; struct regmap *rmap; + const struct max77620_variant *drv_data; struct watchdog_device wdt_dev; }; +static const struct max77620_variant max77620_wdt_data = { + .reg_onoff_cnfg2 = MAX77620_REG_ONOFFCNFG2, + .reg_cnfg_glbl2 = MAX77620_REG_CNFGGLBL2, + .reg_cnfg_glbl3 = MAX77620_REG_CNFGGLBL3, + .wdtc_mask = MAX77620_WDTC_MASK, + .bit_wd_rst_wk = MAX77620_ONOFFCNFG2_WD_RST_WK, + /* Set WDT clear in OFF and sleep mode */ + .cnfg_glbl2_cfg_bits = MAX77620_WDTSLPC | MAX77620_WDTOFFC, +}; + +static const struct max77620_variant max77714_wdt_data = { + .reg_onoff_cnfg2 = MAX77714_CNFG2_ONOFF, + .reg_cnfg_glbl2 = MAX77714_CNFG_GLBL2, + .reg_cnfg_glbl3 = MAX77714_CNFG_GLBL3, + .wdtc_mask = MAX77714_WDTC, + .bit_wd_rst_wk = MAX77714_WD_RST_WK, + /* Set WDT clear in sleep mode (there is no WDTOFFC on MAX77714) */ + .cnfg_glbl2_cfg_bits = MAX77714_WDTSLPC, +}; + static int max77620_wdt_start(struct watchdog_device *wdt_dev) { struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); - return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, MAX77620_WDTEN, MAX77620_WDTEN); } @@ -38,7 +81,7 @@ static int max77620_wdt_stop(struct watchdog_device *wdt_dev) { struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); - return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, MAX77620_WDTEN, 0); } @@ -46,8 +89,8 @@ static int max77620_wdt_ping(struct watchdog_device *wdt_dev) { struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); - return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3, - MAX77620_WDTC_MASK, 0x1); + return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3, + wdt->drv_data->wdtc_mask, 0x1); } static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev, @@ -80,12 +123,12 @@ static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev, break; } - ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3, - MAX77620_WDTC_MASK, 0x1); + ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3, + wdt->drv_data->wdtc_mask, 0x1); if (ret < 0) return ret; - ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, MAX77620_TWD_MASK, regval); if (ret < 0) return ret; @@ -109,6 +152,7 @@ static const struct watchdog_ops max77620_wdt_ops = { static int max77620_wdt_probe(struct platform_device *pdev) { + const struct platform_device_id *id = platform_get_device_id(pdev); struct device *dev = &pdev->dev; struct max77620_wdt *wdt; struct watchdog_device *wdt_dev; @@ -120,6 +164,8 @@ static int max77620_wdt_probe(struct platform_device *pdev) return -ENOMEM; wdt->dev = dev; + wdt->drv_data = (const struct max77620_variant *) id->driver_data; + wdt->rmap = dev_get_regmap(dev->parent, NULL); if (!wdt->rmap) { dev_err(wdt->dev, "Failed to get parent regmap\n"); @@ -136,25 +182,25 @@ static int max77620_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdt); /* Enable WD_RST_WK - WDT expire results in a restart */ - ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2, - MAX77620_ONOFFCNFG2_WD_RST_WK, - MAX77620_ONOFFCNFG2_WD_RST_WK); + ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_onoff_cnfg2, + wdt->drv_data->bit_wd_rst_wk, + wdt->drv_data->bit_wd_rst_wk); if (ret < 0) { dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret); return ret; } - /* Set WDT clear in OFF and sleep mode */ - ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, - MAX77620_WDTOFFC | MAX77620_WDTSLPC, - MAX77620_WDTOFFC | MAX77620_WDTSLPC); + /* Set the "auto WDT clear" bits available on the chip */ + ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, + wdt->drv_data->cnfg_glbl2_cfg_bits, + wdt->drv_data->cnfg_glbl2_cfg_bits); if (ret < 0) { dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret); return ret; } /* Check if WDT running and if yes then set flags properly */ - ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, ®val); + ret = regmap_read(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, ®val); if (ret < 0) { dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret); return ret; @@ -186,7 +232,8 @@ static int max77620_wdt_probe(struct platform_device *pdev) } static const struct platform_device_id max77620_wdt_devtype[] = { - { .name = "max77620-watchdog", }, + { "max77620-watchdog", (kernel_ulong_t)&max77620_wdt_data }, + { "max77714-watchdog", (kernel_ulong_t)&max77714_wdt_data }, { }, }; MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype); @@ -208,4 +255,5 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); MODULE_AUTHOR("Laxman Dewangan "); +MODULE_AUTHOR("Luca Ceresoli "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-71-gd317 From 3f6f1f1f9f12c16934304ceca342b7ba83639da9 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:07 +0100 Subject: watchdog: max77620: Add comment to clarify set_timeout procedure Clarify why we need to ping the watchdog before changing the timeout by quoting the MAX77714 datasheet. Signed-off-by: Luca Ceresoli Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/watchdog/max77620_wdt.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c index cd321c7e0d59..b76ad6ba0915 100644 --- a/drivers/watchdog/max77620_wdt.c +++ b/drivers/watchdog/max77620_wdt.c @@ -123,6 +123,11 @@ static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev, break; } + /* + * "If the value of TWD needs to be changed, clear the system + * watchdog timer first [...], then change the value of TWD." + * (MAX77714 datasheet but applies to MAX77620 too) + */ ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3, wdt->drv_data->wdtc_mask, 0x1); if (ret < 0) -- cgit v1.2.3-71-gd317 From c58e496311a50c087eeeaaae81083cd643fe5128 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 23 Feb 2022 18:59:08 +0100 Subject: rtc: max77686: Add MAX77714 support The RTC included in the MAX77714 PMIC is very similar to the one in the MAX77686. Reuse the rtc-max77686.c driver with the minimum required changes for the MAX77714 RTC. Signed-off-by: Luca Ceresoli Reviewed-by: Krzysztof Kozlowski Acked-by: Alexandre Belloni Signed-off-by: Lee Jones --- drivers/rtc/Kconfig | 2 +- drivers/rtc/rtc-max77686.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d85a3c31347c..f6d6d4c26361 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -375,7 +375,7 @@ config RTC_DRV_MAX8997 config RTC_DRV_MAX77686 tristate "Maxim MAX77686" - depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST + depends on MFD_MAX77686 || MFD_MAX77620 || MFD_MAX77714 || COMPILE_TEST help If you say yes here you will get support for the RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC. diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 5c64d08c0732..b0250d91fb00 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -19,6 +19,7 @@ #define MAX77686_I2C_ADDR_RTC (0x0C >> 1) #define MAX77620_I2C_ADDR_RTC 0x68 +#define MAX77714_I2C_ADDR_RTC 0x48 #define MAX77686_INVALID_I2C_ADDR (-1) /* Define non existing register */ @@ -200,6 +201,28 @@ static const struct max77686_rtc_driver_data max77686_drv_data = { .regmap_config = &max77686_rtc_regmap_config, }; +static const struct regmap_irq_chip max77714_rtc_irq_chip = { + .name = "max77714-rtc", + .status_base = MAX77686_RTC_INT, + .mask_base = MAX77686_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs) - 1, /* no WTSR on 77714 */ +}; + +static const struct max77686_rtc_driver_data max77714_drv_data = { + .delay = 16000, + .mask = 0x7f, + .map = max77686_map, + .alarm_enable_reg = false, + .rtc_irq_from_platform = false, + /* On MAX77714 RTCA1 is BIT 1 of RTCINT (0x00). Not supported by this driver. */ + .alarm_pending_status_reg = MAX77686_INVALID_REG, + .rtc_i2c_addr = MAX77714_I2C_ADDR_RTC, + .rtc_irq_chip = &max77714_rtc_irq_chip, + .regmap_config = &max77686_rtc_regmap_config, +}; + static const struct regmap_config max77620_rtc_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -843,6 +866,7 @@ static const struct platform_device_id rtc_id[] = { { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, }, { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, }, { "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, }, + { "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data, }, {}, }; MODULE_DEVICE_TABLE(platform, rtc_id); -- cgit v1.2.3-71-gd317 From e477e51a41cb5d6034f3c5ea85a71ad4613996b9 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Thu, 24 Feb 2022 10:23:31 +0800 Subject: mfd: mc13xxx: Add check for mc13xxx_irq_request As the potential failure of the devm_request_threaded_irq(), it should be better to check the return value of the mc13xxx_irq_request() and return error if fails. Fixes: 8e00593557c3 ("mfd: Add mc13892 support to mc13xxx") Signed-off-by: Jiasheng Jiang Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220224022331.3208275-1-jiasheng@iscas.ac.cn --- drivers/mfd/mc13xxx-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 8a4f1d90dcfd..1000572761a8 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -323,8 +323,10 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, adc1 |= MC13783_ADC1_ATOX; dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__); - mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, + ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, mc13xxx_handler_adcdone, __func__, &adcdone_data); + if (ret) + goto out; mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0); mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1); -- cgit v1.2.3-71-gd317 From c47383f849097c2b3547e28365578cd9e5811378 Mon Sep 17 00:00:00 2001 From: Johnson Wang Date: Thu, 6 Jan 2022 14:54:04 +0800 Subject: mfd: Add support for the MediaTek MT6366 PMIC This adds support for the MediaTek MT6366 PMIC. This is a multifunction device with the following sub modules: - Regulator - RTC - Codec - Interrupt It is interfaced to the host controller using SPI interface by a proprietary hardware called PMIC wrapper or pwrap. MT6366 MFD is a child device of the pwrap. Signed-off-by: Johnson Wang Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220106065407.16036-2-johnson.wang@mediatek.com --- drivers/mfd/mt6358-irq.c | 1 + include/linux/mfd/mt6358/registers.h | 7 +++++++ include/linux/mfd/mt6397/core.h | 1 + 3 files changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c index 83f3ffbdbb4c..ea5e452510eb 100644 --- a/drivers/mfd/mt6358-irq.c +++ b/drivers/mfd/mt6358-irq.c @@ -212,6 +212,7 @@ int mt6358_irq_init(struct mt6397_chip *chip) switch (chip->chip_id) { case MT6358_CHIP_ID: + case MT6366_CHIP_ID: chip->irq_data = &mt6358_irqd; break; diff --git a/include/linux/mfd/mt6358/registers.h b/include/linux/mfd/mt6358/registers.h index 201139b12140..3d33517f178c 100644 --- a/include/linux/mfd/mt6358/registers.h +++ b/include/linux/mfd/mt6358/registers.h @@ -94,6 +94,10 @@ #define MT6358_BUCK_VCORE_CON0 0x1488 #define MT6358_BUCK_VCORE_DBG0 0x149e #define MT6358_BUCK_VCORE_DBG1 0x14a0 +#define MT6358_BUCK_VCORE_SSHUB_CON0 0x14a4 +#define MT6358_BUCK_VCORE_SSHUB_CON1 0x14a6 +#define MT6358_BUCK_VCORE_SSHUB_ELR0 MT6358_BUCK_VCORE_SSHUB_CON1 +#define MT6358_BUCK_VCORE_SSHUB_DBG1 MT6358_BUCK_VCORE_DBG1 #define MT6358_BUCK_VCORE_ELR0 0x14aa #define MT6358_BUCK_VGPU_CON0 0x1508 #define MT6358_BUCK_VGPU_DBG0 0x151e @@ -169,6 +173,9 @@ #define MT6358_LDO_VSRAM_OTHERS_CON0 0x1ba6 #define MT6358_LDO_VSRAM_OTHERS_DBG0 0x1bc0 #define MT6358_LDO_VSRAM_OTHERS_DBG1 0x1bc2 +#define MT6358_LDO_VSRAM_OTHERS_SSHUB_CON0 0x1bc4 +#define MT6358_LDO_VSRAM_OTHERS_SSHUB_CON1 0x1bc6 +#define MT6358_LDO_VSRAM_OTHERS_SSHUB_DBG1 MT6358_LDO_VSRAM_OTHERS_DBG1 #define MT6358_LDO_VSRAM_GPU_CON0 0x1bc8 #define MT6358_LDO_VSRAM_GPU_DBG0 0x1be2 #define MT6358_LDO_VSRAM_GPU_DBG1 0x1be4 diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h index 56f210eebc54..1cf78726503b 100644 --- a/include/linux/mfd/mt6397/core.h +++ b/include/linux/mfd/mt6397/core.h @@ -14,6 +14,7 @@ enum chip_id { MT6323_CHIP_ID = 0x23, MT6358_CHIP_ID = 0x58, MT6359_CHIP_ID = 0x59, + MT6366_CHIP_ID = 0x66, MT6391_CHIP_ID = 0x91, MT6397_CHIP_ID = 0x97, }; -- cgit v1.2.3-71-gd317 From 4946d58dbb3b35c239881eaedc236b8214bc254b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 Dec 2021 12:55:06 +0100 Subject: mfd: intel_soc_pmic_crc: Sort cells by IRQ order The cells for the Crystal Cove PMIC are already mostly sorted by function / IRQ order. Move the ADC cell so that they are fully sorted. Also move some of the resource definitions so that their order matches the (new) order of the cells. Signed-off-by: Hans de Goede Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20211225115509.94891-2-hdegoede@redhat.com --- drivers/mfd/intel_soc_pmic_crc.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index 38acb20e2d60..574cb8f9c70d 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -28,18 +28,10 @@ #define CRYSTAL_COVE_IRQ_GPIO 5 #define CRYSTAL_COVE_IRQ_VHDMIOCP 6 -static const struct resource gpio_resources[] = { - DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_GPIO, "GPIO"), -}; - static const struct resource pwrsrc_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_PWRSRC, "PWRSRC"), }; -static const struct resource adc_resources[] = { - DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_ADC, "ADC"), -}; - static const struct resource thermal_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_THRM, "THERMAL"), }; @@ -48,17 +40,20 @@ static const struct resource bcu_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_BCU, "BCU"), }; +static const struct resource adc_resources[] = { + DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_ADC, "ADC"), +}; + +static const struct resource gpio_resources[] = { + DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_GPIO, "GPIO"), +}; + static struct mfd_cell crystal_cove_byt_dev[] = { { .name = "crystal_cove_pwrsrc", .num_resources = ARRAY_SIZE(pwrsrc_resources), .resources = pwrsrc_resources, }, - { - .name = "crystal_cove_adc", - .num_resources = ARRAY_SIZE(adc_resources), - .resources = adc_resources, - }, { .name = "crystal_cove_thermal", .num_resources = ARRAY_SIZE(thermal_resources), @@ -69,6 +64,11 @@ static struct mfd_cell crystal_cove_byt_dev[] = { .num_resources = ARRAY_SIZE(bcu_resources), .resources = bcu_resources, }, + { + .name = "crystal_cove_adc", + .num_resources = ARRAY_SIZE(adc_resources), + .resources = adc_resources, + }, { .name = "crystal_cove_gpio", .num_resources = ARRAY_SIZE(gpio_resources), -- cgit v1.2.3-71-gd317 From 50904e9bd686a2b1fe4c98273a57ba048bd0f1c4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 Dec 2021 12:55:07 +0100 Subject: mfd: intel_soc_pmic_crc: Add crystal_cove_charger cell to BYT cells The Crystal Cove PMIC has a pin which can be used to connect the IRQ of an external charger IC. On some boards this is used and we need to have a cell for this, with a driver which creates its own irqchip with a single IRQ for the charger driver to consume. The charger driver cannot directly consume the IRQ from the MFD level irqchip because the PMIC has 2 levels of interrupts and the second level interrupt status register, which is handled by the cell drivers, needs to have the IRQ acked to avoid an IRQ storm. Signed-off-by: Hans de Goede Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20211225115509.94891-3-hdegoede@redhat.com --- drivers/mfd/intel_soc_pmic_crc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index 574cb8f9c70d..5bb0367bd974 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -44,6 +44,10 @@ static const struct resource adc_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_ADC, "ADC"), }; +static const struct resource charger_resources[] = { + DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_CHGR, "CHGR"), +}; + static const struct resource gpio_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_GPIO, "GPIO"), }; @@ -69,6 +73,11 @@ static struct mfd_cell crystal_cove_byt_dev[] = { .num_resources = ARRAY_SIZE(adc_resources), .resources = adc_resources, }, + { + .name = "crystal_cove_charger", + .num_resources = ARRAY_SIZE(charger_resources), + .resources = charger_resources, + }, { .name = "crystal_cove_gpio", .num_resources = ARRAY_SIZE(gpio_resources), -- cgit v1.2.3-71-gd317 From f49f43371b8a680c5972d052598d7c120f1a05d3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 Dec 2021 12:55:08 +0100 Subject: mfd: intel_soc_pmic_crc: Set main IRQ domain bus token to DOMAIN_BUS_NEXUS 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 Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20211225115509.94891-4-hdegoede@redhat.com --- drivers/mfd/intel_soc_pmic_core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index 47cb7f00dfcf..5e8c94e008ed 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -64,6 +64,10 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, /* 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)); -- cgit v1.2.3-71-gd317 From 10377bb293358c4fe06204ed596264f69f85102a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 4 Jan 2022 16:54:35 +0000 Subject: mfd: arizona: Add missing statics to the of_match_tables When the match tables were split for I2C and SPI a static should have been added since the tables are no longer exported. Fixes: 3f65555c417c ("mfd: arizona: Split of_match table into I2C and SPI versions") Reported-by: kernel test robot Signed-off-by: Charles Keepax Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220104165435.26782-1-ckeepax@opensource.cirrus.com --- drivers/mfd/arizona-i2c.c | 2 +- drivers/mfd/arizona-spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 3ed810e81f63..6d83e6b9a692 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -105,7 +105,7 @@ static const struct i2c_device_id arizona_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, arizona_i2c_id); #ifdef CONFIG_OF -const struct of_device_id arizona_i2c_of_match[] = { +static const struct of_device_id arizona_i2c_of_match[] = { { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 9fe06dda3782..98c87d3bd00f 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -226,7 +226,7 @@ static const struct spi_device_id arizona_spi_ids[] = { MODULE_DEVICE_TABLE(spi, arizona_spi_ids); #ifdef CONFIG_OF -const struct of_device_id arizona_spi_of_match[] = { +static const struct of_device_id arizona_spi_of_match[] = { { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, -- cgit v1.2.3-71-gd317 From 56f216d8efbc1212bf5ff8a6ff5e29927965e8db Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Tue, 8 Feb 2022 14:40:23 -0500 Subject: mfd: rk808: Add reboot support to rk808.c This adds reboot support to the rk808 pmic driver and enables it for the rk809 and rk817 devices. This only enables if the rockchip,system-power-controller flag is set. Signed-off-by: Peter Geis Signed-off-by: Frank Wunderlich Reviewed-by: Dmitry Osipenko Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220208194023.929720-1-pgwipeout@gmail.com --- drivers/mfd/rk808.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/rk808.h | 1 + 2 files changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index b181fe401330..4142b638e5fa 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -19,6 +19,7 @@ #include #include #include +#include struct rk808_reg_data { int addr; @@ -543,6 +544,7 @@ static void rk808_pm_power_off(void) reg = RK808_DEVCTRL_REG, bit = DEV_OFF_RST; break; + case RK809_ID: case RK817_ID: reg = RK817_SYS_CFG(3); bit = DEV_OFF; @@ -559,6 +561,34 @@ static void rk808_pm_power_off(void) dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n"); } +static int rk808_restart_notify(struct notifier_block *this, unsigned long mode, void *cmd) +{ + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); + unsigned int reg, bit; + int ret; + + switch (rk808->variant) { + case RK809_ID: + case RK817_ID: + reg = RK817_SYS_CFG(3); + bit = DEV_RST; + break; + + default: + return NOTIFY_DONE; + } + ret = regmap_update_bits(rk808->regmap, reg, bit, bit); + if (ret) + dev_err(&rk808_i2c_client->dev, "Failed to restart device!\n"); + + return NOTIFY_DONE; +} + +static struct notifier_block rk808_restart_handler = { + .notifier_call = rk808_restart_notify, + .priority = 192, +}; + static void rk8xx_shutdown(struct i2c_client *client) { struct rk808 *rk808 = i2c_get_clientdata(client); @@ -727,6 +757,18 @@ static int rk808_probe(struct i2c_client *client, if (of_property_read_bool(np, "rockchip,system-power-controller")) { rk808_i2c_client = client; pm_power_off = rk808_pm_power_off; + + switch (rk808->variant) { + case RK809_ID: + case RK817_ID: + ret = register_restart_handler(&rk808_restart_handler); + if (ret) + dev_warn(&client->dev, "failed to register rst handler, %d\n", ret); + break; + default: + dev_dbg(&client->dev, "pmic controlled board reset not supported\n"); + break; + } } return 0; @@ -749,6 +791,8 @@ static int rk808_remove(struct i2c_client *client) if (pm_power_off == rk808_pm_power_off) pm_power_off = NULL; + unregister_restart_handler(&rk808_restart_handler); + return 0; } diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index a96e6d43ca06..58602032e642 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -373,6 +373,7 @@ enum rk805_reg { #define SWITCH2_EN BIT(6) #define SWITCH1_EN BIT(5) #define DEV_OFF_RST BIT(3) +#define DEV_RST BIT(2) #define DEV_OFF BIT(0) #define RTC_STOP BIT(0) -- cgit v1.2.3-71-gd317 From c788f6e6aa112bf516d0f3f85ae6cc4e6c103239 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 14 Feb 2022 16:07:10 +0100 Subject: mfd: stmfx: Improve error message triggered by regulator fault in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returning a non-zero value in an i2c remove callback results in the i2c core emitting a very generic error message ("remove failed (-ESOMETHING), will be ignored") and as the message indicates not further error handling is done. Instead emit a more specific error message and then return zero in .remove(). The long-term goal is to make the i2c remove prototype return void, making all implementations return 0 is preparatory work for this change. Signed-off-by: Uwe Kleine-König Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220214150710.312269-1-u.kleine-koenig@pengutronix.de --- drivers/mfd/stmfx.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c index e095a3930142..122f96094410 100644 --- a/drivers/mfd/stmfx.c +++ b/drivers/mfd/stmfx.c @@ -392,17 +392,22 @@ err: return ret; } -static int stmfx_chip_exit(struct i2c_client *client) +static void stmfx_chip_exit(struct i2c_client *client) { struct stmfx *stmfx = i2c_get_clientdata(client); regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 0); regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 0); - if (stmfx->vdd) - return regulator_disable(stmfx->vdd); + if (stmfx->vdd) { + int ret; - return 0; + ret = regulator_disable(stmfx->vdd); + if (ret) + dev_err(&client->dev, + "Failed to disable vdd regulator: %pe\n", + ERR_PTR(ret)); + } } static int stmfx_probe(struct i2c_client *client, @@ -466,7 +471,9 @@ static int stmfx_remove(struct i2c_client *client) { stmfx_irq_exit(client); - return stmfx_chip_exit(client); + stmfx_chip_exit(client); + + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3-71-gd317 From 6ceb5f173ff15136f3d76e8d22aa8c6e784ea126 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 10 Feb 2022 21:42:23 +0100 Subject: mfd: sta2x11: Use GFP_KERNEL instead of GFP_ATOMIC pci_driver probe functions aren't called with locks held and thus don't need GFP_ATOMIC. Use GFP_KERNEL instead. Problem found with Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220210204223.104181-10-Julia.Lawall@inria.fr --- drivers/mfd/sta2x11-mfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 1819c8fe4d8f..02cc49daf2e3 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -590,7 +590,7 @@ static int sta2x11_mfd_probe(struct pci_dev *pdev, /* Record this pdev before mfd_add_devices: their probe looks for it */ if (!sta2x11_mfd_find(pdev)) - sta2x11_mfd_add(pdev, GFP_ATOMIC); + sta2x11_mfd_add(pdev, GFP_KERNEL); /* Just 2 bars for all mfd's at present */ for (i = 0; i < 2; i++) { -- cgit v1.2.3-71-gd317 From 3d4d1266597c0206f85c42c387eb942716dfad45 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 12 Feb 2022 22:47:24 +0100 Subject: mfd: ab8500: Drop debugfs module This debugfs isn't used by anyone, if we want to dump the contents of registers we should just convert the driver to regmap instead. I'm also excluding this from the device tree bindings when converting to YAML because it is not a real device, and device trees should only contain real physical devices. Delete it. Signed-off-by: Linus Walleij Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220212214724.681530-1-linus.walleij@linaro.org --- drivers/mfd/Kconfig | 8 - drivers/mfd/Makefile | 1 - drivers/mfd/ab8500-core.c | 20 - drivers/mfd/ab8500-debugfs.c | 2096 ------------------------------------------ 4 files changed, 2125 deletions(-) delete mode 100644 drivers/mfd/ab8500-debugfs.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f6678c07dc5e..3b59456f5545 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1297,14 +1297,6 @@ config AB8500_CORE the irq_chip parts for handling the Mixed Signal chip events. This chip embeds various other multimedia functionalities as well. -config AB8500_DEBUG - bool "Enable debug info via debugfs" - depends on AB8500_GPADC && DEBUG_FS - default y if DEBUG_FS - help - Select this option if you want debug information using the debug - filesystem, debugfs. - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index eea70cb9f9b9..858cacf659d6 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -177,7 +177,6 @@ obj-$(CONFIG_MFD_PCF50633) += pcf50633.o obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o -obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o # ab8500-core need to come after db8500-prcmu (which provides the channel) obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index cca0aac26148..6a059270acdc 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -613,10 +613,6 @@ static const struct mfd_cell ab8500_bm_devs[] = { }; static const struct mfd_cell ab8500_devs[] = { -#ifdef CONFIG_DEBUG_FS - MFD_CELL_OF("ab8500-debug", - NULL, NULL, 0, 0, "stericsson,ab8500-debug"), -#endif MFD_CELL_OF("ab8500-sysctrl", NULL, NULL, 0, 0, "stericsson,ab8500-sysctrl"), MFD_CELL_OF("ab8500-ext-regulator", @@ -652,11 +648,6 @@ static const struct mfd_cell ab8500_devs[] = { }; static const struct mfd_cell ab9540_devs[] = { -#ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - }, -#endif { .name = "ab8500-sysctrl", }, @@ -707,12 +698,6 @@ static const struct mfd_cell ab9540_devs[] = { /* Device list for ab8505 */ static const struct mfd_cell ab8505_devs[] = { -#ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - .of_compatible = "stericsson,ab8500-debug", - }, -#endif { .name = "ab8500-sysctrl", .of_compatible = "stericsson,ab8500-sysctrl", @@ -764,11 +749,6 @@ static const struct mfd_cell ab8505_devs[] = { }; static const struct mfd_cell ab8540_devs[] = { -#ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - }, -#endif { .name = "ab8500-sysctrl", }, diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c deleted file mode 100644 index e43dea89b094..000000000000 --- a/drivers/mfd/ab8500-debugfs.c +++ /dev/null @@ -1,2096 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Mattias Wallin for ST-Ericsson. - */ -/* - * AB8500 register access - * ====================== - * - * read: - * # echo BANK > /ab8500/register-bank - * # echo ADDR > /ab8500/register-address - * # cat /ab8500/register-value - * - * write: - * # echo BANK > /ab8500/register-bank - * # echo ADDR > /ab8500/register-address - * # echo VALUE > /ab8500/register-value - * - * read all registers from a bank: - * # echo BANK > /ab8500/register-bank - * # cat /ab8500/all-bank-register - * - * BANK target AB8500 register bank - * ADDR target AB8500 register address - * VALUE decimal or 0x-prefixed hexadecimal - * - * - * User Space notification on AB8500 IRQ - * ===================================== - * - * Allows user space entity to be notified when target AB8500 IRQ occurs. - * When subscribed, a sysfs entry is created in ab8500.i2c platform device. - * One can pool this file to get target IRQ occurence information. - * - * subscribe to an AB8500 IRQ: - * # echo IRQ > /ab8500/irq-subscribe - * - * unsubscribe from an AB8500 IRQ: - * # echo IRQ > /ab8500/irq-unsubscribe - * - * - * AB8500 register formated read/write access - * ========================================== - * - * Read: read data, data>>SHIFT, data&=MASK, output data - * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE - * Write: read data, data &= ~(MASK< [0xAB123F98] - * - * Usage: - * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg - * - * CMD read read access - * write write access - * - * BANK target reg bank - * ADDRESS target reg address - * VALUE (write) value to be updated - * - * OPTIONS - * -d|-dec (read) output in decimal - * -h|-hexa (read) output in 0x-hexa (default) - * -l|-w|-b 32bit (default), 16bit or 8bit reg access - * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF) - * -s|-shift SHIFT bit shift value (read:left, write:right) - * -o|-offset OFFSET address offset to add to ADDRESS value - * - * Warning: bit shift operation is applied to bit-mask. - * Warning: bit shift direction depends on read or right command. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef CONFIG_DEBUG_FS -#include -#include -#endif - -static u32 debug_bank; -static u32 debug_address; - -static int irq_ab8500; -static int irq_first; -static int irq_last; -static u32 *irq_count; -static int num_irqs; - -static struct device_attribute **dev_attr; -static char **event_name; - -/** - * struct ab8500_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab8500_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab8500_prcmu_ranges - * @num_ranges: the number of ranges in the list - * @bankid: bank identifier - * @range: the list of register ranges - */ -struct ab8500_prcmu_ranges { - u8 num_ranges; - u8 bankid; - const struct ab8500_reg_range *range; -}; - -/* hwreg- "mask" and "shift" entries ressources */ -struct hwreg_cfg { - u32 bank; /* target bank */ - unsigned long addr; /* target address */ - uint fmt; /* format */ - unsigned long mask; /* read/write mask, applied before any bit shift */ - long shift; /* bit shift (read:right shift, write:left shift */ -}; -/* fmt bit #0: 0=hexa, 1=dec */ -#define REG_FMT_DEC(c) ((c)->fmt & 0x1) -#define REG_FMT_HEX(c) (!REG_FMT_DEC(c)) - -static struct hwreg_cfg hwreg_cfg = { - .addr = 0, /* default: invalid phys addr */ - .fmt = 0, /* default: 32bit access, hex output */ - .mask = 0xFFFFFFFF, /* default: no mask */ - .shift = 0, /* default: no bit shift */ -}; - -#define AB8500_NAME_STRING "ab8500" -#define AB8500_NUM_BANKS AB8500_DEBUG_FIELD_LAST - -#define AB8500_REV_REG 0x80 - -static struct ab8500_prcmu_ranges *debug_ranges; - -static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { - [AB8500_M_FSM_RANK] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x02, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x17, - }, - { - .first = 0x30, - .last = 0x30, - }, - { - .first = 0x32, - .last = 0x33, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x03, - .last = 0x10, - }, - { - .first = 0x80, - .last = 0x84, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x22, - }, - { - .first = 0x40, - .last = 0x44, - }, - /* - * 0x80-0x8B are SIM registers and should - * not be accessed from here - */ - }, - }, - [AB8500_USB] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x12, - }, - { - .first = 0x15, - .last = 0x17, - }, - { - .first = 0x19, - .last = 0x21, - }, - { - .first = 0x27, - .last = 0x2C, - }, - { - .first = 0x41, - .last = 0x41, - }, - { - .first = 0x45, - .last = 0x5B, - }, - { - .first = 0x5D, - .last = 0x5D, - }, - { - .first = 0x69, - .last = 0x69, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_DBI] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_RESERVED] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_GPADC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x03, - }, - { - .first = 0x05, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x40, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x44, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x55, - }, - { - .first = 0x80, - .last = 0x82, - }, - { - .first = 0xC0, - .last = 0xC2, - }, - { - .first = 0xf5, - .last = 0xf6, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x6F, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_RTC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0F, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x05, - }, - { - .first = 0x10, - .last = 0x15, - }, - { - .first = 0x20, - .last = 0x25, - }, - { - .first = 0x30, - .last = 0x35, - }, - { - .first = 0x40, - .last = 0x45, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x67, - }, - { - .first = 0x80, - .last = 0x80, - }, - }, - }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x05, - .last = 0x07, - }, - }, - }, - [AB8500_PROD_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_STE_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x0F, - }, - }, - }, -}; - -static struct ab8500_prcmu_ranges ab8505_debug_ranges[AB8500_NUM_BANKS] = { - [0x0] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x52, - .last = 0x52, - }, - { - .first = 0x54, - .last = 0x57, - }, - { - .first = 0x80, - .last = 0x83, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x17, - }, - { - .first = 0x20, - .last = 0x20, - }, - { - .first = 0x30, - .last = 0x30, - }, - { - .first = 0x32, - .last = 0x3A, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x03, - .last = 0x11, - }, - { - .first = 0x80, - .last = 0x86, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 6, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x08, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x30, - }, - { - .first = 0x40, - .last = 0x48, - }, - /* - * 0x80-0x8B are SIM registers and should - * not be accessed from here - */ - }, - }, - [AB8500_USB] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - { - .first = 0x91, - .last = 0x94, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_DBI] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_RESERVED] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_GPADC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x02, - .last = 0x03, - }, - { - .first = 0x05, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x57, - }, - { - .first = 0x60, - .last = 0x60, - }, - { - .first = 0xA0, - .last = 0xA7, - }, - { - .first = 0xAF, - .last = 0xB2, - }, - { - .first = 0xC0, - .last = 0xC2, - }, - { - .first = 0xF5, - .last = 0xF5, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x83, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 11, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - }, - { - .first = 0x06, - .last = 0x07, - }, - { - .first = 0x09, - .last = 0x09, - }, - { - .first = 0x0B, - .last = 0x0C, - }, - { - .first = 0x12, - .last = 0x15, - }, - { - .first = 0x18, - .last = 0x18, - }, - /* Latch registers should not be read here */ - { - .first = 0x40, - .last = 0x44, - }, - { - .first = 0x46, - .last = 0x49, - }, - { - .first = 0x4B, - .last = 0x4D, - }, - { - .first = 0x52, - .last = 0x55, - }, - { - .first = 0x58, - .last = 0x58, - }, - /* LatchHier registers should not be read here */ - }, - }, - [AB8500_RTC] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x14, - }, - { - .first = 0x16, - .last = 0x17, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x10, - .last = 0x16, - }, - { - .first = 0x20, - .last = 0x26, - }, - { - .first = 0x30, - .last = 0x36, - }, - { - .first = 0x40, - .last = 0x46, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x6B, - }, - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x05, - .last = 0x05, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x05, - .last = 0x07, - }, - }, - }, - [AB8500_PROD_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_STE_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x15, - }, - }, - }, -}; - -static struct ab8500_prcmu_ranges ab8540_debug_ranges[AB8500_NUM_BANKS] = { - [AB8500_M_FSM_RANK] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0B, - }, - }, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 6, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x50, - .last = 0x54, - }, - { - .first = 0x57, - .last = 0x57, - }, - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x90, - .last = 0x90, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x10, - }, - { - .first = 0x20, - .last = 0x21, - }, - { - .first = 0x32, - .last = 0x3C, - }, - { - .first = 0x40, - .last = 0x42, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x03, - .last = 0x15, - }, - { - .first = 0x20, - .last = 0x20, - }, - { - .first = 0x80, - .last = 0x85, - }, - { - .first = 0x87, - .last = 0x88, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x08, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x2F, - }, - { - .first = 0x31, - .last = 0x3A, - }, - { - .first = 0x43, - .last = 0x44, - }, - { - .first = 0x48, - .last = 0x49, - }, - }, - }, - [AB8500_USB] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - { - .first = 0x91, - .last = 0x94, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 0, - .range = NULL - }, - [AB8500_DBI] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x07, - }, - { - .first = 0x10, - .last = 0x11, - }, - { - .first = 0x20, - .last = 0x21, - }, - { - .first = 0x30, - .last = 0x43, - }, - }, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x03, - }, - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_RESERVED] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_GPADC] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x01, - }, - { - .first = 0x04, - .last = 0x06, - }, - { - .first = 0x09, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 10, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x02, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x57, - }, - { - .first = 0x60, - .last = 0x60, - }, - { - .first = 0x70, - .last = 0x70, - }, - { - .first = 0xA0, - .last = 0xA9, - }, - { - .first = 0xAF, - .last = 0xB2, - }, - { - .first = 0xC0, - .last = 0xC6, - }, - { - .first = 0xF5, - .last = 0xF5, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x9f, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 6, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x05, - }, - { - .first = 0x0B, - .last = 0x0D, - }, - { - .first = 0x12, - .last = 0x20, - }, - /* Latch registers should not be read here */ - { - .first = 0x40, - .last = 0x45, - }, - { - .first = 0x4B, - .last = 0x4D, - }, - { - .first = 0x52, - .last = 0x60, - }, - /* LatchHier registers should not be read here */ - }, - }, - [AB8500_RTC] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x07, - }, - { - .first = 0x0B, - .last = 0x18, - }, - { - .first = 0x20, - .last = 0x25, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x10, - .last = 0x16, - }, - { - .first = 0x20, - .last = 0x26, - }, - { - .first = 0x30, - .last = 0x36, - }, - { - .first = 0x40, - .last = 0x49, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x6B, - }, - { - .first = 0x70, - .last = 0x74, - }, - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x01, - }, - { - .first = 0x06, - .last = 0x06, - }, - { - .first = 0x10, - .last = 0x21, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x0C, - }, - { - .first = 0x0E, - .last = 0x11, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_PROD_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_STE_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x3F, - }, - }, - }, -}; - -static irqreturn_t ab8500_debug_handler(int irq, void *data) -{ - char buf[16]; - struct kobject *kobj = (struct kobject *)data; - unsigned int irq_abb = irq - irq_first; - - if (irq_abb < num_irqs) - irq_count[irq_abb]++; - /* - * This makes it possible to use poll for events (EPOLLPRI | EPOLLERR) - * from userspace on sysfs file named - */ - sprintf(buf, "%d", irq); - sysfs_notify(kobj, NULL, buf); - - return IRQ_HANDLED; -} - -/* Prints to seq_file or log_buf */ -static int ab8500_registers_print(struct device *dev, u32 bank, - struct seq_file *s) -{ - unsigned int i; - - for (i = 0; i < debug_ranges[bank].num_ranges; i++) { - u32 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - int err; - - err = abx500_get_register_interruptible(dev, - (u8)bank, (u8)reg, &value); - if (err < 0) { - dev_err(dev, "ab->read fail %d\n", err); - return err; - } - - if (s) { - seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); - /* - * Error is not returned here since - * the output is wanted in any case - */ - if (seq_has_overflowed(s)) - return 0; - } else { - dev_info(dev, " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); - } - } - } - - return 0; -} - -static int ab8500_bank_registers_show(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - u32 bank = debug_bank; - - seq_puts(s, AB8500_NAME_STRING " register values:\n"); - - seq_printf(s, " bank 0x%02X:\n", bank); - - return ab8500_registers_print(dev, bank, s); -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_bank_registers); - -static int ab8500_print_all_banks(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - unsigned int i; - - seq_puts(s, AB8500_NAME_STRING " register values:\n"); - - for (i = 0; i < AB8500_NUM_BANKS; i++) { - int err; - - seq_printf(s, " bank 0x%02X:\n", i); - err = ab8500_registers_print(dev, i, s); - if (err) - return err; - } - return 0; -} - -/* Dump registers to kernel log */ -void ab8500_dump_all_banks(struct device *dev) -{ - unsigned int i; - - dev_info(dev, "ab8500 register values:\n"); - - for (i = 1; i < AB8500_NUM_BANKS; i++) { - dev_info(dev, " bank 0x%02X:\n", i); - ab8500_registers_print(dev, i, NULL); - } -} - -static int ab8500_all_banks_open(struct inode *inode, struct file *file) -{ - struct seq_file *s; - int err; - - err = single_open(file, ab8500_print_all_banks, inode->i_private); - if (!err) { - /* Default buf size in seq_read is not enough */ - s = (struct seq_file *)file->private_data; - s->size = (PAGE_SIZE * 2); - s->buf = kmalloc(s->size, GFP_KERNEL); - if (!s->buf) { - single_release(inode, file); - err = -ENOMEM; - } - } - return err; -} - -static const struct file_operations ab8500_all_banks_fops = { - .open = ab8500_all_banks_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_bank_print(struct seq_file *s, void *p) -{ - seq_printf(s, "0x%02X\n", debug_bank); - return 0; -} - -static int ab8500_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_bank_print, inode->i_private); -} - -static ssize_t ab8500_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_bank; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_bank); - if (err) - return err; - - if (user_bank >= AB8500_NUM_BANKS) { - dev_err(dev, "debugfs error input > number of banks\n"); - return -EINVAL; - } - - debug_bank = user_bank; - - return count; -} - -static int ab8500_address_print(struct seq_file *s, void *p) -{ - seq_printf(s, "0x%02X\n", debug_address); - return 0; -} - -static int ab8500_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_address_print, inode->i_private); -} - -static ssize_t ab8500_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_address; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_address); - if (err) - return err; - - if (user_address > 0xff) { - dev_err(dev, "debugfs error input > 0xff\n"); - return -EINVAL; - } - debug_address = user_address; - - return count; -} - -static int ab8500_val_print(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - int ret; - u8 regvalue; - - ret = abx500_get_register_interruptible(dev, - (u8)debug_bank, (u8)debug_address, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab8500_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_val_print, inode->i_private); -} - -static ssize_t ab8500_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val > 0xff) { - dev_err(dev, "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = abx500_set_register_interruptible(dev, - (u8)debug_bank, debug_address, (u8)user_val); - if (err < 0) { - pr_err("abx500_set_reg failed %d, %d", err, __LINE__); - return -EINVAL; - } - - return count; -} - -/* - * Interrupt status - */ -static u32 num_interrupts[AB8500_MAX_NR_IRQS]; -static u32 num_wake_interrupts[AB8500_MAX_NR_IRQS]; -static int num_interrupt_lines; - -void ab8500_debug_register_interrupt(int line) -{ - if (line < num_interrupt_lines) - num_interrupts[line]++; -} - -static int ab8500_interrupts_show(struct seq_file *s, void *p) -{ - int line; - - seq_puts(s, "name: number: irq: number of: wake:\n"); - - for (line = 0; line < num_interrupt_lines; line++) { - seq_printf(s, "%3i: %4i %6i %4i\n", - line, - line + irq_first, - num_interrupts[line], - num_wake_interrupts[line]); - } - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_interrupts); - -/* - * - HWREG DB8500 formated routines - */ -static int ab8500_hwreg_print(struct seq_file *s, void *d) -{ - struct device *dev = s->private; - int ret; - u8 regvalue; - - ret = abx500_get_register_interruptible(dev, - (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - - if (hwreg_cfg.shift >= 0) - regvalue >>= hwreg_cfg.shift; - else - regvalue <<= -hwreg_cfg.shift; - regvalue &= hwreg_cfg.mask; - - if (REG_FMT_DEC(&hwreg_cfg)) - seq_printf(s, "%d\n", regvalue); - else - seq_printf(s, "0x%02X\n", regvalue); - return 0; -} - -static int ab8500_hwreg_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_hwreg_print, inode->i_private); -} - -#define AB8500_SUPPLY_CONTROL_CONFIG_1 0x01 -#define AB8500_SUPPLY_CONTROL_REG 0x00 -#define AB8500_FIRST_SIM_REG 0x80 -#define AB8500_LAST_SIM_REG 0x8B -#define AB8505_LAST_SIM_REG 0x8C - -static int ab8500_modem_show(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - struct ab8500 *ab8500; - int err; - u8 value; - u8 orig_value; - u32 bank = AB8500_REGU_CTRL2; - u32 last_sim_reg = AB8500_LAST_SIM_REG; - u32 reg; - - ab8500 = dev_get_drvdata(dev->parent); - dev_warn(dev, "WARNING! This operation can interfer with modem side\n" - "and should only be done with care\n"); - - err = abx500_get_register_interruptible(dev, - AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, &orig_value); - if (err < 0) - goto report_read_failure; - - /* Config 1 will allow APE side to read SIM registers */ - err = abx500_set_register_interruptible(dev, - AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, - AB8500_SUPPLY_CONTROL_CONFIG_1); - if (err < 0) - goto report_write_failure; - - seq_printf(s, " bank 0x%02X:\n", bank); - - if (is_ab9540(ab8500) || is_ab8505(ab8500)) - last_sim_reg = AB8505_LAST_SIM_REG; - - for (reg = AB8500_FIRST_SIM_REG; reg <= last_sim_reg; reg++) { - err = abx500_get_register_interruptible(dev, - bank, reg, &value); - if (err < 0) - goto report_read_failure; - - seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", bank, reg, value); - } - err = abx500_set_register_interruptible(dev, - AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, orig_value); - if (err < 0) - goto report_write_failure; - - return 0; - -report_read_failure: - dev_err(dev, "ab->read fail %d\n", err); - return err; - -report_write_failure: - dev_err(dev, "ab->write fail %d\n", err); - return err; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_modem); - -/* - * return length of an ASCII numerical value, 0 is string is not a - * numerical value. - * string shall start at value 1st char. - * string can be tailed with \0 or space or newline chars only. - * value can be decimal or hexadecimal (prefixed 0x or 0X). - */ -static int strval_len(char *b) -{ - char *s = b; - - if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) { - s += 2; - for (; *s && (*s != ' ') && (*s != '\n'); s++) { - if (!isxdigit(*s)) - return 0; - } - } else { - if (*s == '-') - s++; - for (; *s && (*s != ' ') && (*s != '\n'); s++) { - if (!isdigit(*s)) - return 0; - } - } - return (int) (s-b); -} - -/* - * parse hwreg input data. - * update global hwreg_cfg only if input data syntax is ok. - */ -static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg, - struct device *dev) -{ - uint write, val = 0; - u8 regvalue; - int ret; - struct hwreg_cfg loc = { - .bank = 0, /* default: invalid phys addr */ - .addr = 0, /* default: invalid phys addr */ - .fmt = 0, /* default: 32bit access, hex output */ - .mask = 0xFFFFFFFF, /* default: no mask */ - .shift = 0, /* default: no bit shift */ - }; - - /* read or write ? */ - if (!strncmp(b, "read ", 5)) { - write = 0; - b += 5; - } else if (!strncmp(b, "write ", 6)) { - write = 1; - b += 6; - } else - return -EINVAL; - - /* OPTIONS -l|-w|-b -s -m -o */ - while ((*b == ' ') || (*b == '-')) { - if (*(b-1) != ' ') { - b++; - continue; - } - if ((!strncmp(b, "-d ", 3)) || - (!strncmp(b, "-dec ", 5))) { - b += (*(b+2) == ' ') ? 3 : 5; - loc.fmt |= (1<<0); - } else if ((!strncmp(b, "-h ", 3)) || - (!strncmp(b, "-hex ", 5))) { - b += (*(b+2) == ' ') ? 3 : 5; - loc.fmt &= ~(1<<0); - } else if ((!strncmp(b, "-m ", 3)) || - (!strncmp(b, "-mask ", 6))) { - b += (*(b+2) == ' ') ? 3 : 6; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtoul(b, 0, &loc.mask); - if (ret) - return ret; - } else if ((!strncmp(b, "-s ", 3)) || - (!strncmp(b, "-shift ", 7))) { - b += (*(b+2) == ' ') ? 3 : 7; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtol(b, 0, &loc.shift); - if (ret) - return ret; - } else { - return -EINVAL; - } - } - /* get arg BANK and ADDRESS */ - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtouint(b, 0, &loc.bank); - if (ret) - return ret; - while (*b == ' ') - b++; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtoul(b, 0, &loc.addr); - if (ret) - return ret; - - if (write) { - while (*b == ' ') - b++; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtouint(b, 0, &val); - if (ret) - return ret; - } - - /* args are ok, update target cfg (mainly for read) */ - *cfg = loc; - -#ifdef ABB_HWREG_DEBUG - pr_warn("HWREG request: %s, %s,\n", (write) ? "write" : "read", - REG_FMT_DEC(cfg) ? "decimal" : "hexa"); - pr_warn(" addr=0x%08X, mask=0x%X, shift=%d" "value=0x%X\n", - cfg->addr, cfg->mask, cfg->shift, val); -#endif - - if (!write) - return 0; - - ret = abx500_get_register_interruptible(dev, - (u8)cfg->bank, (u8)cfg->addr, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - - if (cfg->shift >= 0) { - regvalue &= ~(cfg->mask << (cfg->shift)); - val = (val & cfg->mask) << (cfg->shift); - } else { - regvalue &= ~(cfg->mask >> (-cfg->shift)); - val = (val & cfg->mask) >> (-cfg->shift); - } - val = val | regvalue; - - ret = abx500_set_register_interruptible(dev, - (u8)cfg->bank, (u8)cfg->addr, (u8)val); - if (ret < 0) { - pr_err("abx500_set_reg failed %d, %d", ret, __LINE__); - return -EINVAL; - } - - return 0; -} - -static ssize_t ab8500_hwreg_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - char buf[128]; - int buf_size, ret; - - /* Get userspace string and assure termination */ - buf_size = min((int)count, (int)(sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - /* get args and process */ - ret = hwreg_common_write(buf, &hwreg_cfg, dev); - return (ret) ? ret : buf_size; -} - -/* - * - irq subscribe/unsubscribe stuff - */ -static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", irq_first); - - return 0; -} - -static int ab8500_subscribe_unsubscribe_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_subscribe_unsubscribe_print, - inode->i_private); -} - -/* - * Userspace should use poll() on this file. When an event occur - * the blocking poll will be released. - */ -static ssize_t show_irq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long name; - unsigned int irq_index; - int err; - - err = kstrtoul(attr->attr.name, 0, &name); - if (err) - return err; - - irq_index = name - irq_first; - if (irq_index >= num_irqs) - return -EINVAL; - - return sprintf(buf, "%u\n", irq_count[irq_index]); -} - -static ssize_t ab8500_subscribe_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - unsigned int irq_index; - - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val < irq_first) { - dev_err(dev, "debugfs error input < %d\n", irq_first); - return -EINVAL; - } - if (user_val > irq_last) { - dev_err(dev, "debugfs error input > %d\n", irq_last); - return -EINVAL; - } - - irq_index = user_val - irq_first; - if (irq_index >= num_irqs) - return -EINVAL; - - /* - * This will create a sysfs file named which userspace can - * use to select or poll and get the AB8500 events - */ - dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute), - GFP_KERNEL); - if (!dev_attr[irq_index]) - return -ENOMEM; - - event_name[irq_index] = kasprintf(GFP_KERNEL, "%lu", user_val); - if (!event_name[irq_index]) - return -ENOMEM; - - dev_attr[irq_index]->show = show_irq; - dev_attr[irq_index]->store = NULL; - dev_attr[irq_index]->attr.name = event_name[irq_index]; - dev_attr[irq_index]->attr.mode = S_IRUGO; - err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr); - if (err < 0) { - pr_info("sysfs_create_file failed %d\n", err); - return err; - } - - err = request_threaded_irq(user_val, NULL, ab8500_debug_handler, - IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, - "ab8500-debug", &dev->kobj); - if (err < 0) { - pr_info("request_threaded_irq failed %d, %lu\n", - err, user_val); - sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); - return err; - } - - return count; -} - -static ssize_t ab8500_unsubscribe_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - unsigned int irq_index; - - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val < irq_first) { - dev_err(dev, "debugfs error input < %d\n", irq_first); - return -EINVAL; - } - if (user_val > irq_last) { - dev_err(dev, "debugfs error input > %d\n", irq_last); - return -EINVAL; - } - - irq_index = user_val - irq_first; - if (irq_index >= num_irqs) - return -EINVAL; - - /* Set irq count to 0 when unsubscribe */ - irq_count[irq_index] = 0; - - if (dev_attr[irq_index]) - sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); - - - free_irq(user_val, &dev->kobj); - kfree(event_name[irq_index]); - kfree(dev_attr[irq_index]); - - return count; -} - -/* - * - several debugfs nodes fops - */ - -static const struct file_operations ab8500_bank_fops = { - .open = ab8500_bank_open, - .write = ab8500_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_address_fops = { - .open = ab8500_address_open, - .write = ab8500_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_val_fops = { - .open = ab8500_val_open, - .write = ab8500_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_subscribe_fops = { - .open = ab8500_subscribe_unsubscribe_open, - .write = ab8500_subscribe_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_unsubscribe_fops = { - .open = ab8500_subscribe_unsubscribe_open, - .write = ab8500_unsubscribe_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_hwreg_fops = { - .open = ab8500_hwreg_open, - .write = ab8500_hwreg_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_debug_probe(struct platform_device *plf) -{ - struct dentry *ab8500_dir; - struct ab8500 *ab8500; - struct resource *res; - - debug_bank = AB8500_MISC; - debug_address = AB8500_REV_REG & 0x00FF; - - ab8500 = dev_get_drvdata(plf->dev.parent); - num_irqs = ab8500->mask_size; - - irq_count = devm_kcalloc(&plf->dev, - num_irqs, sizeof(*irq_count), GFP_KERNEL); - if (!irq_count) - return -ENOMEM; - - dev_attr = devm_kcalloc(&plf->dev, - num_irqs, sizeof(*dev_attr), GFP_KERNEL); - if (!dev_attr) - return -ENOMEM; - - event_name = devm_kcalloc(&plf->dev, - num_irqs, sizeof(*event_name), GFP_KERNEL); - if (!event_name) - return -ENOMEM; - - res = platform_get_resource_byname(plf, 0, "IRQ_AB8500"); - if (!res) { - dev_err(&plf->dev, "AB8500 irq not found, err %d\n", irq_first); - return -ENXIO; - } - irq_ab8500 = res->start; - - irq_first = platform_get_irq_byname(plf, "IRQ_FIRST"); - if (irq_first < 0) - return irq_first; - - irq_last = platform_get_irq_byname(plf, "IRQ_LAST"); - if (irq_last < 0) - return irq_last; - - ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); - - debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, - &plf->dev, &ab8500_bank_registers_fops); - debugfs_create_file("all-banks", S_IRUGO, ab8500_dir, - &plf->dev, &ab8500_all_banks_fops); - debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_bank_fops); - debugfs_create_file("register-address", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_address_fops); - debugfs_create_file("register-value", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_val_fops); - debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_subscribe_fops); - - if (is_ab8500(ab8500)) { - debug_ranges = ab8500_debug_ranges; - num_interrupt_lines = AB8500_NR_IRQS; - } else if (is_ab8505(ab8500)) { - debug_ranges = ab8505_debug_ranges; - num_interrupt_lines = AB8505_NR_IRQS; - } else if (is_ab9540(ab8500)) { - debug_ranges = ab8505_debug_ranges; - num_interrupt_lines = AB9540_NR_IRQS; - } else if (is_ab8540(ab8500)) { - debug_ranges = ab8540_debug_ranges; - num_interrupt_lines = AB8540_NR_IRQS; - } - - debugfs_create_file("interrupts", (S_IRUGO), ab8500_dir, &plf->dev, - &ab8500_interrupts_fops); - debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops); - debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR | S_IWGRP), ab8500_dir, - &plf->dev, &ab8500_hwreg_fops); - debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_modem_fops); - - return 0; -} - -static struct platform_driver ab8500_debug_driver = { - .driver = { - .name = "ab8500-debug", - .suppress_bind_attrs = true, - }, - .probe = ab8500_debug_probe, -}; - -static int __init ab8500_debug_init(void) -{ - return platform_driver_register(&ab8500_debug_driver); -} -subsys_initcall(ab8500_debug_init); -- cgit v1.2.3-71-gd317 From 8c70bd5802f4f51979f3b8a887088837edd56341 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 11 Feb 2022 16:50:55 +0200 Subject: mfd: intel-lpss: Add Intel Raptor Lake PCH-S PCI IDs Add Intel Raptor Lake PCH-S LPSS PCI IDs. Signed-off-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220211145055.992179-1-jarkko.nikula@linux.intel.com --- drivers/mfd/intel-lpss-pci.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 5513fae6be92..962ee14c62dd 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -353,6 +353,21 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info }, + /* RPL-S */ + { PCI_VDEVICE(INTEL, 0x7a28), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a29), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7a4c), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a4d), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a4e), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a4f), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a5c), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7a7c), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a7d), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a7e), (kernel_ulong_t)&bxt_uart_info }, /* ADL-S */ { PCI_VDEVICE(INTEL, 0x7aa8), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x7aa9), (kernel_ulong_t)&bxt_uart_info }, -- cgit v1.2.3-71-gd317 From 8d73aa0e6b75761f1cd32d58aa115bf5382c567e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 2 Feb 2022 16:13:09 +0100 Subject: mfd: exynos-lpass: Drop unneeded syscon.h include syscon regmap is not used since commit addebf1588ab ("mfd: exynos-lpass: Remove pad retention control"). Fixes: addebf1588ab ("mfd: exynos-lpass: Remove pad retention control") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Alim Akhtar Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220202151310.285561-3-krzysztof.kozlowski@canonical.com --- drivers/mfd/exynos-lpass.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 99bd0e73c19c..166cd21088cd 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-71-gd317 From 748718bcc0a2bc3bdc03ea86819bdf60dc6412e7 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 7 Dec 2021 13:15:15 +0200 Subject: mfd: atmel-flexcom: Fix compilation warning Fix "unused variable 'atmel_flexcom_pm_ops' [-Wunused-const-variable]" compilation warning by using __maybe_unused on PM ops. Reported-by: kernel test robot Signed-off-by: Claudiu Beznea Acked-by: Nicolas Ferre Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20211207111515.814653-1-claudiu.beznea@microchip.com --- drivers/mfd/atmel-flexcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c index 559eb4d352b6..33caa4fba6af 100644 --- a/drivers/mfd/atmel-flexcom.c +++ b/drivers/mfd/atmel-flexcom.c @@ -105,7 +105,7 @@ static int __maybe_unused atmel_flexcom_resume_noirq(struct device *dev) return 0; } -static const struct dev_pm_ops atmel_flexcom_pm_ops = { +static const struct dev_pm_ops __maybe_unused atmel_flexcom_pm_ops = { .resume_noirq = atmel_flexcom_resume_noirq, }; -- cgit v1.2.3-71-gd317 From 5a2d27c283db049e46c7fac5092ea45c6add2d47 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 1 Mar 2022 22:50:56 +0200 Subject: mfd: intel-lpss: Provide an SSP type to the driver The SPI driver wants to know the exact type of the controller. Provide this information to it, hence it allows to fix the Intel Cannon Lake and others in the future. Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220301205056.79810-1-andriy.shevchenko@linux.intel.com --- drivers/mfd/intel-lpss-acpi.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index f2ea6540a01e..a143c8dca2d9 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -15,11 +15,22 @@ #include #include #include +#include #include "intel-lpss.h" +static const struct property_entry spt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_SPT_SSP), + { } +}; + +static const struct software_node spt_spi_node = { + .properties = spt_spi_properties, +}; + static const struct intel_lpss_platform_info spt_info = { .clk_rate = 120000000, + .swnode = &spt_spi_node, }; static const struct property_entry spt_i2c_properties[] = { @@ -53,8 +64,18 @@ static const struct intel_lpss_platform_info spt_uart_info = { .swnode = &uart_node, }; +static const struct property_entry bxt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BXT_SSP), + { } +}; + +static const struct software_node bxt_spi_node = { + .properties = bxt_spi_properties, +}; + static const struct intel_lpss_platform_info bxt_info = { .clk_rate = 100000000, + .swnode = &bxt_spi_node, }; static const struct property_entry bxt_i2c_properties[] = { @@ -89,6 +110,20 @@ static const struct intel_lpss_platform_info apl_i2c_info = { .swnode = &apl_i2c_node, }; +static const struct property_entry cnl_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_CNL_SSP), + { } +}; + +static const struct software_node cnl_spi_node = { + .properties = cnl_spi_properties, +}; + +static const struct intel_lpss_platform_info cnl_info = { + .clk_rate = 120000000, + .swnode = &cnl_spi_node, +}; + static const struct intel_lpss_platform_info cnl_i2c_info = { .clk_rate = 216000000, .swnode = &spt_i2c_node, @@ -108,8 +143,8 @@ static const struct acpi_device_id intel_lpss_acpi_ids[] = { { "INT3449", (kernel_ulong_t)&spt_uart_info }, { "INT344A", (kernel_ulong_t)&spt_uart_info }, /* CNL */ - { "INT34B0", (kernel_ulong_t)&spt_info }, - { "INT34B1", (kernel_ulong_t)&spt_info }, + { "INT34B0", (kernel_ulong_t)&cnl_info }, + { "INT34B1", (kernel_ulong_t)&cnl_info }, { "INT34B2", (kernel_ulong_t)&cnl_i2c_info }, { "INT34B3", (kernel_ulong_t)&cnl_i2c_info }, { "INT34B4", (kernel_ulong_t)&cnl_i2c_info }, @@ -119,7 +154,7 @@ static const struct acpi_device_id intel_lpss_acpi_ids[] = { { "INT34B8", (kernel_ulong_t)&spt_uart_info }, { "INT34B9", (kernel_ulong_t)&spt_uart_info }, { "INT34BA", (kernel_ulong_t)&spt_uart_info }, - { "INT34BC", (kernel_ulong_t)&spt_info }, + { "INT34BC", (kernel_ulong_t)&cnl_info }, /* BXT */ { "80860AAC", (kernel_ulong_t)&bxt_i2c_info }, { "80860ABC", (kernel_ulong_t)&bxt_info }, -- cgit v1.2.3-71-gd317 From e84ee1a75f944a0fe3c277aaa10c426603d2b0bc Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 7 Mar 2022 07:29:47 +0000 Subject: mfd: asic3: Add missing iounmap() on error asic3_mfd_probe Add the missing iounmap() before return from asic3_mfd_probe in the error handling case. Fixes: 64e8867ba809 ("mfd: tmio_mmc hardware abstraction for CNF area") Signed-off-by: Miaoqian Lin Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220307072947.5369-1-linmq006@gmail.com --- drivers/mfd/asic3.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 8d58c8df46cf..56338f9dbd0b 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -906,14 +906,14 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_ds1wm, 1, mem, asic->irq_base, NULL); if (ret < 0) - goto out; + goto out_unmap; } if (mem_sdio && (irq >= 0)) { ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_mmc, 1, mem_sdio, irq, NULL); if (ret < 0) - goto out; + goto out_unmap; } ret = 0; @@ -927,8 +927,12 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, ret = mfd_add_devices(&pdev->dev, 0, asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0, NULL); } + return ret; - out: +out_unmap: + if (asic->tmio_cnf) + iounmap(asic->tmio_cnf); +out: return ret; } -- cgit v1.2.3-71-gd317 From 3cf2b4344b651b94d62411963e666c9cb5b99e62 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 Mar 2022 18:38:43 +0100 Subject: mfd: arizona-spi: Split Windows ACPI init code into its own function x86/ACPI boards with an arizona WM5102 codec ship with either Windows or Android as factory installed OS. The ACPI fwnode describing the codec differs depending on the factory OS, and the current arizona_spi_acpi_probe() function is tailored for use with the Windows board ACPI tables. Split out the Windows board ACPI tables specific bits into a new arizona_spi_acpi_windows_probe() function in preparation for also adding support for the Android board ACPI tables. Signed-off-by: Hans de Goede Acked-by: Charles Keepax Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220307173844.199135-1-hdegoede@redhat.com --- drivers/mfd/arizona-spi.c | 52 +++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 98c87d3bd00f..6f8076439e74 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -43,31 +43,13 @@ static const struct gpiod_lookup arizona_soc_gpios[] = { { "arizona", 4, "wlf,micd-pol", 0, GPIO_ACTIVE_LOW }, }; -/* - * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: - * Function A Play/Pause: 0 ohm - * Function D Voice assistant: 135 ohm - * Function B Volume Up 240 ohm - * Function C Volume Down 470 ohm - * Minimum Mic DC resistance 1000 ohm - * Minimum Ear speaker impedance 16 ohm - * Note the first max value below must be less then the min. speaker impedance, - * to allow CTIA/OMTP detection to work. The other max values are the closest - * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. - */ -static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { - { .max = 11, .key = KEY_PLAYPAUSE }, - { .max = 186, .key = KEY_VOICECOMMAND }, - { .max = 348, .key = KEY_VOLUMEUP }, - { .max = 752, .key = KEY_VOLUMEDOWN }, -}; - static void arizona_spi_acpi_remove_lookup(void *lookup) { gpiod_remove_lookup_table(lookup); } -static int arizona_spi_acpi_probe(struct arizona *arizona) +/* For ACPI tables from boards which ship with Windows as factory OS */ +static int arizona_spi_acpi_windows_probe(struct arizona *arizona) { struct gpiod_lookup_table *lookup; acpi_status status; @@ -96,6 +78,36 @@ static int arizona_spi_acpi_probe(struct arizona *arizona) if (ACPI_FAILURE(status)) dev_warn(arizona->dev, "Failed to enable 32KHz clk ACPI error %d\n", status); + return 0; +} + +/* + * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: + * Function A Play/Pause: 0 ohm + * Function D Voice assistant: 135 ohm + * Function B Volume Up 240 ohm + * Function C Volume Down 470 ohm + * Minimum Mic DC resistance 1000 ohm + * Minimum Ear speaker impedance 16 ohm + * Note the first max value below must be less then the min. speaker impedance, + * to allow CTIA/OMTP detection to work. The other max values are the closest + * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. + */ +static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { + { .max = 11, .key = KEY_PLAYPAUSE }, + { .max = 186, .key = KEY_VOICECOMMAND }, + { .max = 348, .key = KEY_VOLUMEUP }, + { .max = 752, .key = KEY_VOLUMEDOWN }, +}; + +static int arizona_spi_acpi_probe(struct arizona *arizona) +{ + int ret; + + ret = arizona_spi_acpi_windows_probe(arizona); + if (ret) + return ret; + /* * Some DSDTs wrongly declare the IRQ trigger-type as IRQF_TRIGGER_FALLING * The IRQ line will stay low when a new IRQ event happens between reading -- cgit v1.2.3-71-gd317 From 9ed55e9ac536a3d60e5dd75def3e9158d1d09f5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 Mar 2022 18:38:44 +0100 Subject: mfd: arizona-spi: Add Android board ACPI table handling x86/ACPI boards with an arizona WM5102 codec ship with either Windows or Android as factory installed OS. The ACPI fwnode for the codec on Android boards misses 2 things compared to the Windows boards (this is hardcoded in the Android board kernels): 1. There is no CLKE ACPI method to enabe the 32 KHz clock the codec needs for jack-detection. 2. The GPIOs used by the codec are not listed in the fwnode for the codec. The ACPI tables on x86/ACPI boards shipped with Android being incomplete happens a lot. The special drivers/platform/x86/x86-android-tablets.c module contains DMI based per model handling to compensate for this. This module will enable the 32KHz clock through the pinctrl framework to fix 1. and it will also register a gpio-lookup table for all GPIOs needed by the codec + machine driver, including the GPIOs coming from the codec itself. Add an arizona_spi_acpi_android_probe() function which waits for the x86-android-tablets to have set things up before continue with probing the arizona WM5102 codec. Acked-by: Charles Keepax Signed-off-by: Hans de Goede Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220307173844.199135-2-hdegoede@redhat.com --- drivers/mfd/arizona-spi.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 6f8076439e74..16a675149b11 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -81,6 +81,30 @@ static int arizona_spi_acpi_windows_probe(struct arizona *arizona) return 0; } +/* For ACPI tables from boards which ship with Android as factory OS */ +static int arizona_spi_acpi_android_probe(struct arizona *arizona) +{ + int ret; + + /* + * Get the reset GPIO, treating -ENOENT as -EPROBE_DEFER to wait for + * the x86-android-tablets module to register the board specific GPIO + * lookup table. + */ + arizona->pdata.reset = devm_gpiod_get(arizona->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(arizona->pdata.reset)) { + ret = PTR_ERR(arizona->pdata.reset); + if (ret == -ENOENT) { + dev_info_once(arizona->dev, + "Deferring probe till GPIO lookup is registered\n"); + ret = -EPROBE_DEFER; + } + return dev_err_probe(arizona->dev, ret, "getting reset GPIO\n"); + } + + return 0; +} + /* * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: * Function A Play/Pause: 0 ohm @@ -102,9 +126,14 @@ static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { static int arizona_spi_acpi_probe(struct arizona *arizona) { + struct acpi_device *adev = ACPI_COMPANION(arizona->dev); int ret; - ret = arizona_spi_acpi_windows_probe(arizona); + if (acpi_dev_hid_uid_match(adev, "10WM5102", NULL)) + ret = arizona_spi_acpi_android_probe(arizona); + else + ret = arizona_spi_acpi_windows_probe(arizona); + if (ret) return ret; @@ -142,6 +171,10 @@ static const struct acpi_device_id arizona_acpi_match[] = { .id = "WM510205", .driver_data = WM5102, }, + { + .id = "10WM5102", + .driver_data = WM5102, + }, { } }; MODULE_DEVICE_TABLE(acpi, arizona_acpi_match); -- cgit v1.2.3-71-gd317 From fc832ac98752950e267081d1ea6048d948969037 Mon Sep 17 00:00:00 2001 From: "Minghao Chi (CGEL ZTE)" Date: Wed, 9 Mar 2022 03:56:07 +0000 Subject: mfd: Use platform_get_irq() to get the interrupt It is not recommened to use platform_get_resource(pdev, IORESOURCE_IRQ) for requesting IRQ's resources any more, as they can be not ready yet in case of DT-booting. platform_get_irq() instead is a recommended way for getting IRQ even if it was not retrieved earlier. It also makes code simpler because we're getting "int" value right away and no conversion from resource to int is required. The print function dev_err() is redundant because platform_get_irq() already prints an error. Reported-by: Zeal Robot Signed-off-by: Minghao Chi (CGEL ZTE) Reviewed-by: Linus Walleij Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220309035607.2080884-1-chi.minghao@zte.com.cn --- drivers/mfd/ab8500-core.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 6a059270acdc..9d9e9787d5e8 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1022,9 +1022,9 @@ static int ab8500_probe(struct platform_device *pdev) enum ab8500_version version = AB8500_VERSION_UNDEFINED; struct device_node *np = pdev->dev.of_node; struct ab8500 *ab8500; - struct resource *resource; int ret; int i; + int irq; u8 value; ab8500 = devm_kzalloc(&pdev->dev, sizeof(*ab8500), GFP_KERNEL); @@ -1033,13 +1033,11 @@ static int ab8500_probe(struct platform_device *pdev) ab8500->dev = &pdev->dev; - resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!resource) { - dev_err(&pdev->dev, "no IRQ resource\n"); - return -ENODEV; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; - ab8500->irq = resource->start; + ab8500->irq = irq; ab8500->read = ab8500_prcmu_read; ab8500->write = ab8500_prcmu_write; -- cgit v1.2.3-71-gd317 From def387a2395708ccba333ccc0cea82ef5108f9be Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 14 Mar 2022 12:53:52 +0100 Subject: mfd: bd9576: fix typos in comments Various spelling mistakes in comments. Detected with the help of Coccinelle. Signed-off-by: Julia Lawall Reviewed-by: Matti Vaittinen Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220314115354.144023-29-Julia.Lawall@inria.fr --- drivers/mfd/rohm-bd9576.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/rohm-bd9576.c b/drivers/mfd/rohm-bd9576.c index 6661a27d69a8..f37cd4f27aeb 100644 --- a/drivers/mfd/rohm-bd9576.c +++ b/drivers/mfd/rohm-bd9576.c @@ -23,7 +23,7 @@ enum { }; /* - * Due to the BD9576MUF nasty IRQ behaiour we don't always populate IRQs. + * Due to the BD9576MUF nasty IRQ behaviour we don't always populate IRQs. * These will be added to regulator resources only if IRQ information for the * PMIC is populated in device-tree. */ -- cgit v1.2.3-71-gd317