From ce7e8a65aa1b7e8a6833403b314fa8f2cf133119 Mon Sep 17 00:00:00 2001 From: Tom Vierjahn Date: Mon, 24 Mar 2025 23:09:30 +0100 Subject: [PATCH 01/16] Documentation: ext4: Add fields to ext4_super_block documentation Documentation and implementation of the ext4 super block have slightly diverged: Padding has been removed in order to make room for new fields that are still missing in the documentation. Add the new fields s_encryption_level, s_first_error_errorcode, s_last_error_errorcode to the documentation of the ext4 super block. Fixes: f542fbe8d5e8 ("ext4 crypto: reserve codepoints used by the ext4 encryption feature") Fixes: 878520ac45f9 ("ext4: save the error code which triggered an ext4_error() in the superblock") Signed-off-by: Tom Vierjahn Reviewed-by: Ojaswin Mujoo Link: https://patch.msgid.link/20250324221004.5268-1-tom.vierjahn@acm.org Signed-off-by: Theodore Ts'o --- Documentation/filesystems/ext4/super.rst | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Documentation/filesystems/ext4/super.rst b/Documentation/filesystems/ext4/super.rst index a1eb4a11a1d0..1b240661bfa3 100644 --- a/Documentation/filesystems/ext4/super.rst +++ b/Documentation/filesystems/ext4/super.rst @@ -328,9 +328,13 @@ The ext4 superblock is laid out as follows in - s_checksum_type - Metadata checksum algorithm type. The only valid value is 1 (crc32c). * - 0x176 - - __le16 - - s_reserved_pad - - + - \_\_u8 + - s\_encryption\_level + - Versioning level for encryption. + * - 0x177 + - \_\_u8 + - s\_reserved\_pad + - Padding to next 32bits. * - 0x178 - __le64 - s_kbytes_written @@ -466,9 +470,13 @@ The ext4 superblock is laid out as follows in - s_last_error_time_hi - Upper 8 bits of the s_last_error_time field. * - 0x27A - - __u8 - - s_pad[2] - - Zero padding. + - \_\_u8 + - s\_first\_error\_errcode + - + * - 0x27B + - \_\_u8 + - s\_last\_error\_errcode + - * - 0x27C - __le16 - s_encoding -- 2.51.0 From 7e50bbb134aba1df0854f171b596b3a42d35605a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Mar 2025 16:55:51 -0600 Subject: [PATCH 02/16] ext4: avoid -Wflex-array-member-not-at-end warning -Wflex-array-member-not-at-end was introduced in GCC-14, and we are getting ready to enable it, globally. Use the `DEFINE_RAW_FLEX()` helper for an on-stack definition of a flexible structure where the size of the flexible-array member is known at compile-time, and refactor the rest of the code, accordingly. So, with these changes, fix the following warning: fs/ext4/mballoc.c:3041:40: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Link: https://patch.msgid.link/Z-SF97N3AxcIMlSi@kspp Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 0d523e9fb3d5..f88424c28194 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3037,10 +3037,8 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) unsigned char blocksize_bits = min_t(unsigned char, sb->s_blocksize_bits, EXT4_MAX_BLOCK_LOG_SIZE); - struct sg { - struct ext4_group_info info; - ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2]; - } sg; + DEFINE_RAW_FLEX(struct ext4_group_info, sg, bb_counters, + EXT4_MAX_BLOCK_LOG_SIZE + 2); group--; if (group == 0) @@ -3048,7 +3046,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) " 2^0 2^1 2^2 2^3 2^4 2^5 2^6 " " 2^7 2^8 2^9 2^10 2^11 2^12 2^13 ]\n"); - i = (blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) + + i = (blocksize_bits + 2) * sizeof(sg->bb_counters[0]) + sizeof(struct ext4_group_info); grinfo = ext4_get_group_info(sb, group); @@ -3068,14 +3066,14 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) * We care only about free space counters in the group info and * these are safe to access even after the buddy has been unloaded */ - memcpy(&sg, grinfo, i); - seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, - sg.info.bb_fragments, sg.info.bb_first_free); + memcpy(sg, grinfo, i); + seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg->bb_free, + sg->bb_fragments, sg->bb_first_free); for (i = 0; i <= 13; i++) seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ? - sg.info.bb_counters[i] : 0); + sg->bb_counters[i] : 0); seq_puts(seq, " ]"); - if (EXT4_MB_GRP_BBITMAP_CORRUPT(&sg.info)) + if (EXT4_MB_GRP_BBITMAP_CORRUPT(sg)) seq_puts(seq, " Block bitmap corrupted!"); seq_putc(seq, '\n'); return 0; -- 2.51.0 From ccad447a3d331a239477c281533bacb585b54a98 Mon Sep 17 00:00:00 2001 From: Ojaswin Mujoo Date: Fri, 28 Mar 2025 11:54:52 +0530 Subject: [PATCH 03/16] ext4: make block validity check resistent to sb bh corruption Block validity checks need to be skipped in case they are called for journal blocks since they are part of system's protected zone. Currently, this is done by checking inode->ino against sbi->s_es->s_journal_inum, which is a direct read from the ext4 sb buffer head. If someone modifies this underneath us then the s_journal_inum field might get corrupted. To prevent against this, change the check to directly compare the inode with journal->j_inode. **Slight change in behavior**: During journal init path, check_block_validity etc might be called for journal inode when sbi->s_journal is not set yet. In this case we now proceed with ext4_inode_block_valid() instead of returning early. Since systems zones have not been set yet, it is okay to proceed so we can perform basic checks on the blocks. Suggested-by: Baokun Li Reviewed-by: Baokun Li Reviewed-by: Jan Kara Reviewed-by: Zhang Yi Signed-off-by: Ojaswin Mujoo Link: https://patch.msgid.link/0c06bc9ebfcd6ccfed84a36e79147bf45ff5adc1.1743142920.git.ojaswin@linux.ibm.com Signed-off-by: Theodore Ts'o --- fs/ext4/block_validity.c | 5 ++--- fs/ext4/inode.c | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index 87ee3a17bd29..e8c5525afc67 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -351,10 +351,9 @@ int ext4_check_blockref(const char *function, unsigned int line, { __le32 *bref = p; unsigned int blk; + journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; - if (ext4_has_feature_journal(inode->i_sb) && - (inode->i_ino == - le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) + if (journal && inode == journal->j_inode) return 0; while (bref < p+max) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 5c57a34cd82c..f386de8c12f6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -384,10 +384,11 @@ static int __check_block_validity(struct inode *inode, const char *func, unsigned int line, struct ext4_map_blocks *map) { - if (ext4_has_feature_journal(inode->i_sb) && - (inode->i_ino == - le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) + journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; + + if (journal && inode == journal->j_inode) return 0; + if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { ext4_error_inode(inode, func, line, map->m_pblk, "lblock %lu mapped to illegal pblock %llu " -- 2.51.0 From 94824ac9a8aaf2fb3c54b4bdde842db80ffa555d Mon Sep 17 00:00:00 2001 From: Artem Sadovnikov Date: Fri, 4 Apr 2025 08:28:05 +0000 Subject: [PATCH 04/16] ext4: fix off-by-one error in do_split Syzkaller detected a use-after-free issue in ext4_insert_dentry that was caused by out-of-bounds access due to incorrect splitting in do_split. BUG: KASAN: use-after-free in ext4_insert_dentry+0x36a/0x6d0 fs/ext4/namei.c:2109 Write of size 251 at addr ffff888074572f14 by task syz-executor335/5847 CPU: 0 UID: 0 PID: 5847 Comm: syz-executor335 Not tainted 6.12.0-rc6-syzkaller-00318-ga9cda7c0ffed #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/30/2024 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x241/0x360 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:377 [inline] print_report+0x169/0x550 mm/kasan/report.c:488 kasan_report+0x143/0x180 mm/kasan/report.c:601 kasan_check_range+0x282/0x290 mm/kasan/generic.c:189 __asan_memcpy+0x40/0x70 mm/kasan/shadow.c:106 ext4_insert_dentry+0x36a/0x6d0 fs/ext4/namei.c:2109 add_dirent_to_buf+0x3d9/0x750 fs/ext4/namei.c:2154 make_indexed_dir+0xf98/0x1600 fs/ext4/namei.c:2351 ext4_add_entry+0x222a/0x25d0 fs/ext4/namei.c:2455 ext4_add_nondir+0x8d/0x290 fs/ext4/namei.c:2796 ext4_symlink+0x920/0xb50 fs/ext4/namei.c:3431 vfs_symlink+0x137/0x2e0 fs/namei.c:4615 do_symlinkat+0x222/0x3a0 fs/namei.c:4641 __do_sys_symlink fs/namei.c:4662 [inline] __se_sys_symlink fs/namei.c:4660 [inline] __x64_sys_symlink+0x7a/0x90 fs/namei.c:4660 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f The following loop is located right above 'if' statement. for (i = count-1; i >= 0; i--) { /* is more than half of this entry in 2nd half of the block? */ if (size + map[i].size/2 > blocksize/2) break; size += map[i].size; move++; } 'i' in this case could go down to -1, in which case sum of active entries wouldn't exceed half the block size, but previous behaviour would also do split in half if sum would exceed at the very last block, which in case of having too many long name files in a single block could lead to out-of-bounds access and following use-after-free. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Cc: stable@vger.kernel.org Fixes: 5872331b3d91 ("ext4: fix potential negative array index in do_split()") Signed-off-by: Artem Sadovnikov Reviewed-by: Jan Kara Link: https://patch.msgid.link/20250404082804.2567-3-a.sadovnikov@ispras.ru Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 43f96ef4aa01..dda1791e9e1a 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1971,7 +1971,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, * split it in half by count; each resulting block will have at least * half the space free. */ - if (i > 0) + if (i >= 0) split = count - move; else split = count/2; -- 2.51.0 From 8ffd015db85fea3e15a77027fda6c02ced4d2444 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 13 Apr 2025 11:54:49 -0700 Subject: [PATCH 05/16] Linux 6.15-rc2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f42418556507..c91afd55099e 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 15 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Baby Opossum Posse # *DOCUMENTATION* -- 2.51.0 From 7762fdab23100514e5cb612331c96bd65126ada5 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Mon, 14 Apr 2025 10:19:50 +0800 Subject: [PATCH 06/16] regulator: adp5055: Remove unneeded semicolon Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Link: https://patch.msgid.link/20250414021950.3755819-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/regulator/adp5055-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/adp5055-regulator.c b/drivers/regulator/adp5055-regulator.c index 59648499196f..4b004a6b2f84 100644 --- a/drivers/regulator/adp5055-regulator.c +++ b/drivers/regulator/adp5055-regulator.c @@ -165,7 +165,7 @@ static int adp5055_parse_fw(struct device *dev, struct adp5055 *adp5055) ret = regmap_write(regmap, ADP5055_DVS_LIM0 + i, val); if (ret) return ret; - }; + } val = FIELD_PREP(ADP5055_MASK_EN_MODE, adp5055->en_mode_software); ret = regmap_write(regmap, ADP5055_CTRL_MODE1, val); -- 2.51.0 From 71406b6d1155d883c80c1b4405939a52f723aa05 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=C3=A3o=20Paulo=20Gon=C3=A7alves?= Date: Sun, 20 Apr 2025 15:28:01 -0300 Subject: [PATCH 07/16] regulator: max20086: Fix MAX200086 chip id MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit >From MAX20086-MAX20089 datasheet, the id for a MAX20086 is 0x30 and not 0x40. With the current code, the driver will fail on probe when the driver tries to identify the chip id from a MAX20086 device over I2C. Cc: stable@vger.kernel.org Fixes: bfff546aae50 ("regulator: Add MAX20086-MAX20089 driver") Signed-off-by: João Paulo Gonçalves Link: https://patch.msgid.link/20250420-fix-max20086-v1-1-8cc9ee0d5a08@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/max20086-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/max20086-regulator.c b/drivers/regulator/max20086-regulator.c index 59eb23d467ec..f8081e54815d 100644 --- a/drivers/regulator/max20086-regulator.c +++ b/drivers/regulator/max20086-regulator.c @@ -28,7 +28,7 @@ #define MAX20086_REG_ADC4 0x09 /* DEVICE IDs */ -#define MAX20086_DEVICE_ID_MAX20086 0x40 +#define MAX20086_DEVICE_ID_MAX20086 0x30 #define MAX20086_DEVICE_ID_MAX20087 0x20 #define MAX20086_DEVICE_ID_MAX20088 0x10 #define MAX20086_DEVICE_ID_MAX20089 0x00 -- 2.51.0 From e8ac7336dd62f0443a675ed80b17f0f0e6846e20 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=C3=A3o=20Paulo=20Gon=C3=A7alves?= Date: Sun, 20 Apr 2025 15:28:02 -0300 Subject: [PATCH 08/16] regulator: max20086: Change enable gpio to optional MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The enable pin can be configured as always enabled by the hardware. Make the enable gpio request optional so the driver doesn't fail to probe when `enable-gpios` property is not present in the device tree. Cc: stable@vger.kernel.org Fixes: bfff546aae50 ("regulator: Add MAX20086-MAX20089 driver") Signed-off-by: João Paulo Gonçalves Link: https://patch.msgid.link/20250420-fix-max20086-v1-2-8cc9ee0d5a08@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/max20086-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/max20086-regulator.c b/drivers/regulator/max20086-regulator.c index f8081e54815d..62e9119f446c 100644 --- a/drivers/regulator/max20086-regulator.c +++ b/drivers/regulator/max20086-regulator.c @@ -259,7 +259,7 @@ static int max20086_i2c_probe(struct i2c_client *i2c) * shutdown. */ flags = boot_on ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; - chip->ena_gpiod = devm_gpiod_get(chip->dev, "enable", flags); + chip->ena_gpiod = devm_gpiod_get_optional(chip->dev, "enable", flags); if (IS_ERR(chip->ena_gpiod)) { ret = PTR_ERR(chip->ena_gpiod); dev_err(chip->dev, "Failed to get enable GPIO: %d\n", ret); -- 2.51.0 From 5410aa3aa7f7dfcbdfcf94034595765d7e69ead3 Mon Sep 17 00:00:00 2001 From: Bo Liu Date: Wed, 23 Apr 2025 21:08:55 -0400 Subject: [PATCH 09/16] regulator: pf9453: convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: Bo Liu Link: https://patch.msgid.link/20250424010855.2662-1-liubo03@inspur.com Signed-off-by: Mark Brown --- drivers/regulator/pf9453-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/pf9453-regulator.c b/drivers/regulator/pf9453-regulator.c index b58c9c0a5022..be627f49b617 100644 --- a/drivers/regulator/pf9453-regulator.c +++ b/drivers/regulator/pf9453-regulator.c @@ -214,7 +214,7 @@ static const struct regmap_config pf9453_regmap_config = { .val_bits = 8, .volatile_table = &pf9453_volatile_regs, .max_register = PF9453_MAX_REG - 1, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; /* -- 2.51.0 From 8c04144e156b49980a786e80c855e41f6c71685c Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Fri, 25 Apr 2025 15:57:33 -0500 Subject: [PATCH 10/16] regulator: tps65219: Update struct names Isolate changes that involve renaming to indicate this resource is only for TPS65219 or if it will be common for both devices. The renames are in preparation for adding TPS65215 support. Signed-off-by: Shree Ramamoorthy Link: https://patch.msgid.link/20250425205736.76433-2-s-ramamoorthy@ti.com Signed-off-by: Mark Brown --- drivers/regulator/tps65219-regulator.c | 37 +++++++++++++++----------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index aa65077f9d41..3c7c3a6d4c15 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -125,12 +125,17 @@ static const struct linear_range bucks_ranges[] = { REGULATOR_LINEAR_RANGE(3400000, 0x34, 0x3f, 0), }; -static const struct linear_range ldos_1_2_ranges[] = { +static const struct linear_range ldo_1_range[] = { REGULATOR_LINEAR_RANGE(600000, 0x0, 0x37, 50000), REGULATOR_LINEAR_RANGE(3400000, 0x38, 0x3f, 0), }; -static const struct linear_range ldos_3_4_ranges[] = { +static const struct linear_range tps65219_ldo_2_range[] = { + REGULATOR_LINEAR_RANGE(600000, 0x0, 0x37, 50000), + REGULATOR_LINEAR_RANGE(3400000, 0x38, 0x3f, 0), +}; + +static const struct linear_range tps65219_ldos_3_4_range[] = { REGULATOR_LINEAR_RANGE(1200000, 0x0, 0xC, 0), REGULATOR_LINEAR_RANGE(1250000, 0xD, 0x35, 50000), REGULATOR_LINEAR_RANGE(3300000, 0x36, 0x3F, 0), @@ -174,7 +179,7 @@ static unsigned int tps65219_get_mode(struct regulator_dev *dev) } /* Operations permitted on BUCK1/2/3 */ -static const struct regulator_ops tps65219_bucks_ops = { +static const struct regulator_ops bucks_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -189,7 +194,7 @@ static const struct regulator_ops tps65219_bucks_ops = { }; /* Operations permitted on LDO1/2 */ -static const struct regulator_ops tps65219_ldos_1_2_ops = { +static const struct regulator_ops ldos_1_2_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -204,7 +209,7 @@ static const struct regulator_ops tps65219_ldos_1_2_ops = { }; /* Operations permitted on LDO3/4 */ -static const struct regulator_ops tps65219_ldos_3_4_ops = { +static const struct regulator_ops ldos_3_4_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -218,53 +223,53 @@ static const struct regulator_ops tps65219_ldos_3_4_ops = { static const struct regulator_desc regulators[] = { TPS65219_REGULATOR("BUCK1", "buck1", TPS65219_BUCK_1, - REGULATOR_VOLTAGE, tps65219_bucks_ops, 64, + REGULATOR_VOLTAGE, bucks_ops, 64, TPS65219_REG_BUCK1_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, TPS65219_ENABLE_BUCK1_EN_MASK, 0, 0, bucks_ranges, 3, 4000, 0, NULL, 0, 0), TPS65219_REGULATOR("BUCK2", "buck2", TPS65219_BUCK_2, - REGULATOR_VOLTAGE, tps65219_bucks_ops, 64, + REGULATOR_VOLTAGE, bucks_ops, 64, TPS65219_REG_BUCK2_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, TPS65219_ENABLE_BUCK2_EN_MASK, 0, 0, bucks_ranges, 3, 4000, 0, NULL, 0, 0), TPS65219_REGULATOR("BUCK3", "buck3", TPS65219_BUCK_3, - REGULATOR_VOLTAGE, tps65219_bucks_ops, 64, + REGULATOR_VOLTAGE, bucks_ops, 64, TPS65219_REG_BUCK3_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, TPS65219_ENABLE_BUCK3_EN_MASK, 0, 0, bucks_ranges, 3, 0, 0, NULL, 0, 0), TPS65219_REGULATOR("LDO1", "ldo1", TPS65219_LDO_1, - REGULATOR_VOLTAGE, tps65219_ldos_1_2_ops, 64, + REGULATOR_VOLTAGE, ldos_1_2_ops, 64, TPS65219_REG_LDO1_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, - TPS65219_ENABLE_LDO1_EN_MASK, 0, 0, ldos_1_2_ranges, + TPS65219_ENABLE_LDO1_EN_MASK, 0, 0, ldo_1_range, 2, 0, 0, NULL, 0, TPS65219_LDOS_BYP_CONFIG_MASK), TPS65219_REGULATOR("LDO2", "ldo2", TPS65219_LDO_2, - REGULATOR_VOLTAGE, tps65219_ldos_1_2_ops, 64, + REGULATOR_VOLTAGE, ldos_1_2_ops, 64, TPS65219_REG_LDO2_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, - TPS65219_ENABLE_LDO2_EN_MASK, 0, 0, ldos_1_2_ranges, + TPS65219_ENABLE_LDO2_EN_MASK, 0, 0, tps65219_ldo_2_range, 2, 0, 0, NULL, 0, TPS65219_LDOS_BYP_CONFIG_MASK), TPS65219_REGULATOR("LDO3", "ldo3", TPS65219_LDO_3, - REGULATOR_VOLTAGE, tps65219_ldos_3_4_ops, 64, + REGULATOR_VOLTAGE, ldos_3_4_ops, 64, TPS65219_REG_LDO3_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, - TPS65219_ENABLE_LDO3_EN_MASK, 0, 0, ldos_3_4_ranges, + TPS65219_ENABLE_LDO3_EN_MASK, 0, 0, tps65219_ldos_3_4_range, 3, 0, 0, NULL, 0, 0), TPS65219_REGULATOR("LDO4", "ldo4", TPS65219_LDO_4, - REGULATOR_VOLTAGE, tps65219_ldos_3_4_ops, 64, + REGULATOR_VOLTAGE, ldos_3_4_ops, 64, TPS65219_REG_LDO4_VOUT, TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, - TPS65219_ENABLE_LDO4_EN_MASK, 0, 0, ldos_3_4_ranges, + TPS65219_ENABLE_LDO4_EN_MASK, 0, 0, tps65219_ldos_3_4_range, 3, 0, 0, NULL, 0, 0), }; -- 2.51.0 From 3f2e457efdad5af4164f155bd7ac902258a9b1ce Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Fri, 25 Apr 2025 15:57:34 -0500 Subject: [PATCH 11/16] regulator: tps65219: Add support for TPS65215 regulator resources Isolate all changes involving TPS65215 regulator desc and range resources. - 'chipid' will identify which PMIC to support, and the corresponding chip_data struct element to use in probe(). The chip_data struct is helpful for any new PMICs added to this driver. The goal is to add future PMIC info to necessary structs and minimize probe() function edits. - The probe() will now loop through common (overlapping) regulators first, then device-specific structs identified in the chip_data struct. - Add TI TPS65215 PMIC to the existing platform_device_id struct, so the regulator probe() can handle which PMIC chip_data information to use. Signed-off-by: Shree Ramamoorthy Link: https://patch.msgid.link/20250425205736.76433-3-s-ramamoorthy@ti.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 ++- drivers/regulator/tps65219-regulator.c | 86 +++++++++++++++++++++----- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index ad37f274dd8e..8180e56aa5a0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1590,10 +1590,11 @@ config REGULATOR_TPS65219 tristate "TI TPS65219 Power regulators" depends on MFD_TPS65219 && OF help - This driver supports TPS65219 voltage regulator chips. + This driver supports TPS65219 series and TPS65215 voltage regulator chips. TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs - voltage regulators. It supports software based voltage control - for different voltage domains. + voltage regulators. + TPS65215 PMIC has 3 single phase BUCKs & 2 LDOs. + Both PMICs support software based voltage control for different voltage domains. config REGULATOR_TPS6594 tristate "TI TPS6594 Power regulators" diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index 3c7c3a6d4c15..b95fb676e5d2 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // -// tps65219-regulator.c -// -// Regulator driver for TPS65219 PMIC +// Regulator driver for TPS65215/TPS65219 PMIC // // Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/ +// Copyright (C) 2024 Texas Instruments Incorporated - https://www.ti.com/ // // This implementation derived from tps65218 authored by // "J Keerthy " @@ -130,6 +129,11 @@ static const struct linear_range ldo_1_range[] = { REGULATOR_LINEAR_RANGE(3400000, 0x38, 0x3f, 0), }; +static const struct linear_range tps65215_ldo_2_range[] = { + REGULATOR_LINEAR_RANGE(1200000, 0x0, 0xC, 50000), + REGULATOR_LINEAR_RANGE(3300000, 0x36, 0x3F, 0), +}; + static const struct linear_range tps65219_ldo_2_range[] = { REGULATOR_LINEAR_RANGE(600000, 0x0, 0x37, 50000), REGULATOR_LINEAR_RANGE(3400000, 0x38, 0x3f, 0), @@ -221,7 +225,7 @@ static const struct regulator_ops ldos_3_4_ops = { .map_voltage = regulator_map_voltage_linear_range, }; -static const struct regulator_desc regulators[] = { +static const struct regulator_desc common_regs[] = { TPS65219_REGULATOR("BUCK1", "buck1", TPS65219_BUCK_1, REGULATOR_VOLTAGE, bucks_ops, 64, TPS65219_REG_BUCK1_VOUT, @@ -250,6 +254,20 @@ static const struct regulator_desc regulators[] = { TPS65219_REG_ENABLE_CTRL, TPS65219_ENABLE_LDO1_EN_MASK, 0, 0, ldo_1_range, 2, 0, 0, NULL, 0, TPS65219_LDOS_BYP_CONFIG_MASK), +}; + +static const struct regulator_desc tps65215_regs[] = { + // TPS65215's LDO2 is the same as TPS65219's LDO3 + TPS65219_REGULATOR("LDO2", "ldo2", TPS65215_LDO_2, + REGULATOR_VOLTAGE, ldos_3_4_ops, 64, + TPS65215_REG_LDO2_VOUT, + TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, + TPS65219_REG_ENABLE_CTRL, + TPS65215_ENABLE_LDO2_EN_MASK, 0, 0, tps65215_ldo_2_range, + 3, 0, 0, NULL, 0, 0), +}; + +static const struct regulator_desc tps65219_regs[] = { TPS65219_REGULATOR("LDO2", "ldo2", TPS65219_LDO_2, REGULATOR_VOLTAGE, ldos_1_2_ops, 64, TPS65219_REG_LDO2_VOUT, @@ -292,28 +310,65 @@ static irqreturn_t tps65219_regulator_irq_handler(int irq, void *data) return IRQ_HANDLED; } +struct tps65219_chip_data { + size_t rdesc_size; + size_t common_rdesc_size; + const struct regulator_desc *rdesc; + const struct regulator_desc *common_rdesc; +}; + +static struct tps65219_chip_data chip_info_table[] = { + [TPS65215] = { + .rdesc = tps65215_regs, + .rdesc_size = ARRAY_SIZE(tps65215_regs), + .common_rdesc = common_regs, + .common_rdesc_size = ARRAY_SIZE(common_regs), + }, + [TPS65219] = { + .rdesc = tps65219_regs, + .rdesc_size = ARRAY_SIZE(tps65219_regs), + .common_rdesc = common_regs, + .common_rdesc_size = ARRAY_SIZE(common_regs), + }, +}; + static int tps65219_regulator_probe(struct platform_device *pdev) { - struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); + struct tps65219_regulator_irq_data *irq_data; + struct tps65219_regulator_irq_type *irq_type; + + struct tps65219_chip_data *pmic; struct regulator_dev *rdev; - struct regulator_config config = { }; - int i; int error; int irq; - struct tps65219_regulator_irq_data *irq_data; - struct tps65219_regulator_irq_type *irq_type; + int i; + + struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + enum pmic_id chip = platform_get_device_id(pdev)->driver_data; + + pmic = &chip_info_table[chip]; config.dev = tps->dev; config.driver_data = tps; config.regmap = tps->regmap; - for (i = 0; i < ARRAY_SIZE(regulators); i++) { - rdev = devm_regulator_register(&pdev->dev, ®ulators[i], + for (i = 0; i < pmic->common_rdesc_size; i++) { + rdev = devm_regulator_register(&pdev->dev, &pmic->common_rdesc[i], + &config); + if (IS_ERR(rdev)) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "Failed to register %s regulator\n", + pmic->common_rdesc[i].name); + } + + for (i = 0; i < pmic->rdesc_size; i++) { + rdev = devm_regulator_register(&pdev->dev, &pmic->rdesc[i], &config); if (IS_ERR(rdev)) return dev_err_probe(tps->dev, PTR_ERR(rdev), - "Failed to register %s regulator\n", - regulators[i].name); + "Failed to register %s regulator\n", + pmic->rdesc[i].name); } irq_data = devm_kmalloc(tps->dev, @@ -349,7 +404,8 @@ static int tps65219_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id tps65219_regulator_id_table[] = { - { "tps65219-regulator", }, + { "tps65215-regulator", TPS65215 }, + { "tps65219-regulator", TPS65219 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65219_regulator_id_table); @@ -366,5 +422,5 @@ static struct platform_driver tps65219_regulator_driver = { module_platform_driver(tps65219_regulator_driver); MODULE_AUTHOR("Jerome Neanne "); -MODULE_DESCRIPTION("TPS65219 voltage regulator driver"); +MODULE_DESCRIPTION("TPS65215/TPS65219 voltage regulator driver"); MODULE_LICENSE("GPL"); -- 2.51.0 From 38c9f98db20a649a1f8454f507608b6aef0c9297 Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Fri, 25 Apr 2025 15:57:35 -0500 Subject: [PATCH 12/16] regulator: tps65219: Add support for TPS65215 Regulator IRQs Isolate all changes involving regulator IRQ types: - Adding in TPS65215 resources - Organize what resources are common vs device-specific - How the chip_data uses these resource structs - Restructure the probe() for multi-PMIC support. Signed-off-by: Shree Ramamoorthy Link: https://patch.msgid.link/20250425205736.76433-4-s-ramamoorthy@ti.com Signed-off-by: Mark Brown --- drivers/regulator/tps65219-regulator.c | 68 +++++++++++++++++++------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index b95fb676e5d2..d84ab9586ec4 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -36,6 +36,14 @@ static struct tps65219_regulator_irq_type tps65219_regulator_irq_types[] = { { "LDO4_SCG", "LDO4", "short circuit to ground", REGULATOR_EVENT_REGULATION_OUT }, { "LDO4_OC", "LDO4", "overcurrent", REGULATOR_EVENT_OVER_CURRENT }, { "LDO4_UV", "LDO4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, + { "LDO3_RV", "LDO3", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, + { "LDO4_RV", "LDO4", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, + { "LDO3_RV_SD", "LDO3", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, + { "LDO4_RV_SD", "LDO4", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, +}; + +/* All of TPS65215's irq types are the same as common_regulator_irq_types */ +static struct tps65219_regulator_irq_type common_regulator_irq_types[] = { { "LDO1_SCG", "LDO1", "short circuit to ground", REGULATOR_EVENT_REGULATION_OUT }, { "LDO1_OC", "LDO1", "overcurrent", REGULATOR_EVENT_OVER_CURRENT }, { "LDO1_UV", "LDO1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, @@ -59,8 +67,6 @@ static struct tps65219_regulator_irq_type tps65219_regulator_irq_types[] = { { "BUCK3_RV", "BUCK3", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO1_RV", "LDO1", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO2_RV", "LDO2", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, - { "LDO3_RV", "LDO3", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, - { "LDO4_RV", "LDO4", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "BUCK1_RV_SD", "BUCK1", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "BUCK2_RV_SD", "BUCK2", "residual voltage on shutdown", @@ -69,8 +75,6 @@ static struct tps65219_regulator_irq_type tps65219_regulator_irq_types[] = { REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO1_RV_SD", "LDO1", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO2_RV_SD", "LDO2", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, - { "LDO3_RV_SD", "LDO3", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, - { "LDO4_RV_SD", "LDO4", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "SENSOR_3_WARM", "SENSOR3", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN}, { "SENSOR_2_WARM", "SENSOR2", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN }, { "SENSOR_1_WARM", "SENSOR1", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN }, @@ -264,7 +268,7 @@ static const struct regulator_desc tps65215_regs[] = { TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, TPS65219_REG_ENABLE_CTRL, TPS65215_ENABLE_LDO2_EN_MASK, 0, 0, tps65215_ldo_2_range, - 3, 0, 0, NULL, 0, 0), + 2, 0, 0, NULL, 0, 0), }; static const struct regulator_desc tps65219_regs[] = { @@ -313,8 +317,12 @@ static irqreturn_t tps65219_regulator_irq_handler(int irq, void *data) struct tps65219_chip_data { size_t rdesc_size; size_t common_rdesc_size; + size_t dev_irq_size; + size_t common_irq_size; const struct regulator_desc *rdesc; const struct regulator_desc *common_rdesc; + struct tps65219_regulator_irq_type *irq_types; + struct tps65219_regulator_irq_type *common_irq_types; }; static struct tps65219_chip_data chip_info_table[] = { @@ -323,12 +331,20 @@ static struct tps65219_chip_data chip_info_table[] = { .rdesc_size = ARRAY_SIZE(tps65215_regs), .common_rdesc = common_regs, .common_rdesc_size = ARRAY_SIZE(common_regs), + .irq_types = NULL, + .dev_irq_size = 0, + .common_irq_types = common_regulator_irq_types, + .common_irq_size = ARRAY_SIZE(common_regulator_irq_types), }, [TPS65219] = { .rdesc = tps65219_regs, .rdesc_size = ARRAY_SIZE(tps65219_regs), .common_rdesc = common_regs, .common_rdesc_size = ARRAY_SIZE(common_regs), + .irq_types = tps65219_regulator_irq_types, + .dev_irq_size = ARRAY_SIZE(tps65219_regulator_irq_types), + .common_irq_types = common_regulator_irq_types, + .common_irq_size = ARRAY_SIZE(common_regulator_irq_types), }, }; @@ -336,7 +352,6 @@ static int tps65219_regulator_probe(struct platform_device *pdev) { struct tps65219_regulator_irq_data *irq_data; struct tps65219_regulator_irq_type *irq_type; - struct tps65219_chip_data *pmic; struct regulator_dev *rdev; int error; @@ -371,33 +386,50 @@ static int tps65219_regulator_probe(struct platform_device *pdev) pmic->rdesc[i].name); } - irq_data = devm_kmalloc(tps->dev, - ARRAY_SIZE(tps65219_regulator_irq_types) * - sizeof(struct tps65219_regulator_irq_data), - GFP_KERNEL); + irq_data = devm_kmalloc(tps->dev, pmic->common_irq_size, GFP_KERNEL); if (!irq_data) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(tps65219_regulator_irq_types); ++i) { - irq_type = &tps65219_regulator_irq_types[i]; - + for (i = 0; i < pmic->common_irq_size; ++i) { + irq_type = &pmic->common_irq_types[i]; irq = platform_get_irq_byname(pdev, irq_type->irq_name); if (irq < 0) return -EINVAL; irq_data[i].dev = tps->dev; irq_data[i].type = irq_type; + error = devm_request_threaded_irq(tps->dev, irq, NULL, + tps65219_regulator_irq_handler, + IRQF_ONESHOT, + irq_type->irq_name, + &irq_data[i]); + if (error) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "Failed to request %s IRQ %d: %d\n", + irq_type->irq_name, irq, error); + } + + irq_data = devm_kmalloc(tps->dev, pmic->dev_irq_size, GFP_KERNEL); + if (!irq_data) + return -ENOMEM; + for (i = 0; i < pmic->dev_irq_size; ++i) { + irq_type = &pmic->irq_types[i]; + irq = platform_get_irq_byname(pdev, irq_type->irq_name); + if (irq < 0) + return -EINVAL; + + irq_data[i].dev = tps->dev; + irq_data[i].type = irq_type; error = devm_request_threaded_irq(tps->dev, irq, NULL, tps65219_regulator_irq_handler, IRQF_ONESHOT, irq_type->irq_name, &irq_data[i]); - if (error) { - dev_err(tps->dev, "failed to request %s IRQ %d: %d\n", - irq_type->irq_name, irq, error); - return error; - } + if (error) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "Failed to request %s IRQ %d: %d\n", + irq_type->irq_name, irq, error); } return 0; -- 2.51.0 From f1471bc435afa31c8c0c58551922830dc8f4b06b Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Fri, 25 Apr 2025 15:57:36 -0500 Subject: [PATCH 13/16] regulator: tps65219: Add TI TPS65214 Regulator Support Add support for TPS65214 regulators (bucks and LDOs) to the TPS65219 Regulator Driver as the TPS65214/TPS65214/TPS65219 PMIC devices have significant register map overlap. TPS65214 is a Power Management IC with 3 Buck regulators (like TPS65215/TPS65219) and has 2 LDOs (like TPS65215). Signed-off-by: Shree Ramamoorthy Link: https://patch.msgid.link/20250425205736.76433-5-s-ramamoorthy@ti.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 +++- drivers/regulator/tps65219-regulator.c | 73 ++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8180e56aa5a0..6d8988387da4 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1590,11 +1590,16 @@ config REGULATOR_TPS65219 tristate "TI TPS65219 Power regulators" depends on MFD_TPS65219 && OF help - This driver supports TPS65219 series and TPS65215 voltage regulator chips. + This driver supports TPS65219, TPS65215, and TPS65214 voltage + regulator chips. TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs voltage regulators. TPS65215 PMIC has 3 single phase BUCKs & 2 LDOs. - Both PMICs support software based voltage control for different voltage domains. + TPS65214 PMIC has 3 synchronous stepdown DC-DC converters & 2 + LDOs. One LDO supports a maximum output current of 300 mA and the + other a maximum of 500 mA + All 3 PMICs support software based voltage control for different + voltage domains. config REGULATOR_TPS6594 tristate "TI TPS6594 Power regulators" diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index d84ab9586ec4..b16b300d7f45 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // -// Regulator driver for TPS65215/TPS65219 PMIC +// TPS65214/TPS65215/TPS65219 PMIC Regulator Driver // // Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/ // Copyright (C) 2024 Texas Instruments Incorporated - https://www.ti.com/ @@ -29,6 +29,11 @@ struct tps65219_regulator_irq_type { unsigned long event; }; +static struct tps65219_regulator_irq_type tps65215_regulator_irq_types[] = { + { "SENSOR_3_WARM", "SENSOR3", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN}, + { "SENSOR_3_HOT", "SENSOR3", "hot temperature", REGULATOR_EVENT_OVER_TEMP}, +}; + static struct tps65219_regulator_irq_type tps65219_regulator_irq_types[] = { { "LDO3_SCG", "LDO3", "short circuit to ground", REGULATOR_EVENT_REGULATION_OUT }, { "LDO3_OC", "LDO3", "overcurrent", REGULATOR_EVENT_OVER_CURRENT }, @@ -40,9 +45,11 @@ static struct tps65219_regulator_irq_type tps65219_regulator_irq_types[] = { { "LDO4_RV", "LDO4", "residual voltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO3_RV_SD", "LDO3", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO4_RV_SD", "LDO4", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, + { "SENSOR_3_WARM", "SENSOR3", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN}, + { "SENSOR_3_HOT", "SENSOR3", "hot temperature", REGULATOR_EVENT_OVER_TEMP}, }; -/* All of TPS65215's irq types are the same as common_regulator_irq_types */ +/* All of TPS65214's irq types are the same as common_regulator_irq_types */ static struct tps65219_regulator_irq_type common_regulator_irq_types[] = { { "LDO1_SCG", "LDO1", "short circuit to ground", REGULATOR_EVENT_REGULATION_OUT }, { "LDO1_OC", "LDO1", "overcurrent", REGULATOR_EVENT_OVER_CURRENT }, @@ -75,11 +82,9 @@ static struct tps65219_regulator_irq_type common_regulator_irq_types[] = { REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO1_RV_SD", "LDO1", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { "LDO2_RV_SD", "LDO2", "residual voltage on shutdown", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, - { "SENSOR_3_WARM", "SENSOR3", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN}, { "SENSOR_2_WARM", "SENSOR2", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN }, { "SENSOR_1_WARM", "SENSOR1", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN }, { "SENSOR_0_WARM", "SENSOR0", "warm temperature", REGULATOR_EVENT_OVER_TEMP_WARN }, - { "SENSOR_3_HOT", "SENSOR3", "hot temperature", REGULATOR_EVENT_OVER_TEMP}, { "SENSOR_2_HOT", "SENSOR2", "hot temperature", REGULATOR_EVENT_OVER_TEMP }, { "SENSOR_1_HOT", "SENSOR1", "hot temperature", REGULATOR_EVENT_OVER_TEMP }, { "SENSOR_0_HOT", "SENSOR0", "hot temperature", REGULATOR_EVENT_OVER_TEMP }, @@ -133,6 +138,12 @@ static const struct linear_range ldo_1_range[] = { REGULATOR_LINEAR_RANGE(3400000, 0x38, 0x3f, 0), }; +static const struct linear_range tps65214_ldo_1_2_range[] = { + REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2, 0), + REGULATOR_LINEAR_RANGE(650000, 0x3, 0x37, 50000), + REGULATOR_LINEAR_RANGE(3300000, 0x38, 0x3F, 0), +}; + static const struct linear_range tps65215_ldo_2_range[] = { REGULATOR_LINEAR_RANGE(1200000, 0x0, 0xC, 50000), REGULATOR_LINEAR_RANGE(3300000, 0x36, 0x3F, 0), @@ -251,6 +262,32 @@ static const struct regulator_desc common_regs[] = { TPS65219_REG_ENABLE_CTRL, TPS65219_ENABLE_BUCK3_EN_MASK, 0, 0, bucks_ranges, 3, 0, 0, NULL, 0, 0), +}; + +static const struct regulator_desc tps65214_regs[] = { + // TPS65214's LDO3 pin maps to TPS65219's LDO3 pin + TPS65219_REGULATOR("LDO1", "ldo1", TPS65214_LDO_1, + REGULATOR_VOLTAGE, ldos_3_4_ops, 64, + TPS65214_REG_LDO1_VOUT, + TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, + TPS65219_REG_ENABLE_CTRL, + TPS65219_ENABLE_LDO3_EN_MASK, 0, 0, tps65214_ldo_1_2_range, + 3, 0, 0, NULL, 0, 0), + TPS65219_REGULATOR("LDO2", "ldo2", TPS65214_LDO_2, + REGULATOR_VOLTAGE, ldos_3_4_ops, 64, + TPS65214_REG_LDO2_VOUT, + TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, + TPS65219_REG_ENABLE_CTRL, + TPS65219_ENABLE_LDO2_EN_MASK, 0, 0, tps65214_ldo_1_2_range, + 3, 0, 0, NULL, 0, 0), +}; + +static const struct regulator_desc tps65215_regs[] = { + /* + * TPS65215's LDO1 is the same as TPS65219's LDO1. LDO1 is + * configurable as load switch and bypass-mode. + * TPS65215's LDO2 is the same as TPS65219's LDO3 + */ TPS65219_REGULATOR("LDO1", "ldo1", TPS65219_LDO_1, REGULATOR_VOLTAGE, ldos_1_2_ops, 64, TPS65219_REG_LDO1_VOUT, @@ -258,10 +295,6 @@ static const struct regulator_desc common_regs[] = { TPS65219_REG_ENABLE_CTRL, TPS65219_ENABLE_LDO1_EN_MASK, 0, 0, ldo_1_range, 2, 0, 0, NULL, 0, TPS65219_LDOS_BYP_CONFIG_MASK), -}; - -static const struct regulator_desc tps65215_regs[] = { - // TPS65215's LDO2 is the same as TPS65219's LDO3 TPS65219_REGULATOR("LDO2", "ldo2", TPS65215_LDO_2, REGULATOR_VOLTAGE, ldos_3_4_ops, 64, TPS65215_REG_LDO2_VOUT, @@ -272,6 +305,13 @@ static const struct regulator_desc tps65215_regs[] = { }; static const struct regulator_desc tps65219_regs[] = { + TPS65219_REGULATOR("LDO1", "ldo1", TPS65219_LDO_1, + REGULATOR_VOLTAGE, ldos_1_2_ops, 64, + TPS65219_REG_LDO1_VOUT, + TPS65219_BUCKS_LDOS_VOUT_VSET_MASK, + TPS65219_REG_ENABLE_CTRL, + TPS65219_ENABLE_LDO1_EN_MASK, 0, 0, ldo_1_range, + 2, 0, 0, NULL, 0, TPS65219_LDOS_BYP_CONFIG_MASK), TPS65219_REGULATOR("LDO2", "ldo2", TPS65219_LDO_2, REGULATOR_VOLTAGE, ldos_1_2_ops, 64, TPS65219_REG_LDO2_VOUT, @@ -326,13 +366,23 @@ struct tps65219_chip_data { }; static struct tps65219_chip_data chip_info_table[] = { + [TPS65214] = { + .rdesc = tps65214_regs, + .rdesc_size = ARRAY_SIZE(tps65214_regs), + .common_rdesc = common_regs, + .common_rdesc_size = ARRAY_SIZE(common_regs), + .irq_types = NULL, + .dev_irq_size = 0, + .common_irq_types = common_regulator_irq_types, + .common_irq_size = ARRAY_SIZE(common_regulator_irq_types), + }, [TPS65215] = { .rdesc = tps65215_regs, .rdesc_size = ARRAY_SIZE(tps65215_regs), .common_rdesc = common_regs, .common_rdesc_size = ARRAY_SIZE(common_regs), - .irq_types = NULL, - .dev_irq_size = 0, + .irq_types = tps65215_regulator_irq_types, + .dev_irq_size = ARRAY_SIZE(tps65215_regulator_irq_types), .common_irq_types = common_regulator_irq_types, .common_irq_size = ARRAY_SIZE(common_regulator_irq_types), }, @@ -436,6 +486,7 @@ static int tps65219_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id tps65219_regulator_id_table[] = { + { "tps65214-regulator", TPS65214 }, { "tps65215-regulator", TPS65215 }, { "tps65219-regulator", TPS65219 }, { /* sentinel */ } @@ -454,5 +505,5 @@ static struct platform_driver tps65219_regulator_driver = { module_platform_driver(tps65219_regulator_driver); MODULE_AUTHOR("Jerome Neanne "); -MODULE_DESCRIPTION("TPS65215/TPS65219 voltage regulator driver"); +MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 Regulator driver"); MODULE_LICENSE("GPL"); -- 2.51.0 From d5cc09847fa0df4034076f884de5957ec816dc52 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 May 2025 16:24:49 +0200 Subject: [PATCH 14/16] regulator: da9121: Fix Wvoid-pointer-to-enum-cast warning 'subvariant_id' is an enum, thus cast of pointer on 64-bit compile test with clang and W=1 causes: da9121-regulator.c:1132:24: error: cast to smaller integer type 'enum da9121_subvariant' from 'const void *' [-Werror,-Wvoid-pointer-to-enum-cast] Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250509142448.257199-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 17527a3f53b4..ef161eb0ca27 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -1129,7 +1129,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c) } chip->pdata = i2c->dev.platform_data; - chip->subvariant_id = (enum da9121_subvariant)i2c_get_match_data(i2c); + chip->subvariant_id = (kernel_ulong_t)i2c_get_match_data(i2c); ret = da9121_assign_chip_model(i2c, chip); if (ret < 0) -- 2.51.0 From 6157e62b07d9331cc1d4d9d525dab33d45b0e83c Mon Sep 17 00:00:00 2001 From: Paul Geurts Date: Mon, 5 May 2025 13:59:36 +0200 Subject: [PATCH 15/16] regulator: pca9450: Add restart handler When restarting a CPU powered by the PCA9450 power management IC, it is beneficial to use the PCA9450 to power cycle the CPU and all its connected peripherals to start up in a known state. The PCA9450 features a cold start procedure initiated by an I2C command. Add a restart handler so that the PCA9450 is used to restart the CPU. The restart handler sends command 0x14 to the SW_RST register, initiating a cold reset (Power recycle all regulators except LDO1, LDO2 and CLK_32K_OUT) As the PCA9450 is a PMIC specific for the i.MX8M family CPU, the restart handler priority is set just slightly higher than imx2_wdt and the PSCI restart handler. This makes sure this restart handler takes precedence. Signed-off-by: Paul Geurts Link: https://patch.msgid.link/20250505115936.1946891-1-paul.geurts@prodrive-technologies.com Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 27 +++++++++++++++++++++++++++ include/linux/regulator/pca9450.h | 5 +++++ 2 files changed, 32 insertions(+) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index a56f3ab754fa..14d19a6d6655 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ struct pca9450 { struct device *dev; struct regmap *regmap; struct gpio_desc *sd_vsel_gpio; + struct notifier_block restart_nb; enum pca9450_chip_type type; unsigned int rcnt; int irq; @@ -965,6 +967,25 @@ static irqreturn_t pca9450_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int pca9450_i2c_restart_handler(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct pca9450 *pca9450 = container_of(nb, struct pca9450, restart_nb); + struct i2c_client *i2c = container_of(pca9450->dev, struct i2c_client, dev); + + dev_dbg(&i2c->dev, "Restarting device..\n"); + if (i2c_smbus_write_byte_data(i2c, PCA9450_REG_SWRST, SW_RST_COMMAND) == 0) { + /* tRESTART is 250ms, so 300 should be enough to make sure it happened */ + mdelay(300); + /* When we get here, the PMIC didn't power cycle for some reason. so warn.*/ + dev_warn(&i2c->dev, "Device didn't respond to restart command\n"); + } else { + dev_err(&i2c->dev, "Restart command failed\n"); + } + + return 0; +} + static int pca9450_i2c_probe(struct i2c_client *i2c) { enum pca9450_chip_type type = (unsigned int)(uintptr_t) @@ -1107,6 +1128,12 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) pca9450->sd_vsel_fixed_low = of_property_read_bool(ldo5->dev.of_node, "nxp,sd-vsel-fixed-low"); + pca9450->restart_nb.notifier_call = pca9450_i2c_restart_handler; + pca9450->restart_nb.priority = PCA9450_RESTART_HANDLER_PRIORITY; + + if (register_restart_handler(&pca9450->restart_nb)) + dev_warn(&i2c->dev, "Failed to register restart handler\n"); + dev_info(&i2c->dev, "%s probed.\n", type == PCA9450_TYPE_PCA9450A ? "pca9450a" : (type == PCA9450_TYPE_PCA9451A ? "pca9451a" : "pca9450bc")); diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h index b427b5873de1..85b4fecc10d8 100644 --- a/include/linux/regulator/pca9450.h +++ b/include/linux/regulator/pca9450.h @@ -35,6 +35,8 @@ enum { PCA9450_DVS_LEVEL_MAX, }; +#define PCA9450_RESTART_HANDLER_PRIORITY 130 + #define PCA9450_BUCK1_VOLTAGE_NUM 0x80 #define PCA9450_BUCK2_VOLTAGE_NUM 0x80 #define PCA9450_BUCK3_VOLTAGE_NUM 0x80 @@ -235,4 +237,7 @@ enum { #define I2C_LT_ON_RUN 0x02 #define I2C_LT_FORCE_ENABLE 0x03 +/* PCA9450_REG_SW_RST command */ +#define SW_RST_COMMAND 0x14 + #endif /* __LINUX_REG_PCA9450_H__ */ -- 2.51.0 From cad915e4515790d2e66525a86a166e7ce81e3055 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 12 May 2025 13:57:27 -0500 Subject: [PATCH 16/16] regulator: gpio: Use dev_err_probe During probe the gpio driver may not yet be available. Use dev_err_probe to provide just the pertinent log. Since dev_err_probe takes care of reporting the error value, drop the redundant ret variable while at it. Signed-off-by: Nishanth Menon Link: https://patch.msgid.link/20250512185727.2907411-1-nm@ti.com Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 65927fa2ef16..75bd53445ba7 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -240,7 +240,7 @@ static int gpio_regulator_probe(struct platform_device *pdev) struct regulator_config cfg = { }; struct regulator_dev *rdev; enum gpiod_flags gflags; - int ptr, ret, state, i; + int ptr, state, i; drvdata = devm_kzalloc(dev, sizeof(struct gpio_regulator_data), GFP_KERNEL); @@ -345,11 +345,9 @@ static int gpio_regulator_probe(struct platform_device *pdev) return PTR_ERR(cfg.ena_gpiod); rdev = devm_regulator_register(dev, &drvdata->desc, &cfg); - if (IS_ERR(rdev)) { - ret = PTR_ERR(rdev); - dev_err(dev, "Failed to register regulator: %d\n", ret); - return ret; - } + if (IS_ERR(rdev)) + return dev_err_probe(dev, PTR_ERR(rdev), + "Failed to register regulator\n"); platform_set_drvdata(pdev, drvdata); -- 2.51.0