diff options
Diffstat (limited to 'drivers')
38 files changed, 1587 insertions, 1730 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 13d135257e06..f5496ca0c0d2 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -240,12 +240,12 @@ config INPUT_MAX77650_ONKEY will be called max77650-onkey. config INPUT_MAX77693_HAPTIC - tristate "MAXIM MAX77693/MAX77843 haptic controller support" - depends on (MFD_MAX77693 || MFD_MAX77843) && PWM + tristate "MAXIM MAX77693/MAX77705/MAX77843 haptic controller support" + depends on (MFD_MAX77693 || MFD_MAX77705 || MFD_MAX77843) && PWM select INPUT_FF_MEMLESS help This option enables support for the haptic controller on - MAXIM MAX77693 and MAX77843 chips. + MAXIM MAX77693, MAX77705 and MAX77843 chips. To compile this driver as module, choose M here: the module will be called max77693-haptic. diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index cdb9be737e48..1dfd7b95a4ce 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -24,6 +24,7 @@ #include <linux/mfd/max77693.h> #include <linux/mfd/max77693-common.h> #include <linux/mfd/max77693-private.h> +#include <linux/mfd/max77705-private.h> #include <linux/mfd/max77843-private.h> #define MAX_MAGNITUDE_SHIFT 16 @@ -116,6 +117,13 @@ static int max77693_haptic_configure(struct max77693_haptic *haptic, MAX77693_HAPTIC_PWM_DIVISOR_128); config_reg = MAX77693_HAPTIC_REG_CONFIG2; break; + case TYPE_MAX77705: + value = ((haptic->type << MAX77693_CONFIG2_MODE) | + (enable << MAX77693_CONFIG2_MEN) | + (haptic->mode << MAX77693_CONFIG2_HTYP) | + MAX77693_HAPTIC_PWM_DIVISOR_128); + config_reg = MAX77705_PMIC_REG_MCONFIG; + break; case TYPE_MAX77843: value = (haptic->type << MCONFIG_MODE_SHIFT) | (enable << MCONFIG_MEN_SHIFT) | @@ -313,6 +321,7 @@ static int max77693_haptic_probe(struct platform_device *pdev) case TYPE_MAX77693: haptic->regmap_haptic = max77693->regmap_haptic; break; + case TYPE_MAX77705: case TYPE_MAX77843: haptic->regmap_haptic = max77693->regmap; break; @@ -408,6 +417,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops, static const struct platform_device_id max77693_haptic_id[] = { { "max77693-haptic", }, + { "max77705-haptic", }, { "max77843-haptic", }, {}, }; @@ -415,6 +425,7 @@ MODULE_DEVICE_TABLE(platform, max77693_haptic_id); static const struct of_device_id of_max77693_haptic_dt_match[] = { { .compatible = "maxim,max77693-haptic", }, + { .compatible = "maxim,max77705-haptic", }, { .compatible = "maxim,max77843-haptic", }, { /* sentinel */ }, }; @@ -433,5 +444,5 @@ module_platform_driver(max77693_haptic_driver); MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); -MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver"); +MODULE_DESCRIPTION("MAXIM 77693/77705/77843 Haptic driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 2b27d043921c..856b74125537 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -778,6 +778,14 @@ config LEDS_MAX77650 help LEDs driver for MAX77650 family of PMICs from Maxim Integrated. +config LEDS_MAX77705 + tristate "LED support for Maxim MAX77705 PMIC" + depends on MFD_MAX77705 + depends on LEDS_CLASS + depends on LEDS_CLASS_MULTICOLOR + help + LED driver for MAX77705 PMIC from Maxim Integrated. + config LEDS_MAX8997 tristate "LED support for MAX8997 PMIC" depends on LEDS_CLASS && MFD_MAX8997 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 6ad52e219ec6..685f4185972c 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_LP8864) += leds-lp8864.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o +obj-$(CONFIG_LEDS_MAX77705) += leds-max77705.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o diff --git a/drivers/leds/leds-max77705.c b/drivers/leds/leds-max77705.c new file mode 100644 index 000000000000..933cb4f19be9 --- /dev/null +++ b/drivers/leds/leds-max77705.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Based on leds-max77650 driver + * + * LED driver for MAXIM 77705 PMIC. + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org> + */ + +#include <linux/i2c.h> +#include <linux/led-class-multicolor.h> +#include <linux/leds.h> +#include <linux/mfd/max77705-private.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MAX77705_LED_NUM_LEDS 4 +#define MAX77705_LED_EN_MASK GENMASK(1, 0) +#define MAX77705_LED_MAX_BRIGHTNESS 0xff +#define MAX77705_LED_EN_SHIFT(reg) (reg * MAX77705_RGBLED_EN_WIDTH) +#define MAX77705_LED_REG_BRIGHTNESS(reg) (reg + MAX77705_RGBLED_REG_LED0BRT) + +struct max77705_led { + struct led_classdev cdev; + struct led_classdev_mc mcdev; + struct regmap *regmap; + + struct mc_subled *subled_info; +}; + +static const struct regmap_config max77705_leds_regmap_config = { + .reg_base = MAX77705_RGBLED_REG_BASE, + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77705_LED_REG_END, +}; + +static int max77705_rgb_blink(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct max77705_led *led = container_of(cdev, struct max77705_led, cdev); + int value, on_value, off_value; + + if (*delay_on < MAX77705_RGB_DELAY_100_STEP) + on_value = 0; + else if (*delay_on < MAX77705_RGB_DELAY_100_STEP_LIM) + on_value = *delay_on / MAX77705_RGB_DELAY_100_STEP - 1; + else if (*delay_on < MAX77705_RGB_DELAY_250_STEP_LIM) + on_value = (*delay_on - MAX77705_RGB_DELAY_100_STEP_LIM) / + MAX77705_RGB_DELAY_250_STEP + + MAX77705_RGB_DELAY_100_STEP_COUNT; + else + on_value = 15; + + on_value <<= 4; + + if (*delay_off < 1) + off_value = 0; + else if (*delay_off < MAX77705_RGB_DELAY_500_STEP) + off_value = 1; + else if (*delay_off < MAX77705_RGB_DELAY_500_STEP_LIM) + off_value = *delay_off / MAX77705_RGB_DELAY_500_STEP; + else if (*delay_off < MAX77705_RGB_DELAY_1000_STEP_LIM) + off_value = (*delay_off - MAX77705_RGB_DELAY_1000_STEP_LIM) / + MAX77705_RGB_DELAY_1000_STEP + + MAX77705_RGB_DELAY_500_STEP_COUNT; + else if (*delay_off < MAX77705_RGB_DELAY_2000_STEP_LIM) + off_value = (*delay_off - MAX77705_RGB_DELAY_2000_STEP_LIM) / + MAX77705_RGB_DELAY_2000_STEP + + MAX77705_RGB_DELAY_1000_STEP_COUNT; + else + off_value = 15; + + value = on_value | off_value; + return regmap_write(led->regmap, MAX77705_RGBLED_REG_LEDBLNK, value); +} + +static int max77705_led_brightness_set(struct regmap *regmap, struct mc_subled *subled, + int num_colors) +{ + int ret; + + for (int i = 0; i < num_colors; i++) { + unsigned int channel, brightness; + + channel = subled[i].channel; + brightness = subled[i].brightness; + + if (brightness == LED_OFF) { + /* Flash OFF */ + ret = regmap_update_bits(regmap, + MAX77705_RGBLED_REG_LEDEN, + MAX77705_LED_EN_MASK << MAX77705_LED_EN_SHIFT(channel), 0); + } else { + /* Set current */ + ret = regmap_write(regmap, MAX77705_LED_REG_BRIGHTNESS(channel), + brightness); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, + MAX77705_RGBLED_REG_LEDEN, + LED_ON << MAX77705_LED_EN_SHIFT(channel), + MAX77705_LED_EN_MASK << MAX77705_LED_EN_SHIFT(channel)); + } + } + + return ret; +} + +static int max77705_led_brightness_set_single(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct max77705_led *led = container_of(cdev, struct max77705_led, cdev); + + led->subled_info->brightness = brightness; + + return max77705_led_brightness_set(led->regmap, led->subled_info, 1); +} + +static int max77705_led_brightness_set_multi(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mcdev = lcdev_to_mccdev(cdev); + struct max77705_led *led = container_of(mcdev, struct max77705_led, mcdev); + + led_mc_calc_color_components(mcdev, brightness); + + return max77705_led_brightness_set(led->regmap, led->mcdev.subled_info, mcdev->num_colors); +} + +static int max77705_parse_subled(struct device *dev, struct fwnode_handle *np, + struct mc_subled *info) +{ + u32 color = LED_COLOR_ID_GREEN; + u32 reg; + int ret; + + ret = fwnode_property_read_u32(np, "reg", ®); + if (ret || !reg || reg >= MAX77705_LED_NUM_LEDS) + return dev_err_probe(dev, -EINVAL, "invalid \"reg\" of %pOFn\n", np); + + info->channel = reg; + + ret = fwnode_property_read_u32(np, "color", &color); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "failed to parse \"color\" of %pOF\n", np); + + info->color_index = color; + + return 0; +} + +static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fwnode_handle *np) +{ + int ret, i = 0; + unsigned int color, reg; + struct max77705_led *led; + struct led_classdev *cdev; + struct mc_subled *info; + struct fwnode_handle *child; + struct led_init_data init_data = {}; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + ret = fwnode_property_read_u32(np, "color", &color); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "failed to parse \"color\" of %pOF\n", np); + + led->regmap = regmap; + init_data.fwnode = np; + + if (color == LED_COLOR_ID_RGB) { + int num_channels = of_get_available_child_count(to_of_node(np)); + + ret = fwnode_property_read_u32(np, "reg", ®); + if (ret || reg >= MAX77705_LED_NUM_LEDS) + ret = -EINVAL; + + info = devm_kcalloc(dev, num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + cdev = &led->mcdev.led_cdev; + cdev->max_brightness = MAX77705_LED_MAX_BRIGHTNESS; + cdev->brightness_set_blocking = max77705_led_brightness_set_multi; + cdev->blink_set = max77705_rgb_blink; + + fwnode_for_each_available_child_node(np, child) { + ret = max77705_parse_subled(dev, child, &info[i]); + if (ret < 0) + return ret; + + info[i].intensity = 0; + i++; + } + + led->mcdev.subled_info = info; + led->mcdev.num_colors = num_channels; + led->cdev = *cdev; + + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mcdev, &init_data); + if (ret) + return ret; + + ret = max77705_led_brightness_set_multi(&led->cdev, LED_OFF); + if (ret) + return ret; + } else { + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + max77705_parse_subled(dev, np, info); + + led->subled_info = info; + led->cdev.brightness_set_blocking = max77705_led_brightness_set_single; + led->cdev.blink_set = max77705_rgb_blink; + led->cdev.max_brightness = MAX77705_LED_MAX_BRIGHTNESS; + + ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); + if (ret) + return ret; + + ret = max77705_led_brightness_set_single(&led->cdev, LED_OFF); + if (ret) + return ret; + } + + return 0; +} + +static int max77705_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct i2c_client *i2c = to_i2c_client(pdev->dev.parent); + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &max77705_leds_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register LEDs regmap\n"); + + device_for_each_child_node_scoped(dev, child) { + ret = max77705_add_led(dev, regmap, child); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id max77705_led_of_match[] = { + { .compatible = "maxim,max77705-rgb" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77705_led_of_match); + +static struct platform_driver max77705_led_driver = { + .driver = { + .name = "max77705-led", + .of_match_table = max77705_led_of_match, + }, + .probe = max77705_led_probe, +}; +module_platform_driver(max77705_led_driver); + +MODULE_DESCRIPTION("Maxim MAX77705 LED driver"); +MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6b0682af6e32..22b936310039 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -138,7 +138,7 @@ config MFD_AAT2870_CORE config MFD_AT91_USART tristate "AT91 USART Driver" select MFD_CORE - depends on ARCH_AT91 || COMPILE_TEST + depends on ARCH_AT91 || ARCH_LAN969X || COMPILE_TEST help Select this to get support for AT91 USART IP. This is a wrapper over at91-usart-serial driver and usart-spi-driver. Only one function @@ -858,7 +858,7 @@ config MFD_MAX77541 There are regulators and adc. config MFD_MAX77620 - bool "Maxim Semiconductor MAX77620 and MAX20024 PMIC Support" + tristate "Maxim Semiconductor MAX77620 and MAX20024 PMIC Support" depends on I2C=y depends on OF select MFD_CORE @@ -916,6 +916,19 @@ config MFD_MAX77693 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77705 + tristate "Maxim MAX77705 PMIC Support" + depends on I2C + select MFD_CORE + select MFD_SIMPLE_MFD_I2C + help + Say yes here to add support for Maxim Integrated MAX77705 PMIC. + This is a Power Management IC with Charger, safe LDOs, Flash, Haptic + and MUIC controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX77714 tristate "Maxim Semiconductor MAX77714 PMIC Support" depends on I2C @@ -1119,30 +1132,6 @@ config MFD_RETU Retu and Tahvo are a multi-function devices found on Nokia Internet Tablets (770, N800 and N810). -config MFD_PCF50633 - tristate "NXP PCF50633" - depends on I2C - select REGMAP_I2C - help - Say yes here if you have NXP PCF50633 chip on your board. - This core driver provides register access and IRQ handling - facilities, and registers devices for the various functions - so that function-specific drivers can bind to them. - -config PCF50633_ADC - tristate "NXP PCF50633 ADC" - depends on MFD_PCF50633 - help - Say yes here if you want to include support for ADC in the - NXP PCF50633 chip. - -config PCF50633_GPIO - tristate "NXP PCF50633 GPIO" - depends on MFD_PCF50633 - help - Say yes here if you want to include support GPIO for pins on - the PCF50633 chip. - config MFD_PM8XXX tristate "Qualcomm PM8xxx PMIC chips driver" depends on ARM || HEXAGON || COMPILE_TEST @@ -1495,12 +1484,6 @@ config STMPE_SPI This is used to enable SPI interface of STMPE endmenu -config MFD_STA2X11 - bool "STMicroelectronics STA2X11" - depends on STA2X11 - select MFD_CORE - select REGMAP_MMIO - config MFD_SUN6I_PRCM bool "Allwinner A31/A23/A33 PRCM controller" depends on ARCH_SUNXI || COMPILE_TEST diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9220eaf7cf12..948cbdf42a18 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o -obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o @@ -168,6 +167,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_MAX77705) += max77705.o obj-$(CONFIG_MFD_MAX77714) += max77714.o obj-$(CONFIG_MFD_MAX77843) += max77843.o obj-$(CONFIG_MFD_MAX8907) += max8907.o @@ -183,10 +183,6 @@ obj-$(CONFIG_MFD_MT6370) += mt6370.o mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o obj-$(CONFIG_MFD_MT6397) += mt6397.o -pcf50633-objs := pcf50633-core.o pcf50633-irq.o -obj-$(CONFIG_MFD_PCF50633) += pcf50633.o -obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o -obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_RZ_MTU3) += rz-mtu3.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index cff56deba24f..e9914e8a29a3 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -224,6 +224,7 @@ static const struct regmap_range axp717_writeable_ranges[] = { regmap_reg_range(AXP717_VSYS_V_POWEROFF, AXP717_VSYS_V_POWEROFF), regmap_reg_range(AXP717_IRQ0_EN, AXP717_IRQ4_EN), regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE), + regmap_reg_range(AXP717_TS_PIN_CFG, AXP717_TS_PIN_CFG), regmap_reg_range(AXP717_ICC_CHG_SET, AXP717_CV_CHG_SET), regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL), regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL), diff --git a/drivers/mfd/cgbc-core.c b/drivers/mfd/cgbc-core.c index 85283c8dde25..4782ff1114a9 100644 --- a/drivers/mfd/cgbc-core.c +++ b/drivers/mfd/cgbc-core.c @@ -96,7 +96,7 @@ static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd) static int cgbc_session_request(struct cgbc_device_data *cgbc) { - unsigned int ret; + int ret; ret = cgbc_wait_device(cgbc); @@ -236,6 +236,7 @@ static struct mfd_cell cgbc_devs[] = { { .name = "cgbc-gpio" }, { .name = "cgbc-i2c", .id = 1 }, { .name = "cgbc-i2c", .id = 2 }, + { .name = "cgbc-hwmon" }, }; static int cgbc_map(struct cgbc_device_data *cgbc) @@ -384,6 +385,13 @@ static const struct dmi_system_id cgbc_dmi_table[] __initconst = { DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"), }, }, + { + .ident = "SA8", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "congatec"), + DMI_MATCH(DMI_BOARD_NAME, "conga-SA8"), + }, + }, {} }; MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table); diff --git a/drivers/mfd/ene-kb3930.c b/drivers/mfd/ene-kb3930.c index fa0ad2f14a39..9460a67acb0b 100644 --- a/drivers/mfd/ene-kb3930.c +++ b/drivers/mfd/ene-kb3930.c @@ -162,7 +162,7 @@ static int kb3930_probe(struct i2c_client *client) devm_gpiod_get_array_optional(dev, "off", GPIOD_IN); if (IS_ERR(ddata->off_gpios)) return PTR_ERR(ddata->off_gpios); - if (ddata->off_gpios->ndescs < 2) { + if (ddata->off_gpios && ddata->off_gpios->ndescs < 2) { dev_err(dev, "invalid off-gpios property\n"); return -EINVAL; } diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index 8d006f6be48c..1be4557b7bdd 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -25,11 +25,6 @@ struct pcap_adc_request { void *data; }; -struct pcap_adc_sync_request { - u16 res[2]; - struct completion completion; -}; - struct pcap_chip { struct spi_device *spi; @@ -335,34 +330,6 @@ int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[], } EXPORT_SYMBOL_GPL(pcap_adc_async); -static void pcap_adc_sync_cb(void *param, u16 res[]) -{ - struct pcap_adc_sync_request *req = param; - - req->res[0] = res[0]; - req->res[1] = res[1]; - complete(&req->completion); -} - -int pcap_adc_sync(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[], - u16 res[]) -{ - struct pcap_adc_sync_request sync_data; - int ret; - - init_completion(&sync_data.completion); - ret = pcap_adc_async(pcap, bank, flags, ch, pcap_adc_sync_cb, - &sync_data); - if (ret) - return ret; - wait_for_completion(&sync_data.completion); - res[0] = sync_data.res[0]; - res[1] = sync_data.res[1]; - - return 0; -} -EXPORT_SYMBOL_GPL(pcap_adc_sync); - /* subdevs */ static int pcap_remove_subdev(struct device *dev, void *unused) { diff --git a/drivers/mfd/intel_soc_pmic_chtdc_ti.c b/drivers/mfd/intel_soc_pmic_chtdc_ti.c index 8582ae65a802..4c1a68c9f575 100644 --- a/drivers/mfd/intel_soc_pmic_chtdc_ti.c +++ b/drivers/mfd/intel_soc_pmic_chtdc_ti.c @@ -82,7 +82,6 @@ static const struct regmap_config chtdc_ti_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff, - .cache_type = REGCACHE_NONE, }; static const struct regmap_irq chtdc_ti_irqs[] = { diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index 879fbf5cd162..41429f9bcb69 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -113,7 +113,6 @@ static const struct regmap_config crystal_cove_regmap_config = { .val_bits = 8, .max_register = CRYSTAL_COVE_MAX_REGISTER, - .cache_type = REGCACHE_NONE, }; static const struct regmap_irq crystal_cove_irqs[] = { diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c index 2370b44e2214..4b757d847282 100644 --- a/drivers/mfd/ipaq-micro.c +++ b/drivers/mfd/ipaq-micro.c @@ -22,6 +22,7 @@ #include <linux/mfd/core.h> #include <linux/mfd/ipaq-micro.h> #include <linux/string.h> +#include <linux/string_choices.h> #include <linux/random.h> #include <linux/slab.h> #include <linux/list.h> @@ -267,7 +268,7 @@ static void __init ipaq_micro_eeprom_dump(struct ipaq_micro *micro) dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84)); dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86)); dev_info(micro->dev, "color display: %s\n", - ipaq_micro_to_u16(dump+88) ? "yes" : "no"); + str_yes_no(ipaq_micro_to_u16(dump + 88))); dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90)); dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92)); dev_info(micro->dev, "screen: %u x %u\n", diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index 89b30ef91f4f..21d2ab3db254 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -29,6 +29,7 @@ #include <linux/mfd/core.h> #include <linux/mfd/max77620.h> #include <linux/init.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -700,3 +701,7 @@ static struct i2c_driver max77620_driver = { .id_table = max77620_id, }; builtin_i2c_driver(max77620_driver); + +MODULE_DESCRIPTION("Maxim Semiconductor MAX77620 and MAX20024 PMIC Support"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max77705.c b/drivers/mfd/max77705.c new file mode 100644 index 000000000000..60c457c21d95 --- /dev/null +++ b/drivers/mfd/max77705.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Maxim MAX77705 PMIC core driver + * + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com> + **/ +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77705-private.h> +#include <linux/mfd/max77693-common.h> +#include <linux/pm.h> +#include <linux/power/max17042_battery.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/of.h> + +static struct mfd_cell max77705_devs[] = { + MFD_CELL_OF("max77705-rgb", NULL, NULL, 0, 0, "maxim,max77705-rgb"), + MFD_CELL_OF("max77705-charger", NULL, NULL, 0, 0, "maxim,max77705-charger"), + MFD_CELL_OF("max77705-haptic", NULL, NULL, 0, 0, "maxim,max77705-haptic"), +}; + +static const struct regmap_range max77705_readable_ranges[] = { + regmap_reg_range(MAX77705_PMIC_REG_PMICID1, MAX77705_PMIC_REG_BSTOUT_MASK), + regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29), + regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), + regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2), + regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK), + regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), + regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2), + regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET), +}; + +static const struct regmap_range max77705_writable_ranges[] = { + regmap_reg_range(MAX77705_PMIC_REG_MAINCTRL1, MAX77705_PMIC_REG_BSTOUT_MASK), + regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29), + regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), + regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2), + regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK), + regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), + regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2), + regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET), +}; + +static const struct regmap_access_table max77705_readable_table = { + .yes_ranges = max77705_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(max77705_readable_ranges), +}; + +static const struct regmap_access_table max77705_writable_table = { + .yes_ranges = max77705_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(max77705_writable_ranges), +}; + +static const struct regmap_config max77705_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &max77705_readable_table, + .wr_table = &max77705_writable_table, + .max_register = MAX77705_PMIC_REG_USBC_RESET, +}; + +static const struct regmap_irq max77705_topsys_irqs[] = { + { .mask = MAX77705_SYSTEM_IRQ_BSTEN_INT, }, + { .mask = MAX77705_SYSTEM_IRQ_SYSUVLO_INT, }, + { .mask = MAX77705_SYSTEM_IRQ_SYSOVLO_INT, }, + { .mask = MAX77705_SYSTEM_IRQ_TSHDN_INT, }, + { .mask = MAX77705_SYSTEM_IRQ_TM_INT, }, +}; + +static const struct regmap_irq_chip max77705_topsys_irq_chip = { + .name = "max77705-topsys", + .status_base = MAX77705_PMIC_REG_SYSTEM_INT, + .mask_base = MAX77705_PMIC_REG_SYSTEM_INT_MASK, + .num_regs = 1, + .irqs = max77705_topsys_irqs, + .num_irqs = ARRAY_SIZE(max77705_topsys_irqs), +}; + +static int max77705_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct max77693_dev *max77705; + struct regmap_irq_chip_data *irq_data; + struct irq_domain *domain; + enum max77705_hw_rev pmic_rev; + unsigned int pmic_rev_value; + int ret; + + max77705 = devm_kzalloc(dev, sizeof(*max77705), GFP_KERNEL); + if (!max77705) + return -ENOMEM; + + max77705->i2c = i2c; + max77705->type = TYPE_MAX77705; + i2c_set_clientdata(i2c, max77705); + + max77705->regmap = devm_regmap_init_i2c(i2c, &max77705_regmap_config); + if (IS_ERR(max77705->regmap)) + return PTR_ERR(max77705->regmap); + + ret = regmap_read(max77705->regmap, MAX77705_PMIC_REG_PMICREV, &pmic_rev_value); + if (ret < 0) + return -ENODEV; + + pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK; + if (pmic_rev != MAX77705_PASS3) + return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev); + + ret = devm_regmap_add_irq_chip(dev, max77705->regmap, + i2c->irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77705_topsys_irq_chip, + &irq_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add IRQ chip\n"); + + /* Unmask interrupts from all blocks in interrupt source register */ + ret = regmap_update_bits(max77705->regmap, + MAX77705_PMIC_REG_INTSRC_MASK, + MAX77705_SRC_IRQ_ALL, (unsigned int)~MAX77705_SRC_IRQ_ALL); + if (ret < 0) + return dev_err_probe(dev, ret, "Could not unmask interrupts in INTSRC\n"); + + domain = regmap_irq_get_domain(irq_data); + + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + max77705_devs, ARRAY_SIZE(max77705_devs), + NULL, 0, domain); + if (ret) + return dev_err_probe(dev, ret, "Failed to register child devices\n"); + + device_init_wakeup(dev, true); + + return 0; +} + +static int max77705_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + + disable_irq(i2c->irq); + + if (device_may_wakeup(dev)) + enable_irq_wake(i2c->irq); + + return 0; +} + +static int max77705_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(i2c->irq); + + enable_irq(i2c->irq); + + return 0; +} +DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume); + +static const struct of_device_id max77705_i2c_of_match[] = { + { .compatible = "maxim,max77705" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77705_i2c_of_match); + +static struct i2c_driver max77705_i2c_driver = { + .driver = { + .name = "max77705", + .of_match_table = max77705_i2c_of_match, + .pm = pm_sleep_ptr(&max77705_pm_ops), + }, + .probe = max77705_i2c_probe +}; +module_i2c_driver(max77705_i2c_driver); + +MODULE_DESCRIPTION("Maxim MAX77705 PMIC core driver"); +MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 93a3b1698d9c..92e348df03d1 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -335,7 +335,8 @@ int max8997_irq_init(struct max8997_dev *max8997) } max8997->irq_domain = domain; - ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, + ret = devm_request_threaded_irq(max8997->dev, max8997->irq, NULL, + max8997_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max8997-irq", max8997); @@ -348,7 +349,8 @@ int max8997_irq_init(struct max8997_dev *max8997) if (!max8997->ono) return 0; - ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread, + ret = devm_request_threaded_irq(max8997->dev, max8997->ono, NULL, + max8997_irq_thread, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "max8997-ono", max8997); @@ -358,12 +360,3 @@ int max8997_irq_init(struct max8997_dev *max8997) return 0; } - -void max8997_irq_exit(struct max8997_dev *max8997) -{ - if (max8997->ono) - free_irq(max8997->ono, max8997); - - if (max8997->irq) - free_irq(max8997->irq, max8997); -} diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 0e5d59ae064a..5f8ed8988907 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -84,6 +84,12 @@ static const struct resource mt6359_keys_resources[] = { DEFINE_RES_IRQ_NAMED(MT6359_IRQ_HOMEKEY_R, "homekey_r"), }; +static const struct resource mt6359_accdet_resources[] = { + DEFINE_RES_IRQ_NAMED(MT6359_IRQ_ACCDET, "accdet_irq"), + DEFINE_RES_IRQ_NAMED(MT6359_IRQ_ACCDET_EINT0, "accdet_eint0"), + DEFINE_RES_IRQ_NAMED(MT6359_IRQ_ACCDET_EINT1, "accdet_eint1"), +}; + static const struct resource mt6323_keys_resources[] = { DEFINE_RES_IRQ_NAMED(MT6323_IRQ_STATUS_PWRKEY, "powerkey"), DEFINE_RES_IRQ_NAMED(MT6323_IRQ_STATUS_FCHRKEY, "homekey"), @@ -239,6 +245,12 @@ static const struct mfd_cell mt6359_devs[] = { .resources = mt6359_keys_resources, .of_compatible = "mediatek,mt6359-keys" }, + { + .name = "mt6359-accdet", + .of_compatible = "mediatek,mt6359-accdet", + .num_resources = ARRAY_SIZE(mt6359_accdet_resources), + .resources = mt6359_accdet_resources, + }, }; static const struct mfd_cell mt6397_devs[] = { diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c deleted file mode 100644 index 1fbba0e666d5..000000000000 --- a/drivers/mfd/pcf50633-adc.c +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 ADC Driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - * - * Broken down from monstrous PCF50633 driver mainly by - * Harald Welte, Andy Green and Werner Almesberger - * - * NOTE: This driver does not yet support subtractive ADC mode, which means - * you can do only one measurement per read request. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/completion.h> - -#include <linux/mfd/pcf50633/core.h> -#include <linux/mfd/pcf50633/adc.h> - -struct pcf50633_adc_request { - int mux; - int avg; - void (*callback)(struct pcf50633 *, void *, int); - void *callback_param; -}; - -struct pcf50633_adc_sync_request { - int result; - struct completion completion; -}; - -#define PCF50633_MAX_ADC_FIFO_DEPTH 8 - -struct pcf50633_adc { - struct pcf50633 *pcf; - - /* Private stuff */ - struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH]; - int queue_head; - int queue_tail; - struct mutex queue_mutex; -}; - -static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf) -{ - return platform_get_drvdata(pcf->adc_pdev); -} - -static void adc_setup(struct pcf50633 *pcf, int channel, int avg) -{ - channel &= PCF50633_ADCC1_ADCMUX_MASK; - - /* kill ratiometric, but enable ACCSW biasing */ - pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01); - - /* start ADC conversion on selected channel */ - pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg | - PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT); -} - -static void trigger_next_adc_job_if_any(struct pcf50633 *pcf) -{ - struct pcf50633_adc *adc = __to_adc(pcf); - int head; - - head = adc->queue_head; - - if (!adc->queue[head]) - return; - - adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg); -} - -static int -adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) -{ - struct pcf50633_adc *adc = __to_adc(pcf); - int head, tail; - - mutex_lock(&adc->queue_mutex); - - head = adc->queue_head; - tail = adc->queue_tail; - - if (adc->queue[tail]) { - mutex_unlock(&adc->queue_mutex); - dev_err(pcf->dev, "ADC queue is full, dropping request\n"); - return -EBUSY; - } - - adc->queue[tail] = req; - if (head == tail) - trigger_next_adc_job_if_any(pcf); - adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1); - - mutex_unlock(&adc->queue_mutex); - - return 0; -} - -static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, - int result) -{ - struct pcf50633_adc_sync_request *req = param; - - req->result = result; - complete(&req->completion); -} - -int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg) -{ - struct pcf50633_adc_sync_request req; - int ret; - - init_completion(&req.completion); - - ret = pcf50633_adc_async_read(pcf, mux, avg, - pcf50633_adc_sync_read_callback, &req); - if (ret) - return ret; - - wait_for_completion(&req.completion); - - return req.result; -} -EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read); - -int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg, - void (*callback)(struct pcf50633 *, void *, int), - void *callback_param) -{ - struct pcf50633_adc_request *req; - int ret; - - /* req is freed when the result is ready, in interrupt handler */ - req = kmalloc(sizeof(*req), GFP_KERNEL); - if (!req) - return -ENOMEM; - - req->mux = mux; - req->avg = avg; - req->callback = callback; - req->callback_param = callback_param; - - ret = adc_enqueue_request(pcf, req); - if (ret) - kfree(req); - - return ret; -} -EXPORT_SYMBOL_GPL(pcf50633_adc_async_read); - -static int adc_result(struct pcf50633 *pcf) -{ - u8 adcs1, adcs3; - u16 result; - - adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1); - adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3); - result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK); - - dev_dbg(pcf->dev, "adc result = %d\n", result); - - return result; -} - -static void pcf50633_adc_irq(int irq, void *data) -{ - struct pcf50633_adc *adc = data; - struct pcf50633 *pcf = adc->pcf; - struct pcf50633_adc_request *req; - int head, res; - - mutex_lock(&adc->queue_mutex); - head = adc->queue_head; - - req = adc->queue[head]; - if (WARN_ON(!req)) { - dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n"); - mutex_unlock(&adc->queue_mutex); - return; - } - adc->queue[head] = NULL; - adc->queue_head = (head + 1) & - (PCF50633_MAX_ADC_FIFO_DEPTH - 1); - - res = adc_result(pcf); - trigger_next_adc_job_if_any(pcf); - - mutex_unlock(&adc->queue_mutex); - - req->callback(pcf, req->callback_param, res); - kfree(req); -} - -static int pcf50633_adc_probe(struct platform_device *pdev) -{ - struct pcf50633_adc *adc; - - adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL); - if (!adc) - return -ENOMEM; - - adc->pcf = dev_to_pcf50633(pdev->dev.parent); - platform_set_drvdata(pdev, adc); - - pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY, - pcf50633_adc_irq, adc); - - mutex_init(&adc->queue_mutex); - - return 0; -} - -static void pcf50633_adc_remove(struct platform_device *pdev) -{ - struct pcf50633_adc *adc = platform_get_drvdata(pdev); - int i, head; - - pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY); - - mutex_lock(&adc->queue_mutex); - head = adc->queue_head; - - if (WARN_ON(adc->queue[head])) - dev_err(adc->pcf->dev, - "adc driver removed with request pending\n"); - - for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++) - kfree(adc->queue[i]); - - mutex_unlock(&adc->queue_mutex); -} - -static struct platform_driver pcf50633_adc_driver = { - .driver = { - .name = "pcf50633-adc", - }, - .probe = pcf50633_adc_probe, - .remove = pcf50633_adc_remove, -}; - -module_platform_driver(pcf50633_adc_driver); - -MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); -MODULE_DESCRIPTION("PCF50633 adc driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pcf50633-adc"); - diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c deleted file mode 100644 index 014a68711b18..000000000000 --- a/drivers/mfd/pcf50633-core.c +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 Power Management Unit (PMU) driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Harald Welte <laforge@openmoko.org> - * Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - */ - -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/sysfs.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> -#include <linux/pm.h> -#include <linux/slab.h> -#include <linux/regmap.h> -#include <linux/err.h> - -#include <linux/mfd/pcf50633/core.h> - -/* Read a block of up to 32 regs */ -int pcf50633_read_block(struct pcf50633 *pcf, u8 reg, - int nr_regs, u8 *data) -{ - int ret; - - ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs); - if (ret != 0) - return ret; - - return nr_regs; -} -EXPORT_SYMBOL_GPL(pcf50633_read_block); - -/* Write a block of up to 32 regs */ -int pcf50633_write_block(struct pcf50633 *pcf , u8 reg, - int nr_regs, u8 *data) -{ - return regmap_raw_write(pcf->regmap, reg, data, nr_regs); -} -EXPORT_SYMBOL_GPL(pcf50633_write_block); - -u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg) -{ - unsigned int val; - int ret; - - ret = regmap_read(pcf->regmap, reg, &val); - if (ret < 0) - return -1; - - return val; -} -EXPORT_SYMBOL_GPL(pcf50633_reg_read); - -int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val) -{ - return regmap_write(pcf->regmap, reg, val); -} -EXPORT_SYMBOL_GPL(pcf50633_reg_write); - -int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val) -{ - return regmap_update_bits(pcf->regmap, reg, mask, val); -} -EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask); - -int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val) -{ - return regmap_update_bits(pcf->regmap, reg, val, 0); -} -EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits); - -/* sysfs attributes */ -static ssize_t dump_regs_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pcf50633 *pcf = dev_get_drvdata(dev); - u8 dump[16]; - int n, n1, idx = 0; - char *buf1 = buf; - static u8 address_no_read[] = { /* must be ascending */ - PCF50633_REG_INT1, - PCF50633_REG_INT2, - PCF50633_REG_INT3, - PCF50633_REG_INT4, - PCF50633_REG_INT5, - 0 /* terminator */ - }; - - for (n = 0; n < 256; n += sizeof(dump)) { - for (n1 = 0; n1 < sizeof(dump); n1++) - if (n == address_no_read[idx]) { - idx++; - dump[n1] = 0x00; - } else - dump[n1] = pcf50633_reg_read(pcf, n + n1); - - buf1 += sprintf(buf1, "%*ph\n", (int)sizeof(dump), dump); - } - - return buf1 - buf; -} -static DEVICE_ATTR_ADMIN_RO(dump_regs); - -static ssize_t resume_reason_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pcf50633 *pcf = dev_get_drvdata(dev); - int n; - - n = sprintf(buf, "%02x%02x%02x%02x%02x\n", - pcf->resume_reason[0], - pcf->resume_reason[1], - pcf->resume_reason[2], - pcf->resume_reason[3], - pcf->resume_reason[4]); - - return n; -} -static DEVICE_ATTR_ADMIN_RO(resume_reason); - -static struct attribute *pcf_sysfs_entries[] = { - &dev_attr_dump_regs.attr, - &dev_attr_resume_reason.attr, - NULL, -}; - -static struct attribute_group pcf_attr_group = { - .name = NULL, /* put in device directory */ - .attrs = pcf_sysfs_entries, -}; - -static void -pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, - struct platform_device **pdev) -{ - int ret; - - *pdev = platform_device_alloc(name, -1); - if (!*pdev) { - dev_err(pcf->dev, "Failed to allocate %s\n", name); - return; - } - - (*pdev)->dev.parent = pcf->dev; - - ret = platform_device_add(*pdev); - if (ret) { - dev_err(pcf->dev, "Failed to register %s: %d\n", name, ret); - platform_device_put(*pdev); - *pdev = NULL; - } -} - -static const struct regmap_config pcf50633_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - -static int pcf50633_probe(struct i2c_client *client) -{ - struct pcf50633 *pcf; - struct platform_device *pdev; - struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev); - int i, j, ret; - int version, variant; - - if (!client->irq) { - dev_err(&client->dev, "Missing IRQ\n"); - return -ENOENT; - } - - pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL); - if (!pcf) - return -ENOMEM; - - i2c_set_clientdata(client, pcf); - pcf->dev = &client->dev; - pcf->pdata = pdata; - - mutex_init(&pcf->lock); - - pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config); - if (IS_ERR(pcf->regmap)) { - ret = PTR_ERR(pcf->regmap); - dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret); - return ret; - } - - version = pcf50633_reg_read(pcf, 0); - variant = pcf50633_reg_read(pcf, 1); - if (version < 0 || variant < 0) { - dev_err(pcf->dev, "Unable to probe pcf50633\n"); - ret = -ENODEV; - return ret; - } - - dev_info(pcf->dev, "Probed device version %d variant %d\n", - version, variant); - - pcf50633_irq_init(pcf, client->irq); - - /* Create sub devices */ - pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev); - - - for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { - pdev = platform_device_alloc("pcf50633-regulator", i); - if (!pdev) { - ret = -ENOMEM; - goto err2; - } - - pdev->dev.parent = pcf->dev; - ret = platform_device_add_data(pdev, &pdata->reg_init_data[i], - sizeof(pdata->reg_init_data[i])); - if (ret) - goto err; - - ret = platform_device_add(pdev); - if (ret) - goto err; - - pcf->regulator_pdev[i] = pdev; - } - - ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group); - if (ret) - dev_warn(pcf->dev, "error creating sysfs entries\n"); - - if (pdata->probe_done) - pdata->probe_done(pcf); - - return 0; - -err: - platform_device_put(pdev); -err2: - for (j = 0; j < i; j++) - platform_device_put(pcf->regulator_pdev[j]); - - return ret; -} - -static void pcf50633_remove(struct i2c_client *client) -{ - struct pcf50633 *pcf = i2c_get_clientdata(client); - int i; - - sysfs_remove_group(&client->dev.kobj, &pcf_attr_group); - pcf50633_irq_free(pcf); - - platform_device_unregister(pcf->input_pdev); - platform_device_unregister(pcf->rtc_pdev); - platform_device_unregister(pcf->mbc_pdev); - platform_device_unregister(pcf->adc_pdev); - platform_device_unregister(pcf->bl_pdev); - - for (i = 0; i < PCF50633_NUM_REGULATORS; i++) - platform_device_unregister(pcf->regulator_pdev[i]); -} - -static const struct i2c_device_id pcf50633_id_table[] = { - {"pcf50633", 0x73}, - {/* end of list */} -}; -MODULE_DEVICE_TABLE(i2c, pcf50633_id_table); - -static struct i2c_driver pcf50633_driver = { - .driver = { - .name = "pcf50633", - .pm = pm_sleep_ptr(&pcf50633_pm), - }, - .id_table = pcf50633_id_table, - .probe = pcf50633_probe, - .remove = pcf50633_remove, -}; - -static int __init pcf50633_init(void) -{ - return i2c_add_driver(&pcf50633_driver); -} - -static void __exit pcf50633_exit(void) -{ - i2c_del_driver(&pcf50633_driver); -} - -MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU"); -MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); -MODULE_LICENSE("GPL"); - -subsys_initcall(pcf50633_init); -module_exit(pcf50633_exit); diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c deleted file mode 100644 index 3e368219479a..000000000000 --- a/drivers/mfd/pcf50633-gpio.c +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 GPIO Driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - * - * Broken down from monstrous PCF50633 driver mainly by - * Harald Welte, Andy Green and Werner Almesberger - */ - -#include <linux/kernel.h> -#include <linux/module.h> - -#include <linux/mfd/pcf50633/core.h> -#include <linux/mfd/pcf50633/gpio.h> -#include <linux/mfd/pcf50633/pmic.h> - -static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = { - [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT, - [PCF50633_REGULATOR_DOWN1] = PCF50633_REG_DOWN1OUT, - [PCF50633_REGULATOR_DOWN2] = PCF50633_REG_DOWN2OUT, - [PCF50633_REGULATOR_MEMLDO] = PCF50633_REG_MEMLDOOUT, - [PCF50633_REGULATOR_LDO1] = PCF50633_REG_LDO1OUT, - [PCF50633_REGULATOR_LDO2] = PCF50633_REG_LDO2OUT, - [PCF50633_REGULATOR_LDO3] = PCF50633_REG_LDO3OUT, - [PCF50633_REGULATOR_LDO4] = PCF50633_REG_LDO4OUT, - [PCF50633_REGULATOR_LDO5] = PCF50633_REG_LDO5OUT, - [PCF50633_REGULATOR_LDO6] = PCF50633_REG_LDO6OUT, - [PCF50633_REGULATOR_HCLDO] = PCF50633_REG_HCLDOOUT, -}; - -int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val) -{ - u8 reg; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - - return pcf50633_reg_set_bit_mask(pcf, reg, 0x07, val); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_set); - -u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio) -{ - u8 reg, val; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - val = pcf50633_reg_read(pcf, reg) & 0x07; - - return val; -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_get); - -int pcf50633_gpio_invert_set(struct pcf50633 *pcf, int gpio, int invert) -{ - u8 val, reg; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - val = !!invert << 3; - - return pcf50633_reg_set_bit_mask(pcf, reg, 1 << 3, val); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_set); - -int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio) -{ - u8 reg, val; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - val = pcf50633_reg_read(pcf, reg); - - return val & (1 << 3); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_get); - -int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf, - int gpio, int regulator, int on) -{ - u8 reg, val, mask; - - /* the *ENA register is always one after the *OUT register */ - reg = pcf50633_regulator_registers[regulator] + 1; - - val = !!on << (gpio - PCF50633_GPIO1); - mask = 1 << (gpio - PCF50633_GPIO1); - - return pcf50633_reg_set_bit_mask(pcf, reg, mask, val); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set); - -MODULE_DESCRIPTION("NXP PCF50633 GPIO Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c deleted file mode 100644 index e85af7f1cb0b..000000000000 --- a/drivers/mfd/pcf50633-irq.c +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 Power Management Unit (PMU) driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Harald Welte <laforge@openmoko.org> - * Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - */ - -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/mutex.h> -#include <linux/export.h> -#include <linux/slab.h> - -#include <linux/mfd/pcf50633/core.h> -#include <linux/mfd/pcf50633/mbc.h> - -int pcf50633_register_irq(struct pcf50633 *pcf, int irq, - void (*handler) (int, void *), void *data) -{ - if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler) - return -EINVAL; - - if (WARN_ON(pcf->irq_handler[irq].handler)) - return -EBUSY; - - mutex_lock(&pcf->lock); - pcf->irq_handler[irq].handler = handler; - pcf->irq_handler[irq].data = data; - mutex_unlock(&pcf->lock); - - return 0; -} -EXPORT_SYMBOL_GPL(pcf50633_register_irq); - -int pcf50633_free_irq(struct pcf50633 *pcf, int irq) -{ - if (irq < 0 || irq >= PCF50633_NUM_IRQ) - return -EINVAL; - - mutex_lock(&pcf->lock); - pcf->irq_handler[irq].handler = NULL; - mutex_unlock(&pcf->lock); - - return 0; -} -EXPORT_SYMBOL_GPL(pcf50633_free_irq); - -static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask) -{ - u8 reg, bit; - int idx; - - idx = irq >> 3; - reg = PCF50633_REG_INT1M + idx; - bit = 1 << (irq & 0x07); - - pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0); - - mutex_lock(&pcf->lock); - - if (mask) - pcf->mask_regs[idx] |= bit; - else - pcf->mask_regs[idx] &= ~bit; - - mutex_unlock(&pcf->lock); - - return 0; -} - -int pcf50633_irq_mask(struct pcf50633 *pcf, int irq) -{ - dev_dbg(pcf->dev, "Masking IRQ %d\n", irq); - - return __pcf50633_irq_mask_set(pcf, irq, 1); -} -EXPORT_SYMBOL_GPL(pcf50633_irq_mask); - -int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq) -{ - dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq); - - return __pcf50633_irq_mask_set(pcf, irq, 0); -} -EXPORT_SYMBOL_GPL(pcf50633_irq_unmask); - -int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq) -{ - u8 reg, bits; - - reg = irq >> 3; - bits = 1 << (irq & 0x07); - - return pcf->mask_regs[reg] & bits; -} -EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get); - -static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq) -{ - if (pcf->irq_handler[irq].handler) - pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data); -} - -/* Maximum amount of time ONKEY is held before emergency action is taken */ -#define PCF50633_ONKEY1S_TIMEOUT 8 - -static irqreturn_t pcf50633_irq(int irq, void *data) -{ - struct pcf50633 *pcf = data; - int ret, i, j; - u8 pcf_int[5], chgstat; - - /* Read the 5 INT regs in one transaction */ - ret = pcf50633_read_block(pcf, PCF50633_REG_INT1, - ARRAY_SIZE(pcf_int), pcf_int); - if (ret != ARRAY_SIZE(pcf_int)) { - dev_err(pcf->dev, "Error reading INT registers\n"); - - /* - * If this doesn't ACK the interrupt to the chip, we'll be - * called once again as we're level triggered. - */ - goto out; - } - - /* defeat 8s death from lowsys on A5 */ - pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04); - - /* We immediately read the usb and adapter status. We thus make sure - * only of USBINS/USBREM IRQ handlers are called */ - if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) { - chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); - if (chgstat & (0x3 << 4)) - pcf_int[0] &= ~PCF50633_INT1_USBREM; - else - pcf_int[0] &= ~PCF50633_INT1_USBINS; - } - - /* Make sure only one of ADPINS or ADPREM is set */ - if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) { - chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); - if (chgstat & (0x3 << 4)) - pcf_int[0] &= ~PCF50633_INT1_ADPREM; - else - pcf_int[0] &= ~PCF50633_INT1_ADPINS; - } - - dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x " - "INT4=0x%02x INT5=0x%02x\n", pcf_int[0], - pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]); - - /* Some revisions of the chip don't have a 8s standby mode on - * ONKEY1S press. We try to manually do it in such cases. */ - if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) { - dev_info(pcf->dev, "ONKEY1S held for %d secs\n", - pcf->onkey1s_held); - if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT) - if (pcf->pdata->force_shutdown) - pcf->pdata->force_shutdown(pcf); - } - - if (pcf_int[2] & PCF50633_INT3_ONKEY1S) { - dev_info(pcf->dev, "ONKEY1S held\n"); - pcf->onkey1s_held = 1 ; - - /* Unmask IRQ_SECOND */ - pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M, - PCF50633_INT1_SECOND); - - /* Unmask IRQ_ONKEYR */ - pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M, - PCF50633_INT2_ONKEYR); - } - - if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) { - pcf->onkey1s_held = 0; - - /* Mask SECOND and ONKEYR interrupts */ - if (pcf->mask_regs[0] & PCF50633_INT1_SECOND) - pcf50633_reg_set_bit_mask(pcf, - PCF50633_REG_INT1M, - PCF50633_INT1_SECOND, - PCF50633_INT1_SECOND); - - if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR) - pcf50633_reg_set_bit_mask(pcf, - PCF50633_REG_INT2M, - PCF50633_INT2_ONKEYR, - PCF50633_INT2_ONKEYR); - } - - /* Have we just resumed ? */ - if (pcf->is_suspended) { - pcf->is_suspended = 0; - - /* Set the resume reason filtering out non resumers */ - for (i = 0; i < ARRAY_SIZE(pcf_int); i++) - pcf->resume_reason[i] = pcf_int[i] & - pcf->pdata->resumers[i]; - - /* Make sure we don't pass on any ONKEY events to - * userspace now */ - pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF); - } - - for (i = 0; i < ARRAY_SIZE(pcf_int); i++) { - /* Unset masked interrupts */ - pcf_int[i] &= ~pcf->mask_regs[i]; - - for (j = 0; j < 8 ; j++) - if (pcf_int[i] & (1 << j)) - pcf50633_irq_call_handler(pcf, (i * 8) + j); - } - -out: - return IRQ_HANDLED; -} - -static int pcf50633_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf50633 *pcf = i2c_get_clientdata(client); - int ret; - int i; - u8 res[5]; - - - /* Make sure our interrupt handlers are not called - * henceforth */ - disable_irq(pcf->irq); - - /* Save the masks */ - ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M, - ARRAY_SIZE(pcf->suspend_irq_masks), - pcf->suspend_irq_masks); - if (ret < 0) { - dev_err(pcf->dev, "error saving irq masks\n"); - goto out; - } - - /* Write wakeup irq masks */ - for (i = 0; i < ARRAY_SIZE(res); i++) - res[i] = ~pcf->pdata->resumers[i]; - - ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M, - ARRAY_SIZE(res), &res[0]); - if (ret < 0) { - dev_err(pcf->dev, "error writing wakeup irq masks\n"); - goto out; - } - - pcf->is_suspended = 1; - -out: - return ret; -} - -static int pcf50633_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf50633 *pcf = i2c_get_clientdata(client); - int ret; - - /* Write the saved mask registers */ - ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M, - ARRAY_SIZE(pcf->suspend_irq_masks), - pcf->suspend_irq_masks); - if (ret < 0) - dev_err(pcf->dev, "Error restoring saved suspend masks\n"); - - enable_irq(pcf->irq); - - return ret; -} - -EXPORT_GPL_SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); - -int pcf50633_irq_init(struct pcf50633 *pcf, int irq) -{ - int ret; - - pcf->irq = irq; - - /* Enable all interrupts except RTC SECOND */ - pcf->mask_regs[0] = 0x80; - pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]); - pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00); - - ret = request_threaded_irq(irq, NULL, pcf50633_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "pcf50633", pcf); - - if (ret) - dev_err(pcf->dev, "Failed to request IRQ %d\n", ret); - - if (enable_irq_wake(irq) < 0) - dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source" - "in this hardware revision", irq); - - return ret; -} - -void pcf50633_irq_free(struct pcf50633 *pcf) -{ - free_irq(pcf->irq, pcf); -} diff --git a/drivers/mfd/qnap-mcu.c b/drivers/mfd/qnap-mcu.c index 4be39d8b2905..89a8a1913d42 100644 --- a/drivers/mfd/qnap-mcu.c +++ b/drivers/mfd/qnap-mcu.c @@ -158,9 +158,9 @@ int qnap_mcu_exec(struct qnap_mcu *mcu, mutex_lock(&mcu->bus_lock); - reply->data = rx, - reply->length = length, - reply->received = 0, + reply->data = rx; + reply->length = length; + reply->received = 0; reinit_completion(&reply->done); qnap_mcu_write(mcu, cmd_data, cmd_data_size); diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index cdfe738e1d76..3e9b65c988a7 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -83,6 +83,11 @@ static const struct mfd_cell s2mpu02_devs[] = { { .name = "s2mpu02-regulator", }, }; +static const struct mfd_cell s2mpu05_devs[] = { + { .name = "s2mpu05-regulator", }, + { .name = "s2mps15-rtc", }, +}; + static const struct of_device_id sec_dt_match[] = { { .compatible = "samsung,s5m8767-pmic", @@ -109,6 +114,9 @@ static const struct of_device_id sec_dt_match[] = { .compatible = "samsung,s2mpu02-pmic", .data = (void *)S2MPU02, }, { + .compatible = "samsung,s2mpu05-pmic", + .data = (void *)S2MPU05, + }, { /* Sentinel */ }, }; @@ -374,6 +382,10 @@ static int sec_pmic_probe(struct i2c_client *i2c) sec_devs = s2mpu02_devs; num_sec_devs = ARRAY_SIZE(s2mpu02_devs); break; + case S2MPU05: + sec_devs = s2mpu05_devs; + num_sec_devs = ARRAY_SIZE(s2mpu05_devs); + break; default: dev_err(&i2c->dev, "Unsupported device type (%lu)\n", sec_pmic->device_type); diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index e191aeb0c07c..047fc065fcf1 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -14,6 +14,7 @@ #include <linux/mfd/samsung/s2mps11.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mpu02.h> +#include <linux/mfd/samsung/s2mpu05.h> #include <linux/mfd/samsung/s5m8767.h> static const struct regmap_irq s2mps11_irqs[] = { @@ -225,6 +226,26 @@ static const struct regmap_irq s2mpu02_irqs[] = { }, }; +static const struct regmap_irq s2mpu05_irqs[] = { + REGMAP_IRQ_REG(S2MPU05_IRQ_PWRONF, 0, S2MPU05_IRQ_PWRONF_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_PWRONR, 0, S2MPU05_IRQ_PWRONR_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_JIGONBF, 0, S2MPU05_IRQ_JIGONBF_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_JIGONBR, 0, S2MPU05_IRQ_JIGONBR_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_ACOKF, 0, S2MPU05_IRQ_ACOKF_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_ACOKR, 0, S2MPU05_IRQ_ACOKR_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_PWRON1S, 0, S2MPU05_IRQ_PWRON1S_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_MRB, 0, S2MPU05_IRQ_MRB_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_RTC60S, 1, S2MPU05_IRQ_RTC60S_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_RTCA1, 1, S2MPU05_IRQ_RTCA1_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_RTCA0, 1, S2MPU05_IRQ_RTCA0_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_SMPL, 1, S2MPU05_IRQ_SMPL_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_RTC1S, 1, S2MPU05_IRQ_RTC1S_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_WTSR, 1, S2MPU05_IRQ_WTSR_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_INT120C, 2, S2MPU05_IRQ_INT120C_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_INT140C, 2, S2MPU05_IRQ_INT140C_MASK), + REGMAP_IRQ_REG(S2MPU05_IRQ_TSD, 2, S2MPU05_IRQ_TSD_MASK), +}; + static const struct regmap_irq s5m8767_irqs[] = { [S5M8767_IRQ_PWRR] = { .reg_offset = 0, @@ -339,6 +360,16 @@ static const struct regmap_irq_chip s2mpu02_irq_chip = { .ack_base = S2MPU02_REG_INT1, }; +static const struct regmap_irq_chip s2mpu05_irq_chip = { + .name = "s2mpu05", + .irqs = s2mpu05_irqs, + .num_irqs = ARRAY_SIZE(s2mpu05_irqs), + .num_regs = 3, + .status_base = S2MPU05_REG_INT1, + .mask_base = S2MPU05_REG_INT1M, + .ack_base = S2MPU05_REG_INT1, +}; + static const struct regmap_irq_chip s5m8767_irq_chip = { .name = "s5m8767", .irqs = s5m8767_irqs, @@ -383,6 +414,9 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) case S2MPU02: sec_irq_chip = &s2mpu02_irq_chip; break; + case S2MPU05: + sec_irq_chip = &s2mpu05_irq_chip; + break; default: dev_err(sec_pmic->dev, "Unknown device type %lu\n", sec_pmic->device_type); diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c index 6eda79533208..22159913bea0 100644 --- a/drivers/mfd/simple-mfd-i2c.c +++ b/drivers/mfd/simple-mfd-i2c.c @@ -83,11 +83,22 @@ static const struct simple_mfd_data maxim_max5970 = { .mfd_cell_size = ARRAY_SIZE(max5970_cells), }; +static const struct mfd_cell max77705_sensor_cells[] = { + { .name = "max77705-battery" }, + { .name = "max77705-hwmon", }, +}; + +static const struct simple_mfd_data maxim_mon_max77705 = { + .mfd_cell = max77705_sensor_cells, + .mfd_cell_size = ARRAY_SIZE(max77705_sensor_cells), +}; + static const struct of_device_id simple_mfd_i2c_of_match[] = { { .compatible = "kontron,sl28cpld" }, { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a}, { .compatible = "maxim,max5970", .data = &maxim_max5970}, { .compatible = "maxim,max5978", .data = &maxim_max5970}, + { .compatible = "maxim,max77705-battery", .data = &maxim_mon_max77705}, {} }; MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 0469e85d72cf..7ee293b09f62 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -920,7 +920,7 @@ static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); struct sm501_gpio *smgpio = smchip->ourgpio; - unsigned long bit = 1 << offset; + unsigned long bit = BIT(offset); void __iomem *regs = smchip->regbase; unsigned long save; unsigned long val; @@ -946,7 +946,7 @@ static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); struct sm501_gpio *smgpio = smchip->ourgpio; void __iomem *regs = smchip->regbase; - unsigned long bit = 1 << offset; + unsigned long bit = BIT(offset); unsigned long save; unsigned long ddr; @@ -971,7 +971,7 @@ static int sm501_gpio_output(struct gpio_chip *chip, { struct sm501_gpio_chip *smchip = gpiochip_get_data(chip); struct sm501_gpio *smgpio = smchip->ourgpio; - unsigned long bit = 1 << offset; + unsigned long bit = BIT(offset); void __iomem *regs = smchip->regbase; unsigned long save; unsigned long val; diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c deleted file mode 100644 index 02cc49daf2e3..000000000000 --- a/drivers/mfd/sta2x11-mfd.c +++ /dev/null @@ -1,645 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * STA2x11 mfd for GPIO, SCTL and APBREG - * - * Copyright (c) 2009-2011 Wind River Systems, Inc. - * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/export.h> -#include <linux/spinlock.h> -#include <linux/errno.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/pci.h> -#include <linux/seq_file.h> -#include <linux/platform_device.h> -#include <linux/mfd/core.h> -#include <linux/mfd/sta2x11-mfd.h> -#include <linux/regmap.h> - -#include <asm/sta2x11.h> - -static inline int __reg_within_range(unsigned int r, - unsigned int start, - unsigned int end) -{ - return ((r >= start) && (r <= end)); -} - -/* This describes STA2X11 MFD chip for us, we may have several */ -struct sta2x11_mfd { - struct sta2x11_instance *instance; - struct regmap *regmap[sta2x11_n_mfd_plat_devs]; - spinlock_t lock[sta2x11_n_mfd_plat_devs]; - struct list_head list; - void __iomem *regs[sta2x11_n_mfd_plat_devs]; -}; - -static LIST_HEAD(sta2x11_mfd_list); - -/* Three functions to act on the list */ -static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) -{ - struct sta2x11_instance *instance; - struct sta2x11_mfd *mfd; - - if (!pdev && !list_empty(&sta2x11_mfd_list)) { - pr_warn("%s: Unspecified device, using first instance\n", - __func__); - return list_entry(sta2x11_mfd_list.next, - struct sta2x11_mfd, list); - } - - instance = sta2x11_get_instance(pdev); - if (!instance) - return NULL; - list_for_each_entry(mfd, &sta2x11_mfd_list, list) { - if (mfd->instance == instance) - return mfd; - } - return NULL; -} - -static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) -{ - int i; - struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); - struct sta2x11_instance *instance; - - if (mfd) - return -EBUSY; - instance = sta2x11_get_instance(pdev); - if (!instance) - return -EINVAL; - mfd = kzalloc(sizeof(*mfd), flags); - if (!mfd) - return -ENOMEM; - INIT_LIST_HEAD(&mfd->list); - for (i = 0; i < ARRAY_SIZE(mfd->lock); i++) - spin_lock_init(&mfd->lock[i]); - mfd->instance = instance; - list_add(&mfd->list, &sta2x11_mfd_list); - return 0; -} - -/* This function is exported and is not expected to fail */ -u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, - enum sta2x11_mfd_plat_dev index) -{ - struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); - u32 r; - unsigned long flags; - void __iomem *regs; - - if (!mfd) { - dev_warn(&pdev->dev, ": can't access sctl regs\n"); - return 0; - } - - regs = mfd->regs[index]; - if (!regs) { - dev_warn(&pdev->dev, ": system ctl not initialized\n"); - return 0; - } - spin_lock_irqsave(&mfd->lock[index], flags); - r = readl(regs + reg); - r &= ~mask; - r |= val; - if (mask) - writel(r, regs + reg); - spin_unlock_irqrestore(&mfd->lock[index], flags); - return r; -} -EXPORT_SYMBOL(__sta2x11_mfd_mask); - -int sta2x11_mfd_get_regs_data(struct platform_device *dev, - enum sta2x11_mfd_plat_dev index, - void __iomem **regs, - spinlock_t **lock) -{ - struct pci_dev *pdev = *(struct pci_dev **)dev_get_platdata(&dev->dev); - struct sta2x11_mfd *mfd; - - if (!pdev) - return -ENODEV; - mfd = sta2x11_mfd_find(pdev); - if (!mfd) - return -ENODEV; - if (index >= sta2x11_n_mfd_plat_devs) - return -ENODEV; - *regs = mfd->regs[index]; - *lock = &mfd->lock[index]; - pr_debug("%s %d *regs = %p\n", __func__, __LINE__, *regs); - return *regs ? 0 : -ENODEV; -} -EXPORT_SYMBOL(sta2x11_mfd_get_regs_data); - -/* - * Special sta2x11-mfd regmap lock/unlock functions - */ - -static void sta2x11_regmap_lock(void *__lock) -{ - spinlock_t *lock = __lock; - spin_lock(lock); -} - -static void sta2x11_regmap_unlock(void *__lock) -{ - spinlock_t *lock = __lock; - spin_unlock(lock); -} - -/* OTP (one time programmable registers do not require locking */ -static void sta2x11_regmap_nolock(void *__lock) -{ -} - -static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { - [sta2x11_sctl] = STA2X11_MFD_SCTL_NAME, - [sta2x11_apbreg] = STA2X11_MFD_APBREG_NAME, - [sta2x11_apb_soc_regs] = STA2X11_MFD_APB_SOC_REGS_NAME, - [sta2x11_scr] = STA2X11_MFD_SCR_NAME, -}; - -static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg) -{ - return !__reg_within_range(reg, SCTL_SCPCIECSBRST, SCTL_SCRSTSTA); -} - -static struct regmap_config sta2x11_sctl_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .lock = sta2x11_regmap_lock, - .unlock = sta2x11_regmap_unlock, - .max_register = SCTL_SCRSTSTA, - .writeable_reg = sta2x11_sctl_writeable_reg, -}; - -static bool sta2x11_scr_readable_reg(struct device *dev, unsigned int reg) -{ - return (reg == STA2X11_SECR_CR) || - __reg_within_range(reg, STA2X11_SECR_FVR0, STA2X11_SECR_FVR1); -} - -static bool sta2x11_scr_writeable_reg(struct device *dev, unsigned int reg) -{ - return false; -} - -static struct regmap_config sta2x11_scr_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .lock = sta2x11_regmap_nolock, - .unlock = sta2x11_regmap_nolock, - .max_register = STA2X11_SECR_FVR1, - .readable_reg = sta2x11_scr_readable_reg, - .writeable_reg = sta2x11_scr_writeable_reg, -}; - -static bool sta2x11_apbreg_readable_reg(struct device *dev, unsigned int reg) -{ - /* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */ - if (reg >= APBREG_BSR_SARAC) - reg -= APBREG_BSR_SARAC; - switch (reg) { - case APBREG_BSR: - case APBREG_PAER: - case APBREG_PWAC: - case APBREG_PRAC: - case APBREG_PCG: - case APBREG_PUR: - case APBREG_EMU_PCG: - return true; - default: - return false; - } -} - -static bool sta2x11_apbreg_writeable_reg(struct device *dev, unsigned int reg) -{ - if (reg >= APBREG_BSR_SARAC) - reg -= APBREG_BSR_SARAC; - if (!sta2x11_apbreg_readable_reg(dev, reg)) - return false; - return reg != APBREG_PAER; -} - -static struct regmap_config sta2x11_apbreg_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .lock = sta2x11_regmap_lock, - .unlock = sta2x11_regmap_unlock, - .max_register = APBREG_EMU_PCG_SARAC, - .readable_reg = sta2x11_apbreg_readable_reg, - .writeable_reg = sta2x11_apbreg_writeable_reg, -}; - -static bool sta2x11_apb_soc_regs_readable_reg(struct device *dev, - unsigned int reg) -{ - return reg <= PCIE_SoC_INT_ROUTER_STATUS3_REG || - __reg_within_range(reg, DMA_IP_CTRL_REG, SPARE3_RESERVED) || - __reg_within_range(reg, MASTER_LOCK_REG, - SYSTEM_CONFIG_STATUS_REG) || - reg == MSP_CLK_CTRL_REG || - __reg_within_range(reg, COMPENSATION_REG1, TEST_CTL_REG); -} - -static bool sta2x11_apb_soc_regs_writeable_reg(struct device *dev, - unsigned int reg) -{ - if (!sta2x11_apb_soc_regs_readable_reg(dev, reg)) - return false; - switch (reg) { - case PCIE_COMMON_CLOCK_CONFIG_0_4_0: - case SYSTEM_CONFIG_STATUS_REG: - case COMPENSATION_REG1: - case PCIE_SoC_INT_ROUTER_STATUS0_REG...PCIE_SoC_INT_ROUTER_STATUS3_REG: - case PCIE_PM_STATUS_0_PORT_0_4...PCIE_PM_STATUS_7_0_EP4: - return false; - default: - return true; - } -} - -static struct regmap_config sta2x11_apb_soc_regs_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .lock = sta2x11_regmap_lock, - .unlock = sta2x11_regmap_unlock, - .max_register = TEST_CTL_REG, - .readable_reg = sta2x11_apb_soc_regs_readable_reg, - .writeable_reg = sta2x11_apb_soc_regs_writeable_reg, -}; - -static struct regmap_config * -sta2x11_mfd_regmap_configs[sta2x11_n_mfd_plat_devs] = { - [sta2x11_sctl] = &sta2x11_sctl_regmap_config, - [sta2x11_apbreg] = &sta2x11_apbreg_regmap_config, - [sta2x11_apb_soc_regs] = &sta2x11_apb_soc_regs_regmap_config, - [sta2x11_scr] = &sta2x11_scr_regmap_config, -}; - -/* Probe for the four platform devices */ - -static int sta2x11_mfd_platform_probe(struct platform_device *dev, - enum sta2x11_mfd_plat_dev index) -{ - struct pci_dev **pdev; - struct sta2x11_mfd *mfd; - struct resource *res; - const char *name = sta2x11_mfd_names[index]; - struct regmap_config *regmap_config = sta2x11_mfd_regmap_configs[index]; - - pdev = dev_get_platdata(&dev->dev); - mfd = sta2x11_mfd_find(*pdev); - if (!mfd) - return -ENODEV; - if (!regmap_config) - return -ENODEV; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENOMEM; - - if (!request_mem_region(res->start, resource_size(res), name)) - return -EBUSY; - - mfd->regs[index] = ioremap(res->start, resource_size(res)); - if (!mfd->regs[index]) { - release_mem_region(res->start, resource_size(res)); - return -ENOMEM; - } - regmap_config->lock_arg = &mfd->lock; - /* - No caching, registers could be reached both via regmap and via - void __iomem * - */ - regmap_config->cache_type = REGCACHE_NONE; - mfd->regmap[index] = devm_regmap_init_mmio(&dev->dev, mfd->regs[index], - regmap_config); - WARN_ON(IS_ERR(mfd->regmap[index])); - - return 0; -} - -static int sta2x11_sctl_probe(struct platform_device *dev) -{ - return sta2x11_mfd_platform_probe(dev, sta2x11_sctl); -} - -static int sta2x11_apbreg_probe(struct platform_device *dev) -{ - return sta2x11_mfd_platform_probe(dev, sta2x11_apbreg); -} - -static int sta2x11_apb_soc_regs_probe(struct platform_device *dev) -{ - return sta2x11_mfd_platform_probe(dev, sta2x11_apb_soc_regs); -} - -static int sta2x11_scr_probe(struct platform_device *dev) -{ - return sta2x11_mfd_platform_probe(dev, sta2x11_scr); -} - -/* The three platform drivers */ -static struct platform_driver sta2x11_sctl_platform_driver = { - .driver = { - .name = STA2X11_MFD_SCTL_NAME, - }, - .probe = sta2x11_sctl_probe, -}; - -static struct platform_driver sta2x11_platform_driver = { - .driver = { - .name = STA2X11_MFD_APBREG_NAME, - }, - .probe = sta2x11_apbreg_probe, -}; - -static struct platform_driver sta2x11_apb_soc_regs_platform_driver = { - .driver = { - .name = STA2X11_MFD_APB_SOC_REGS_NAME, - }, - .probe = sta2x11_apb_soc_regs_probe, -}; - -static struct platform_driver sta2x11_scr_platform_driver = { - .driver = { - .name = STA2X11_MFD_SCR_NAME, - }, - .probe = sta2x11_scr_probe, -}; - -static struct platform_driver * const drivers[] = { - &sta2x11_platform_driver, - &sta2x11_sctl_platform_driver, - &sta2x11_apb_soc_regs_platform_driver, - &sta2x11_scr_platform_driver, -}; - -static int __init sta2x11_drivers_init(void) -{ - return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); -} - -/* - * What follows are the PCI devices that host the above pdevs. - * Each logic block is 4kB and they are all consecutive: we use this info. - */ - -/* Mfd 0 device */ - -/* Mfd 0, Bar 0 */ -enum mfd0_bar0_cells { - STA2X11_GPIO_0 = 0, - STA2X11_GPIO_1, - STA2X11_GPIO_2, - STA2X11_GPIO_3, - STA2X11_SCTL, - STA2X11_SCR, - STA2X11_TIME, -}; -/* Mfd 0 , Bar 1 */ -enum mfd0_bar1_cells { - STA2X11_APBREG = 0, -}; -#define CELL_4K(_name, _cell) { \ - .name = _name, \ - .start = _cell * 4096, .end = _cell * 4096 + 4095, \ - .flags = IORESOURCE_MEM, \ - } - -static const struct resource gpio_resources[] = { - { - /* 4 consecutive cells, 1 driver */ - .name = STA2X11_MFD_GPIO_NAME, - .start = 0, - .end = (4 * 4096) - 1, - .flags = IORESOURCE_MEM, - } -}; -static const struct resource sctl_resources[] = { - CELL_4K(STA2X11_MFD_SCTL_NAME, STA2X11_SCTL), -}; -static const struct resource scr_resources[] = { - CELL_4K(STA2X11_MFD_SCR_NAME, STA2X11_SCR), -}; -static const struct resource time_resources[] = { - CELL_4K(STA2X11_MFD_TIME_NAME, STA2X11_TIME), -}; - -static const struct resource apbreg_resources[] = { - CELL_4K(STA2X11_MFD_APBREG_NAME, STA2X11_APBREG), -}; - -#define DEV(_name, _r) \ - { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } - -static struct mfd_cell sta2x11_mfd0_bar0[] = { - /* offset 0: we add pdata later */ - DEV(STA2X11_MFD_GPIO_NAME, gpio_resources), - DEV(STA2X11_MFD_SCTL_NAME, sctl_resources), - DEV(STA2X11_MFD_SCR_NAME, scr_resources), - DEV(STA2X11_MFD_TIME_NAME, time_resources), -}; - -static struct mfd_cell sta2x11_mfd0_bar1[] = { - DEV(STA2X11_MFD_APBREG_NAME, apbreg_resources), -}; - -/* Mfd 1 devices */ - -/* Mfd 1, Bar 0 */ -enum mfd1_bar0_cells { - STA2X11_VIC = 0, -}; - -/* Mfd 1, Bar 1 */ -enum mfd1_bar1_cells { - STA2X11_APB_SOC_REGS = 0, -}; - -static const struct resource vic_resources[] = { - CELL_4K(STA2X11_MFD_VIC_NAME, STA2X11_VIC), -}; - -static const struct resource apb_soc_regs_resources[] = { - CELL_4K(STA2X11_MFD_APB_SOC_REGS_NAME, STA2X11_APB_SOC_REGS), -}; - -static struct mfd_cell sta2x11_mfd1_bar0[] = { - DEV(STA2X11_MFD_VIC_NAME, vic_resources), -}; - -static struct mfd_cell sta2x11_mfd1_bar1[] = { - DEV(STA2X11_MFD_APB_SOC_REGS_NAME, apb_soc_regs_resources), -}; - - -static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state) -{ - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - return 0; -} - -static int sta2x11_mfd_resume(struct pci_dev *pdev) -{ - int err; - - pci_set_power_state(pdev, PCI_D0); - err = pci_enable_device(pdev); - if (err) - return err; - pci_restore_state(pdev); - - return 0; -} - -struct sta2x11_mfd_bar_setup_data { - struct mfd_cell *cells; - int ncells; -}; - -struct sta2x11_mfd_setup_data { - struct sta2x11_mfd_bar_setup_data bars[2]; -}; - -#define STA2X11_MFD0 0 -#define STA2X11_MFD1 1 - -static struct sta2x11_mfd_setup_data mfd_setup_data[] = { - /* Mfd 0: gpio, sctl, scr, timers / apbregs */ - [STA2X11_MFD0] = { - .bars = { - [0] = { - .cells = sta2x11_mfd0_bar0, - .ncells = ARRAY_SIZE(sta2x11_mfd0_bar0), - }, - [1] = { - .cells = sta2x11_mfd0_bar1, - .ncells = ARRAY_SIZE(sta2x11_mfd0_bar1), - }, - }, - }, - /* Mfd 1: vic / apb-soc-regs */ - [STA2X11_MFD1] = { - .bars = { - [0] = { - .cells = sta2x11_mfd1_bar0, - .ncells = ARRAY_SIZE(sta2x11_mfd1_bar0), - }, - [1] = { - .cells = sta2x11_mfd1_bar1, - .ncells = ARRAY_SIZE(sta2x11_mfd1_bar1), - }, - }, - }, -}; - -static void sta2x11_mfd_setup(struct pci_dev *pdev, - struct sta2x11_mfd_setup_data *sd) -{ - int i, j; - for (i = 0; i < ARRAY_SIZE(sd->bars); i++) - for (j = 0; j < sd->bars[i].ncells; j++) { - sd->bars[i].cells[j].pdata_size = sizeof(pdev); - sd->bars[i].cells[j].platform_data = &pdev; - } -} - -static int sta2x11_mfd_probe(struct pci_dev *pdev, - const struct pci_device_id *pci_id) -{ - int err, i; - struct sta2x11_mfd_setup_data *setup_data; - - dev_info(&pdev->dev, "%s\n", __func__); - - err = pci_enable_device(pdev); - if (err) { - dev_err(&pdev->dev, "Can't enable device.\n"); - return err; - } - - err = pci_enable_msi(pdev); - if (err) - dev_info(&pdev->dev, "Enable msi failed\n"); - - setup_data = pci_id->device == PCI_DEVICE_ID_STMICRO_GPIO ? - &mfd_setup_data[STA2X11_MFD0] : - &mfd_setup_data[STA2X11_MFD1]; - - /* platform data is the pci device for all of them */ - sta2x11_mfd_setup(pdev, setup_data); - - /* Record this pdev before mfd_add_devices: their probe looks for it */ - if (!sta2x11_mfd_find(pdev)) - sta2x11_mfd_add(pdev, GFP_KERNEL); - - /* Just 2 bars for all mfd's at present */ - for (i = 0; i < 2; i++) { - err = mfd_add_devices(&pdev->dev, -1, - setup_data->bars[i].cells, - setup_data->bars[i].ncells, - &pdev->resource[i], - 0, NULL); - if (err) { - dev_err(&pdev->dev, - "mfd_add_devices[%d] failed: %d\n", i, err); - goto err_disable; - } - } - - return 0; - -err_disable: - mfd_remove_devices(&pdev->dev); - pci_disable_device(pdev); - pci_disable_msi(pdev); - return err; -} - -static const struct pci_device_id sta2x11_mfd_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)}, - {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIC)}, - {0,}, -}; - -static struct pci_driver sta2x11_mfd_driver = { - .name = "sta2x11-mfd", - .id_table = sta2x11_mfd_tbl, - .probe = sta2x11_mfd_probe, - .suspend = sta2x11_mfd_suspend, - .resume = sta2x11_mfd_resume, -}; - -static int __init sta2x11_mfd_init(void) -{ - pr_info("%s\n", __func__); - return pci_register_driver(&sta2x11_mfd_driver); -} - -/* - * All of this must be ready before "normal" devices like MMCI appear. - * But MFD (the pci device) can't be too early. The following choice - * prepares platform drivers very early and probe the PCI device later, - * but before other PCI devices. - */ -subsys_initcall(sta2x11_drivers_init); -rootfs_initcall(sta2x11_mfd_init); diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c index 650724e19b88..e3c116ee4034 100644 --- a/drivers/mfd/stm32-timers.c +++ b/drivers/mfd/stm32-timers.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/reset.h> #define STM32_TIMERS_MAX_REGISTERS 0x3fc @@ -173,6 +174,31 @@ static void stm32_timers_get_arr_size(struct stm32_timers *ddata) regmap_write(ddata->regmap, TIM_ARR, arr); } +static int stm32_timers_probe_hwcfgr(struct device *dev, struct stm32_timers *ddata) +{ + u32 val; + + ddata->ipidr = (uintptr_t)device_get_match_data(dev); + if (!ddata->ipidr) { + /* Fallback to legacy method for probing counter width */ + stm32_timers_get_arr_size(ddata); + return 0; + } + + regmap_read(ddata->regmap, TIM_IPIDR, &val); + if (val != ddata->ipidr) { + dev_err(dev, "Unsupported device detected: %u\n", val); + return -EINVAL; + } + + regmap_read(ddata->regmap, TIM_HWCFGR2, &val); + + /* Counter width in bits, max reload value is BIT(width) - 1 */ + ddata->max_arr = BIT(FIELD_GET(TIM_HWCFGR2_CNT_WIDTH, val)) - 1; + + return 0; +} + static int stm32_timers_dma_probe(struct device *dev, struct stm32_timers *ddata) { @@ -285,7 +311,9 @@ static int stm32_timers_probe(struct platform_device *pdev) if (IS_ERR(ddata->clk)) return PTR_ERR(ddata->clk); - stm32_timers_get_arr_size(ddata); + ret = stm32_timers_probe_hwcfgr(dev, ddata); + if (ret) + return ret; ret = stm32_timers_irq_probe(pdev, ddata); if (ret) @@ -320,6 +348,7 @@ static void stm32_timers_remove(struct platform_device *pdev) static const struct of_device_id stm32_timers_of_match[] = { { .compatible = "st,stm32-timers", }, + { .compatible = "st,stm32mp25-timers", .data = (void *)STM32MP25_TIM_IPIDR }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, stm32_timers_of_match); diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index aa4a9940b569..ae71a2710bed 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -47,6 +47,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) struct regmap_config syscon_config = syscon_regmap_config; struct resource res; struct reset_control *reset; + resource_size_t res_size; WARN_ON(!mutex_is_locked(&syscon_list_lock)); @@ -96,6 +97,12 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) } } + res_size = resource_size(&res); + if (res_size < reg_io_width) { + ret = -EFAULT; + goto err_regmap; + } + syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%pa", np, &res.start); if (!syscon_config.name) { ret = -ENOMEM; @@ -103,7 +110,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) } syscon_config.reg_stride = reg_io_width; syscon_config.val_bits = reg_io_width * 8; - syscon_config.max_register = resource_size(&res) - reg_io_width; + syscon_config.max_register = res_size - reg_io_width; if (!syscon_config.max_register) syscon_config.max_register_is_0 = true; diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c index 710364435b6b..00fb12c4f491 100644 --- a/drivers/mfd/tps65010.c +++ b/drivers/mfd/tps65010.c @@ -16,6 +16,7 @@ #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/string_choices.h> #include <linux/mutex.h> #include <linux/platform_device.h> @@ -250,7 +251,7 @@ static int dbg_show(struct seq_file *s, void *_) v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER); seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n", (value & 0x80) - ? ((v2 & 0x80) ? "on" : "off") + ? str_on_off(v2 & 0x80) : ((v2 & 0x80) ? "blink" : "(nPG)"), value, v2, (value & 0x7f) * 10, (v2 & 0x7f) * 100); @@ -259,7 +260,7 @@ static int dbg_show(struct seq_file *s, void *_) v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER); seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n", (value & 0x80) - ? ((v2 & 0x80) ? "on" : "off") + ? str_on_off(v2 & 0x80) : ((v2 & 0x80) ? "blink" : "off"), value, v2, (value & 0x7f) * 10, (v2 & 0x7f) * 100); @@ -738,7 +739,7 @@ int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) TPS_DEFGPIO, defgpio); pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME, - gpio, value ? "high" : "low", + gpio, str_high_low(value), i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO)); mutex_unlock(&the_tps->lock); @@ -850,7 +851,7 @@ int tps65010_set_vib(unsigned value) status = i2c_smbus_write_byte_data(the_tps->client, TPS_VDCDC2, vdcdc2); - pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off"); + pr_debug("%s: vibrator %s\n", DRIVER_NAME, str_on_off(value)); mutex_unlock(&the_tps->lock); return status; @@ -872,7 +873,7 @@ int tps65010_set_low_pwr(unsigned mode) mutex_lock(&the_tps->lock); pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME, - mode ? "enable" : "disable", + str_enable_disable(mode), i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1)); vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1); @@ -984,7 +985,7 @@ int tps65013_set_low_pwr(unsigned mode) pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n", DRIVER_NAME, - mode ? "enable" : "disable", + str_enable_disable(mode), i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG), i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1)); diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c index 081c5a30b04a..fd390600fbf0 100644 --- a/drivers/mfd/tps65219.c +++ b/drivers/mfd/tps65219.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // -// Driver for TPS65219 Integrated Power Management Integrated Chips (PMIC) +// Driver for TPS65214/TPS65215/TPS65219 Power Management Integrated Chips // // Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/ +// Copyright (C) 2024 Texas Instruments Incorporated - https://www.ti.com/ #include <linux/i2c.h> #include <linux/reboot.h> @@ -59,6 +60,84 @@ static const struct resource tps65219_pwrbutton_resources[] = { DEFINE_RES_IRQ_NAMED(TPS65219_INT_PB_RISING_EDGE_DETECT, "rising"), }; +static const struct resource tps65214_regulator_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_SCG, "LDO1_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_OC, "LDO1_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_UV, "LDO1_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_SCG, "LDO2_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_OC, "LDO2_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_UV, "LDO2_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_SCG, "BUCK3_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_OC, "BUCK3_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_NEG_OC, "BUCK3_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_UV, "BUCK3_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_SCG, "BUCK1_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_OC, "BUCK1_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_NEG_OC, "BUCK1_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_UV, "BUCK1_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_SCG, "BUCK2_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_OC, "BUCK2_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_NEG_OC, "BUCK2_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_UV, "BUCK2_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV, "BUCK1_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV, "BUCK2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV, "BUCK3_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV, "LDO1_RV"), + DEFINE_RES_IRQ_NAMED(TPS65214_INT_LDO2_RV, "LDO2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV_SD, "BUCK1_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV_SD, "BUCK2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV_SD, "BUCK3_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65214_INT_LDO1_RV_SD, "LDO1_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_RV_SD, "LDO2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_TIMEOUT, "TIMEOUT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_WARM, "SENSOR_2_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_WARM, "SENSOR_1_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_WARM, "SENSOR_0_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_HOT, "SENSOR_2_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_HOT, "SENSOR_1_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"), +}; + +static const struct resource tps65215_regulator_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO1_SCG, "LDO1_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO1_OC, "LDO1_OC"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO1_UV, "LDO1_UV"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_SCG, "LDO2_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_OC, "LDO2_OC"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_UV, "LDO2_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_SCG, "BUCK3_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_OC, "BUCK3_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_NEG_OC, "BUCK3_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_UV, "BUCK3_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_SCG, "BUCK1_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_OC, "BUCK1_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_NEG_OC, "BUCK1_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_UV, "BUCK1_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_SCG, "BUCK2_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_OC, "BUCK2_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_NEG_OC, "BUCK2_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_UV, "BUCK2_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV, "BUCK1_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV, "BUCK2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV, "BUCK3_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV, "LDO1_RV"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_RV, "LDO2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV_SD, "BUCK1_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV_SD, "BUCK2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV_SD, "BUCK3_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV_SD, "LDO1_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_RV_SD, "LDO2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_TIMEOUT, "TIMEOUT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_3_WARM, "SENSOR_3_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_WARM, "SENSOR_2_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_WARM, "SENSOR_1_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_WARM, "SENSOR_0_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_3_HOT, "SENSOR_3_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_HOT, "SENSOR_2_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_HOT, "SENSOR_1_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"), +}; + static const struct resource tps65219_regulator_resources[] = { DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_SCG, "LDO3_SCG"), DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_OC, "LDO3_OC"), @@ -109,6 +188,16 @@ static const struct resource tps65219_regulator_resources[] = { DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"), }; +static const struct mfd_cell tps65214_cells[] = { + MFD_CELL_RES("tps65214-regulator", tps65214_regulator_resources), + MFD_CELL_NAME("tps65215-gpio"), +}; + +static const struct mfd_cell tps65215_cells[] = { + MFD_CELL_RES("tps65215-regulator", tps65215_regulator_resources), + MFD_CELL_NAME("tps65215-gpio"), +}; + static const struct mfd_cell tps65219_cells[] = { MFD_CELL_RES("tps65219-regulator", tps65219_regulator_resources), MFD_CELL_NAME("tps65219-gpio"), @@ -136,8 +225,19 @@ static unsigned int bit3_offsets[] = { TPS65219_REG_INT_BUCK_1_2_POS }; /* Buck static unsigned int bit4_offsets[] = { TPS65219_REG_INT_BUCK_3_POS }; /* Buck 3 */ static unsigned int bit5_offsets[] = { TPS65219_REG_INT_LDO_1_2_POS }; /* LDO 1-2 */ static unsigned int bit6_offsets[] = { TPS65219_REG_INT_LDO_3_4_POS }; /* LDO 3-4 */ +static unsigned int tps65215_bit5_offsets[] = { TPS65215_REG_INT_LDO_1_POS }; +static unsigned int tps65215_bit6_offsets[] = { TPS65215_REG_INT_LDO_2_POS }; static unsigned int bit7_offsets[] = { TPS65219_REG_INT_PB_POS }; /* Power Button */ +/* TPS65214 INT_SOURCE bit 6 is 'RESERVED'*/ +static unsigned int tps65214_bit0_offsets[] = { TPS65214_REG_INT_TO_RV_POS }; +static unsigned int tps65214_bit1_offsets[] = { TPS65214_REG_INT_RV_POS }; +static unsigned int tps65214_bit2_offsets[] = { TPS65214_REG_INT_SYS_POS }; +static unsigned int tps65214_bit3_offsets[] = { TPS65214_REG_INT_BUCK_1_2_POS }; +static unsigned int tps65214_bit4_offsets[] = { TPS65214_REG_INT_BUCK_3_POS }; +static unsigned int tps65214_bit5_offsets[] = { TPS65214_REG_INT_LDO_1_2_POS }; +static unsigned int tps65214_bit7_offsets[] = { TPS65214_REG_INT_PB_POS }; + static struct regmap_irq_sub_irq_map tps65219_sub_irq_offsets[] = { REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets), REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets), @@ -149,9 +249,112 @@ static struct regmap_irq_sub_irq_map tps65219_sub_irq_offsets[] = { REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets), }; +static struct regmap_irq_sub_irq_map tps65215_sub_irq_offsets[] = { + REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65215_bit5_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65215_bit6_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets), +}; + +static struct regmap_irq_sub_irq_map tps65214_sub_irq_offsets[] = { + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit0_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit1_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit2_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit3_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit4_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit5_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit7_offsets), +}; + #define TPS65219_REGMAP_IRQ_REG(int_name, register_position) \ REGMAP_IRQ_REG(int_name, register_position, int_name##_MASK) +static const struct regmap_irq tps65214_irqs[] = { + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_SCG, TPS65214_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_OC, TPS65214_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_UV, TPS65214_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_SCG, TPS65214_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_OC, TPS65214_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_UV, TPS65214_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_SCG, TPS65214_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_OC, TPS65214_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_NEG_OC, TPS65214_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_UV, TPS65214_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_SCG, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_OC, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_NEG_OC, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_UV, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_SCG, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_OC, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_NEG_OC, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_UV, TPS65214_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_WARM, TPS65214_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_WARM, TPS65214_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_WARM, TPS65214_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_HOT, TPS65214_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_HOT, TPS65214_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_HOT, TPS65214_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV, TPS65214_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV, TPS65214_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV, TPS65214_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV, TPS65214_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65214_INT_LDO2_RV, TPS65214_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV_SD, TPS65214_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV_SD, TPS65214_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV_SD, TPS65214_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65214_INT_LDO1_RV_SD, TPS65214_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_RV_SD, TPS65214_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_TIMEOUT, TPS65214_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_FALLING_EDGE_DETECT, TPS65214_REG_INT_PB_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65214_REG_INT_PB_POS), +}; + +static const struct regmap_irq tps65215_irqs[] = { + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO1_SCG, TPS65215_REG_INT_LDO_1_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO1_OC, TPS65215_REG_INT_LDO_1_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO1_UV, TPS65215_REG_INT_LDO_1_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_SCG, TPS65215_REG_INT_LDO_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_OC, TPS65215_REG_INT_LDO_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_UV, TPS65215_REG_INT_LDO_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_SCG, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_OC, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_NEG_OC, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_UV, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_SCG, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_NEG_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_UV, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_SCG, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_NEG_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_UV, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_3_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_3_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_TIMEOUT, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_FALLING_EDGE_DETECT, TPS65219_REG_INT_PB_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65219_REG_INT_PB_POS), +}; + static const struct regmap_irq tps65219_irqs[] = { TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_SCG, TPS65219_REG_INT_LDO_3_4_POS), TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_OC, TPS65219_REG_INT_LDO_3_4_POS), @@ -204,6 +407,34 @@ static const struct regmap_irq tps65219_irqs[] = { TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65219_REG_INT_PB_POS), }; +static const struct regmap_irq_chip tps65214_irq_chip = { + .name = "tps65214_irq", + .main_status = TPS65219_REG_INT_SOURCE, + .num_main_regs = 1, + .num_main_status_bits = 8, + .irqs = tps65214_irqs, + .num_irqs = ARRAY_SIZE(tps65214_irqs), + .status_base = TPS65214_REG_INT_LDO_1_2, + .ack_base = TPS65214_REG_INT_LDO_1_2, + .clear_ack = 1, + .num_regs = 8, + .sub_reg_offsets = tps65214_sub_irq_offsets, +}; + +static const struct regmap_irq_chip tps65215_irq_chip = { + .name = "tps65215_irq", + .main_status = TPS65219_REG_INT_SOURCE, + .num_main_regs = 1, + .num_main_status_bits = 8, + .irqs = tps65215_irqs, + .num_irqs = ARRAY_SIZE(tps65215_irqs), + .status_base = TPS65215_REG_INT_LDO_2, + .ack_base = TPS65215_REG_INT_LDO_2, + .clear_ack = 1, + .num_regs = 8, + .sub_reg_offsets = tps65215_sub_irq_offsets, +}; + static const struct regmap_irq_chip tps65219_irq_chip = { .name = "tps65219_irq", .main_status = TPS65219_REG_INT_SOURCE, @@ -218,10 +449,34 @@ static const struct regmap_irq_chip tps65219_irq_chip = { .sub_reg_offsets = tps65219_sub_irq_offsets, }; +struct tps65219_chip_data { + const struct regmap_irq_chip *irq_chip; + const struct mfd_cell *cells; + int n_cells; +}; + +static struct tps65219_chip_data chip_info_table[] = { + [TPS65214] = { + .irq_chip = &tps65214_irq_chip, + .cells = tps65214_cells, + .n_cells = ARRAY_SIZE(tps65214_cells), + }, + [TPS65215] = { + .irq_chip = &tps65215_irq_chip, + .cells = tps65215_cells, + .n_cells = ARRAY_SIZE(tps65215_cells), + }, + [TPS65219] = { + .irq_chip = &tps65219_irq_chip, + .cells = tps65219_cells, + .n_cells = ARRAY_SIZE(tps65219_cells), + }, +}; + static int tps65219_probe(struct i2c_client *client) { struct tps65219 *tps; - unsigned int chipid; + struct tps65219_chip_data *pmic; bool pwr_button; int ret; @@ -232,6 +487,8 @@ static int tps65219_probe(struct i2c_client *client) i2c_set_clientdata(client, tps); tps->dev = &client->dev; + tps->chip_id = (uintptr_t)i2c_get_match_data(client); + pmic = &chip_info_table[tps->chip_id]; tps->regmap = devm_regmap_init_i2c(client, &tps65219_regmap_config); if (IS_ERR(tps->regmap)) { @@ -240,20 +497,14 @@ static int tps65219_probe(struct i2c_client *client) return ret; } - ret = devm_regmap_add_irq_chip(&client->dev, tps->regmap, client->irq, - IRQF_ONESHOT, 0, &tps65219_irq_chip, + ret = devm_regmap_add_irq_chip(tps->dev, tps->regmap, client->irq, + IRQF_ONESHOT, 0, pmic->irq_chip, &tps->irq_data); if (ret) return ret; - ret = regmap_read(tps->regmap, TPS65219_REG_TI_DEV_ID, &chipid); - if (ret) { - dev_err(tps->dev, "Failed to read device ID: %d\n", ret); - return ret; - } - ret = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, - tps65219_cells, ARRAY_SIZE(tps65219_cells), + pmic->cells, pmic->n_cells, NULL, 0, regmap_irq_get_domain(tps->irq_data)); if (ret) { dev_err(tps->dev, "Failed to add child devices: %d\n", ret); @@ -291,7 +542,9 @@ static int tps65219_probe(struct i2c_client *client) } static const struct of_device_id of_tps65219_match_table[] = { - { .compatible = "ti,tps65219", }, + { .compatible = "ti,tps65214", .data = (void *)TPS65214, }, + { .compatible = "ti,tps65215", .data = (void *)TPS65215, }, + { .compatible = "ti,tps65219", .data = (void *)TPS65219, }, {} }; MODULE_DEVICE_TABLE(of, of_tps65219_match_table); @@ -306,5 +559,5 @@ static struct i2c_driver tps65219_driver = { module_i2c_driver(tps65219_driver); MODULE_AUTHOR("Jerome Neanne <jneanne@baylibre.com>"); -MODULE_DESCRIPTION("TPS65219 power management IC driver"); +MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 PMIC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/upboard-fpga.c b/drivers/mfd/upboard-fpga.c index 5a330e2f2229..afce623bbba5 100644 --- a/drivers/mfd/upboard-fpga.c +++ b/drivers/mfd/upboard-fpga.c @@ -11,7 +11,6 @@ * Author: Thomas Richard <thomas.richard@bootlin.com> */ -#include <linux/acpi.h> #include <linux/bitfield.h> #include <linux/device.h> #include <linux/err.h> @@ -311,7 +310,7 @@ MODULE_DEVICE_TABLE(acpi, upboard_fpga_acpi_match); static struct platform_driver upboard_fpga_driver = { .driver = { .name = "upboard-fpga", - .acpi_match_table = ACPI_PTR(upboard_fpga_acpi_match), + .acpi_match_table = upboard_fpga_acpi_match, .dev_groups = upboard_fpga_groups, }, .probe = upboard_fpga_probe, diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 7b18358f194a..c120d8ed870f 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -583,6 +583,12 @@ config CHARGER_MAX77693 help Say Y to enable support for the Maxim MAX77693 battery charger. +config CHARGER_MAX77705 + tristate "Maxim MAX77705 battery charger driver" + depends on MFD_MAX77705 + help + Say Y to enable support for the Maxim MAX77705 battery charger. + config CHARGER_MAX77976 tristate "Maxim MAX77976 battery charger driver" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index b55cc48a4c86..a4669383d53e 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o +obj-$(CONFIG_CHARGER_MAX77705) += max77705_charger.o obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o diff --git a/drivers/power/supply/max77705_charger.c b/drivers/power/supply/max77705_charger.c new file mode 100644 index 000000000000..eec5e9ef795e --- /dev/null +++ b/drivers/power/supply/max77705_charger.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Based on max77650-charger.c + * + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org> + * + * Battery charger driver for MAXIM 77705 charger/power-supply. + */ + +#include <linux/devm-helpers.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/max77693-common.h> +#include <linux/mfd/max77705-private.h> +#include <linux/power/max77705_charger.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> + +static const char *max77705_charger_model = "max77705"; +static const char *max77705_charger_manufacturer = "Maxim Integrated"; + +static const struct regmap_config max77705_chg_regmap_config = { + .reg_base = MAX77705_CHG_REG_BASE, + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77705_CHG_REG_SAFEOUT_CTRL, +}; + +static enum power_supply_property max77705_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, +}; + +static int max77705_chgin_irq(void *irq_drv_data) +{ + struct max77705_charger_data *charger = irq_drv_data; + + queue_work(charger->wqueue, &charger->chgin_work); + + return 0; +} + +static const struct regmap_irq max77705_charger_irqs[] = { + { .mask = MAX77705_BYP_IM, }, + { .mask = MAX77705_INP_LIMIT_IM, }, + { .mask = MAX77705_BATP_IM, }, + { .mask = MAX77705_BAT_IM, }, + { .mask = MAX77705_CHG_IM, }, + { .mask = MAX77705_WCIN_IM, }, + { .mask = MAX77705_CHGIN_IM, }, + { .mask = MAX77705_AICL_IM, }, +}; + +static struct regmap_irq_chip max77705_charger_irq_chip = { + .name = "max77705-charger", + .status_base = MAX77705_CHG_REG_INT, + .mask_base = MAX77705_CHG_REG_INT_MASK, + .handle_post_irq = max77705_chgin_irq, + .num_regs = 1, + .irqs = max77705_charger_irqs, + .num_irqs = ARRAY_SIZE(max77705_charger_irqs), +}; + +static int max77705_charger_enable(struct max77705_charger_data *chg) +{ + int rv; + + rv = regmap_update_bits(chg->regmap, MAX77705_CHG_REG_CNFG_09, + MAX77705_CHG_EN_MASK, MAX77705_CHG_EN_MASK); + if (rv) + dev_err(chg->dev, "unable to enable the charger: %d\n", rv); + + return rv; +} + +static void max77705_charger_disable(void *data) +{ + struct max77705_charger_data *chg = data; + int rv; + + rv = regmap_update_bits(chg->regmap, + MAX77705_CHG_REG_CNFG_09, + MAX77705_CHG_EN_MASK, + MAX77705_CHG_DISABLE); + if (rv) + dev_err(chg->dev, "unable to disable the charger: %d\n", rv); +} + +static int max77705_get_online(struct regmap *regmap, int *val) +{ + unsigned int data; + int ret; + + ret = regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &data); + if (ret < 0) + return ret; + + *val = !!(data & MAX77705_CHGIN_OK); + + return 0; +} + +static int max77705_check_battery(struct max77705_charger_data *charger, int *val) +{ + unsigned int reg_data; + unsigned int reg_data2; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_INT_OK, ®_data); + + dev_dbg(charger->dev, "CHG_INT_OK(0x%x)\n", reg_data); + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, ®_data2); + + dev_dbg(charger->dev, "CHG_DETAILS00(0x%x)\n", reg_data2); + + if ((reg_data & MAX77705_BATP_OK) || !(reg_data2 & MAX77705_BATP_DTLS)) + *val = true; + else + *val = false; + + return 0; +} + +static int max77705_get_charge_type(struct max77705_charger_data *charger, int *val) +{ + struct regmap *regmap = charger->regmap; + unsigned int reg_data; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data); + if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) { + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + return 0; + } + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, ®_data); + reg_data &= MAX77705_CHG_DTLS; + + switch (reg_data) { + case 0x0: + case MAX77705_CHARGER_CONSTANT_CURRENT: + case MAX77705_CHARGER_CONSTANT_VOLTAGE: + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; + return 0; + default: + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + return 0; + } + + return 0; +} + +static int max77705_get_status(struct max77705_charger_data *charger, int *val) +{ + struct regmap *regmap = charger->regmap; + unsigned int reg_data; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data); + if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) { + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + return 0; + } + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, ®_data); + reg_data &= MAX77705_CHG_DTLS; + + switch (reg_data) { + case 0x0: + case MAX77705_CHARGER_CONSTANT_CURRENT: + case MAX77705_CHARGER_CONSTANT_VOLTAGE: + *val = POWER_SUPPLY_STATUS_CHARGING; + return 0; + case MAX77705_CHARGER_END_OF_CHARGE: + case MAX77705_CHARGER_DONE: + *val = POWER_SUPPLY_STATUS_FULL; + return 0; + /* those values hard coded as in vendor kernel, because of */ + /* failure to determine it's actual meaning. */ + case 0x05: + case 0x06: + case 0x07: + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; + case 0x08: + case 0xA: + case 0xB: + *val = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + default: + *val = POWER_SUPPLY_STATUS_UNKNOWN; + return 0; + } + + return 0; +} + +static int max77705_get_vbus_state(struct regmap *regmap, int *value) +{ + int ret; + unsigned int charge_dtls; + + ret = regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &charge_dtls); + if (ret) + return ret; + + charge_dtls = ((charge_dtls & MAX77705_CHGIN_DTLS) >> + MAX77705_CHGIN_DTLS_SHIFT); + + switch (charge_dtls) { + case 0x00: + *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + break; + case 0x01: + *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + break; + case 0x02: + *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + case 0x03: + *value = POWER_SUPPLY_HEALTH_GOOD; + break; + default: + return 0; + } + return 0; +} + +static int max77705_get_battery_health(struct max77705_charger_data *charger, + int *value) +{ + struct regmap *regmap = charger->regmap; + unsigned int bat_dtls; + + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &bat_dtls); + bat_dtls = ((bat_dtls & MAX77705_BAT_DTLS) >> MAX77705_BAT_DTLS_SHIFT); + + switch (bat_dtls) { + case MAX77705_BATTERY_NOBAT: + dev_dbg(charger->dev, "%s: No battery and the charger is suspended\n", + __func__); + *value = POWER_SUPPLY_HEALTH_NO_BATTERY; + break; + case MAX77705_BATTERY_PREQUALIFICATION: + dev_dbg(charger->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n", + __func__); + break; + case MAX77705_BATTERY_DEAD: + dev_dbg(charger->dev, "%s: battery dead\n", __func__); + *value = POWER_SUPPLY_HEALTH_DEAD; + break; + case MAX77705_BATTERY_GOOD: + case MAX77705_BATTERY_LOWVOLTAGE: + *value = POWER_SUPPLY_HEALTH_GOOD; + break; + case MAX77705_BATTERY_OVERVOLTAGE: + dev_dbg(charger->dev, "%s: battery ovp\n", __func__); + *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + default: + dev_dbg(charger->dev, "%s: battery unknown\n", __func__); + *value = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + } + + return 0; +} + +static int max77705_get_health(struct max77705_charger_data *charger, int *val) +{ + struct regmap *regmap = charger->regmap; + int ret, is_online = 0; + + ret = max77705_get_online(regmap, &is_online); + if (ret) + return ret; + if (is_online) { + ret = max77705_get_vbus_state(regmap, val); + if (ret || (*val != POWER_SUPPLY_HEALTH_GOOD)) + return ret; + } + return max77705_get_battery_health(charger, val); +} + +static int max77705_get_input_current(struct max77705_charger_data *charger, + int *val) +{ + unsigned int reg_data; + int get_current = 0; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data); + + reg_data &= MAX77705_CHG_CHGIN_LIM_MASK; + + if (reg_data <= 3) + get_current = MAX77705_CURRENT_CHGIN_MIN; + else if (reg_data >= MAX77705_CHG_CHGIN_LIM_MASK) + get_current = MAX77705_CURRENT_CHGIN_MAX; + else + get_current = (reg_data + 1) * MAX77705_CURRENT_CHGIN_STEP; + + *val = get_current; + + return 0; +} + +static int max77705_get_charge_current(struct max77705_charger_data *charger, + int *val) +{ + unsigned int reg_data; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_02, ®_data); + reg_data &= MAX77705_CHG_CC; + + *val = reg_data <= 0x2 ? MAX77705_CURRENT_CHGIN_MIN : reg_data * MAX77705_CURRENT_CHG_STEP; + + return 0; +} + +static int max77705_set_float_voltage(struct max77705_charger_data *charger, + int float_voltage) +{ + int float_voltage_mv; + unsigned int reg_data = 0; + struct regmap *regmap = charger->regmap; + + float_voltage_mv = float_voltage / 1000; + reg_data = float_voltage_mv <= 4000 ? 0x0 : + float_voltage_mv >= 4500 ? 0x23 : + (float_voltage_mv <= 4200) ? (float_voltage_mv - 4000) / 50 : + (((float_voltage_mv - 4200) / 10) + 0x04); + + return regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_04, + MAX77705_CHG_CV_PRM_MASK, + (reg_data << MAX77705_CHG_CV_PRM_SHIFT)); +} + +static int max77705_get_float_voltage(struct max77705_charger_data *charger, + int *val) +{ + unsigned int reg_data = 0; + int voltage_mv; + struct regmap *regmap = charger->regmap; + + regmap_read(regmap, MAX77705_CHG_REG_CNFG_04, ®_data); + reg_data &= MAX77705_CHG_PRM_MASK; + voltage_mv = reg_data <= 0x04 ? reg_data * 50 + 4000 : + (reg_data - 4) * 10 + 4200; + *val = voltage_mv * 1000; + + return 0; +} + +static int max77705_chg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max77705_charger_data *charger = power_supply_get_drvdata(psy); + struct regmap *regmap = charger->regmap; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + return max77705_get_online(regmap, &val->intval); + case POWER_SUPPLY_PROP_PRESENT: + return max77705_check_battery(charger, &val->intval); + case POWER_SUPPLY_PROP_STATUS: + return max77705_get_status(charger, &val->intval); + case POWER_SUPPLY_PROP_CHARGE_TYPE: + return max77705_get_charge_type(charger, &val->intval); + case POWER_SUPPLY_PROP_HEALTH: + return max77705_get_health(charger, &val->intval); + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return max77705_get_input_current(charger, &val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return max77705_get_charge_current(charger, &val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return max77705_get_float_voltage(charger, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = charger->bat_info->voltage_max_design_uv; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = max77705_charger_model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = max77705_charger_manufacturer; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct power_supply_desc max77705_charger_psy_desc = { + .name = "max77705-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = max77705_charger_props, + .num_properties = ARRAY_SIZE(max77705_charger_props), + .get_property = max77705_chg_get_property, +}; + +static void max77705_chgin_isr_work(struct work_struct *work) +{ + struct max77705_charger_data *charger = + container_of(work, struct max77705_charger_data, chgin_work); + + power_supply_changed(charger->psy_chg); +} + +static void max77705_charger_initialize(struct max77705_charger_data *chg) +{ + u8 reg_data; + struct power_supply_battery_info *info; + struct regmap *regmap = chg->regmap; + + if (power_supply_get_battery_info(chg->psy_chg, &info) < 0) + return; + + chg->bat_info = info; + + /* unlock charger setting protect */ + /* slowest LX slope */ + reg_data = MAX77705_CHGPROT_MASK | MAX77705_SLOWEST_LX_SLOPE; + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_06, reg_data, + reg_data); + + /* fast charge timer disable */ + /* restart threshold disable */ + /* pre-qual charge disable */ + reg_data = (MAX77705_FCHGTIME_DISABLE << MAX77705_FCHGTIME_SHIFT) | + (MAX77705_CHG_RSTRT_DISABLE << MAX77705_CHG_RSTRT_SHIFT) | + (MAX77705_CHG_PQEN_DISABLE << MAX77705_PQEN_SHIFT); + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_01, + (MAX77705_FCHGTIME_MASK | + MAX77705_CHG_RSTRT_MASK | + MAX77705_PQEN_MASK), + reg_data); + + /* OTG off(UNO on), boost off */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00, + MAX77705_OTG_CTRL, 0); + + /* charge current 450mA(default) */ + /* otg current limit 900mA */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_02, + MAX77705_OTG_ILIM_MASK, + MAX77705_OTG_ILIM_900 << MAX77705_OTG_ILIM_SHIFT); + + /* BAT to SYS OCP 4.80A */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_05, + MAX77705_REG_B2SOVRC_MASK, + MAX77705_B2SOVRC_4_8A << MAX77705_REG_B2SOVRC_SHIFT); + /* top off current 150mA */ + /* top off timer 30min */ + reg_data = (MAX77705_TO_ITH_150MA << MAX77705_TO_ITH_SHIFT) | + (MAX77705_TO_TIME_30M << MAX77705_TO_TIME_SHIFT) | + (MAX77705_SYS_TRACK_DISABLE << MAX77705_SYS_TRACK_DIS_SHIFT); + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_03, + (MAX77705_TO_ITH_MASK | + MAX77705_TO_TIME_MASK | + MAX77705_SYS_TRACK_DIS_MASK), reg_data); + + /* cv voltage 4.2V or 4.35V */ + /* MINVSYS 3.6V(default) */ + if (info->voltage_max_design_uv < 0) { + dev_warn(chg->dev, "missing battery:voltage-max-design-microvolt\n"); + max77705_set_float_voltage(chg, 4200000); + } else { + max77705_set_float_voltage(chg, info->voltage_max_design_uv); + } + + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, + MAX77705_VCHGIN_REG_MASK, MAX77705_VCHGIN_4_5); + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, + MAX77705_WCIN_REG_MASK, MAX77705_WCIN_4_5); + + /* Watchdog timer */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00, + MAX77705_WDTEN_MASK, 0); + + /* Active Discharge Enable */ + regmap_update_bits(regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1); + + /* VBYPSET=5.0V */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_11, MAX77705_VBYPSET_MASK, 0); + + /* Switching Frequency : 1.5MHz */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_08, MAX77705_REG_FSW_MASK, + (MAX77705_CHG_FSW_1_5MHz << MAX77705_REG_FSW_SHIFT)); + + /* Auto skip mode */ + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, MAX77705_REG_DISKIP_MASK, + (MAX77705_AUTO_SKIP << MAX77705_REG_DISKIP_SHIFT)); +} + +static int max77705_charger_probe(struct i2c_client *i2c) +{ + struct power_supply_config pscfg = {}; + struct max77705_charger_data *chg; + struct device *dev; + struct regmap_irq_chip_data *irq_data; + int ret; + + dev = &i2c->dev; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + chg->dev = dev; + i2c_set_clientdata(i2c, chg); + + chg->regmap = devm_regmap_init_i2c(i2c, &max77705_chg_regmap_config); + if (IS_ERR(chg->regmap)) + return PTR_ERR(chg->regmap); + + ret = regmap_update_bits(chg->regmap, + MAX77705_CHG_REG_INT_MASK, + MAX77705_CHGIN_IM, 0); + if (ret) + return ret; + + pscfg.fwnode = dev_fwnode(dev); + pscfg.drv_data = chg; + + chg->psy_chg = devm_power_supply_register(dev, &max77705_charger_psy_desc, &pscfg); + if (IS_ERR(chg->psy_chg)) + return PTR_ERR(chg->psy_chg); + + max77705_charger_irq_chip.irq_drv_data = chg; + ret = devm_regmap_add_irq_chip(chg->dev, chg->regmap, i2c->irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77705_charger_irq_chip, + &irq_data); + if (ret) + return dev_err_probe(dev, ret, "failed to add irq chip\n"); + + chg->wqueue = create_singlethread_workqueue(dev_name(dev)); + if (IS_ERR(chg->wqueue)) + return dev_err_probe(dev, PTR_ERR(chg->wqueue), "failed to create workqueue\n"); + + ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); + + max77705_charger_initialize(chg); + + ret = max77705_charger_enable(chg); + if (ret) + return dev_err_probe(dev, ret, "failed to enable charge\n"); + + return devm_add_action_or_reset(dev, max77705_charger_disable, chg); +} + +static const struct of_device_id max77705_charger_of_match[] = { + { .compatible = "maxim,max77705-charger" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77705_charger_of_match); + +static struct i2c_driver max77705_charger_driver = { + .driver = { + .name = "max77705-charger", + .of_match_table = max77705_charger_of_match, + }, + .probe = max77705_charger_probe, +}; +module_i2c_driver(max77705_charger_driver); + +MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); +MODULE_DESCRIPTION("Maxim MAX77705 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 807d07067951..05e32d764028 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1330,10 +1330,10 @@ config REGULATOR_S2MPA01 via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. config REGULATOR_S2MPS11 - tristate "Samsung S2MPS11/13/14/15/S2MPU02 voltage regulator" + tristate "Samsung S2MPS11/13/14/15/S2MPU02/05 voltage regulator" depends on MFD_SEC_CORE || COMPILE_TEST help - This driver supports a Samsung S2MPS11/13/14/15/S2MPU02 voltage + This driver supports a Samsung S2MPS11/13/14/15/S2MPU02/05 voltage output regulator via I2C bus. The chip is comprised of high efficient Buck converters including Dual-Phase Buck converter, Buck-Boost converter, various LDOs. diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 7dcf92af8f15..04ae9c6150bd 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -21,6 +21,7 @@ #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mps15.h> #include <linux/mfd/samsung/s2mpu02.h> +#include <linux/mfd/samsung/s2mpu05.h> /* The highest number of possible regulators for supported devices. */ #define S2MPS_REGULATOR_MAX S2MPS13_REGULATOR_MAX @@ -254,6 +255,9 @@ static int s2mps11_regulator_enable(struct regulator_dev *rdev) else val = rdev->desc->enable_mask; break; + case S2MPU05: + val = rdev->desc->enable_mask; + break; default: return -EINVAL; } @@ -1118,6 +1122,86 @@ static const struct regulator_desc s2mpu02_regulators[] = { regulator_desc_s2mpu02_buck7(7), }; +#define regulator_desc_s2mpu05_ldo_reg(num, min, step, reg) { \ + .name = "ldo"#num, \ + .id = S2MPU05_LDO##num, \ + .ops = &s2mpu02_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = S2MPU05_LDO_N_VOLTAGES, \ + .vsel_reg = reg, \ + .vsel_mask = S2MPU05_LDO_VSEL_MASK, \ + .enable_reg = reg, \ + .enable_mask = S2MPU05_ENABLE_MASK, \ + .enable_time = S2MPU05_ENABLE_TIME_LDO \ +} + +#define regulator_desc_s2mpu05_ldo(num, reg, min, step) \ + regulator_desc_s2mpu05_ldo_reg(num, min, step, S2MPU05_REG_L##num##reg) + +#define regulator_desc_s2mpu05_ldo1(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN1, S2MPU05_LDO_STEP1) + +#define regulator_desc_s2mpu05_ldo2(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN1, S2MPU05_LDO_STEP2) + +#define regulator_desc_s2mpu05_ldo3(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN2, S2MPU05_LDO_STEP2) + +#define regulator_desc_s2mpu05_ldo4(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN3, S2MPU05_LDO_STEP2) + +#define regulator_desc_s2mpu05_buck(num, which) { \ + .name = "buck"#num, \ + .id = S2MPU05_BUCK##num, \ + .ops = &s2mpu02_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPU05_BUCK_MIN##which, \ + .uV_step = S2MPU05_BUCK_STEP##which, \ + .n_voltages = S2MPU05_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPU05_REG_B##num##CTRL2, \ + .vsel_mask = S2MPU05_BUCK_VSEL_MASK, \ + .enable_reg = S2MPU05_REG_B##num##CTRL1, \ + .enable_mask = S2MPU05_ENABLE_MASK, \ + .enable_time = S2MPU05_ENABLE_TIME_BUCK##num \ +} + +#define regulator_desc_s2mpu05_buck123(num) regulator_desc_s2mpu05_buck(num, 1) +#define regulator_desc_s2mpu05_buck45(num) regulator_desc_s2mpu05_buck(num, 2) + +static const struct regulator_desc s2mpu05_regulators[] = { + regulator_desc_s2mpu05_ldo4(1, CTRL), + regulator_desc_s2mpu05_ldo3(2, CTRL), + regulator_desc_s2mpu05_ldo2(3, CTRL), + regulator_desc_s2mpu05_ldo1(4, CTRL), + regulator_desc_s2mpu05_ldo1(5, CTRL), + regulator_desc_s2mpu05_ldo1(6, CTRL), + regulator_desc_s2mpu05_ldo2(7, CTRL), + regulator_desc_s2mpu05_ldo3(8, CTRL), + regulator_desc_s2mpu05_ldo4(9, CTRL1), + regulator_desc_s2mpu05_ldo4(10, CTRL), + /* LDOs 11-24 are used for CP. They aren't documented. */ + regulator_desc_s2mpu05_ldo2(25, CTRL), + regulator_desc_s2mpu05_ldo3(26, CTRL), + regulator_desc_s2mpu05_ldo2(27, CTRL), + regulator_desc_s2mpu05_ldo3(28, CTRL), + regulator_desc_s2mpu05_ldo3(29, CTRL), + regulator_desc_s2mpu05_ldo2(30, CTRL), + regulator_desc_s2mpu05_ldo3(31, CTRL), + regulator_desc_s2mpu05_ldo3(32, CTRL), + regulator_desc_s2mpu05_ldo3(33, CTRL), + regulator_desc_s2mpu05_ldo3(34, CTRL), + regulator_desc_s2mpu05_ldo3(35, CTRL), + regulator_desc_s2mpu05_buck123(1), + regulator_desc_s2mpu05_buck123(2), + regulator_desc_s2mpu05_buck123(3), + regulator_desc_s2mpu05_buck45(4), + regulator_desc_s2mpu05_buck45(5), +}; + static int s2mps11_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); @@ -1159,6 +1243,11 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) regulators = s2mpu02_regulators; BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu02_regulators)); break; + case S2MPU05: + rdev_num = ARRAY_SIZE(s2mpu05_regulators); + regulators = s2mpu05_regulators; + BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu05_regulators)); + break; default: dev_err(&pdev->dev, "Invalid device type: %u\n", s2mps11->dev_type); @@ -1228,6 +1317,7 @@ static const struct platform_device_id s2mps11_pmic_id[] = { { "s2mps14-regulator", S2MPS14X}, { "s2mps15-regulator", S2MPS15X}, { "s2mpu02-regulator", S2MPU02}, + { "s2mpu05-regulator", S2MPU05}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id); @@ -1245,5 +1335,5 @@ module_platform_driver(s2mps11_pmic_driver); /* Module information */ MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); -MODULE_DESCRIPTION("Samsung S2MPS11/S2MPS14/S2MPS15/S2MPU02 Regulator Driver"); +MODULE_DESCRIPTION("Samsung S2MPS11/14/15/S2MPU02/05 Regulator Driver"); MODULE_LICENSE("GPL"); |