From 74f44ad07d1063933c237a7db16f6a4036643d60 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 30 Jul 2025 17:46:14 +0100 Subject: mmc: tmio: Add 64-bit read/write support for SD_BUF0 in polling mode As per the RZ/{G2L,G3E} HW manual SD_BUF0 can be accessed by 16/32/64 bits. Most of the data transfer in SD/SDIO/eMMC mode is more than 8 bytes. During testing it is found that, if the DMA buffer is not aligned to 128 bit it fallback to PIO mode. In such cases, 64-bit access is much more efficient than the current 16-bit. Tested-by: Wolfram Sang Reviewed-by: Wolfram Sang Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250730164618.233117-2-biju.das.jz@bp.renesas.com Signed-off-by: Ulf Hansson --- include/linux/platform_data/tmio.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/tmio.h b/include/linux/platform_data/tmio.h index b060124ba1ae..426291713b83 100644 --- a/include/linux/platform_data/tmio.h +++ b/include/linux/platform_data/tmio.h @@ -47,6 +47,9 @@ /* Some controllers have a CBSY bit */ #define TMIO_MMC_HAVE_CBSY BIT(11) +/* Some controllers have a 64-bit wide data port register */ +#define TMIO_MMC_64BIT_DATA_PORT BIT(12) + struct tmio_mmc_data { void *chan_priv_tx; void *chan_priv_rx; -- cgit v1.2.3 From c9f62564252c21d739a5003e9b2d6ad0828aa7bd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 30 Aug 2025 19:01:11 +0200 Subject: mtd: rawnand: s3c2410: Drop driver (no actual S3C64xx user) The s3c2410 NAND driver still supports S3C64xx platform, which in general is supported in the kernel. There are however no references of "s3c6400-nand" platform device ID or "s3c24xx-nand" driver, thus this driver cannot be instantiated for S3C64xx platform and is basically unused. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 26 - drivers/mtd/nand/raw/Makefile | 1 - drivers/mtd/nand/raw/s3c2410.c | 1192 ------------------------ include/linux/platform_data/mtd-nand-s3c2410.h | 70 -- 4 files changed, 1289 deletions(-) delete mode 100644 drivers/mtd/nand/raw/s3c2410.c delete mode 100644 include/linux/platform_data/mtd-nand-s3c2410.h (limited to 'include/linux/platform_data') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 4b99d9c422c3..f053806333c3 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -77,32 +77,6 @@ config MTD_NAND_NDFC help NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs -config MTD_NAND_S3C2410 - tristate "Samsung S3C NAND controller" - depends on ARCH_S3C64XX - help - This enables the NAND flash controller on the S3C24xx and S3C64xx - SoCs - - No board specific support is done by this driver, each board - must advertise a platform_device for the driver to attach. - -config MTD_NAND_S3C2410_DEBUG - bool "Samsung S3C NAND controller debug" - depends on MTD_NAND_S3C2410 - help - Enable debugging of the S3C NAND driver - -config MTD_NAND_S3C2410_CLKSTOP - bool "Samsung S3C NAND IDLE clock stop" - depends on MTD_NAND_S3C2410 - default n - help - Stop the clock to the NAND controller when there is no chip - selected to save power. This will mean there is a small delay - when the is NAND chip selected or released, but will save - approximately 5mA of power when there is nothing happening. - config MTD_NAND_SHARPSL tristate "Sharp SL Series (C7xx + others) NAND controller" depends on ARCH_PXA || COMPILE_TEST diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 711d043ad4f8..363cd18eaf16 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o -obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c deleted file mode 100644 index 33130d75c5ba..000000000000 --- a/drivers/mtd/nand/raw/s3c2410.c +++ /dev/null @@ -1,1192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright © 2004-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * Samsung S3C2410/S3C2440/S3C2412 NAND driver -*/ - -#define pr_fmt(fmt) "nand-s3c2410: " fmt - -#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG -#define DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define S3C2410_NFREG(x) (x) - -#define S3C2410_NFCONF S3C2410_NFREG(0x00) -#define S3C2410_NFCMD S3C2410_NFREG(0x04) -#define S3C2410_NFADDR S3C2410_NFREG(0x08) -#define S3C2410_NFDATA S3C2410_NFREG(0x0C) -#define S3C2410_NFSTAT S3C2410_NFREG(0x10) -#define S3C2410_NFECC S3C2410_NFREG(0x14) -#define S3C2440_NFCONT S3C2410_NFREG(0x04) -#define S3C2440_NFCMD S3C2410_NFREG(0x08) -#define S3C2440_NFADDR S3C2410_NFREG(0x0C) -#define S3C2440_NFDATA S3C2410_NFREG(0x10) -#define S3C2440_NFSTAT S3C2410_NFREG(0x20) -#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) -#define S3C2412_NFSTAT S3C2410_NFREG(0x28) -#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) -#define S3C2410_NFCONF_EN (1<<15) -#define S3C2410_NFCONF_INITECC (1<<12) -#define S3C2410_NFCONF_nFCE (1<<11) -#define S3C2410_NFCONF_TACLS(x) ((x)<<8) -#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) -#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) -#define S3C2410_NFSTAT_BUSY (1<<0) -#define S3C2440_NFCONF_TACLS(x) ((x)<<12) -#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) -#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) -#define S3C2440_NFCONT_INITECC (1<<4) -#define S3C2440_NFCONT_nFCE (1<<1) -#define S3C2440_NFCONT_ENABLE (1<<0) -#define S3C2440_NFSTAT_READY (1<<0) -#define S3C2412_NFCONF_NANDBOOT (1<<31) -#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) -#define S3C2412_NFCONT_nFCE0 (1<<1) -#define S3C2412_NFSTAT_READY (1<<0) - -/* new oob placement block for use with hardware ecc generation - */ -static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 0; - oobregion->length = 3; - - return 0; -} - -static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 8; - oobregion->length = 8; - - return 0; -} - -static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = { - .ecc = s3c2410_ooblayout_ecc, - .free = s3c2410_ooblayout_free, -}; - -/* controller and mtd information */ - -struct s3c2410_nand_info; - -/** - * struct s3c2410_nand_mtd - driver MTD structure - * @chip: The NAND chip information. - * @set: The platform information supplied for this set of NAND chips. - * @info: Link back to the hardware information. -*/ -struct s3c2410_nand_mtd { - struct nand_chip chip; - struct s3c2410_nand_set *set; - struct s3c2410_nand_info *info; -}; - -enum s3c_cpu_type { - TYPE_S3C2410, - TYPE_S3C2412, - TYPE_S3C2440, -}; - -enum s3c_nand_clk_state { - CLOCK_DISABLE = 0, - CLOCK_ENABLE, - CLOCK_SUSPEND, -}; - -/* overview of the s3c2410 nand state */ - -/** - * struct s3c2410_nand_info - NAND controller state. - * @controller: Base controller structure. - * @mtds: An array of MTD instances on this controller. - * @platform: The platform data for this board. - * @device: The platform device we bound to. - * @clk: The clock resource for this controller. - * @regs: The area mapped for the hardware registers. - * @sel_reg: Pointer to the register controlling the NAND selection. - * @sel_bit: The bit in @sel_reg to select the NAND chip. - * @mtd_count: The number of MTDs created from this controller. - * @save_sel: The contents of @sel_reg to be saved over suspend. - * @clk_rate: The clock rate from @clk. - * @clk_state: The current clock state. - * @cpu_type: The exact type of this controller. - */ -struct s3c2410_nand_info { - /* mtd info */ - struct nand_controller controller; - struct s3c2410_nand_mtd *mtds; - struct s3c2410_platform_nand *platform; - - /* device info */ - struct device *device; - struct clk *clk; - void __iomem *regs; - void __iomem *sel_reg; - int sel_bit; - int mtd_count; - unsigned long save_sel; - unsigned long clk_rate; - enum s3c_nand_clk_state clk_state; - - enum s3c_cpu_type cpu_type; -}; - -struct s3c24XX_nand_devtype_data { - enum s3c_cpu_type type; -}; - -/* conversion functions */ - -static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd, - chip); -} - -static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) -{ - return s3c2410_nand_mtd_toours(mtd)->info; -} - -static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) -{ - return platform_get_drvdata(dev); -} - -static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) -{ - return dev_get_platdata(&dev->dev); -} - -static inline int allow_clk_suspend(struct s3c2410_nand_info *info) -{ -#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP - return 1; -#else - return 0; -#endif -} - -/** - * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. - * @info: The controller instance. - * @new_state: State to which clock should be set. - */ -static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, - enum s3c_nand_clk_state new_state) -{ - if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) - return; - - if (info->clk_state == CLOCK_ENABLE) { - if (new_state != CLOCK_ENABLE) - clk_disable_unprepare(info->clk); - } else { - if (new_state == CLOCK_ENABLE) - clk_prepare_enable(info->clk); - } - - info->clk_state = new_state; -} - -/* timing calculations */ - -#define NS_IN_KHZ 1000000 - -/** - * s3c_nand_calc_rate - calculate timing data. - * @wanted: The cycle time in nanoseconds. - * @clk: The clock rate in kHz. - * @max: The maximum divider value. - * - * Calculate the timing value from the given parameters. - */ -static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) -{ - int result; - - result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); - - pr_debug("result %d from %ld, %d\n", result, clk, wanted); - - if (result > max) { - pr_err("%d ns is too big for current clock rate %ld\n", - wanted, clk); - return -1; - } - - if (result < 1) - result = 1; - - return result; -} - -#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) - -/* controller setup */ - -/** - * s3c2410_nand_setrate - setup controller timing information. - * @info: The controller instance. - * - * Given the information supplied by the platform, calculate and set - * the necessary timing registers in the hardware to generate the - * necessary timing cycles to the hardware. - */ -static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) -{ - struct s3c2410_platform_nand *plat = info->platform; - int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; - int tacls, twrph0, twrph1; - unsigned long clkrate = clk_get_rate(info->clk); - unsigned long set, cfg, mask; - unsigned long flags; - - /* calculate the timing information for the controller */ - - info->clk_rate = clkrate; - clkrate /= 1000; /* turn clock into kHz for ease of use */ - - if (plat != NULL) { - tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); - twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); - twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); - } else { - /* default timings */ - tacls = tacls_max; - twrph0 = 8; - twrph1 = 8; - } - - if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { - dev_err(info->device, "cannot get suitable timings\n"); - return -EINVAL; - } - - dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", - tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), - twrph1, to_ns(twrph1, clkrate)); - - switch (info->cpu_type) { - case TYPE_S3C2410: - mask = (S3C2410_NFCONF_TACLS(3) | - S3C2410_NFCONF_TWRPH0(7) | - S3C2410_NFCONF_TWRPH1(7)); - set = S3C2410_NFCONF_EN; - set |= S3C2410_NFCONF_TACLS(tacls - 1); - set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); - set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); - break; - - case TYPE_S3C2440: - case TYPE_S3C2412: - mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | - S3C2440_NFCONF_TWRPH0(7) | - S3C2440_NFCONF_TWRPH1(7)); - - set = S3C2440_NFCONF_TACLS(tacls - 1); - set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); - set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); - break; - - default: - BUG(); - } - - local_irq_save(flags); - - cfg = readl(info->regs + S3C2410_NFCONF); - cfg &= ~mask; - cfg |= set; - writel(cfg, info->regs + S3C2410_NFCONF); - - local_irq_restore(flags); - - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); - - return 0; -} - -/** - * s3c2410_nand_inithw - basic hardware initialisation - * @info: The hardware state. - * - * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() - * to setup the hardware access speeds and set the controller to be enabled. -*/ -static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) -{ - int ret; - - ret = s3c2410_nand_setrate(info); - if (ret < 0) - return ret; - - switch (info->cpu_type) { - case TYPE_S3C2410: - default: - break; - - case TYPE_S3C2440: - case TYPE_S3C2412: - /* enable the controller and de-assert nFCE */ - - writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); - } - - return 0; -} - -/** - * s3c2410_nand_select_chip - select the given nand chip - * @this: NAND chip object. - * @chip: The chip number. - * - * This is called by the MTD layer to either select a given chip for the - * @mtd instance, or to indicate that the access has finished and the - * chip can be de-selected. - * - * The routine ensures that the nFCE line is correctly setup, and any - * platform specific selection code is called to route nFCE to the specific - * chip. - */ -static void s3c2410_nand_select_chip(struct nand_chip *this, int chip) -{ - struct s3c2410_nand_info *info; - struct s3c2410_nand_mtd *nmtd; - unsigned long cur; - - nmtd = nand_get_controller_data(this); - info = nmtd->info; - - if (chip != -1) - s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); - - cur = readl(info->sel_reg); - - if (chip == -1) { - cur |= info->sel_bit; - } else { - if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { - dev_err(info->device, "invalid chip %d\n", chip); - return; - } - - if (info->platform != NULL) { - if (info->platform->select_chip != NULL) - (info->platform->select_chip) (nmtd->set, chip); - } - - cur &= ~info->sel_bit; - } - - writel(cur, info->sel_reg); - - if (chip == -1) - s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); -} - -/* s3c2410_nand_hwcontrol - * - * Issue command and address cycles to the chip -*/ - -static void s3c2410_nand_hwcontrol(struct nand_chip *chip, int cmd, - unsigned int ctrl) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, info->regs + S3C2410_NFCMD); - else - writeb(cmd, info->regs + S3C2410_NFADDR); -} - -/* command and control functions */ - -static void s3c2440_nand_hwcontrol(struct nand_chip *chip, int cmd, - unsigned int ctrl) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, info->regs + S3C2440_NFCMD); - else - writeb(cmd, info->regs + S3C2440_NFADDR); -} - -/* s3c2410_nand_devready() - * - * returns 0 if the nand is busy, 1 if it is ready -*/ - -static int s3c2410_nand_devready(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; -} - -static int s3c2440_nand_devready(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; -} - -static int s3c2412_nand_devready(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY; -} - -/* ECC handling functions */ - -static int s3c2410_nand_correct_data(struct nand_chip *chip, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned int diff0, diff1, diff2; - unsigned int bit, byte; - - pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); - - diff0 = read_ecc[0] ^ calc_ecc[0]; - diff1 = read_ecc[1] ^ calc_ecc[1]; - diff2 = read_ecc[2] ^ calc_ecc[2]; - - pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n", - __func__, 3, read_ecc, 3, calc_ecc, - diff0, diff1, diff2); - - if (diff0 == 0 && diff1 == 0 && diff2 == 0) - return 0; /* ECC is ok */ - - /* sometimes people do not think about using the ECC, so check - * to see if we have an 0xff,0xff,0xff read ECC and then ignore - * the error, on the assumption that this is an un-eccd page. - */ - if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff - && info->platform->ignore_unset_ecc) - return 0; - - /* Can we correct this ECC (ie, one row and column change). - * Note, this is similar to the 256 error code on smartmedia */ - - if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && - ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && - ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { - /* calculate the bit position of the error */ - - bit = ((diff2 >> 3) & 1) | - ((diff2 >> 4) & 2) | - ((diff2 >> 5) & 4); - - /* calculate the byte position of the error */ - - byte = ((diff2 << 7) & 0x100) | - ((diff1 << 0) & 0x80) | - ((diff1 << 1) & 0x40) | - ((diff1 << 2) & 0x20) | - ((diff1 << 3) & 0x10) | - ((diff0 >> 4) & 0x08) | - ((diff0 >> 3) & 0x04) | - ((diff0 >> 2) & 0x02) | - ((diff0 >> 1) & 0x01); - - dev_dbg(info->device, "correcting error bit %d, byte %d\n", - bit, byte); - - dat[byte] ^= (1 << bit); - return 1; - } - - /* if there is only one bit difference in the ECC, then - * one of only a row or column parity has changed, which - * means the error is most probably in the ECC itself */ - - diff0 |= (diff1 << 8); - diff0 |= (diff2 << 16); - - /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */ - if ((diff0 & (diff0 - 1)) == 0) - return 1; - - return -1; -} - -/* ECC functions - * - * These allow the s3c2410 and s3c2440 to use the controller's ECC - * generator block to ECC the data as it passes through] -*/ - -static void s3c2410_nand_enable_hwecc(struct nand_chip *chip, int mode) -{ - struct s3c2410_nand_info *info; - unsigned long ctrl; - - info = s3c2410_nand_mtd_toinfo(nand_to_mtd(chip)); - ctrl = readl(info->regs + S3C2410_NFCONF); - ctrl |= S3C2410_NFCONF_INITECC; - writel(ctrl, info->regs + S3C2410_NFCONF); -} - -static void s3c2412_nand_enable_hwecc(struct nand_chip *chip, int mode) -{ - struct s3c2410_nand_info *info; - unsigned long ctrl; - - info = s3c2410_nand_mtd_toinfo(nand_to_mtd(chip)); - ctrl = readl(info->regs + S3C2440_NFCONT); - writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, - info->regs + S3C2440_NFCONT); -} - -static void s3c2440_nand_enable_hwecc(struct nand_chip *chip, int mode) -{ - struct s3c2410_nand_info *info; - unsigned long ctrl; - - info = s3c2410_nand_mtd_toinfo(nand_to_mtd(chip)); - ctrl = readl(info->regs + S3C2440_NFCONT); - writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); -} - -static int s3c2410_nand_calculate_ecc(struct nand_chip *chip, - const u_char *dat, u_char *ecc_code) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); - ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); - ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); - - pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); - - return 0; -} - -static int s3c2412_nand_calculate_ecc(struct nand_chip *chip, - const u_char *dat, u_char *ecc_code) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); - - ecc_code[0] = ecc; - ecc_code[1] = ecc >> 8; - ecc_code[2] = ecc >> 16; - - pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); - - return 0; -} - -static int s3c2440_nand_calculate_ecc(struct nand_chip *chip, - const u_char *dat, u_char *ecc_code) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); - - ecc_code[0] = ecc; - ecc_code[1] = ecc >> 8; - ecc_code[2] = ecc >> 16; - - pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); - - return 0; -} - -/* over-ride the standard functions for a little more speed. We can - * use read/write block to move the data buffers to/from the controller -*/ - -static void s3c2410_nand_read_buf(struct nand_chip *this, u_char *buf, int len) -{ - readsb(this->legacy.IO_ADDR_R, buf, len); -} - -static void s3c2440_nand_read_buf(struct nand_chip *this, u_char *buf, int len) -{ - struct mtd_info *mtd = nand_to_mtd(this); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); - - /* cleanup if we've got less than a word to do */ - if (len & 3) { - buf += len & ~3; - - for (; len & 3; len--) - *buf++ = readb(info->regs + S3C2440_NFDATA); - } -} - -static void s3c2410_nand_write_buf(struct nand_chip *this, const u_char *buf, - int len) -{ - writesb(this->legacy.IO_ADDR_W, buf, len); -} - -static void s3c2440_nand_write_buf(struct nand_chip *this, const u_char *buf, - int len) -{ - struct mtd_info *mtd = nand_to_mtd(this); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); - - /* cleanup any fractional write */ - if (len & 3) { - buf += len & ~3; - - for (; len & 3; len--, buf++) - writeb(*buf, info->regs + S3C2440_NFDATA); - } -} - -/* device management functions */ - -static void s3c24xx_nand_remove(struct platform_device *pdev) -{ - struct s3c2410_nand_info *info = to_nand_info(pdev); - - if (info == NULL) - return; - - /* Release all our mtds and their partitions, then go through - * freeing the resources used - */ - - if (info->mtds != NULL) { - struct s3c2410_nand_mtd *ptr = info->mtds; - int mtdno; - - for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { - pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); - WARN_ON(mtd_device_unregister(nand_to_mtd(&ptr->chip))); - nand_cleanup(&ptr->chip); - } - } - - /* free the common resources */ - - if (!IS_ERR(info->clk)) - s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); -} - -static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *mtd, - struct s3c2410_nand_set *set) -{ - if (set) { - struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip); - - mtdinfo->name = set->name; - - return mtd_device_register(mtdinfo, set->partitions, - set->nr_partitions); - } - - return -ENODEV; -} - -static int s3c2410_nand_setup_interface(struct nand_chip *chip, int csline, - const struct nand_interface_config *conf) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - struct s3c2410_platform_nand *pdata = info->platform; - const struct nand_sdr_timings *timings; - int tacls; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return -ENOTSUPP; - - tacls = timings->tCLS_min - timings->tWP_min; - if (tacls < 0) - tacls = 0; - - pdata->tacls = DIV_ROUND_UP(tacls, 1000); - pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000); - pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000); - - return s3c2410_nand_setrate(info); -} - -/** - * s3c2410_nand_init_chip - initialise a single instance of an chip - * @info: The base NAND controller the chip is on. - * @nmtd: The new controller MTD instance to fill in. - * @set: The information passed from the board specific platform data. - * - * Initialise the given @nmtd from the information in @info and @set. This - * readies the structure for use with the MTD layer functions by ensuring - * all pointers are setup and the necessary control routines selected. - */ -static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *nmtd, - struct s3c2410_nand_set *set) -{ - struct device_node *np = info->device->of_node; - struct nand_chip *chip = &nmtd->chip; - void __iomem *regs = info->regs; - - nand_set_flash_node(chip, set->of_node); - - chip->legacy.write_buf = s3c2410_nand_write_buf; - chip->legacy.read_buf = s3c2410_nand_read_buf; - chip->legacy.select_chip = s3c2410_nand_select_chip; - chip->legacy.chip_delay = 50; - nand_set_controller_data(chip, nmtd); - chip->options = set->options; - chip->controller = &info->controller; - - /* - * let's keep behavior unchanged for legacy boards booting via pdata and - * auto-detect timings only when booting with a device tree. - */ - if (!np) - chip->options |= NAND_KEEP_TIMINGS; - - switch (info->cpu_type) { - case TYPE_S3C2410: - chip->legacy.IO_ADDR_W = regs + S3C2410_NFDATA; - info->sel_reg = regs + S3C2410_NFCONF; - info->sel_bit = S3C2410_NFCONF_nFCE; - chip->legacy.cmd_ctrl = s3c2410_nand_hwcontrol; - chip->legacy.dev_ready = s3c2410_nand_devready; - break; - - case TYPE_S3C2440: - chip->legacy.IO_ADDR_W = regs + S3C2440_NFDATA; - info->sel_reg = regs + S3C2440_NFCONT; - info->sel_bit = S3C2440_NFCONT_nFCE; - chip->legacy.cmd_ctrl = s3c2440_nand_hwcontrol; - chip->legacy.dev_ready = s3c2440_nand_devready; - chip->legacy.read_buf = s3c2440_nand_read_buf; - chip->legacy.write_buf = s3c2440_nand_write_buf; - break; - - case TYPE_S3C2412: - chip->legacy.IO_ADDR_W = regs + S3C2440_NFDATA; - info->sel_reg = regs + S3C2440_NFCONT; - info->sel_bit = S3C2412_NFCONT_nFCE0; - chip->legacy.cmd_ctrl = s3c2440_nand_hwcontrol; - chip->legacy.dev_ready = s3c2412_nand_devready; - - if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) - dev_info(info->device, "System booted from NAND\n"); - - break; - } - - chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W; - - nmtd->info = info; - nmtd->set = set; - - chip->ecc.engine_type = info->platform->engine_type; - - /* - * If you use u-boot BBT creation code, specifying this flag will - * let the kernel fish out the BBT from the NAND. - */ - if (set->flash_bbt) - chip->bbt_options |= NAND_BBT_USE_FLASH; -} - -/** - * s3c2410_nand_attach_chip - Init the ECC engine after NAND scan - * @chip: The NAND chip - * - * This hook is called by the core after the identification of the NAND chip, - * once the relevant per-chip information is up to date.. This call ensure that - * we update the internal state accordingly. - * - * The internal state is currently limited to the ECC state information. -*/ -static int s3c2410_nand_attach_chip(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - switch (chip->ecc.engine_type) { - - case NAND_ECC_ENGINE_TYPE_NONE: - dev_info(info->device, "ECC disabled\n"); - break; - - case NAND_ECC_ENGINE_TYPE_SOFT: - /* - * This driver expects Hamming based ECC when engine_type is set - * to NAND_ECC_ENGINE_TYPE_SOFT. Force ecc.algo to - * NAND_ECC_ALGO_HAMMING to avoid adding an extra ecc_algo field - * to s3c2410_platform_nand. - */ - chip->ecc.algo = NAND_ECC_ALGO_HAMMING; - dev_info(info->device, "soft ECC\n"); - break; - - case NAND_ECC_ENGINE_TYPE_ON_HOST: - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - chip->ecc.correct = s3c2410_nand_correct_data; - chip->ecc.strength = 1; - - switch (info->cpu_type) { - case TYPE_S3C2410: - chip->ecc.hwctl = s3c2410_nand_enable_hwecc; - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - break; - - case TYPE_S3C2412: - chip->ecc.hwctl = s3c2412_nand_enable_hwecc; - chip->ecc.calculate = s3c2412_nand_calculate_ecc; - break; - - case TYPE_S3C2440: - chip->ecc.hwctl = s3c2440_nand_enable_hwecc; - chip->ecc.calculate = s3c2440_nand_calculate_ecc; - break; - } - - dev_dbg(info->device, "chip %p => page shift %d\n", - chip, chip->page_shift); - - /* change the behaviour depending on whether we are using - * the large or small page nand device */ - if (chip->page_shift > 10) { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - } else { - chip->ecc.size = 512; - chip->ecc.bytes = 3; - mtd_set_ooblayout(nand_to_mtd(chip), - &s3c2410_ooblayout_ops); - } - - dev_info(info->device, "hardware ECC\n"); - break; - - default: - dev_err(info->device, "invalid ECC mode!\n"); - return -EINVAL; - } - - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->options |= NAND_SKIP_BBTSCAN; - - return 0; -} - -static const struct nand_controller_ops s3c24xx_nand_controller_ops = { - .attach_chip = s3c2410_nand_attach_chip, - .setup_interface = s3c2410_nand_setup_interface, -}; - -static int s3c24xx_nand_probe_dt(struct platform_device *pdev) -{ - const struct s3c24XX_nand_devtype_data *devtype_data; - struct s3c2410_platform_nand *pdata; - struct s3c2410_nand_info *info = platform_get_drvdata(pdev); - struct device_node *np = pdev->dev.of_node, *child; - struct s3c2410_nand_set *sets; - - devtype_data = of_device_get_match_data(&pdev->dev); - if (!devtype_data) - return -ENODEV; - - info->cpu_type = devtype_data->type; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdev->dev.platform_data = pdata; - - pdata->nr_sets = of_get_child_count(np); - if (!pdata->nr_sets) - return 0; - - sets = devm_kcalloc(&pdev->dev, pdata->nr_sets, sizeof(*sets), - GFP_KERNEL); - if (!sets) - return -ENOMEM; - - pdata->sets = sets; - - for_each_available_child_of_node(np, child) { - sets->name = (char *)child->name; - sets->of_node = child; - sets->nr_chips = 1; - - of_node_get(child); - - sets++; - } - - return 0; -} - -static int s3c24xx_nand_probe_pdata(struct platform_device *pdev) -{ - struct s3c2410_nand_info *info = platform_get_drvdata(pdev); - - info->cpu_type = platform_get_device_id(pdev)->driver_data; - - return 0; -} - -/* s3c24xx_nand_probe - * - * called by device layer when it finds a device matching - * one our driver can handled. This code checks to see if - * it can allocate all necessary resources then calls the - * nand layer to look for devices -*/ -static int s3c24xx_nand_probe(struct platform_device *pdev) -{ - struct s3c2410_platform_nand *plat; - struct s3c2410_nand_info *info; - struct s3c2410_nand_mtd *nmtd; - struct s3c2410_nand_set *sets; - struct resource *res; - int err = 0; - int size; - int nr_sets; - int setno; - - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (info == NULL) { - err = -ENOMEM; - goto exit_error; - } - - platform_set_drvdata(pdev, info); - - nand_controller_init(&info->controller); - info->controller.ops = &s3c24xx_nand_controller_ops; - - /* get the clock source and enable it */ - - info->clk = devm_clk_get(&pdev->dev, "nand"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get clock\n"); - err = -ENOENT; - goto exit_error; - } - - s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); - - if (pdev->dev.of_node) - err = s3c24xx_nand_probe_dt(pdev); - else - err = s3c24xx_nand_probe_pdata(pdev); - - if (err) - goto exit_error; - - plat = to_nand_plat(pdev); - - /* allocate and map the resource */ - - /* currently we assume we have the one resource */ - res = pdev->resource; - size = resource_size(res); - - info->device = &pdev->dev; - info->platform = plat; - - info->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(info->regs)) { - err = PTR_ERR(info->regs); - goto exit_error; - } - - dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); - - if (!plat->sets || plat->nr_sets < 1) { - err = -EINVAL; - goto exit_error; - } - - sets = plat->sets; - nr_sets = plat->nr_sets; - - info->mtd_count = nr_sets; - - /* allocate our information */ - - size = nr_sets * sizeof(*info->mtds); - info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (info->mtds == NULL) { - err = -ENOMEM; - goto exit_error; - } - - /* initialise all possible chips */ - - nmtd = info->mtds; - - for (setno = 0; setno < nr_sets; setno++, nmtd++, sets++) { - struct mtd_info *mtd = nand_to_mtd(&nmtd->chip); - - pr_debug("initialising set %d (%p, info %p)\n", - setno, nmtd, info); - - mtd->dev.parent = &pdev->dev; - s3c2410_nand_init_chip(info, nmtd, sets); - - err = nand_scan(&nmtd->chip, sets ? sets->nr_chips : 1); - if (err) - goto exit_error; - - s3c2410_nand_add_partition(info, nmtd, sets); - } - - /* initialise the hardware */ - err = s3c2410_nand_inithw(info); - if (err != 0) - goto exit_error; - - if (allow_clk_suspend(info)) { - dev_info(&pdev->dev, "clock idle support enabled\n"); - s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); - } - - return 0; - - exit_error: - s3c24xx_nand_remove(pdev); - - if (err == 0) - err = -EINVAL; - return err; -} - -/* PM Support */ -#ifdef CONFIG_PM - -static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) -{ - struct s3c2410_nand_info *info = platform_get_drvdata(dev); - - if (info) { - info->save_sel = readl(info->sel_reg); - - /* For the moment, we must ensure nFCE is high during - * the time we are suspended. This really should be - * handled by suspending the MTDs we are using, but - * that is currently not the case. */ - - writel(info->save_sel | info->sel_bit, info->sel_reg); - - s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); - } - - return 0; -} - -static int s3c24xx_nand_resume(struct platform_device *dev) -{ - struct s3c2410_nand_info *info = platform_get_drvdata(dev); - unsigned long sel; - - if (info) { - s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); - s3c2410_nand_inithw(info); - - /* Restore the state of the nFCE line. */ - - sel = readl(info->sel_reg); - sel &= ~info->sel_bit; - sel |= info->save_sel & info->sel_bit; - writel(sel, info->sel_reg); - - s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); - } - - return 0; -} - -#else -#define s3c24xx_nand_suspend NULL -#define s3c24xx_nand_resume NULL -#endif - -/* driver device registration */ - -static const struct platform_device_id s3c24xx_driver_ids[] = { - { - .name = "s3c6400-nand", - .driver_data = TYPE_S3C2412, /* compatible with 2412 */ - }, - { } -}; - -MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); - -static struct platform_driver s3c24xx_nand_driver = { - .probe = s3c24xx_nand_probe, - .remove = s3c24xx_nand_remove, - .suspend = s3c24xx_nand_suspend, - .resume = s3c24xx_nand_resume, - .id_table = s3c24xx_driver_ids, - .driver = { - .name = "s3c24xx-nand", - }, -}; - -module_platform_driver(s3c24xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h deleted file mode 100644 index 25390fc3e795..000000000000 --- a/include/linux/platform_data/mtd-nand-s3c2410.h +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks - * - * S3C2410 - NAND device controller platform_device info -*/ - -#ifndef __MTD_NAND_S3C2410_H -#define __MTD_NAND_S3C2410_H - -#include - -/** - * struct s3c2410_nand_set - define a set of one or more nand chips - * @flash_bbt: Openmoko u-boot can create a Bad Block Table - * Setting this flag will allow the kernel to - * look for it at boot time and also skip the NAND - * scan. - * @options: Default value to set into 'struct nand_chip' options. - * @nr_chips: Number of chips in this set - * @nr_partitions: Number of partitions pointed to by @partitions - * @name: Name of set (optional) - * @nr_map: Map for low-layer logical to physical chip numbers (option) - * @partitions: The mtd partition list - * - * define a set of one or more nand chips registered with an unique mtd. Also - * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger - * a warning at boot time. - */ -struct s3c2410_nand_set { - unsigned int flash_bbt:1; - - unsigned int options; - int nr_chips; - int nr_partitions; - char *name; - int *nr_map; - struct mtd_partition *partitions; - struct device_node *of_node; -}; - -struct s3c2410_platform_nand { - /* timing information for controller, all times in nanoseconds */ - - int tacls; /* time for active CLE/ALE to nWE/nOE */ - int twrph0; /* active time for nWE/nOE */ - int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ - - unsigned int ignore_unset_ecc:1; - - enum nand_ecc_engine_type engine_type; - - int nr_sets; - struct s3c2410_nand_set *sets; - - void (*select_chip)(struct s3c2410_nand_set *, - int chip); -}; - -/** - * s3c_nand_set_platdata() - register NAND platform data. - * @nand: The NAND platform data to register with s3c_device_nand. - * - * This function copies the given NAND platform data, @nand and registers - * it with the s3c_device_nand. This allows @nand to be __initdata. -*/ -extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand); - -#endif /*__MTD_NAND_S3C2410_H */ -- cgit v1.2.3 From ca734f54b346db170be20ff3382bc3da8eb65904 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 17 Aug 2025 14:53:15 -0700 Subject: Input: pxa27x-keypad - drop support for platform data There are no in-kernel users of pxa27x_keypad_platform_data in the kernel, and the driver supports configuration via device tree, so drop support of static platform data and move properties parsing from OF-specific methods to generic ones. Link: https://lore.kernel.org/r/20250817215316.1872689-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pxa27x_keypad.c | 378 +++++++++------------------- include/linux/platform_data/keypad-pxa27x.h | 73 ------ 2 files changed, 121 insertions(+), 330 deletions(-) delete mode 100644 include/linux/platform_data/keypad-pxa27x.h (limited to 'include/linux/platform_data') diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index e6f6adb56de4..4519eecb317b 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -21,13 +21,13 @@ #include #include #include +#include #include #include #include #include #include -#include /* * Keypad Controller registers */ @@ -100,54 +100,69 @@ #define keypad_readl(off) __raw_readl(keypad->mmio_base + (off)) #define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off)) +#define MAX_MATRIX_KEY_ROWS 8 +#define MAX_MATRIX_KEY_COLS 8 +#define MAX_DIRECT_KEY_NUM 8 +#define MAX_ROTARY_ENCODERS 2 + #define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) #define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM) -struct pxa27x_keypad { - const struct pxa27x_keypad_platform_data *pdata; +struct pxa27x_keypad_rotary { + unsigned short *key_codes; + int rel_code; + bool enabled; +}; +struct pxa27x_keypad { struct clk *clk; struct input_dev *input_dev; void __iomem *mmio_base; int irq; - unsigned short keycodes[MAX_KEYPAD_KEYS]; - int rotary_rel_code[2]; - + unsigned int matrix_key_rows; + unsigned int matrix_key_cols; unsigned int row_shift; + unsigned int direct_key_num; + unsigned int direct_key_mask; + bool direct_key_low_active; + + /* key debounce interval */ + unsigned int debounce_interval; + + unsigned short keycodes[MAX_KEYPAD_KEYS]; + /* state row bits of each column scan */ u32 matrix_key_state[MAX_MATRIX_KEY_COLS]; u32 direct_key_state; - unsigned int direct_key_mask; + struct pxa27x_keypad_rotary rotary[MAX_ROTARY_ENCODERS]; }; -#ifdef CONFIG_OF -static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, - struct pxa27x_keypad_platform_data *pdata) +static int pxa27x_keypad_matrix_key_parse(struct pxa27x_keypad *keypad) { struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - u32 rows, cols; int error; - error = matrix_keypad_parse_properties(dev, &rows, &cols); + error = matrix_keypad_parse_properties(dev, &keypad->matrix_key_rows, + &keypad->matrix_key_cols); if (error) return error; - if (rows > MAX_MATRIX_KEY_ROWS || cols > MAX_MATRIX_KEY_COLS) { + if (keypad->matrix_key_rows > MAX_MATRIX_KEY_ROWS || + keypad->matrix_key_cols > MAX_MATRIX_KEY_COLS) { dev_err(dev, "rows or cols exceeds maximum value\n"); return -EINVAL; } - pdata->matrix_key_rows = rows; - pdata->matrix_key_cols = cols; + keypad->row_shift = get_count_order(keypad->matrix_key_cols); error = matrix_keypad_build_keymap(NULL, NULL, - pdata->matrix_key_rows, - pdata->matrix_key_cols, + keypad->matrix_key_rows, + keypad->matrix_key_cols, keypad->keycodes, input_dev); if (error) return error; @@ -155,20 +170,17 @@ static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, return 0; } -static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad, - struct pxa27x_keypad_platform_data *pdata) +static int pxa27x_keypad_direct_key_parse(struct pxa27x_keypad *keypad) { struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; - const __be16 *prop; unsigned short code; - unsigned int proplen, size; + int count; int i; int error; - error = of_property_read_u32(np, "marvell,direct-key-count", - &pdata->direct_key_num); + error = device_property_read_u32(dev, "marvell,direct-key-count", + &keypad->direct_key_num); if (error) { /* * If do not have marvel,direct-key-count defined, @@ -177,151 +189,121 @@ static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad, return error == -EINVAL ? 0 : error; } - error = of_property_read_u32(np, "marvell,direct-key-mask", - &pdata->direct_key_mask); + error = device_property_read_u32(dev, "marvell,direct-key-mask", + &keypad->direct_key_mask); if (error) { if (error != -EINVAL) return error; /* * If marvell,direct-key-mask is not defined, driver will use - * default value. Default value is set when configure the keypad. + * a default value based on number of direct keys set up. + * The default value is calculated in pxa27x_keypad_config(). */ - pdata->direct_key_mask = 0; + keypad->direct_key_mask = 0; } - pdata->direct_key_low_active = of_property_read_bool(np, - "marvell,direct-key-low-active"); - - prop = of_get_property(np, "marvell,direct-key-map", &proplen); - if (!prop) - return -EINVAL; + keypad->direct_key_low_active = + device_property_read_bool(dev, "marvell,direct-key-low-active"); - if (proplen % sizeof(u16)) + count = device_property_count_u16(dev, "marvell,direct-key-map"); + if (count <= 0 || count > MAX_DIRECT_KEY_NUM) return -EINVAL; - size = proplen / sizeof(u16); - - /* Only MAX_DIRECT_KEY_NUM is accepted.*/ - if (size > MAX_DIRECT_KEY_NUM) - return -EINVAL; + error = device_property_read_u16_array(dev, "marvell,direct-key-map", + &keypad->keycodes[MAX_MATRIX_KEY_NUM], + count); - for (i = 0; i < size; i++) { - code = be16_to_cpup(prop + i); - keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = code; + for (i = 0; i < count; i++) { + code = keypad->keycodes[MAX_MATRIX_KEY_NUM + i]; __set_bit(code, input_dev->keybit); } return 0; } -static int pxa27x_keypad_rotary_parse_dt(struct pxa27x_keypad *keypad, - struct pxa27x_keypad_platform_data *pdata) +static int pxa27x_keypad_rotary_parse(struct pxa27x_keypad *keypad) { - const __be32 *prop; - int i, relkey_ret; - unsigned int code, proplen; - const char *rotaryname[2] = { - "marvell,rotary0", "marvell,rotary1"}; - const char relkeyname[] = {"marvell,rotary-rel-key"}; + static const char * const rotaryname[] = { "marvell,rotary0", "marvell,rotary1" }; struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; - - relkey_ret = of_property_read_u32(np, relkeyname, &code); - /* if can read correct rotary key-code, we do not need this. */ - if (relkey_ret == 0) { - unsigned short relcode; + struct pxa27x_keypad_rotary *encoder; + unsigned int code; + int i; + int error; - /* rotary0 taks lower half, rotary1 taks upper half. */ - relcode = code & 0xffff; - pdata->rotary0_rel_code = (code & 0xffff); - __set_bit(relcode, input_dev->relbit); + error = device_property_read_u32(dev, "marvell,rotary-rel-key", &code); + if (!error) { + for (i = 0; i < MAX_ROTARY_ENCODERS; i++, code >>= 16) { + encoder = &keypad->rotary[i]; + encoder->enabled = true; + encoder->rel_code = code & 0xffff; + input_set_capability(input_dev, EV_REL, encoder->rel_code); + } - relcode = code >> 16; - pdata->rotary1_rel_code = relcode; - __set_bit(relcode, input_dev->relbit); + return 0; } - for (i = 0; i < 2; i++) { - prop = of_get_property(np, rotaryname[i], &proplen); + for (i = 0; i < MAX_ROTARY_ENCODERS; i++) { + encoder = &keypad->rotary[i]; + /* * If the prop is not set, it means keypad does not need * initialize the rotaryX. */ - if (!prop) + if (!device_property_present(dev, rotaryname[i])) continue; - code = be32_to_cpup(prop); + error = device_property_read_u32(dev, rotaryname[i], &code); + if (error) + return error; + /* * Not all up/down key code are valid. * Now we depends on direct-rel-code. */ - if ((!(code & 0xffff) || !(code >> 16)) && relkey_ret) { - return relkey_ret; - } else { - unsigned int n = MAX_MATRIX_KEY_NUM + (i << 1); - unsigned short keycode; - - keycode = code & 0xffff; - keypad->keycodes[n] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = code >> 16; - keypad->keycodes[n + 1] = keycode; - __set_bit(keycode, input_dev->keybit); - - if (i == 0) - pdata->rotary0_rel_code = -1; - else - pdata->rotary1_rel_code = -1; - } - if (i == 0) - pdata->enable_rotary0 = 1; - else - pdata->enable_rotary1 = 1; - } + if (!(code & 0xffff) || !(code >> 16)) + return -EINVAL; + + encoder->enabled = true; + encoder->rel_code = -1; + encoder->key_codes = &keypad->keycodes[MAX_MATRIX_KEY_NUM + i * 2]; + encoder->key_codes[0] = code & 0xffff; + encoder->key_codes[1] = code >> 16; - keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; - keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + input_set_capability(input_dev, EV_KEY, encoder->key_codes[0]); + input_set_capability(input_dev, EV_KEY, encoder->key_codes[1]); + } return 0; } -static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) +static int pxa27x_keypad_parse_properties(struct pxa27x_keypad *keypad) { struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; - struct pxa27x_keypad_platform_data *pdata; int error; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "failed to allocate memory for pdata\n"); - return -ENOMEM; - } - - error = pxa27x_keypad_matrix_key_parse_dt(keypad, pdata); + error = pxa27x_keypad_matrix_key_parse(keypad); if (error) { dev_err(dev, "failed to parse matrix key\n"); return error; } - error = pxa27x_keypad_direct_key_parse_dt(keypad, pdata); + error = pxa27x_keypad_direct_key_parse(keypad); if (error) { dev_err(dev, "failed to parse direct key\n"); return error; } - error = pxa27x_keypad_rotary_parse_dt(keypad, pdata); + error = pxa27x_keypad_rotary_parse(keypad); if (error) { dev_err(dev, "failed to parse rotary key\n"); return error; } - error = of_property_read_u32(np, "marvell,debounce-interval", - &pdata->debounce_interval); + error = device_property_read_u32(dev, "marvell,debounce-interval", + &keypad->debounce_interval); if (error) { dev_err(dev, "failed to parse debounce-interval\n"); return error; @@ -333,91 +315,11 @@ static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) */ input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - keypad->pdata = pdata; - return 0; -} - -#else - -static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) -{ - dev_info(keypad->input_dev->dev.parent, "missing platform data\n"); - - return -EINVAL; -} - -#endif - -static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) -{ - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - struct input_dev *input_dev = keypad->input_dev; - unsigned short keycode; - int i; - int error; - - error = matrix_keypad_build_keymap(pdata->matrix_keymap_data, NULL, - pdata->matrix_key_rows, - pdata->matrix_key_cols, - keypad->keycodes, input_dev); - if (error) - return error; - - /* - * The keycodes may not only include matrix keys but also the direct - * or rotary keys. - */ - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - - /* For direct keys. */ - for (i = 0; i < pdata->direct_key_num; i++) { - keycode = pdata->direct_key_map[i]; - keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; - __set_bit(keycode, input_dev->keybit); - } - - if (pdata->enable_rotary0) { - if (pdata->rotary0_up_key && pdata->rotary0_down_key) { - keycode = pdata->rotary0_up_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = pdata->rotary0_down_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode; - __set_bit(keycode, input_dev->keybit); - - keypad->rotary_rel_code[0] = -1; - } else { - keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; - __set_bit(pdata->rotary0_rel_code, input_dev->relbit); - } - } - - if (pdata->enable_rotary1) { - if (pdata->rotary1_up_key && pdata->rotary1_down_key) { - keycode = pdata->rotary1_up_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = pdata->rotary1_down_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode; - __set_bit(keycode, input_dev->keybit); - - keypad->rotary_rel_code[1] = -1; - } else { - keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; - __set_bit(pdata->rotary1_rel_code, input_dev->relbit); - } - } - - __clear_bit(KEY_RESERVED, input_dev->keybit); - return 0; } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; int row, col, num_keys_pressed = 0; u32 new_state[MAX_MATRIX_KEY_COLS]; @@ -435,8 +337,8 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) row = KPAS_RP(kpas); /* if invalid row/col, treat as no key pressed */ - if (col >= pdata->matrix_key_cols || - row >= pdata->matrix_key_rows) + if (col >= keypad->matrix_key_cols || + row >= keypad->matrix_key_rows) goto scan; new_state[col] = BIT(row); @@ -459,7 +361,7 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK; } scan: - for (col = 0; col < pdata->matrix_key_cols; col++) { + for (col = 0; col < keypad->matrix_key_cols; col++) { u32 bits_changed; int code; @@ -467,7 +369,7 @@ scan: if (bits_changed == 0) continue; - for (row = 0; row < pdata->matrix_key_rows; row++) { + for (row = 0; row < keypad->matrix_key_rows; row++) { if ((bits_changed & BIT(row)) == 0) continue; @@ -496,14 +398,16 @@ static inline int rotary_delta(u32 kprec) static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) { + struct pxa27x_keypad_rotary *encoder = &keypad->rotary[r]; struct input_dev *dev = keypad->input_dev; - if (delta == 0) + if (!encoder->enabled || delta == 0) return; - if (keypad->rotary_rel_code[r] == -1) { - int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1); - unsigned char keycode = keypad->keycodes[code]; + if (encoder->rel_code == -1) { + int idx = delta > 0 ? 0 : 1; + int code = MAX_MATRIX_KEY_NUM + 2 * r + idx; + unsigned char keycode = encoder->key_codes[idx]; /* simulate a press-n-release */ input_event(dev, EV_MSC, MSC_SCAN, code); @@ -513,30 +417,28 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) input_report_key(dev, keycode, 0); input_sync(dev); } else { - input_report_rel(dev, keypad->rotary_rel_code[r], delta); + input_report_rel(dev, encoder->rel_code, delta); input_sync(dev); } } static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; u32 kprec; + int i; /* read and reset to default count value */ kprec = keypad_readl(KPREC); keypad_writel(KPREC, DEFAULT_KPREC); - if (pdata->enable_rotary0) + for (i = 0; i < MAX_ROTARY_ENCODERS; i++) { report_rotary_event(keypad, 0, rotary_delta(kprec)); - - if (pdata->enable_rotary1) - report_rotary_event(keypad, 1, rotary_delta(kprec >> 16)); + kprec >>= 16; + } } static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; unsigned int new_state; u32 kpdk, bits_changed; @@ -544,14 +446,14 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) kpdk = keypad_readl(KPDK); - if (pdata->enable_rotary0 || pdata->enable_rotary1) + if (keypad->rotary[0].enabled || keypad->rotary[1].enabled) pxa27x_keypad_scan_rotary(keypad); /* * The KPDR_DK only output the key pin level, so it relates to board, * and low level may be active. */ - if (pdata->direct_key_low_active) + if (keypad->direct_key_low_active) new_state = ~KPDK_DK(kpdk) & keypad->direct_key_mask; else new_state = KPDK_DK(kpdk) & keypad->direct_key_mask; @@ -561,7 +463,7 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) if (bits_changed == 0) return; - for (i = 0; i < pdata->direct_key_num; i++) { + for (i = 0; i < keypad->direct_key_num; i++) { if (bits_changed & BIT(i)) { int code = MAX_MATRIX_KEY_NUM + i; @@ -574,21 +476,11 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) keypad->direct_key_state = new_state; } -static void clear_wakeup_event(struct pxa27x_keypad *keypad) -{ - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - - if (pdata->clear_wakeup_event) - (pdata->clear_wakeup_event)(); -} - static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) { struct pxa27x_keypad *keypad = dev_id; unsigned long kpc = keypad_readl(KPC); - clear_wakeup_event(keypad); - if (kpc & KPC_DI) pxa27x_keypad_scan_direct(keypad); @@ -600,7 +492,6 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; unsigned int mask = 0, direct_key_num = 0; unsigned long kpc = 0; @@ -608,35 +499,33 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) keypad_readl(KPC); /* enable matrix keys with automatic scan */ - if (pdata->matrix_key_rows && pdata->matrix_key_cols) { + if (keypad->matrix_key_rows && keypad->matrix_key_cols) { kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL; - kpc |= KPC_MKRN(pdata->matrix_key_rows) | - KPC_MKCN(pdata->matrix_key_cols); + kpc |= KPC_MKRN(keypad->matrix_key_rows) | + KPC_MKCN(keypad->matrix_key_cols); } /* enable rotary key, debounce interval same as direct keys */ - if (pdata->enable_rotary0) { + if (keypad->rotary[0].enabled) { mask |= 0x03; direct_key_num = 2; kpc |= KPC_REE0; } - if (pdata->enable_rotary1) { + if (keypad->rotary[1].enabled) { mask |= 0x0c; direct_key_num = 4; kpc |= KPC_REE1; } - if (pdata->direct_key_num > direct_key_num) - direct_key_num = pdata->direct_key_num; + if (keypad->direct_key_num > direct_key_num) + direct_key_num = keypad->direct_key_num; /* * Direct keys usage may not start from KP_DKIN0, check the platfrom * mask data to config the specific. */ - if (pdata->direct_key_mask) - keypad->direct_key_mask = pdata->direct_key_mask; - else + if (!keypad->direct_key_mask) keypad->direct_key_mask = GENMASK(direct_key_num - 1, 0) & ~mask; /* enable direct key */ @@ -645,7 +534,7 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB); keypad_writel(KPREC, DEFAULT_KPREC); - keypad_writel(KPKDI, pdata->debounce_interval); + keypad_writel(KPKDI, keypad->debounce_interval); } static int pxa27x_keypad_open(struct input_dev *dev) @@ -719,19 +608,12 @@ static int pxa27x_keypad_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops, pxa27x_keypad_suspend, pxa27x_keypad_resume); - static int pxa27x_keypad_probe(struct platform_device *pdev) { - const struct pxa27x_keypad_platform_data *pdata = - dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; struct pxa27x_keypad *keypad; struct input_dev *input_dev; - int irq, error; - - /* Driver need build keycode from device tree or pdata */ - if (!np && !pdata) - return -EINVAL; + int irq; + int error; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -746,7 +628,6 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) if (!input_dev) return -ENOMEM; - keypad->pdata = pdata; keypad->input_dev = input_dev; keypad->irq = irq; @@ -775,29 +656,12 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - if (pdata) { - error = pxa27x_keypad_build_keycode(keypad); - } else { - error = pxa27x_keypad_build_keycode_from_dt(keypad); - /* - * Data that we get from DT resides in dynamically - * allocated memory so we need to update our pdata - * pointer. - */ - pdata = keypad->pdata; - } + error = pxa27x_keypad_parse_properties(keypad); if (error) { - dev_err(&pdev->dev, "failed to build keycode\n"); + dev_err(&pdev->dev, "failed to parse keypad properties\n"); return error; } - keypad->row_shift = get_count_order(pdata->matrix_key_cols); - - if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || - (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { - input_dev->evbit[0] |= BIT_MASK(EV_REL); - } - error = devm_request_irq(&pdev->dev, irq, pxa27x_keypad_irq_handler, 0, pdev->name, keypad); if (error) { diff --git a/include/linux/platform_data/keypad-pxa27x.h b/include/linux/platform_data/keypad-pxa27x.h deleted file mode 100644 index a376442b9935..000000000000 --- a/include/linux/platform_data/keypad-pxa27x.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_ARCH_PXA27x_KEYPAD_H -#define __ASM_ARCH_PXA27x_KEYPAD_H - -#include -#include - -#define MAX_MATRIX_KEY_ROWS (8) -#define MAX_MATRIX_KEY_COLS (8) -#define MATRIX_ROW_SHIFT (3) -#define MAX_DIRECT_KEY_NUM (8) - -/* pxa3xx keypad platform specific parameters - * - * NOTE: - * 1. direct_key_num indicates the number of keys in the direct keypad - * _plus_ the number of rotary-encoder sensor inputs, this can be - * left as 0 if only rotary encoders are enabled, the driver will - * automatically calculate this - * - * 2. direct_key_map is the key code map for the direct keys, if rotary - * encoder(s) are enabled, direct key 0/1(2/3) will be ignored - * - * 3. rotary can be either interpreted as a relative input event (e.g. - * REL_WHEEL/REL_HWHEEL) or specific keys (e.g. UP/DOWN/LEFT/RIGHT) - * - * 4. matrix key and direct key will use the same debounce_interval by - * default, which should be sufficient in most cases - * - * pxa168 keypad platform specific parameter - * - * NOTE: - * clear_wakeup_event callback is a workaround required to clear the - * keypad interrupt. The keypad wake must be cleared in addition to - * reading the MI/DI bits in the KPC register. - */ -struct pxa27x_keypad_platform_data { - - /* code map for the matrix keys */ - const struct matrix_keymap_data *matrix_keymap_data; - unsigned int matrix_key_rows; - unsigned int matrix_key_cols; - - /* direct keys */ - int direct_key_num; - unsigned int direct_key_map[MAX_DIRECT_KEY_NUM]; - /* the key output may be low active */ - int direct_key_low_active; - /* give board a chance to choose the start direct key */ - unsigned int direct_key_mask; - - /* rotary encoders 0 */ - int enable_rotary0; - int rotary0_rel_code; - int rotary0_up_key; - int rotary0_down_key; - - /* rotary encoders 1 */ - int enable_rotary1; - int rotary1_rel_code; - int rotary1_up_key; - int rotary1_down_key; - - /* key debounce interval */ - unsigned int debounce_interval; - - /* clear wakeup event requirement for pxa168 */ - void (*clear_wakeup_event)(void); -}; - -extern void pxa_set_keypad_info(struct pxa27x_keypad_platform_data *info); - -#endif /* __ASM_ARCH_PXA27x_KEYPAD_H */ -- cgit v1.2.3 From 890ba82a60cb7a6584968a4ac15546f434afa40b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 17 Aug 2025 14:39:06 -0700 Subject: Input: spear-keyboard - drop support for platform data There are no in-kernel users of spear kbd_platform_data in the kernel, and the driver supports configuration via device tree, so drop support of static platform data and move properties parsing from OF-specific methods to generic ones. Link: https://lore.kernel.org/r/vppjxui76im26uamznx7evm5lmbe3d6v3oxsa7mqyytykh4zm6@nhlf33v3hp6g Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/spear-keyboard.c | 71 +++--------- include/linux/platform_data/keyboard-spear.h | 164 --------------------------- 2 files changed, 15 insertions(+), 220 deletions(-) delete mode 100644 include/linux/platform_data/keyboard-spear.h (limited to 'include/linux/platform_data') diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 2fae337562a2..53f3ac64c980 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include /* Keyboard Registers */ #define MODE_CTL_REG 0x00 @@ -56,13 +56,12 @@ struct spear_kbd { void __iomem *io_base; struct clk *clk; unsigned int irq; - unsigned int mode; - unsigned int suspended_rate; + u32 mode; + u32 suspended_rate; + u32 mode_ctl_reg; unsigned short last_key; unsigned short keycodes[NUM_ROWS * NUM_COLS]; - bool rep; bool irq_wake_enabled; - u32 mode_ctl_reg; }; static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) @@ -143,46 +142,8 @@ static void spear_kbd_close(struct input_dev *dev) kbd->last_key = KEY_RESERVED; } -#ifdef CONFIG_OF -static int spear_kbd_parse_dt(struct platform_device *pdev, - struct spear_kbd *kbd) -{ - struct device_node *np = pdev->dev.of_node; - int error; - u32 val, suspended_rate; - - if (!np) { - dev_err(&pdev->dev, "Missing DT data\n"); - return -EINVAL; - } - - if (of_property_read_bool(np, "autorepeat")) - kbd->rep = true; - - if (of_property_read_u32(np, "suspended_rate", &suspended_rate)) - kbd->suspended_rate = suspended_rate; - - error = of_property_read_u32(np, "st,mode", &val); - if (error) { - dev_err(&pdev->dev, "DT: Invalid or missing mode\n"); - return error; - } - - kbd->mode = val; - return 0; -} -#else -static inline int spear_kbd_parse_dt(struct platform_device *pdev, - struct spear_kbd *kbd) -{ - return -ENOSYS; -} -#endif - static int spear_kbd_probe(struct platform_device *pdev) { - struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev); - const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL; struct spear_kbd *kbd; struct input_dev *input_dev; int irq; @@ -198,6 +159,14 @@ static int spear_kbd_probe(struct platform_device *pdev) return -ENOMEM; } + error = device_property_read_u32(&pdev->dev, "st,mode", &kbd->mode); + if (error) { + dev_err(&pdev->dev, "Invalid or missing mode\n"); + return error; + } + + device_property_read_u32(&pdev->dev, "suspended_rate", &kbd->suspended_rate); + input_dev = devm_input_allocate_device(&pdev->dev); if (!input_dev) { dev_err(&pdev->dev, "unable to allocate input device\n"); @@ -207,16 +176,6 @@ static int spear_kbd_probe(struct platform_device *pdev) kbd->input = input_dev; kbd->irq = irq; - if (!pdata) { - error = spear_kbd_parse_dt(pdev, kbd); - if (error) - return error; - } else { - kbd->mode = pdata->mode; - kbd->rep = pdata->rep; - kbd->suspended_rate = pdata->suspended_rate; - } - kbd->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(kbd->io_base)) return PTR_ERR(kbd->io_base); @@ -234,21 +193,21 @@ static int spear_kbd_probe(struct platform_device *pdev) input_dev->open = spear_kbd_open; input_dev->close = spear_kbd_close; - error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS, + error = matrix_keypad_build_keymap(NULL, NULL, NUM_ROWS, NUM_COLS, kbd->keycodes, input_dev); if (error) { dev_err(&pdev->dev, "Failed to build keymap\n"); return error; } - if (kbd->rep) + if (device_property_read_bool(&pdev->dev, "autorepeat")) __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, kbd); error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0, - "keyboard", kbd); + "keyboard", kbd); if (error) { dev_err(&pdev->dev, "request_irq failed\n"); return error; diff --git a/include/linux/platform_data/keyboard-spear.h b/include/linux/platform_data/keyboard-spear.h deleted file mode 100644 index 5e3ff653900c..000000000000 --- a/include/linux/platform_data/keyboard-spear.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2010 ST Microelectronics - * Rajeev Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __PLAT_KEYBOARD_H -#define __PLAT_KEYBOARD_H - -#include -#include -#include -#include - -#define DECLARE_9x9_KEYMAP(_name) \ -int _name[] = { \ - KEY(0, 0, KEY_ESC), \ - KEY(0, 1, KEY_1), \ - KEY(0, 2, KEY_2), \ - KEY(0, 3, KEY_3), \ - KEY(0, 4, KEY_4), \ - KEY(0, 5, KEY_5), \ - KEY(0, 6, KEY_6), \ - KEY(0, 7, KEY_7), \ - KEY(0, 8, KEY_8), \ - KEY(1, 0, KEY_9), \ - KEY(1, 1, KEY_MINUS), \ - KEY(1, 2, KEY_EQUAL), \ - KEY(1, 3, KEY_BACKSPACE), \ - KEY(1, 4, KEY_TAB), \ - KEY(1, 5, KEY_Q), \ - KEY(1, 6, KEY_W), \ - KEY(1, 7, KEY_E), \ - KEY(1, 8, KEY_R), \ - KEY(2, 0, KEY_T), \ - KEY(2, 1, KEY_Y), \ - KEY(2, 2, KEY_U), \ - KEY(2, 3, KEY_I), \ - KEY(2, 4, KEY_O), \ - KEY(2, 5, KEY_P), \ - KEY(2, 6, KEY_LEFTBRACE), \ - KEY(2, 7, KEY_RIGHTBRACE), \ - KEY(2, 8, KEY_ENTER), \ - KEY(3, 0, KEY_LEFTCTRL), \ - KEY(3, 1, KEY_A), \ - KEY(3, 2, KEY_S), \ - KEY(3, 3, KEY_D), \ - KEY(3, 4, KEY_F), \ - KEY(3, 5, KEY_G), \ - KEY(3, 6, KEY_H), \ - KEY(3, 7, KEY_J), \ - KEY(3, 8, KEY_K), \ - KEY(4, 0, KEY_L), \ - KEY(4, 1, KEY_SEMICOLON), \ - KEY(4, 2, KEY_APOSTROPHE), \ - KEY(4, 3, KEY_GRAVE), \ - KEY(4, 4, KEY_LEFTSHIFT), \ - KEY(4, 5, KEY_BACKSLASH), \ - KEY(4, 6, KEY_Z), \ - KEY(4, 7, KEY_X), \ - KEY(4, 8, KEY_C), \ - KEY(5, 0, KEY_V), \ - KEY(5, 1, KEY_B), \ - KEY(5, 2, KEY_N), \ - KEY(5, 3, KEY_M), \ - KEY(5, 4, KEY_COMMA), \ - KEY(5, 5, KEY_DOT), \ - KEY(5, 6, KEY_SLASH), \ - KEY(5, 7, KEY_RIGHTSHIFT), \ - KEY(5, 8, KEY_KPASTERISK), \ - KEY(6, 0, KEY_LEFTALT), \ - KEY(6, 1, KEY_SPACE), \ - KEY(6, 2, KEY_CAPSLOCK), \ - KEY(6, 3, KEY_F1), \ - KEY(6, 4, KEY_F2), \ - KEY(6, 5, KEY_F3), \ - KEY(6, 6, KEY_F4), \ - KEY(6, 7, KEY_F5), \ - KEY(6, 8, KEY_F6), \ - KEY(7, 0, KEY_F7), \ - KEY(7, 1, KEY_F8), \ - KEY(7, 2, KEY_F9), \ - KEY(7, 3, KEY_F10), \ - KEY(7, 4, KEY_NUMLOCK), \ - KEY(7, 5, KEY_SCROLLLOCK), \ - KEY(7, 6, KEY_KP7), \ - KEY(7, 7, KEY_KP8), \ - KEY(7, 8, KEY_KP9), \ - KEY(8, 0, KEY_KPMINUS), \ - KEY(8, 1, KEY_KP4), \ - KEY(8, 2, KEY_KP5), \ - KEY(8, 3, KEY_KP6), \ - KEY(8, 4, KEY_KPPLUS), \ - KEY(8, 5, KEY_KP1), \ - KEY(8, 6, KEY_KP2), \ - KEY(8, 7, KEY_KP3), \ - KEY(8, 8, KEY_KP0), \ -} - -#define DECLARE_6x6_KEYMAP(_name) \ -int _name[] = { \ - KEY(0, 0, KEY_RESERVED), \ - KEY(0, 1, KEY_1), \ - KEY(0, 2, KEY_2), \ - KEY(0, 3, KEY_3), \ - KEY(0, 4, KEY_4), \ - KEY(0, 5, KEY_5), \ - KEY(1, 0, KEY_Q), \ - KEY(1, 1, KEY_W), \ - KEY(1, 2, KEY_E), \ - KEY(1, 3, KEY_R), \ - KEY(1, 4, KEY_T), \ - KEY(1, 5, KEY_Y), \ - KEY(2, 0, KEY_D), \ - KEY(2, 1, KEY_F), \ - KEY(2, 2, KEY_G), \ - KEY(2, 3, KEY_H), \ - KEY(2, 4, KEY_J), \ - KEY(2, 5, KEY_K), \ - KEY(3, 0, KEY_B), \ - KEY(3, 1, KEY_N), \ - KEY(3, 2, KEY_M), \ - KEY(3, 3, KEY_COMMA), \ - KEY(3, 4, KEY_DOT), \ - KEY(3, 5, KEY_SLASH), \ - KEY(4, 0, KEY_F6), \ - KEY(4, 1, KEY_F7), \ - KEY(4, 2, KEY_F8), \ - KEY(4, 3, KEY_F9), \ - KEY(4, 4, KEY_F10), \ - KEY(4, 5, KEY_NUMLOCK), \ - KEY(5, 0, KEY_KP2), \ - KEY(5, 1, KEY_KP3), \ - KEY(5, 2, KEY_KP0), \ - KEY(5, 3, KEY_KPDOT), \ - KEY(5, 4, KEY_RO), \ - KEY(5, 5, KEY_ZENKAKUHANKAKU), \ -} - -#define KEYPAD_9x9 0 -#define KEYPAD_6x6 1 -#define KEYPAD_2x2 2 - -/** - * struct kbd_platform_data - spear keyboard platform data - * keymap: pointer to keymap data (table and size) - * rep: enables key autorepeat - * mode: choose keyboard support(9x9, 6x6, 2x2) - * suspended_rate: rate at which keyboard would operate in suspended mode - * - * This structure is supposed to be used by platform code to supply - * keymaps to drivers that implement keyboards. - */ -struct kbd_platform_data { - const struct matrix_keymap_data *keymap; - bool rep; - unsigned int mode; - unsigned int suspended_rate; -}; - -#endif /* __PLAT_KEYBOARD_H */ -- cgit v1.2.3 From 1dfdf4527fd391f653c53b634af9122613c58904 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 30 Aug 2025 18:48:32 +0200 Subject: iio: adc: exynos_adc: Drop platform data support There are no Samsung Exynos SoC ADC driver users which bind via platform ID, thus platform data is never set and can be dropped. Reviewed-by: Andy Shevchenko Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250830-s3c-cleanup-adc-v2-3-4f8299343d32@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/exynos_adc.c | 11 +---------- include/linux/platform_data/touchscreen-s3c2410.h | 22 ---------------------- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 include/linux/platform_data/touchscreen-s3c2410.h (limited to 'include/linux/platform_data') diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index dd4b9921a8b5..1484adff00df 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -29,8 +29,6 @@ #include #include -#include - /* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */ #define ADC_V1_CON(x) ((x) + 0x00) #define ADC_V1_DLY(x) ((x) + 0x08) @@ -106,7 +104,6 @@ struct exynos_adc { struct clk *clk; struct clk *sclk; unsigned int irq; - unsigned int delay; struct regulator *vdd; struct completion completion; @@ -213,7 +210,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info) writel(con1, ADC_V1_CON(info->regs)); /* set touchscreen delay */ - writel(info->delay, ADC_V1_DLY(info->regs)); + writel(10000, ADC_V1_DLY(info->regs)); } static void exynos_adc_v1_exit_hw(struct exynos_adc *info) @@ -556,7 +553,6 @@ static int exynos_adc_probe(struct platform_device *pdev) { struct exynos_adc *info = NULL; struct device_node *np = pdev->dev.of_node; - struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = NULL; int ret; int irq; @@ -655,11 +651,6 @@ static int exynos_adc_probe(struct platform_device *pdev) if (info->data->init_hw) info->data->init_hw(info); - if (pdata) - info->delay = pdata->delay; - else - info->delay = 10000; - ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev); if (ret < 0) { dev_err(&pdev->dev, "failed adding child nodes\n"); diff --git a/include/linux/platform_data/touchscreen-s3c2410.h b/include/linux/platform_data/touchscreen-s3c2410.h deleted file mode 100644 index bf8d3b9d7c6a..000000000000 --- a/include/linux/platform_data/touchscreen-s3c2410.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2005 Arnaud Patard -*/ - -#ifndef __TOUCHSCREEN_S3C2410_H -#define __TOUCHSCREEN_S3C2410_H - -struct s3c2410_ts_mach_info { - int delay; - int presc; - int oversampling_shift; - void (*cfg_gpio)(struct platform_device *dev); -}; - -extern void s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *); -extern void s3c64xx_ts_set_platdata(struct s3c2410_ts_mach_info *); - -/* defined by architecture to configure gpio */ -extern void s3c24xx_ts_cfg_gpio(struct platform_device *dev); - -#endif /*__TOUCHSCREEN_S3C2410_H */ -- cgit v1.2.3 From e19ceeb1c0f63e3e15b197c5f34797134b51ba0e Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Thu, 28 Aug 2025 08:35:58 +0000 Subject: platform/chrome: Centralize common cros_ec_device initialization Move the common initialization from protocol device drivers into central cros_ec_device_alloc(). This removes duplicated code from each driver's probe function. The buffer sizes are now calculated once, using the maximum possible overhead required by any of the transport protocols, ensuring the allocated buffers are sufficient for all cases. Link: https://lore.kernel.org/r/20250828083601.856083-3-tzungbi@kernel.org Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec.c | 9 +++++++++ drivers/platform/chrome/cros_ec_i2c.c | 5 ----- drivers/platform/chrome/cros_ec_ishtp.c | 4 ---- drivers/platform/chrome/cros_ec_lpc.c | 4 ---- drivers/platform/chrome/cros_ec_rpmsg.c | 4 ---- drivers/platform/chrome/cros_ec_spi.c | 5 ----- drivers/platform/chrome/cros_ec_uart.c | 4 ---- include/linux/platform_data/cros_ec_proto.h | 14 ++++++++++---- 8 files changed, 19 insertions(+), 30 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index 25283a148ab9..da049068b6e9 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -38,6 +38,15 @@ struct cros_ec_device *cros_ec_device_alloc(struct device *dev) if (!ec_dev) return NULL; + ec_dev->din_size = sizeof(struct ec_host_response) + + sizeof(struct ec_response_get_protocol_info) + + EC_MAX_RESPONSE_OVERHEAD; + ec_dev->dout_size = sizeof(struct ec_host_request) + + sizeof(struct ec_params_rwsig_action) + + EC_MAX_REQUEST_OVERHEAD; + + ec_dev->dev = dev; + return ec_dev; } EXPORT_SYMBOL(cros_ec_device_alloc); diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index ee3c5130ec3f..def1144a077e 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -297,16 +297,11 @@ static int cros_ec_i2c_probe(struct i2c_client *client) return -ENOMEM; i2c_set_clientdata(client, ec_dev); - ec_dev->dev = dev; ec_dev->priv = client; ec_dev->irq = client->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c; ec_dev->phys_name = client->adapter->name; - ec_dev->din_size = sizeof(struct ec_host_response_i2c) + - sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request_i2c) + - sizeof(struct ec_params_rwsig_action); err = cros_ec_register(ec_dev); if (err) { diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c index c102a796670c..4e74e702c5a2 100644 --- a/drivers/platform/chrome/cros_ec_ishtp.c +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -550,14 +550,10 @@ static int cros_ec_dev_init(struct ishtp_cl_data *client_data) client_data->ec_dev = ec_dev; dev->driver_data = ec_dev; - ec_dev->dev = dev; ec_dev->priv = client_data->cros_ish_cl; ec_dev->cmd_xfer = NULL; ec_dev->pkt_xfer = cros_ec_pkt_xfer_ish; ec_dev->phys_name = dev_name(dev); - ec_dev->din_size = sizeof(struct cros_ish_in_msg) + - sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct cros_ish_out_msg) + sizeof(struct ec_params_rwsig_action); return cros_ec_register(ec_dev); } diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 30fa89b81666..78cfff80cdea 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -642,14 +642,10 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, ec_dev); - ec_dev->dev = dev; ec_dev->phys_name = dev_name(dev); ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc; ec_dev->cmd_readmem = cros_ec_lpc_readmem; - ec_dev->din_size = sizeof(struct ec_host_response) + - sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); ec_dev->priv = ec_lpc; /* diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c index 9ac2b923db6d..09bd9e49464e 100644 --- a/drivers/platform/chrome/cros_ec_rpmsg.c +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -224,14 +224,10 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev) if (!ec_rpmsg) return -ENOMEM; - ec_dev->dev = dev; ec_dev->priv = ec_rpmsg; ec_dev->cmd_xfer = cros_ec_cmd_xfer_rpmsg; ec_dev->pkt_xfer = cros_ec_pkt_xfer_rpmsg; ec_dev->phys_name = dev_name(&rpdev->dev); - ec_dev->din_size = sizeof(struct ec_host_response) + - sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); dev_set_drvdata(dev, ec_dev); ec_rpmsg->rpdev = rpdev; diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index c778300a4145..28fa82f8cb07 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -757,16 +757,11 @@ static int cros_ec_spi_probe(struct spi_device *spi) cros_ec_spi_dt_probe(ec_spi, dev); spi_set_drvdata(spi, ec_dev); - ec_dev->dev = dev; ec_dev->priv = ec_spi; ec_dev->irq = spi->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); - ec_dev->din_size = EC_MSG_PREAMBLE_COUNT + - sizeof(struct ec_host_response) + - sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); ec_spi->last_transfer_ns = ktime_get_ns(); diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c index 1a7511b1bbe3..d5b37414ff12 100644 --- a/drivers/platform/chrome/cros_ec_uart.c +++ b/drivers/platform/chrome/cros_ec_uart.c @@ -276,14 +276,10 @@ static int cros_ec_uart_probe(struct serdev_device *serdev) /* Initialize ec_dev for cros_ec */ ec_dev->phys_name = dev_name(dev); - ec_dev->dev = dev; ec_dev->priv = ec_uart; ec_dev->irq = ec_uart->irq; ec_dev->cmd_xfer = NULL; ec_dev->pkt_xfer = cros_ec_uart_pkt_xfer; - ec_dev->din_size = sizeof(struct ec_host_response) + - sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action); serdev_device_set_client_ops(serdev, &cros_ec_uart_client_ops); diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h index 3ec24f445c29..4d96cffbb9e3 100644 --- a/include/linux/platform_data/cros_ec_proto.h +++ b/include/linux/platform_data/cros_ec_proto.h @@ -33,12 +33,18 @@ /* * Max bus-specific overhead incurred by request/responses. - * I2C requires 1 additional byte for requests. - * I2C requires 2 additional bytes for responses. - * SPI requires up to 32 additional bytes for responses. + * + * Request: + * - I2C requires 1 byte (see struct ec_host_request_i2c). + * - ISHTP requires 4 bytes (see struct cros_ish_out_msg). + * + * Response: + * - I2C requires 2 bytes (see struct ec_host_response_i2c). + * - ISHTP requires 4 bytes (see struct cros_ish_in_msg). + * - SPI requires 32 bytes (see EC_MSG_PREAMBLE_COUNT). */ #define EC_PROTO_VERSION_UNKNOWN 0 -#define EC_MAX_REQUEST_OVERHEAD 1 +#define EC_MAX_REQUEST_OVERHEAD 4 #define EC_MAX_RESPONSE_OVERHEAD 32 /* -- cgit v1.2.3 From 56cb557279d70397cefb497e0f06bdd6fd685f8e Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Thu, 28 Aug 2025 08:36:00 +0000 Subject: platform/chrome: cros_ec: Add a flag to track registration state Introduce a `registered` flag to the `struct cros_ec_device` to allow callers to determine if the device has been fully registered and is ready for use. This is a preparatory step to prevent race conditions where other drivers might try to access the device before it is fully registered or after it has been unregistered. Link: https://lore.kernel.org/r/20250828083601.856083-5-tzungbi@kernel.org Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec.c | 7 +++++++ drivers/platform/chrome/cros_ec_proto.c | 15 +++++++++++++++ include/linux/platform_data/cros_ec_proto.h | 4 ++++ 3 files changed, 26 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index 61bcef8741db..1da79e3d215b 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -9,6 +9,7 @@ * battery charging and regulator control, firmware update. */ +#include #include #include #include @@ -316,6 +317,9 @@ int cros_ec_register(struct cros_ec_device *ec_dev) goto exit; } + scoped_guard(mutex, &ec_dev->lock) + ec_dev->registered = true; + dev_info(dev, "Chrome EC device registered\n"); /* @@ -343,6 +347,9 @@ EXPORT_SYMBOL(cros_ec_register); */ void cros_ec_unregister(struct cros_ec_device *ec_dev) { + scoped_guard(mutex, &ec_dev->lock) + ec_dev->registered = false; + if (ec_dev->mkbp_event_supported) blocking_notifier_chain_unregister(&ec_dev->event_notifier, &ec_dev->notifier_ready); diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 3e94a0a82173..1d8d9168ec1a 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -3,6 +3,7 @@ // // Copyright (C) 2015 Google, Inc +#include #include #include #include @@ -1153,5 +1154,19 @@ int cros_ec_get_cmd_versions(struct cros_ec_device *ec_dev, u16 cmd) } EXPORT_SYMBOL_GPL(cros_ec_get_cmd_versions); +/** + * cros_ec_device_registered - Return if the ec_dev is registered. + * + * @ec_dev: EC device + * + * Return: true if registered. Otherwise, false. + */ +bool cros_ec_device_registered(struct cros_ec_device *ec_dev) +{ + guard(mutex)(&ec_dev->lock); + return ec_dev->registered; +} +EXPORT_SYMBOL_GPL(cros_ec_device_registered); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ChromeOS EC communication protocol helpers"); diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h index 4d96cffbb9e3..de14923720a5 100644 --- a/include/linux/platform_data/cros_ec_proto.h +++ b/include/linux/platform_data/cros_ec_proto.h @@ -128,6 +128,7 @@ struct cros_ec_command { * @dout_size: Size of dout buffer to allocate (zero to use static dout). * @wake_enabled: True if this device can wake the system from sleep. * @suspended: True if this device had been suspended. + * @registered: True if this device had been registered. * @cmd_xfer: Send command to EC and get response. * Returns the number of bytes received if the communication * succeeded, but that doesn't mean the EC was happy with the @@ -186,6 +187,7 @@ struct cros_ec_device { int dout_size; bool wake_enabled; bool suspended; + bool registered; int (*cmd_xfer)(struct cros_ec_device *ec, struct cros_ec_command *msg); int (*pkt_xfer)(struct cros_ec_device *ec, @@ -278,6 +280,8 @@ int cros_ec_cmd_readmem(struct cros_ec_device *ec_dev, u8 offset, u8 size, void int cros_ec_get_cmd_versions(struct cros_ec_device *ec_dev, u16 cmd); +bool cros_ec_device_registered(struct cros_ec_device *ec_dev); + /** * cros_ec_get_time_ns() - Return time in ns. * -- cgit v1.2.3 From 0494fc345b377d1207c2cbfef67dc51f6ec874c0 Mon Sep 17 00:00:00 2001 From: Gokul Praveen Date: Tue, 12 Aug 2025 16:23:46 +0530 Subject: clocksource/drivers/timer-ti-dm : Capture functionality for OMAP DM timer Add PWM capture function in DM timer driver. OMAP DM timer hardware supports capture feature.It can be used to timestamp events (falling/rising edges) detected on input signal. Signed-off-by: Gokul Praveen Signed-off-by: Daniel Lezcano Reviewed-by: Neha Malcom Francis Link: https://lore.kernel.org/r/20250812105346.203541-1-g-praveen@ti.com --- drivers/clocksource/timer-ti-dm.c | 119 ++++++++++++++++++++++++++++- include/linux/platform_data/dmtimer-omap.h | 4 + 2 files changed, 121 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index e9e32df6b566..793e7cdcb1b1 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -31,6 +31,7 @@ #include #include +#include /* * timer errata flags @@ -836,6 +837,48 @@ static int omap_dm_timer_set_match(struct omap_dm_timer *cookie, int enable, return 0; } +static int omap_dm_timer_set_cap(struct omap_dm_timer *cookie, + int autoreload, bool config_period) +{ + struct dmtimer *timer; + struct device *dev; + int rc; + u32 l; + + timer = to_dmtimer(cookie); + if (unlikely(!timer)) + return -EINVAL; + + dev = &timer->pdev->dev; + rc = pm_runtime_resume_and_get(dev); + if (rc) + return rc; + /* + * 1. Select autoreload mode. TIMER_TCLR[1] AR bit. + * 2. TIMER_TCLR[14]: Sets the functionality of the TIMER IO pin. + * 3. TIMER_TCLR[13] : Capture mode select bit. + * 3. TIMER_TCLR[9-8] : Select transition capture mode. + */ + + l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); + + if (autoreload) + l |= OMAP_TIMER_CTRL_AR; + + l |= OMAP_TIMER_CTRL_CAPTMODE | OMAP_TIMER_CTRL_GPOCFG; + + if (config_period == true) + l |= OMAP_TIMER_CTRL_TCM_LOWTOHIGH; /* Time Period config */ + else + l |= OMAP_TIMER_CTRL_TCM_BOTHEDGES; /* Duty Cycle config */ + + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); + + pm_runtime_put_sync(dev); + + return 0; +} + static int omap_dm_timer_set_pwm(struct omap_dm_timer *cookie, int def_on, int toggle, int trigger, int autoreload) { @@ -1023,23 +1066,92 @@ static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *cookie) return __omap_dm_timer_read_counter(timer); } +static inline unsigned int __omap_dm_timer_cap(struct dmtimer *timer, int idx) +{ + return idx == 0 ? dmtimer_read(timer, OMAP_TIMER_CAPTURE_REG) : + dmtimer_read(timer, OMAP_TIMER_CAPTURE2_REG); +} + static int omap_dm_timer_write_counter(struct omap_dm_timer *cookie, unsigned int value) { struct dmtimer *timer; + struct device *dev; timer = to_dmtimer(cookie); - if (unlikely(!timer || !atomic_read(&timer->enabled))) { - pr_err("%s: timer not available or enabled.\n", __func__); + if (unlikely(!timer)) { + pr_err("%s: timer not available.\n", __func__); return -EINVAL; } + dev = &timer->pdev->dev; + + pm_runtime_resume_and_get(dev); dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, value); + pm_runtime_put_sync(dev); /* Save the context */ timer->context.tcrr = value; return 0; } +/** + * omap_dm_timer_cap_counter() - Calculate the high count or period count depending on the + * configuration. + * @cookie:Pointer to OMAP DM timer + * @is_period:Whether to configure timer in period or duty cycle mode + * + * Return high count or period count if timer is enabled else appropriate error. + */ +static unsigned int omap_dm_timer_cap_counter(struct omap_dm_timer *cookie, bool is_period) +{ + struct dmtimer *timer; + unsigned int cap1 = 0; + unsigned int cap2 = 0; + u32 l, ret; + + timer = to_dmtimer(cookie); + if (unlikely(!timer || !atomic_read(&timer->enabled))) { + pr_err("%s:timer is not available or enabled.%p\n", __func__, (void *)timer); + return -EINVAL; + } + + /* Stop the timer */ + omap_dm_timer_stop(cookie); + + /* Clear the timer counter value to 0 */ + ret = omap_dm_timer_write_counter(cookie, 0); + if (ret) + return ret; + + /* Sets the timer capture configuration for period/duty cycle calculation */ + ret = omap_dm_timer_set_cap(cookie, true, is_period); + if (ret) { + pr_err("%s: Failed to set timer capture configuration.\n", __func__); + return ret; + } + /* Start the timer */ + omap_dm_timer_start(cookie); + + /* + * 1 sec delay is given so as to provide + * enough time to capture low frequency signals. + */ + msleep(1000); + + cap1 = __omap_dm_timer_cap(timer, 0); + cap2 = __omap_dm_timer_cap(timer, 1); + + /* + * Clears the TCLR configuration. + * The start bit must be set to 1 as the timer is already in start mode. + */ + l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); + l &= ~(0xffff) | 0x1; + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); + + return (cap2-cap1); +} + static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev) { struct dmtimer *timer = dev_get_drvdata(dev); @@ -1246,6 +1358,9 @@ static const struct omap_dm_timer_ops dmtimer_ops = { .write_counter = omap_dm_timer_write_counter, .read_status = omap_dm_timer_read_status, .write_status = omap_dm_timer_write_status, + .set_cap = omap_dm_timer_set_cap, + .get_cap_status = omap_dm_timer_get_pwm_status, + .read_cap = omap_dm_timer_cap_counter, }; static const struct dmtimer_platform_data omap3plus_pdata = { diff --git a/include/linux/platform_data/dmtimer-omap.h b/include/linux/platform_data/dmtimer-omap.h index 95d852aef130..726d89143842 100644 --- a/include/linux/platform_data/dmtimer-omap.h +++ b/include/linux/platform_data/dmtimer-omap.h @@ -36,9 +36,13 @@ struct omap_dm_timer_ops { int (*set_pwm)(struct omap_dm_timer *timer, int def_on, int toggle, int trigger, int autoreload); int (*get_pwm_status)(struct omap_dm_timer *timer); + int (*set_cap)(struct omap_dm_timer *timer, + int autoreload, bool config_period); + int (*get_cap_status)(struct omap_dm_timer *timer); int (*set_prescaler)(struct omap_dm_timer *timer, int prescaler); unsigned int (*read_counter)(struct omap_dm_timer *timer); + unsigned int (*read_cap)(struct omap_dm_timer *timer, bool is_period); int (*write_counter)(struct omap_dm_timer *timer, unsigned int value); unsigned int (*read_status)(struct omap_dm_timer *timer); -- cgit v1.2.3 From 60ac65a31041b0e5dfd736a79027314b9d533ef5 Mon Sep 17 00:00:00 2001 From: Sung-Chi Li Date: Thu, 11 Sep 2025 06:56:34 +0000 Subject: platform/chrome: update pwm fan control host commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update cros_ec_commands.h to include definitions for getting PWM fan duty, getting and setting the fan control mode. Signed-off-by: Sung-Chi Li Acked-by: Tzung-Bi Shih Reviewed-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20250911-cros_ec_fan-v6-1-a1446cc098af@google.com Signed-off-by: Guenter Roeck --- include/linux/platform_data/cros_ec_commands.h | 29 +++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index c19b404e3d8d..69294f79cc88 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -1825,6 +1825,16 @@ struct ec_response_pwm_get_duty { uint16_t duty; /* Duty cycle, EC_PWM_MAX_DUTY = 100% */ } __ec_align2; +#define EC_CMD_PWM_GET_FAN_DUTY 0x0027 + +struct ec_params_pwm_get_fan_duty { + uint8_t fan_idx; +} __ec_align1; + +struct ec_response_pwm_get_fan_duty { + uint32_t percent; /* Percentage of duty cycle, ranging from 0 ~ 100 */ +} __ec_align4; + /*****************************************************************************/ /* * Lightbar commands. This looks worse than it is. Since we only use one HOST @@ -3127,14 +3137,31 @@ struct ec_params_thermal_set_threshold_v1 { /****************************************************************************/ -/* Toggle automatic fan control */ +/* Set or get fan control mode */ #define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x0052 +enum ec_auto_fan_ctrl_cmd { + EC_AUTO_FAN_CONTROL_CMD_SET = 0, + EC_AUTO_FAN_CONTROL_CMD_GET, +}; + /* Version 1 of input params */ struct ec_params_auto_fan_ctrl_v1 { uint8_t fan_idx; } __ec_align1; +/* Version 2 of input params */ +struct ec_params_auto_fan_ctrl_v2 { + uint8_t fan_idx; + uint8_t cmd; /* enum ec_auto_fan_ctrl_cmd */ + uint8_t set_auto; /* only used with EC_AUTO_FAN_CONTROL_CMD_SET - bool + */ +} __ec_align4; + +struct ec_response_auto_fan_control { + uint8_t is_auto; /* bool */ +} __ec_align1; + /* Get/Set TMP006 calibration data */ #define EC_CMD_TMP006_GET_CALIBRATION 0x0053 #define EC_CMD_TMP006_SET_CALIBRATION 0x0054 -- cgit v1.2.3 From 378e6523ebb1e80b3955b7675cfe40b07028d085 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 14 Oct 2025 08:02:47 +0200 Subject: net: bcmgenet: remove unused platform code This effectively reverts b0ba512e25d7 ("net: bcmgenet: enable driver to work without a device tree"). There has never been an in-tree user of struct bcmgenet_platform_data, all devices use OF or ACPI. Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/108b4e64-55d4-4b4e-9a11-3c810c319d66@gmail.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 - drivers/net/ethernet/broadcom/genet/bcmgenet.c | 20 +++---- drivers/net/ethernet/broadcom/genet/bcmmii.c | 75 +------------------------- include/linux/platform_data/bcmgenet.h | 19 ------- 4 files changed, 7 insertions(+), 108 deletions(-) delete mode 100644 include/linux/platform_data/bcmgenet.h (limited to 'include/linux/platform_data') diff --git a/MAINTAINERS b/MAINTAINERS index 3a27901781c2..4c4b519171f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5123,7 +5123,6 @@ F: Documentation/devicetree/bindings/net/brcm,unimac-mdio.yaml F: drivers/net/ethernet/broadcom/genet/ F: drivers/net/ethernet/broadcom/unimac.h F: drivers/net/mdio/mdio-bcm-unimac.c -F: include/linux/platform_data/bcmgenet.h F: include/linux/platform_data/mdio-bcm-unimac.h BROADCOM IPROC ARM ARCHITECTURE diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 98971ae4f87d..d99ef92feb82 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -35,7 +35,6 @@ #include #include #include -#include #include @@ -3926,7 +3925,6 @@ MODULE_DEVICE_TABLE(of, bcmgenet_match); static int bcmgenet_probe(struct platform_device *pdev) { - struct bcmgenet_platform_data *pd = pdev->dev.platform_data; const struct bcmgenet_plat_data *pdata; struct bcmgenet_priv *priv; struct net_device *dev; @@ -4010,9 +4008,6 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->version = pdata->version; priv->dma_max_burst_length = pdata->dma_max_burst_length; priv->flags = pdata->flags; - } else { - priv->version = pd->genet_version; - priv->dma_max_burst_length = DMA_MAX_BURST_LENGTH; } priv->clk = devm_clk_get_optional(&priv->pdev->dev, "enet"); @@ -4062,16 +4057,13 @@ static int bcmgenet_probe(struct platform_device *pdev) if (device_get_phy_mode(&pdev->dev) == PHY_INTERFACE_MODE_INTERNAL) bcmgenet_power_up(priv, GENET_POWER_PASSIVE); - if (pd && !IS_ERR_OR_NULL(pd->mac_address)) - eth_hw_addr_set(dev, pd->mac_address); - else - if (device_get_ethdev_address(&pdev->dev, dev)) - if (has_acpi_companion(&pdev->dev)) { - u8 addr[ETH_ALEN]; + if (device_get_ethdev_address(&pdev->dev, dev)) + if (has_acpi_companion(&pdev->dev)) { + u8 addr[ETH_ALEN]; - bcmgenet_get_hw_addr(priv, addr); - eth_hw_addr_set(dev, addr); - } + bcmgenet_get_hw_addr(priv, addr); + eth_hw_addr_set(dev, addr); + } if (!is_valid_ether_addr(dev->dev_addr)) { dev_warn(&pdev->dev, "using random Ethernet MAC\n"); diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 573e8b279e52..38f854b94a79 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "bcmgenet.h" @@ -436,23 +435,6 @@ static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv) return priv->mdio_dn; } -static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv, - struct unimac_mdio_pdata *ppd) -{ - struct device *kdev = &priv->pdev->dev; - struct bcmgenet_platform_data *pd = kdev->platform_data; - - if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { - /* - * Internal or external PHY with MDIO access - */ - if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) - ppd->phy_mask = 1 << pd->phy_address; - else - ppd->phy_mask = 0; - } -} - static int bcmgenet_mii_wait(void *wait_func_data) { struct bcmgenet_priv *priv = wait_func_data; @@ -467,7 +449,6 @@ static int bcmgenet_mii_wait(void *wait_func_data) static int bcmgenet_mii_register(struct bcmgenet_priv *priv) { struct platform_device *pdev = priv->pdev; - struct bcmgenet_platform_data *pdata = pdev->dev.platform_data; struct device_node *dn = pdev->dev.of_node; struct unimac_mdio_pdata ppd; struct platform_device *ppdev; @@ -511,8 +492,6 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv) ppdev->dev.parent = &pdev->dev; if (dn) ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv); - else if (pdata) - bcmgenet_mii_pdata_init(priv, &ppd); else ppd.phy_mask = ~0; @@ -594,58 +573,6 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) return 0; } -static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) -{ - struct device *kdev = &priv->pdev->dev; - struct bcmgenet_platform_data *pd = kdev->platform_data; - char phy_name[MII_BUS_ID_SIZE + 3]; - char mdio_bus_id[MII_BUS_ID_SIZE]; - struct phy_device *phydev; - - snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d", - UNIMAC_MDIO_DRV_NAME, priv->pdev->id); - - if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { - snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, - mdio_bus_id, pd->phy_address); - - /* - * Internal or external PHY with MDIO access - */ - phydev = phy_attach(priv->dev, phy_name, pd->phy_interface); - if (IS_ERR(phydev)) { - dev_err(kdev, "failed to register PHY device\n"); - return PTR_ERR(phydev); - } - } else { - /* - * MoCA port or no MDIO access. - * Use fixed PHY to represent the link layer. - */ - struct fixed_phy_status fphy_status = { - .link = 1, - .speed = pd->phy_speed, - .duplex = pd->phy_duplex, - .pause = 0, - .asym_pause = 0, - }; - - phydev = fixed_phy_register(&fphy_status, NULL); - if (IS_ERR(phydev)) { - dev_err(kdev, "failed to register fixed PHY device\n"); - return PTR_ERR(phydev); - } - - /* Make sure we initialize MoCA PHYs with a link down */ - phydev->link = 0; - - } - - priv->phy_interface = pd->phy_interface; - - return 0; -} - static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; @@ -656,7 +583,7 @@ static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv) else if (has_acpi_companion(kdev)) return bcmgenet_phy_interface_init(priv); else - return bcmgenet_mii_pd_init(priv); + return -EINVAL; } int bcmgenet_mii_init(struct net_device *dev) diff --git a/include/linux/platform_data/bcmgenet.h b/include/linux/platform_data/bcmgenet.h deleted file mode 100644 index d8f8738629d2..000000000000 --- a/include/linux/platform_data/bcmgenet.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LINUX_PLATFORM_DATA_BCMGENET_H__ -#define __LINUX_PLATFORM_DATA_BCMGENET_H__ - -#include -#include -#include - -struct bcmgenet_platform_data { - bool mdio_enabled; - phy_interface_t phy_interface; - int phy_address; - int phy_speed; - int phy_duplex; - u8 mac_address[ETH_ALEN]; - int genet_version; -}; - -#endif -- cgit v1.2.3 From f0f7a3f542c1698edb69075f25a3f846207facba Mon Sep 17 00:00:00 2001 From: Qiu Wenbo Date: Tue, 28 Oct 2025 14:30:09 +0800 Subject: platform/x86: int3472: Fix double free of GPIO device during unregister MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit regulator_unregister() already frees the associated GPIO device. On ThinkPad X9 (Lunar Lake), this causes a double free issue that leads to random failures when other drivers (typically Intel THC) attempt to allocate interrupts. The root cause is that the reference count of the pinctrl_intel_platform module unexpectedly drops to zero when this driver defers its probe. This behavior can also be reproduced by unloading the module directly. Fix the issue by removing the redundant release of the GPIO device during regulator unregistration. Cc: stable@vger.kernel.org Fixes: 1e5d088a52c2 ("platform/x86: int3472: Stop using devm_gpiod_get()") Signed-off-by: Qiu Wenbo Reviewed-by: Andy Shevchenko Reviewed-by: Sakari Ailus Reviewed-by: Hans de Goede Reviewed-by: Daniel Scally Link: https://patch.msgid.link/20251028063009.289414-1-qiuwenbo@gnome.org Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/clk_and_regulator.c | 5 +---- include/linux/platform_data/x86/int3472.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 476ec24d3702..9e052b164a1a 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -245,15 +245,12 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, if (IS_ERR(regulator->rdev)) return PTR_ERR(regulator->rdev); - int3472->regulators[int3472->n_regulator_gpios].ena_gpio = gpio; int3472->n_regulator_gpios++; return 0; } void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472) { - for (int i = 0; i < int3472->n_regulator_gpios; i++) { + for (int i = 0; i < int3472->n_regulator_gpios; i++) regulator_unregister(int3472->regulators[i].rdev); - gpiod_put(int3472->regulators[i].ena_gpio); - } } diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index 1571e9157fa5..b1b837583d54 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -100,7 +100,6 @@ struct int3472_gpio_regulator { struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT * 2]; char supply_name_upper[GPIO_SUPPLY_NAME_LENGTH]; char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; - struct gpio_desc *ena_gpio; struct regulator_dev *rdev; struct regulator_desc rdesc; }; -- cgit v1.2.3 From 22ea7b9d96e26147b7a3ea1be7aa106cc700907c Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 2 Nov 2025 22:53:11 +0100 Subject: platform/x86: asus-wmi: export symbols used for read/write WMI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export symbols for reading/writing WMI symbols using a namespace. Existing functions: - asus_wmi_evaluate_method - asus_wmi_set_devstate New function: - asus_wmi_get_devstate_dsts The new function is intended for use with DSTS WMI method only and avoids requiring the asus_wmi driver data to select the WMI method. Co-developed-by: Denis Benato Signed-off-by: Denis Benato Signed-off-by: Luke D. Jones Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20251102215319.3126879-2-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 46 ++++++++++++++++++++++++++++-- include/linux/platform_data/x86/asus-wmi.h | 5 ++++ 2 files changed, 48 insertions(+), 3 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index e72a2b5d158e..c3e90517ce0f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -390,7 +390,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) { return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval); } -EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); +EXPORT_SYMBOL_NS_GPL(asus_wmi_evaluate_method, "ASUS_WMI"); static int asus_wmi_evaluate_method5(u32 method_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 *retval) @@ -554,12 +554,52 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) return 0; } -int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, - u32 *retval) +/** + * asus_wmi_get_devstate_dsts() - Get the WMI function state. + * @dev_id: The WMI method ID to call. + * @retval: A pointer to where to store the value returned from WMI. + * + * Returns: + * * %-ENODEV - method ID is unsupported. + * * %0 - successful and retval is filled. + * * %other - error from WMI call. + */ +int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval) +{ + int err; + + err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval); + if (err) + return err; + + if ((*retval & ASUS_WMI_DSTS_PRESENCE_BIT) == 0x00) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(asus_wmi_get_devstate_dsts, "ASUS_WMI"); + +/** + * asus_wmi_set_devstate() - Set the WMI function state. + * + * Note: an asus_wmi_set_devstate() call must be paired with a + * asus_wmi_get_devstate_dsts() to check if the WMI function is supported. + * + * @dev_id: The WMI function to call. + * @ctrl_param: The argument to be used for this WMI function. + * @retval: A pointer to where to store the value returned from WMI. + * + * Returns: + * * %-ENODEV - method ID is unsupported. + * * %0 - successful and retval is filled. + * * %other - error from WMI call. + */ +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) { return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval); } +EXPORT_SYMBOL_NS_GPL(asus_wmi_set_devstate, "ASUS_WMI"); /* Helper for special devices with magic return codes */ static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 8a515179113d..dbd44d9fbb6f 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -166,6 +166,7 @@ enum asus_ally_mcu_hack { #if IS_REACHABLE(CONFIG_ASUS_WMI) void set_ally_mcu_hack(enum asus_ally_mcu_hack status); void set_ally_mcu_powersave(bool enabled); +int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval); int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); #else @@ -179,6 +180,10 @@ static inline int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) { return -ENODEV; } +static inline int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval) +{ + return -ENODEV; +} static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) { -- cgit v1.2.3 From f99eb098090e4c8bfca4190b545e20450fee8250 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 2 Nov 2025 22:53:12 +0100 Subject: platform/x86: asus-armoury: move existing tunings to asus-armoury module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fw_attributes_class provides a much cleaner interface to all of the attributes introduced to asus-wmi. This patch moves all of these extra attributes over to fw_attributes_class, and shifts the bulk of these definitions to a new kernel module to reduce the clutter of asus-wmi with the intention of deprecating the asus-wmi attributes in future. The work applies only to WMI methods which don't have a clearly defined place within the sysfs and as a result ended up lumped together in /sys/devices/platform/asus-nb-wmi/ with no standard API. Where possible the fw attrs now implement defaults, min, max, scalar, choices, etc. As en example dgpu_disable becomes: /sys/class/firmware-attributes/asus-armoury/attributes/dgpu_disable/ ├── current_value ├── display_name ├── possible_values └── type as do other attributes. Co-developed-by: Denis Benato Signed-off-by: Denis Benato Signed-off-by: Luke D. Jones Link: https://patch.msgid.link/20251102215319.3126879-3-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/hid/hid-asus.c | 1 + drivers/platform/x86/Kconfig | 12 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/asus-armoury.c | 763 +++++++++++++++++++++ drivers/platform/x86/asus-armoury.h | 200 ++++++ drivers/platform/x86/asus-wmi.c | 10 +- .../linux/platform_data/x86/asus-wmi-leds-ids.h | 50 ++ include/linux/platform_data/x86/asus-wmi.h | 44 +- 8 files changed, 1034 insertions(+), 47 deletions(-) create mode 100644 drivers/platform/x86/asus-armoury.c create mode 100644 drivers/platform/x86/asus-armoury.h create mode 100644 include/linux/platform_data/x86/asus-wmi-leds-ids.h (limited to 'include/linux/platform_data') diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index a444d41e53b6..472bca54642b 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include /* For to_usb_interface for T100 touchpad intf check */ #include diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1e9b84f1098f..ba0806b48bb9 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -264,6 +264,18 @@ config ASUS_WIRELESS If you choose to compile this driver as a module the module will be called asus-wireless. +config ASUS_ARMOURY + tristate "ASUS Armoury driver" + depends on ASUS_WMI + select FW_ATTR_CLASS + help + Say Y here if you have a WMI aware Asus machine and would like to use the + firmware_attributes API to control various settings typically exposed in + the ASUS Armoury Crate application available on Windows. + + To compile this driver as a module, choose M here: the module will + be called asus-armoury. + config ASUS_WMI tristate "ASUS WMI Driver" depends on ACPI_WMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d722e244a4a7..d59a2ed5932c 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o # ASUS obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o +obj-$(CONFIG_ASUS_ARMOURY) += asus-armoury.o obj-$(CONFIG_ASUS_WMI) += asus-wmi.o obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c new file mode 100644 index 000000000000..81b4972df818 --- /dev/null +++ b/drivers/platform/x86/asus-armoury.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Asus Armoury (WMI) attributes driver. + * + * This driver uses the fw_attributes class to expose various WMI functions + * that are present in many gaming and some non-gaming ASUS laptops. + * + * These typically don't fit anywhere else in the sysfs such as under LED class, + * hwmon or others, and are set in Windows using the ASUS Armoury Crate tool. + * + * Copyright(C) 2024 Luke Jones + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asus-armoury.h" +#include "firmware_attributes_class.h" + +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" + +#define ASUS_MINI_LED_MODE_MASK GENMASK(1, 0) +/* Standard modes for devices with only on/off */ +#define ASUS_MINI_LED_OFF 0x00 +#define ASUS_MINI_LED_ON 0x01 +/* Like "on" but the effect is more vibrant or brighter */ +#define ASUS_MINI_LED_STRONG_MODE 0x02 +/* New modes for devices with 3 mini-led mode types */ +#define ASUS_MINI_LED_2024_WEAK 0x00 +#define ASUS_MINI_LED_2024_STRONG 0x01 +#define ASUS_MINI_LED_2024_OFF 0x02 + +struct asus_armoury_priv { + struct device *fw_attr_dev; + struct kset *fw_attr_kset; + + /* + * Mutex to protect eGPU activation/deactivation + * sequences and dGPU connection status: + * do not allow concurrent changes or changes + * before a reboot if dGPU got disabled. + */ + struct mutex egpu_mutex; + + u32 mini_led_dev_id; + u32 gpu_mux_dev_id; +}; + +static struct asus_armoury_priv asus_armoury = { + .egpu_mutex = __MUTEX_INITIALIZER(asus_armoury.egpu_mutex), +}; + +struct fw_attrs_group { + bool pending_reboot; +}; + +static struct fw_attrs_group fw_attrs = { + .pending_reboot = false, +}; + +struct asus_attr_group { + const struct attribute_group *attr_group; + u32 wmi_devid; +}; + +static void asus_set_reboot_and_signal_event(void) +{ + fw_attrs.pending_reboot = true; + kobject_uevent(&asus_armoury.fw_attr_dev->kobj, KOBJ_CHANGE); +} + +static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot); +} + +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); + +static bool asus_bios_requires_reboot(struct kobj_attribute *attr) +{ + return !strcmp(attr->attr.name, "gpu_mux_mode"); +} + +/** + * armoury_has_devstate() - Check presence of the WMI function state. + * + * @dev_id: The WMI method ID to check for presence. + * + * Returns: true iif method is supported. + */ +static bool armoury_has_devstate(u32 dev_id) +{ + u32 retval; + int status; + + status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, &retval); + pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval); + + return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); +} + +/** + * armoury_get_devstate() - Get the WMI function state. + * @attr: NULL or the kobj_attribute associated to called WMI function. + * @dev_id: The WMI method ID to call. + * @retval: + * * non-NULL pointer to where to store the value returned from WMI + * * with the function presence bit cleared. + * + * Intended usage is from sysfs attribute checking associated WMI function. + * + * Returns: + * * %-ENODEV - method ID is unsupported. + * * %0 - successful and retval is filled. + * * %other - error from WMI call. + */ +static int armoury_get_devstate(struct kobj_attribute *attr, u32 *retval, u32 dev_id) +{ + int err; + + err = asus_wmi_get_devstate_dsts(dev_id, retval); + if (err) { + if (attr) + pr_err("Failed to get %s: %d\n", attr->attr.name, err); + else + pr_err("Failed to get devstate for 0x%x: %d\n", dev_id, err); + + return err; + } + + /* + * asus_wmi_get_devstate_dsts will populate retval with WMI return, but + * the true value is expressed when ASUS_WMI_DSTS_PRESENCE_BIT is clear. + */ + *retval &= ~ASUS_WMI_DSTS_PRESENCE_BIT; + + return 0; +} + +/** + * armoury_set_devstate() - Set the WMI function state. + * @attr: The kobj_attribute associated to called WMI function. + * @dev_id: The WMI method ID to call. + * @value: The new value to be set. + * @retval: Where to store the value returned from WMI or NULL. + * + * Intended usage is from sysfs attribute setting associated WMI function. + * Before calling the presence of the function should be checked. + * + * Every WMI write MUST go through this function to enforce safety checks. + * + * Results !1 is usually considered a fail by ASUS, but some WMI methods + * (like eGPU or CPU cores) do use > 1 to return a status code or similar: + * in these cases caller is interested in the actual return value + * and should perform relevant checks. + * + * Returns: + * * %-EIO - WMI function returned an error. + * * %0 - successful and retval is filled. + * * %other - error from WMI call. + */ +static int armoury_set_devstate(struct kobj_attribute *attr, + u32 value, u32 *retval, u32 dev_id) +{ + u32 result; + int err; + + err = asus_wmi_set_devstate(dev_id, value, retval ? retval : &result); + if (err) { + if (attr) + pr_err("Failed to set %s: %d\n", attr->attr.name, err); + else + pr_err("Failed to set devstate for 0x%x: %d\n", dev_id, err); + + return err; + } + + /* + * If retval == NULL caller is uninterested in return value: + * perform the most common result check here. + */ + if ((retval == NULL) && (result == 0)) { + pr_err("Failed to set %s: (result): 0x%x\n", attr->attr.name, result); + return -EIO; + } + + return 0; +} + +static int armoury_attr_enum_list(char *buf, size_t enum_values) +{ + size_t i; + int len = 0; + + for (i = 0; i < enum_values; i++) { + if (i == 0) + len += sysfs_emit_at(buf, len, "%zu", i); + else + len += sysfs_emit_at(buf, len, ";%zu", i); + } + len += sysfs_emit_at(buf, len, "\n"); + + return len; +} + +ssize_t armoury_attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count, u32 min, u32 max, + u32 *store_value, u32 wmi_dev) +{ + u32 value; + int err; + + err = kstrtou32(buf, 10, &value); + if (err) + return err; + + if (value < min || value > max) + return -EINVAL; + + err = armoury_set_devstate(attr, value, NULL, wmi_dev); + if (err) + return err; + + if (store_value != NULL) + *store_value = value; + sysfs_notify(kobj, NULL, attr->attr.name); + + if (asus_bios_requires_reboot(attr)) + asus_set_reboot_and_signal_event(); + + return count; +} + +ssize_t armoury_attr_uint_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf, u32 wmi_dev) +{ + u32 result; + int err; + + err = armoury_get_devstate(attr, &result, wmi_dev); + if (err) + return err; + + return sysfs_emit(buf, "%u\n", result); +} + +static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "enumeration\n"); +} + +/* Mini-LED mode **************************************************************/ + +/* Values map for mini-led modes on 2023 and earlier models. */ +static u32 mini_led_mode1_map[] = { + [0] = ASUS_MINI_LED_OFF, + [1] = ASUS_MINI_LED_ON, +}; + +/* Values map for mini-led modes on 2024 and later models. */ +static u32 mini_led_mode2_map[] = { + [0] = ASUS_MINI_LED_2024_OFF, + [1] = ASUS_MINI_LED_2024_WEAK, + [2] = ASUS_MINI_LED_2024_STRONG, +}; + +static ssize_t mini_led_mode_current_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + u32 *mini_led_mode_map; + size_t mini_led_mode_map_size; + u32 i, mode; + int err; + + switch (asus_armoury.mini_led_dev_id) { + case ASUS_WMI_DEVID_MINI_LED_MODE: + mini_led_mode_map = mini_led_mode1_map; + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode1_map); + break; + + case ASUS_WMI_DEVID_MINI_LED_MODE2: + mini_led_mode_map = mini_led_mode2_map; + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode2_map); + break; + + default: + pr_err("Unrecognized mini-LED device: %u\n", asus_armoury.mini_led_dev_id); + return -ENODEV; + } + + err = armoury_get_devstate(attr, &mode, asus_armoury.mini_led_dev_id); + if (err) + return err; + + mode = FIELD_GET(ASUS_MINI_LED_MODE_MASK, 0); + + for (i = 0; i < mini_led_mode_map_size; i++) + return sysfs_emit(buf, "%u\n", mini_led_mode_map[i]); + + pr_warn("Unrecognized mini-LED mode: %u", mode); + return -EINVAL; +} + +static ssize_t mini_led_mode_current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + u32 *mini_led_mode_map; + size_t mini_led_mode_map_size; + u32 mode; + int err; + + err = kstrtou32(buf, 10, &mode); + if (err) + return err; + + switch (asus_armoury.mini_led_dev_id) { + case ASUS_WMI_DEVID_MINI_LED_MODE: + mini_led_mode_map = mini_led_mode1_map; + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode1_map); + break; + + case ASUS_WMI_DEVID_MINI_LED_MODE2: + mini_led_mode_map = mini_led_mode2_map; + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode2_map); + break; + + default: + pr_err("Unrecognized mini-LED devid: %u\n", asus_armoury.mini_led_dev_id); + return -EINVAL; + } + + if (mode >= mini_led_mode_map_size) { + return pr_warn("mini-LED mode unrecognized device: %u\n", mode); + return -ENODEV; + } + + return armoury_attr_uint_store(kobj, attr, buf, count, + 0, mini_led_mode_map[mode], + NULL, asus_armoury.mini_led_dev_id); +} + +static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + switch (asus_armoury.mini_led_dev_id) { + case ASUS_WMI_DEVID_MINI_LED_MODE: + return armoury_attr_enum_list(buf, ARRAY_SIZE(mini_led_mode1_map)); + case ASUS_WMI_DEVID_MINI_LED_MODE2: + return armoury_attr_enum_list(buf, ARRAY_SIZE(mini_led_mode2_map)); + default: + return -ENODEV; + } +} +ASUS_ATTR_GROUP_ENUM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode"); + +static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int result, err; + bool optimus; + + err = kstrtobool(buf, &optimus); + if (err) + return err; + + if (armoury_has_devstate(ASUS_WMI_DEVID_DGPU)) { + err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_DGPU); + if (err) + return err; + if (result && !optimus) { + pr_warn("Cannot switch MUX to dGPU mode when dGPU is disabled: %02X\n", + result); + return -ENODEV; + } + } + + if (armoury_has_devstate(ASUS_WMI_DEVID_EGPU)) { + err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_EGPU); + if (err) + return err; + if (result && !optimus) { + pr_warn("Cannot switch MUX to dGPU mode when eGPU is enabled\n"); + return -EBUSY; + } + } + + err = armoury_set_devstate(attr, optimus ? 1 : 0, NULL, asus_armoury.gpu_mux_dev_id); + if (err) + return err; + + sysfs_notify(kobj, NULL, attr->attr.name); + asus_set_reboot_and_signal_event(); + + return count; +} +ASUS_WMI_SHOW_INT(gpu_mux_mode_current_value, asus_armoury.gpu_mux_dev_id); +ASUS_ATTR_GROUP_BOOL(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode"); + +static ssize_t dgpu_disable_current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t count) +{ + int result, err; + bool disable; + + err = kstrtobool(buf, &disable); + if (err) + return err; + + if (asus_armoury.gpu_mux_dev_id) { + err = armoury_get_devstate(NULL, &result, asus_armoury.gpu_mux_dev_id); + if (err) + return err; + if (!result && disable) { + pr_warn("Cannot disable dGPU when the MUX is in dGPU mode\n"); + return -EBUSY; + } + } + + scoped_guard(mutex, &asus_armoury.egpu_mutex) { + err = armoury_set_devstate(attr, disable ? 1 : 0, NULL, ASUS_WMI_DEVID_DGPU); + if (err) + return err; + } + + sysfs_notify(kobj, NULL, attr->attr.name); + + return count; +} +ASUS_WMI_SHOW_INT(dgpu_disable_current_value, ASUS_WMI_DEVID_DGPU); +ASUS_ATTR_GROUP_BOOL(dgpu_disable, "dgpu_disable", "Disable the dGPU"); + +/* Values map for eGPU activation requests. */ +static u32 egpu_status_map[] = { + [0] = 0x00000000U, + [1] = 0x00000001U, + [2] = 0x00000101U, + [3] = 0x00000201U, +}; + +/* + * armoury_pci_rescan() - Performs a PCI rescan + * + * Bring up any GPU that has been hotplugged in the system. + */ +static void armoury_pci_rescan(void) +{ + struct pci_bus *b = NULL; + + pci_lock_rescan_remove(); + while ((b = pci_find_next_bus(b)) != NULL) + pci_rescan_bus(b); + pci_unlock_rescan_remove(); +} + +/* + * The ACPI call to enable the eGPU might also disable the internal dGPU, + * but this is not always the case and on certain models enabling the eGPU + * when the dGPU is either still active or has been disabled without rebooting + * will make both GPUs malfunction and the kernel will detect many + * PCI AER unrecoverable errors. + */ +static ssize_t egpu_enable_current_value_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int err; + u32 requested, enable, result; + + err = kstrtou32(buf, 10, &requested); + if (err) + return err; + + if (requested >= ARRAY_SIZE(egpu_status_map)) + return -EINVAL; + enable = egpu_status_map[requested]; + + scoped_guard(mutex, &asus_armoury.egpu_mutex) { + /* Ensure the eGPU is connected before attempting to activate it. */ + if (enable) { + err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_EGPU_CONNECTED); + if (err) { + pr_warn("Failed to get eGPU connection status: %d\n", err); + return err; + } + if (!result) { + pr_warn("Cannot activate eGPU while undetected\n"); + return -ENOENT; + } + } + + if (asus_armoury.gpu_mux_dev_id) { + err = armoury_get_devstate(NULL, &result, asus_armoury.gpu_mux_dev_id); + if (err) + return err; + + if (!result && enable) { + pr_warn("Cannot enable eGPU when the MUX is in dGPU mode\n"); + return -ENODEV; + } + } + + err = armoury_set_devstate(attr, enable, &result, ASUS_WMI_DEVID_EGPU); + if (err) { + pr_err("Failed to set %s: %d\n", attr->attr.name, err); + return err; + } + + /* + * ACPI returns value 0x01 on success and 0x02 on a partial activation: + * performing a pci rescan will bring up the device in pci-e 3.0 speed, + * after a reboot the device will work at full speed. + */ + switch (result) { + case 0x01: + /* + * When a GPU is in use it does not get disconnected even if + * the ACPI call returns a success. + */ + if (!enable) { + err = armoury_get_devstate(attr, &result, ASUS_WMI_DEVID_EGPU); + if (err) { + pr_warn("Failed to ensure eGPU is deactivated: %d\n", err); + return err; + } + + if (result != 0) + return -EBUSY; + } + + pr_debug("Success changing the eGPU status\n"); + break; + case 0x02: + pr_info("Success changing the eGPU status, a reboot is strongly advised\n"); + asus_set_reboot_and_signal_event(); + break; + default: + pr_err("Failed to change the eGPU status: wmi result is 0x%x\n", result); + return -EIO; + } + } + + /* + * Perform a PCI rescan: on every tested model this is necessary + * to make the eGPU visible on the bus without rebooting. + */ + armoury_pci_rescan(); + + sysfs_notify(kobj, NULL, attr->attr.name); + + return count; +} + +static ssize_t egpu_enable_current_value_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int i, err; + u32 status; + + scoped_guard(mutex, &asus_armoury.egpu_mutex) { + err = armoury_get_devstate(attr, &status, ASUS_WMI_DEVID_EGPU); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(egpu_status_map); i++) { + if (egpu_status_map[i] == status) + return sysfs_emit(buf, "%u\n", i); + } + + return -EIO; +} + +static ssize_t egpu_enable_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return armoury_attr_enum_list(buf, ARRAY_SIZE(egpu_status_map)); +} +ASUS_ATTR_GROUP_ENUM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)"); + +/* Simple attribute creation */ +ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2\n", + "Show the current mode of charging"); +ASUS_ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND, + "Set the boot POST sound"); +ASUS_ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE, + "Set MCU powersaving mode"); +ASUS_ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD, + "Set the panel refresh overdrive"); +ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED, + "Show the eGPU connection status"); + +/* If an attribute does not require any special case handling add it here */ +static const struct asus_attr_group armoury_attr_groups[] = { + { &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED }, + { &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU }, + { &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU }, + + { &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE }, + { &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND }, + { &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE }, + { &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD }, +}; + +static int asus_fw_attr_add(void) +{ + int err, i; + + asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), + NULL, "%s", DRIVER_NAME); + if (IS_ERR(asus_armoury.fw_attr_dev)) { + err = PTR_ERR(asus_armoury.fw_attr_dev); + goto fail_class_get; + } + + asus_armoury.fw_attr_kset = kset_create_and_add("attributes", NULL, + &asus_armoury.fw_attr_dev->kobj); + if (!asus_armoury.fw_attr_kset) { + err = -ENOMEM; + goto err_destroy_classdev; + } + + err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); + if (err) { + pr_err("Failed to create sysfs level attributes\n"); + goto err_destroy_kset; + } + + asus_armoury.mini_led_dev_id = 0; + if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE)) + asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE; + else if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE2)) + asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2; + + if (asus_armoury.mini_led_dev_id) { + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, + &mini_led_mode_attr_group); + if (err) { + pr_err("Failed to create sysfs-group for mini_led\n"); + goto err_remove_file; + } + } + + asus_armoury.gpu_mux_dev_id = 0; + if (armoury_has_devstate(ASUS_WMI_DEVID_GPU_MUX)) + asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX; + else if (armoury_has_devstate(ASUS_WMI_DEVID_GPU_MUX_VIVO)) + asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO; + + if (asus_armoury.gpu_mux_dev_id) { + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, + &gpu_mux_mode_attr_group); + if (err) { + pr_err("Failed to create sysfs-group for gpu_mux\n"); + goto err_remove_mini_led_group; + } + } + + for (i = 0; i < ARRAY_SIZE(armoury_attr_groups); i++) { + if (!armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) + continue; + + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, + armoury_attr_groups[i].attr_group); + if (err) { + pr_err("Failed to create sysfs-group for %s\n", + armoury_attr_groups[i].attr_group->name); + goto err_remove_groups; + } + } + + return 0; + +err_remove_groups: + while (i--) { + if (armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, + armoury_attr_groups[i].attr_group); + } + if (asus_armoury.gpu_mux_dev_id) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group); +err_remove_mini_led_group: + if (asus_armoury.mini_led_dev_id) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group); +err_remove_file: + sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); +err_destroy_kset: + kset_unregister(asus_armoury.fw_attr_kset); +err_destroy_classdev: +fail_class_get: + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); + return err; +} + +/* Init / exit ****************************************************************/ + +static int __init asus_fw_init(void) +{ + char *wmi_uid; + + wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); + if (!wmi_uid) + return -ENODEV; + + /* + * if equal to "ASUSWMI" then it's DCTS that can't be used for this + * driver, DSTS is required. + */ + if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) + return -ENODEV; + + return asus_fw_attr_add(); +} + +static void __exit asus_fw_exit(void) +{ + int i; + + for (i = ARRAY_SIZE(armoury_attr_groups) - 1; i >= 0; i--) { + if (armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, + armoury_attr_groups[i].attr_group); + } + + if (asus_armoury.gpu_mux_dev_id) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group); + + if (asus_armoury.mini_led_dev_id) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group); + + sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); + kset_unregister(asus_armoury.fw_attr_kset); + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); +} + +module_init(asus_fw_init); +module_exit(asus_fw_exit); + +MODULE_IMPORT_NS("ASUS_WMI"); +MODULE_AUTHOR("Luke Jones "); +MODULE_DESCRIPTION("ASUS BIOS Configuration Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:" ASUS_NB_WMI_EVENT_GUID); diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h new file mode 100644 index 000000000000..3a2a674a1b55 --- /dev/null +++ b/drivers/platform/x86/asus-armoury.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Definitions for kernel modules using asus-armoury driver + * + * Copyright (c) 2024 Luke Jones + */ + +#ifndef _ASUS_ARMOURY_H_ +#define _ASUS_ARMOURY_H_ + +#include +#include +#include + +#define DRIVER_NAME "asus-armoury" + +/** + * armoury_attr_uint_store() - Send an uint to WMI method if within min/max. + * @kobj: Pointer to the driver object. + * @attr: Pointer to the attribute calling this function. + * @buf: The buffer to read from, this is parsed to `uint` type. + * @count: Required by sysfs attribute macros, pass in from the callee attr. + * @min: Minimum accepted value. Below this returns -EINVAL. + * @max: Maximum accepted value. Above this returns -EINVAL. + * @store_value: Pointer to where the parsed value should be stored. + * @wmi_dev: The WMI function ID to use. + * + * This function is intended to be generic so it can be called from any "_store" + * attribute which works only with integers. + * + * Integers to be sent to the WMI method is inclusive range checked and + * an error returned if out of range. + * + * If the value is valid and WMI is success then the sysfs attribute is notified + * and if asus_bios_requires_reboot() is true then reboot attribute + * is also notified. + * + * Returns: Either count, or an error. + */ +ssize_t armoury_attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count, u32 min, u32 max, + u32 *store_value, u32 wmi_dev); + +/** + * armoury_attr_uint_show() - Receive an uint from a WMI method. + * @kobj: Pointer to the driver object. + * @attr: Pointer to the attribute calling this function. + * @buf: The buffer to write to, as an `uint` type. + * @wmi_dev: The WMI function ID to use. + * + * This function is intended to be generic so it can be called from any "_show" + * attribute which works only with integers. + * + * Returns: Either count, or an error. + */ +ssize_t armoury_attr_uint_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf, u32 wmi_dev); + +#define __ASUS_ATTR_RO(_func, _name) \ + { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = _func##_##_name##_show, \ + } + +#define __ASUS_ATTR_RO_AS(_name, _show) \ + { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = _show, \ + } + +#define __ASUS_ATTR_RW(_func, _name) \ + __ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store) + +#define __WMI_STORE_INT(_attr, _min, _max, _wmi) \ + static ssize_t _attr##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return armoury_attr_uint_store(kobj, attr, buf, count, _min, \ + _max, NULL, _wmi); \ + } + +#define ASUS_WMI_SHOW_INT(_attr, _wmi) \ + static ssize_t _attr##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ + { \ + return armoury_attr_uint_show(kobj, attr, buf, _wmi); \ + } + +/* Create functions and attributes for use in other macros or on their own */ + +/* Shows a formatted static variable */ +#define __ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \ + static ssize_t _attrname##_##_prop##_show( \ + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ + { \ + return sysfs_emit(buf, _fmt, _val); \ + } \ + static struct kobj_attribute attr_##_attrname##_##_prop = \ + __ASUS_ATTR_RO(_attrname, _prop) + +#define __ATTR_RO_INT_GROUP_ENUM(_attrname, _wmi, _fsname, _possible, _dispname)\ + ASUS_WMI_SHOW_INT(_attrname##_current_value, _wmi); \ + static struct kobj_attribute attr_##_attrname##_current_value = \ + __ASUS_ATTR_RO(_attrname, current_value); \ + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __ASUS_ATTR_RO_AS(type, enum_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_possible_values.attr, \ + &attr_##_attrname##_type.attr, \ + NULL \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +#define __ATTR_RW_INT_GROUP_ENUM(_attrname, _minv, _maxv, _wmi, _fsname,\ + _possible, _dispname) \ + __WMI_STORE_INT(_attrname##_current_value, _minv, _maxv, _wmi); \ + ASUS_WMI_SHOW_INT(_attrname##_current_value, _wmi); \ + static struct kobj_attribute attr_##_attrname##_current_value = \ + __ASUS_ATTR_RW(_attrname, current_value); \ + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __ASUS_ATTR_RO_AS(type, enum_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_possible_values.attr, \ + &attr_##_attrname##_type.attr, \ + NULL \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +/* Boolean style enumeration, base macro. Requires adding show/store */ +#define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \ + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __ASUS_ATTR_RO_AS(type, enum_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_possible_values.attr, \ + &attr_##_attrname##_type.attr, \ + NULL \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +#define ASUS_ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \ + __ATTR_RO_INT_GROUP_ENUM(_attrname, _wmi, _fsname, "0;1", _dispname) + + +#define ASUS_ATTR_GROUP_BOOL_RW(_attrname, _fsname, _wmi, _dispname) \ + __ATTR_RW_INT_GROUP_ENUM(_attrname, 0, 1, _wmi, _fsname, "0;1", _dispname) + +#define ASUS_ATTR_GROUP_ENUM_INT_RO(_attrname, _fsname, _wmi, _possible, _dispname) \ + __ATTR_RO_INT_GROUP_ENUM(_attrname, _wmi, _fsname, _possible, _dispname) + +/* + * Requires _current_value_show(), _current_value_show() + */ +#define ASUS_ATTR_GROUP_BOOL(_attrname, _fsname, _dispname) \ + static struct kobj_attribute attr_##_attrname##_current_value = \ + __ASUS_ATTR_RW(_attrname, current_value); \ + __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname) + +/* + * Requires _current_value_show(), _current_value_show() + * and _possible_values_show() + */ +#define ASUS_ATTR_GROUP_ENUM(_attrname, _fsname, _dispname) \ + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + static struct kobj_attribute attr_##_attrname##_current_value = \ + __ASUS_ATTR_RW(_attrname, current_value); \ + static struct kobj_attribute attr_##_attrname##_possible_values = \ + __ASUS_ATTR_RO(_attrname, possible_values); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __ASUS_ATTR_RO_AS(type, enum_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_possible_values.attr, \ + &attr_##_attrname##_type.attr, \ + NULL \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +#endif /* _ASUS_ARMOURY_H_ */ diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index c3e90517ce0f..ff98267e5981 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -55,8 +57,6 @@ module_param(fnlock_default, bool, 0444); #define to_asus_wmi_driver(pdrv) \ (container_of((pdrv), struct asus_wmi_driver, platform_driver)) -#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" - #define NOTIFY_BRNUP_MIN 0x11 #define NOTIFY_BRNUP_MAX 0x1f #define NOTIFY_BRNDOWN_MIN 0x20 @@ -105,8 +105,6 @@ module_param(fnlock_default, bool, 0444); #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 -#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" - #define WMI_EVENT_MASK 0xFFFF #define FAN_CURVE_POINTS 8 @@ -561,8 +559,8 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) * * Returns: * * %-ENODEV - method ID is unsupported. - * * %0 - successful and retval is filled. - * * %other - error from WMI call. + * * %0 - successful and retval is filled. + * * %other - error from WMI call. */ int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval) { diff --git a/include/linux/platform_data/x86/asus-wmi-leds-ids.h b/include/linux/platform_data/x86/asus-wmi-leds-ids.h new file mode 100644 index 000000000000..034a039c4e37 --- /dev/null +++ b/include/linux/platform_data/x86/asus-wmi-leds-ids.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H +#define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H + +#include +#include + +/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ +#if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS) +static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA403U"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU605M"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "RC71L"), + }, + }, + { }, +}; +#endif + +#endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */ diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index dbd44d9fbb6f..8ea8925a0fc5 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -4,7 +4,9 @@ #include #include -#include + +#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" +#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" /* WMI Methods */ #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ @@ -191,44 +193,4 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, } #endif -/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ -static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "GA403U"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "GU605M"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "RC71L"), - }, - }, - { }, -}; - #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ -- cgit v1.2.3 From 628cb03b15f2a0f10534979b3ea9c8befe87c381 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 2 Nov 2025 22:53:13 +0100 Subject: platform/x86: asus-armoury: add panel_hd_mode attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add panel_hd_mode to toggle the panel mode between single and high definition modes. Signed-off-by: Denis Benato Signed-off-by: Luke D. Jones Reviewed-by: Mario Limonciello Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20251102215319.3126879-4-denis.benato@linux.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.c | 6 +++++- include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index 81b4972df818..f0cb973a487e 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -96,7 +96,8 @@ static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); static bool asus_bios_requires_reboot(struct kobj_attribute *attr) { - return !strcmp(attr->attr.name, "gpu_mux_mode"); + return !strcmp(attr->attr.name, "gpu_mux_mode") || + !strcmp(attr->attr.name, "panel_hd_mode"); } /** @@ -607,6 +608,8 @@ ASUS_ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWER "Set MCU powersaving mode"); ASUS_ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD, "Set the panel refresh overdrive"); +ASUS_ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD, + "Set the panel HD mode to UHD<0> or FHD<1>"); ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED, "Show the eGPU connection status"); @@ -620,6 +623,7 @@ static const struct asus_attr_group armoury_attr_groups[] = { { &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND }, { &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE }, { &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD }, + { &panel_hd_mode_attr_group, ASUS_WMI_DEVID_PANEL_HD }, }; static int asus_fw_attr_add(void) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 8ea8925a0fc5..3cc235b20be4 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -75,6 +75,7 @@ #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019 /* Misc */ +#define ASUS_WMI_DEVID_PANEL_HD 0x0005001C #define ASUS_WMI_DEVID_PANEL_OD 0x00050019 #define ASUS_WMI_DEVID_CAMERA 0x00060013 #define ASUS_WMI_DEVID_LID_FLIP 0x00060062 -- cgit v1.2.3 From 96498e804cb6629e02747336a0a33e4955449732 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 17 Nov 2025 17:12:47 +0100 Subject: spi: davinci: remove platform data header There are no longer any board files including the DaVinci SPI platform data header. Let's move the bits and pieces that are used in the driver into the driver .c file itself and remove the header. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20251117-davinci-spi-v2-1-cd799d17f04a@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-davinci.c | 64 ++++++++++++++++++++++++++- include/linux/platform_data/spi-davinci.h | 73 ------------------------------- 2 files changed, 62 insertions(+), 75 deletions(-) delete mode 100644 include/linux/platform_data/spi-davinci.h (limited to 'include/linux/platform_data') diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index a29934422356..21a14e800eed 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -19,8 +20,6 @@ #include #include -#include - #define CS_DEFAULT 0xFF #define SPIFMT_PHASE_MASK BIT(16) @@ -98,8 +97,69 @@ #define SPIDEF 0x4c #define SPIFMT0 0x50 +#define SPI_IO_TYPE_POLL 1 +#define SPI_IO_TYPE_DMA 2 + #define DMA_MIN_BYTES 16 +enum { + SPI_VERSION_1, /* For DM355/DM365/DM6467 */ + SPI_VERSION_2, /* For DA8xx */ +}; + +/** + * struct davinci_spi_platform_data - Platform data for SPI master device on DaVinci + * + * @version: version of the SPI IP. Different DaVinci devices have slightly + * varying versions of the same IP. + * @num_chipselect: number of chipselects supported by this SPI master + * @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt + * controller withn the SoC. Possible values are 0 and 1. + * @prescaler_limit: max clock prescaler value + * @cshold_bug: set this to true if the SPI controller on your chip requires + * a write to CSHOLD bit in between transfers (like in DM355). + * @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any + * device on the bus. + */ +struct davinci_spi_platform_data { + u8 version; + u8 num_chipselect; + u8 intr_line; + u8 prescaler_limit; + bool cshold_bug; + enum dma_event_q dma_event_q; +}; + +/** + * struct davinci_spi_config - Per-chip-select configuration for SPI slave devices + * + * @wdelay: amount of delay between transmissions. Measured in number of + * SPI module clocks. + * @odd_parity: polarity of parity flag at the end of transmit data stream. + * 0 - odd parity, 1 - even parity. + * @parity_enable: enable transmission of parity at end of each transmit + * data stream. + * @io_type: type of IO transfer. Choose between polled, interrupt and DMA. + * @timer_disable: disable chip-select timers (setup and hold) + * @c2tdelay: chip-select setup time. Measured in number of SPI module clocks. + * @t2cdelay: chip-select hold time. Measured in number of SPI module clocks. + * @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured + * in number of SPI clocks. + * @c2edelay: chip-select active to SPI ENAn signal active time. Measured in + * number of SPI clocks. + */ +struct davinci_spi_config { + u8 wdelay; + u8 odd_parity; + u8 parity_enable; + u8 io_type; + u8 timer_disable; + u8 c2tdelay; + u8 t2cdelay; + u8 t2edelay; + u8 c2edelay; +}; + /* SPI Controller driver's private data. */ struct davinci_spi { struct spi_bitbang bitbang; diff --git a/include/linux/platform_data/spi-davinci.h b/include/linux/platform_data/spi-davinci.h deleted file mode 100644 index 2cb5cc70fd9d..000000000000 --- a/include/linux/platform_data/spi-davinci.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright 2009 Texas Instruments. - */ - -#ifndef __ARCH_ARM_DAVINCI_SPI_H -#define __ARCH_ARM_DAVINCI_SPI_H - -#include - -#define SPI_INTERN_CS 0xFF - -enum { - SPI_VERSION_1, /* For DM355/DM365/DM6467 */ - SPI_VERSION_2, /* For DA8xx */ -}; - -/** - * davinci_spi_platform_data - Platform data for SPI master device on DaVinci - * - * @version: version of the SPI IP. Different DaVinci devices have slightly - * varying versions of the same IP. - * @num_chipselect: number of chipselects supported by this SPI master - * @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt - * controller withn the SoC. Possible values are 0 and 1. - * @cshold_bug: set this to true if the SPI controller on your chip requires - * a write to CSHOLD bit in between transfers (like in DM355). - * @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any - * device on the bus. - */ -struct davinci_spi_platform_data { - u8 version; - u8 num_chipselect; - u8 intr_line; - u8 prescaler_limit; - bool cshold_bug; - enum dma_event_q dma_event_q; -}; - -/** - * davinci_spi_config - Per-chip-select configuration for SPI slave devices - * - * @wdelay: amount of delay between transmissions. Measured in number of - * SPI module clocks. - * @odd_parity: polarity of parity flag at the end of transmit data stream. - * 0 - odd parity, 1 - even parity. - * @parity_enable: enable transmission of parity at end of each transmit - * data stream. - * @io_type: type of IO transfer. Choose between polled, interrupt and DMA. - * @timer_disable: disable chip-select timers (setup and hold) - * @c2tdelay: chip-select setup time. Measured in number of SPI module clocks. - * @t2cdelay: chip-select hold time. Measured in number of SPI module clocks. - * @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured - * in number of SPI clocks. - * @c2edelay: chip-select active to SPI ENAn signal active time. Measured in - * number of SPI clocks. - */ -struct davinci_spi_config { - u8 wdelay; - u8 odd_parity; - u8 parity_enable; -#define SPI_IO_TYPE_INTR 0 -#define SPI_IO_TYPE_POLL 1 -#define SPI_IO_TYPE_DMA 2 - u8 io_type; - u8 timer_disable; - u8 c2tdelay; - u8 t2cdelay; - u8 t2edelay; - u8 c2edelay; -}; - -#endif /* __ARCH_ARM_DAVINCI_SPI_H */ -- cgit v1.2.3 From 9c7dacf5d51910f34a3bd709403f6a82ffc8c960 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 2 Nov 2025 22:53:14 +0100 Subject: platform/x86: asus-armoury: add apu-mem control support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the APU memory size control under the asus-armoury module using the fw_attributes class. This allows the APU allocated memory size to be adjusted depending on the users priority. A reboot is required after change. Co-developed-by: Denis Benato Signed-off-by: Denis Benato Signed-off-by: Luke D. Jones Link: https://patch.msgid.link/20251102215319.3126879-5-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.c | 98 ++++++++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 2 + 2 files changed, 100 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index f0cb973a487e..1b972260c5dd 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -174,6 +174,7 @@ static int armoury_get_devstate(struct kobj_attribute *attr, u32 *retval, u32 de * and should perform relevant checks. * * Returns: + * * %-EINVAL - attempt to set a dangerous or unsupported value. * * %-EIO - WMI function returned an error. * * %0 - successful and retval is filled. * * %other - error from WMI call. @@ -184,6 +185,26 @@ static int armoury_set_devstate(struct kobj_attribute *attr, u32 result; int err; + /* + * Prevent developers from bricking devices or issuing dangerous + * commands that can be difficult or impossible to recover from. + */ + switch (dev_id) { + case ASUS_WMI_DEVID_APU_MEM: + /* + * A hard reset might suffice to save the device, + * but there is no value in sending these commands. + */ + if (value == 0x100 || value == 0x101) { + pr_err("Refusing to set APU memory to unsafe value: 0x%x\n", value); + return -EINVAL; + } + break; + default: + /* No problems are known for this dev_id */ + break; + } + err = asus_wmi_set_devstate(dev_id, value, retval ? retval : &result); if (err) { if (attr) @@ -599,6 +620,82 @@ static ssize_t egpu_enable_possible_values_show(struct kobject *kobj, struct kob } ASUS_ATTR_GROUP_ENUM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)"); +/* Device memory available to APU */ + +/* + * Values map for APU reserved memory (index + 1 number of GB). + * Some looks out of order, but are actually correct. + */ +static u32 apu_mem_map[] = { + [0] = 0x000, /* called "AUTO" on the BIOS, is the minimum available */ + [1] = 0x102, + [2] = 0x103, + [3] = 0x104, + [4] = 0x105, + [5] = 0x107, + [6] = 0x108, + [7] = 0x109, + [8] = 0x106, +}; + +static ssize_t apu_mem_current_value_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int err; + u32 mem; + + err = armoury_get_devstate(attr, &mem, ASUS_WMI_DEVID_APU_MEM); + if (err) + return err; + + /* After 0x000 is set, a read will return 0x100 */ + if (mem == 0x100) + return sysfs_emit(buf, "0\n"); + + for (unsigned int i = 0; i < ARRAY_SIZE(apu_mem_map); i++) { + if (apu_mem_map[i] == mem) + return sysfs_emit(buf, "%u\n", i); + } + + pr_warn("Unrecognised value for APU mem 0x%08x\n", mem); + return -EIO; +} + +static ssize_t apu_mem_current_value_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int result, err; + u32 requested, mem; + + result = kstrtou32(buf, 10, &requested); + if (result) + return result; + + if (requested >= ARRAY_SIZE(apu_mem_map)) + return -EINVAL; + mem = apu_mem_map[requested]; + + err = armoury_set_devstate(attr, mem, NULL, ASUS_WMI_DEVID_APU_MEM); + if (err) { + pr_warn("Failed to set apu_mem 0x%x: %d\n", mem, err); + return err; + } + + pr_info("APU memory changed to %uGB, reboot required\n", requested + 1); + sysfs_notify(kobj, NULL, attr->attr.name); + + asus_set_reboot_and_signal_event(); + + return count; +} + +static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return armoury_attr_enum_list(buf, ARRAY_SIZE(apu_mem_map)); +} +ASUS_ATTR_GROUP_ENUM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use"); + /* Simple attribute creation */ ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2\n", "Show the current mode of charging"); @@ -618,6 +715,7 @@ static const struct asus_attr_group armoury_attr_groups[] = { { &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED }, { &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU }, { &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU }, + { &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM }, { &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE }, { &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND }, diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 3cc235b20be4..9a6433d08973 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -136,6 +136,8 @@ /* dgpu on/off */ #define ASUS_WMI_DEVID_DGPU 0x00090020 +#define ASUS_WMI_DEVID_APU_MEM 0x000600C1 + /* gpu mux switch, 0 = dGPU, 1 = Optimus */ #define ASUS_WMI_DEVID_GPU_MUX 0x00090016 #define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026 -- cgit v1.2.3 From 7725a2dc58632cb44eeef2e5b959ab7b7931298d Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 2 Nov 2025 22:53:16 +0100 Subject: platform/x86: asus-armoury: add screen auto-brightness toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add screen_auto_brightness toggle supported on some laptops. Signed-off-by: Denis Benato Signed-off-by: Luke D. Jones Reviewed-by: Mario Limonciello Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20251102215319.3126879-7-denis.benato@linux.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.c | 4 ++++ include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 5 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index 1b972260c5dd..c1dbaed409d2 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -707,6 +707,9 @@ ASUS_ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD, "Set the panel refresh overdrive"); ASUS_ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD, "Set the panel HD mode to UHD<0> or FHD<1>"); +ASUS_ATTR_GROUP_BOOL_RW(screen_auto_brightness, "screen_auto_brightness", + ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS, + "Set the panel brightness to Off<0> or On<1>"); ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED, "Show the eGPU connection status"); @@ -722,6 +725,7 @@ static const struct asus_attr_group armoury_attr_groups[] = { { &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE }, { &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD }, { &panel_hd_mode_attr_group, ASUS_WMI_DEVID_PANEL_HD }, + { &screen_auto_brightness_attr_group, ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS }, }; static int asus_fw_attr_add(void) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 9a6433d08973..3af075baf9f7 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -82,6 +82,7 @@ #define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077 #define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E #define ASUS_WMI_DEVID_MINI_LED_MODE2 0x0005002E +#define ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS 0x0005002A /* Storage */ #define ASUS_WMI_DEVID_CARDREADER 0x00080013 -- cgit v1.2.3 From d849a9f2380d5287d5133eac5bae602a147b86c2 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Sun, 2 Nov 2025 22:53:18 +0100 Subject: platform/x86: asus-wmi: rename ASUS_WMI_DEVID_PPT_FPPT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintain power-related WMI macros naming consistency: rename ASUS_WMI_DEVID_PPT_FPPT to ASUS_WMI_DEVID_PPT_PL3_FPPT. Link: https://lore.kernel.org/all/cad7b458-5a7a-4975-94a1-d0c74f6f3de5@oracle.com/ Suggested-by: ALOK TIWARI Signed-off-by: Denis Benato Link: https://.../ Link: https://patch.msgid.link/20251102215319.3126879-9-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 4 ++-- include/linux/platform_data/x86/asus-wmi.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6de633d4a748..64cfc0bf98dd 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1218,7 +1218,7 @@ static ssize_t ppt_fppt_store(struct device *dev, if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX) return -EINVAL; - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_FPPT, value, &result); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL3_FPPT, value, &result); if (err) { pr_warn("Failed to set ppt_fppt: %d\n", err); return err; @@ -4602,7 +4602,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, else if (attr == &dev_attr_ppt_pl1_spl.attr) devid = ASUS_WMI_DEVID_PPT_PL1_SPL; else if (attr == &dev_attr_ppt_fppt.attr) - devid = ASUS_WMI_DEVID_PPT_FPPT; + devid = ASUS_WMI_DEVID_PPT_PL3_FPPT; else if (attr == &dev_attr_ppt_apu_sppt.attr) devid = ASUS_WMI_DEVID_PPT_APU_SPPT; else if (attr == &dev_attr_ppt_platform_sppt.attr) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 3af075baf9f7..e7c95e9d29db 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -107,7 +107,7 @@ #define ASUS_WMI_DEVID_PPT_PL1_SPL 0x001200A3 #define ASUS_WMI_DEVID_PPT_APU_SPPT 0x001200B0 #define ASUS_WMI_DEVID_PPT_PLAT_SPPT 0x001200B1 -#define ASUS_WMI_DEVID_PPT_FPPT 0x001200C1 +#define ASUS_WMI_DEVID_PPT_PL3_FPPT 0x001200C1 #define ASUS_WMI_DEVID_NV_DYN_BOOST 0x001200C0 #define ASUS_WMI_DEVID_NV_THERM_TARGET 0x001200C2 -- cgit v1.2.3 From 39ae6c50e599aa0cf62ea3d0dcf06492f7690ed7 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sun, 2 Nov 2025 22:53:19 +0100 Subject: platform/x86: asus-armoury: add ppt_* and nv_* tuning knobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the ppt_* and nv_* tuning knobs that are available via WMI methods and adds proper min/max levels plus defaults. The min/max are defined by ASUS and typically gained by looking at what they allow in the ASUS Armoury Crate application - ASUS does not share the values outside of this. It could also be possible to gain the AMD values by use of ryzenadj and testing for the minimum stable value. The general rule of thumb for adding to the match table is that if the model range has a single CPU used throughout, then the DMI match can omit the last letter of the model number as this is the GPU model. If a min or max value is not provided it is assumed that the particular setting is not supported. for example ppt_pl2_sppt_min/max is not set. If a _def is not set then the default is assumed to be _max It is assumed that at least AC settings are available so that the firmware attributes will be created - if no DC table is available and power is on DC, then reading the attributes is -ENODEV. Co-developed-by: Denis Benato Signed-off-by: Denis Benato Signed-off-by: Luke D. Jones Reviewed-by: Mario Limonciello Tested-by: Mateusz Schyboll Tested-by: Porfet Lillian Link: https://patch.msgid.link/20251102215319.3126879-10-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.c | 302 ++++++- drivers/platform/x86/asus-armoury.h | 1294 ++++++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 3 + 3 files changed, 1593 insertions(+), 6 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index c1dbaed409d2..d6aba68515e2 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "asus-armoury.h" @@ -48,6 +49,33 @@ #define ASUS_MINI_LED_2024_STRONG 0x01 #define ASUS_MINI_LED_2024_OFF 0x02 +/* Power tunable attribute name defines */ +#define ATTR_PPT_PL1_SPL "ppt_pl1_spl" +#define ATTR_PPT_PL2_SPPT "ppt_pl2_sppt" +#define ATTR_PPT_PL3_FPPT "ppt_pl3_fppt" +#define ATTR_PPT_APU_SPPT "ppt_apu_sppt" +#define ATTR_PPT_PLATFORM_SPPT "ppt_platform_sppt" +#define ATTR_NV_DYNAMIC_BOOST "nv_dynamic_boost" +#define ATTR_NV_TEMP_TARGET "nv_temp_target" +#define ATTR_NV_BASE_TGP "nv_base_tgp" +#define ATTR_NV_TGP "nv_tgp" + +#define ASUS_ROG_TUNABLE_DC 0 +#define ASUS_ROG_TUNABLE_AC 1 + +struct rog_tunables { + const struct power_limits *power_limits; + u32 ppt_pl1_spl; // cpu + u32 ppt_pl2_sppt; // cpu + u32 ppt_pl3_fppt; // cpu + u32 ppt_apu_sppt; // plat + u32 ppt_platform_sppt; // plat + + u32 nv_dynamic_boost; + u32 nv_temp_target; + u32 nv_tgp; +}; + struct asus_armoury_priv { struct device *fw_attr_dev; struct kset *fw_attr_kset; @@ -60,6 +88,9 @@ struct asus_armoury_priv { */ struct mutex egpu_mutex; + /* Index 0 for DC, 1 for AC */ + struct rog_tunables *rog_tunables[2]; + u32 mini_led_dev_id; u32 gpu_mux_dev_id; }; @@ -290,6 +321,12 @@ static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr, return sysfs_emit(buf, "enumeration\n"); } +static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "integer\n"); +} + /* Mini-LED mode **************************************************************/ /* Values map for mini-led modes on 2023 and earlier models. */ @@ -696,6 +733,15 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_at } ASUS_ATTR_GROUP_ENUM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use"); +/* Define helper to access the current power mode tunable values */ +static inline struct rog_tunables *get_current_tunables(void) +{ + if (power_supply_is_system_supplied()) + return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]; + + return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]; +} + /* Simple attribute creation */ ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2\n", "Show the current mode of charging"); @@ -712,6 +758,24 @@ ASUS_ATTR_GROUP_BOOL_RW(screen_auto_brightness, "screen_auto_brightness", "Set the panel brightness to Off<0> or On<1>"); ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED, "Show the eGPU connection status"); +ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl1_spl, ATTR_PPT_PL1_SPL, ASUS_WMI_DEVID_PPT_PL1_SPL, + "Set the CPU slow package limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl2_sppt, ATTR_PPT_PL2_SPPT, ASUS_WMI_DEVID_PPT_PL2_SPPT, + "Set the CPU fast package limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl3_fppt, ATTR_PPT_PL3_FPPT, ASUS_WMI_DEVID_PPT_PL3_FPPT, + "Set the CPU fastest package limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_apu_sppt, ATTR_PPT_APU_SPPT, ASUS_WMI_DEVID_PPT_APU_SPPT, + "Set the APU package limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_platform_sppt, ATTR_PPT_PLATFORM_SPPT, ASUS_WMI_DEVID_PPT_PLAT_SPPT, + "Set the platform package limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(nv_dynamic_boost, ATTR_NV_DYNAMIC_BOOST, ASUS_WMI_DEVID_NV_DYN_BOOST, + "Set the Nvidia dynamic boost limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(nv_temp_target, ATTR_NV_TEMP_TARGET, ASUS_WMI_DEVID_NV_THERM_TARGET, + "Set the Nvidia max thermal limit"); +ASUS_ATTR_GROUP_ROG_TUNABLE(nv_tgp, "nv_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP, + "Set the additional TGP on top of the base TGP"); +ASUS_ATTR_GROUP_INT_VALUE_ONLY_RO(nv_base_tgp, ATTR_NV_BASE_TGP, ASUS_WMI_DEVID_DGPU_BASE_TGP, + "Read the base TGP value"); /* If an attribute does not require any special case handling add it here */ static const struct asus_attr_group armoury_attr_groups[] = { @@ -720,6 +784,16 @@ static const struct asus_attr_group armoury_attr_groups[] = { { &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU }, { &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM }, + { &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL }, + { &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT }, + { &ppt_pl3_fppt_attr_group, ASUS_WMI_DEVID_PPT_PL3_FPPT }, + { &ppt_apu_sppt_attr_group, ASUS_WMI_DEVID_PPT_APU_SPPT }, + { &ppt_platform_sppt_attr_group, ASUS_WMI_DEVID_PPT_PLAT_SPPT }, + { &nv_dynamic_boost_attr_group, ASUS_WMI_DEVID_NV_DYN_BOOST }, + { &nv_temp_target_attr_group, ASUS_WMI_DEVID_NV_THERM_TARGET }, + { &nv_base_tgp_attr_group, ASUS_WMI_DEVID_DGPU_BASE_TGP }, + { &nv_tgp_attr_group, ASUS_WMI_DEVID_DGPU_SET_TGP }, + { &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE }, { &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND }, { &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE }, @@ -728,8 +802,76 @@ static const struct asus_attr_group armoury_attr_groups[] = { { &screen_auto_brightness_attr_group, ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS }, }; +/** + * is_power_tunable_attr - Determines if an attribute is a power-related tunable + * @name: The name of the attribute to check + * + * This function checks if the given attribute name is related to power tuning. + * + * Return: true if the attribute is a power-related tunable, false otherwise + */ +static bool is_power_tunable_attr(const char *name) +{ + static const char * const power_tunable_attrs[] = { + ATTR_PPT_PL1_SPL, ATTR_PPT_PL2_SPPT, + ATTR_PPT_PL3_FPPT, ATTR_PPT_APU_SPPT, + ATTR_PPT_PLATFORM_SPPT, ATTR_NV_DYNAMIC_BOOST, + ATTR_NV_TEMP_TARGET, ATTR_NV_BASE_TGP, + ATTR_NV_TGP + }; + + for (unsigned int i = 0; i < ARRAY_SIZE(power_tunable_attrs); i++) { + if (!strcmp(name, power_tunable_attrs[i])) + return true; + } + + return false; +} + +/** + * has_valid_limit - Checks if a power-related attribute has a valid limit value + * @name: The name of the attribute to check + * @limits: Pointer to the power_limits structure containing limit values + * + * This function checks if a power-related attribute has a valid limit value. + * It returns false if limits is NULL or if the corresponding limit value is zero. + * + * Return: true if the attribute has a valid limit value, false otherwise + */ +static bool has_valid_limit(const char *name, const struct power_limits *limits) +{ + u32 limit_value = 0; + + if (!limits) + return false; + + if (!strcmp(name, ATTR_PPT_PL1_SPL)) + limit_value = limits->ppt_pl1_spl_max; + else if (!strcmp(name, ATTR_PPT_PL2_SPPT)) + limit_value = limits->ppt_pl2_sppt_max; + else if (!strcmp(name, ATTR_PPT_PL3_FPPT)) + limit_value = limits->ppt_pl3_fppt_max; + else if (!strcmp(name, ATTR_PPT_APU_SPPT)) + limit_value = limits->ppt_apu_sppt_max; + else if (!strcmp(name, ATTR_PPT_PLATFORM_SPPT)) + limit_value = limits->ppt_platform_sppt_max; + else if (!strcmp(name, ATTR_NV_DYNAMIC_BOOST)) + limit_value = limits->nv_dynamic_boost_max; + else if (!strcmp(name, ATTR_NV_TEMP_TARGET)) + limit_value = limits->nv_temp_target_max; + else if (!strcmp(name, ATTR_NV_BASE_TGP) || + !strcmp(name, ATTR_NV_TGP)) + limit_value = limits->nv_tgp_max; + + return limit_value > 0; +} + static int asus_fw_attr_add(void) { + const struct rog_tunables *const ac_rog_tunables = asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]; + const struct power_limits *limits; + bool should_create; + const char *name; int err, i; asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), @@ -786,12 +928,28 @@ static int asus_fw_attr_add(void) if (!armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) continue; - err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, - armoury_attr_groups[i].attr_group); - if (err) { - pr_err("Failed to create sysfs-group for %s\n", - armoury_attr_groups[i].attr_group->name); - goto err_remove_groups; + /* Always create by default, unless PPT is not present */ + should_create = true; + name = armoury_attr_groups[i].attr_group->name; + + /* Check if this is a power-related tunable requiring limits */ + if (ac_rog_tunables && ac_rog_tunables->power_limits && + is_power_tunable_attr(name)) { + limits = ac_rog_tunables->power_limits; + /* Check only AC: if not present then DC won't be either */ + should_create = has_valid_limit(name, limits); + if (!should_create) + pr_debug("Missing max value for tunable %s\n", name); + } + + if (should_create) { + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, + armoury_attr_groups[i].attr_group); + if (err) { + pr_err("Failed to create sysfs-group for %s\n", + armoury_attr_groups[i].attr_group->name); + goto err_remove_groups; + } } } @@ -820,6 +978,132 @@ fail_class_get: /* Init / exit ****************************************************************/ +/* Set up the min/max and defaults for ROG tunables */ +static void init_rog_tunables(void) +{ + const struct power_limits *ac_limits, *dc_limits; + struct rog_tunables *ac_rog_tunables = NULL, *dc_rog_tunables = NULL; + const struct power_data *power_data; + const struct dmi_system_id *dmi_id; + + /* Match the system against the power_limits table */ + dmi_id = dmi_first_match(power_limits); + if (!dmi_id) { + pr_warn("No matching power limits found for this system\n"); + return; + } + + /* Get the power data for this system */ + power_data = dmi_id->driver_data; + if (!power_data) { + pr_info("No power data available for this system\n"); + return; + } + + /* Initialize AC power tunables */ + ac_limits = power_data->ac_data; + if (ac_limits) { + ac_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]), + GFP_KERNEL); + if (!ac_rog_tunables) + goto err_nomem; + + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC] = ac_rog_tunables; + ac_rog_tunables->power_limits = ac_limits; + + /* Set initial AC values */ + ac_rog_tunables->ppt_pl1_spl = + ac_limits->ppt_pl1_spl_def ? + ac_limits->ppt_pl1_spl_def : + ac_limits->ppt_pl1_spl_max; + + ac_rog_tunables->ppt_pl2_sppt = + ac_limits->ppt_pl2_sppt_def ? + ac_limits->ppt_pl2_sppt_def : + ac_limits->ppt_pl2_sppt_max; + + ac_rog_tunables->ppt_pl3_fppt = + ac_limits->ppt_pl3_fppt_def ? + ac_limits->ppt_pl3_fppt_def : + ac_limits->ppt_pl3_fppt_max; + + ac_rog_tunables->ppt_apu_sppt = + ac_limits->ppt_apu_sppt_def ? + ac_limits->ppt_apu_sppt_def : + ac_limits->ppt_apu_sppt_max; + + ac_rog_tunables->ppt_platform_sppt = + ac_limits->ppt_platform_sppt_def ? + ac_limits->ppt_platform_sppt_def : + ac_limits->ppt_platform_sppt_max; + + ac_rog_tunables->nv_dynamic_boost = + ac_limits->nv_dynamic_boost_max; + ac_rog_tunables->nv_temp_target = + ac_limits->nv_temp_target_max; + ac_rog_tunables->nv_tgp = ac_limits->nv_tgp_max; + + pr_debug("AC power limits initialized for %s\n", dmi_id->matches[0].substr); + } else { + pr_debug("No AC PPT limits defined\n"); + } + + /* Initialize DC power tunables */ + dc_limits = power_data->dc_data; + if (dc_limits) { + dc_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]), + GFP_KERNEL); + if (!dc_rog_tunables) { + kfree(ac_rog_tunables); + goto err_nomem; + } + + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC] = dc_rog_tunables; + dc_rog_tunables->power_limits = dc_limits; + + /* Set initial DC values */ + dc_rog_tunables->ppt_pl1_spl = + dc_limits->ppt_pl1_spl_def ? + dc_limits->ppt_pl1_spl_def : + dc_limits->ppt_pl1_spl_max; + + dc_rog_tunables->ppt_pl2_sppt = + dc_limits->ppt_pl2_sppt_def ? + dc_limits->ppt_pl2_sppt_def : + dc_limits->ppt_pl2_sppt_max; + + dc_rog_tunables->ppt_pl3_fppt = + dc_limits->ppt_pl3_fppt_def ? + dc_limits->ppt_pl3_fppt_def : + dc_limits->ppt_pl3_fppt_max; + + dc_rog_tunables->ppt_apu_sppt = + dc_limits->ppt_apu_sppt_def ? + dc_limits->ppt_apu_sppt_def : + dc_limits->ppt_apu_sppt_max; + + dc_rog_tunables->ppt_platform_sppt = + dc_limits->ppt_platform_sppt_def ? + dc_limits->ppt_platform_sppt_def : + dc_limits->ppt_platform_sppt_max; + + dc_rog_tunables->nv_dynamic_boost = + dc_limits->nv_dynamic_boost_max; + dc_rog_tunables->nv_temp_target = + dc_limits->nv_temp_target_max; + dc_rog_tunables->nv_tgp = dc_limits->nv_tgp_max; + + pr_debug("DC power limits initialized for %s\n", dmi_id->matches[0].substr); + } else { + pr_debug("No DC PPT limits defined\n"); + } + + return; + +err_nomem: + pr_err("Failed to allocate memory for tunables\n"); +} + static int __init asus_fw_init(void) { char *wmi_uid; @@ -835,6 +1119,9 @@ static int __init asus_fw_init(void) if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) return -ENODEV; + init_rog_tunables(); + + /* Must always be last step to ensure data is available */ return asus_fw_attr_add(); } @@ -857,6 +1144,9 @@ static void __exit asus_fw_exit(void) sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); kset_unregister(asus_armoury.fw_attr_kset); device_destroy(&firmware_attributes_class, MKDEV(0, 0)); + + kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]); + kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]); } module_init(asus_fw_init); diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 3a2a674a1b55..548c66c590a6 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -8,6 +8,7 @@ #ifndef _ASUS_ARMOURY_H_ #define _ASUS_ARMOURY_H_ +#include #include #include #include @@ -197,4 +198,1297 @@ ssize_t armoury_attr_uint_show(struct kobject *kobj, struct kobj_attribute *attr .name = _fsname, .attrs = _attrname##_attrs \ } +#define ASUS_ATTR_GROUP_INT_VALUE_ONLY_RO(_attrname, _fsname, _wmi, _dispname) \ + ASUS_WMI_SHOW_INT(_attrname##_current_value, _wmi); \ + static struct kobj_attribute attr_##_attrname##_current_value = \ + __ASUS_ATTR_RO(_attrname, current_value); \ + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __ASUS_ATTR_RO_AS(type, int_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_type.attr, NULL \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +/* + * ROG PPT attributes need a little different in setup as they + * require rog_tunables members. + */ + +#define __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \ + static ssize_t _attrname##_##_prop##_show( \ + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ + { \ + struct rog_tunables *tunables = get_current_tunables(); \ + \ + if (!tunables || !tunables->power_limits) \ + return -ENODEV; \ + \ + return sysfs_emit(buf, "%d\n", tunables->power_limits->_val); \ + } \ + static struct kobj_attribute attr_##_attrname##_##_prop = \ + __ASUS_ATTR_RO(_attrname, _prop) + +#define __ROG_TUNABLE_SHOW_DEFAULT(_attrname) \ + static ssize_t _attrname##_default_value_show( \ + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ + { \ + struct rog_tunables *tunables = get_current_tunables(); \ + \ + if (!tunables || !tunables->power_limits) \ + return -ENODEV; \ + \ + return sysfs_emit( \ + buf, "%d\n", \ + tunables->power_limits->_attrname##_def ? \ + tunables->power_limits->_attrname##_def : \ + tunables->power_limits->_attrname##_max); \ + } \ + static struct kobj_attribute attr_##_attrname##_default_value = \ + __ASUS_ATTR_RO(_attrname, default_value) + +#define __ROG_TUNABLE_RW(_attr, _wmi) \ + static ssize_t _attr##_current_value_store( \ + struct kobject *kobj, struct kobj_attribute *attr, \ + const char *buf, size_t count) \ + { \ + struct rog_tunables *tunables = get_current_tunables(); \ + \ + if (!tunables || !tunables->power_limits) \ + return -ENODEV; \ + \ + if (tunables->power_limits->_attr##_min == \ + tunables->power_limits->_attr##_max) \ + return -EINVAL; \ + \ + return armoury_attr_uint_store(kobj, attr, buf, count, \ + tunables->power_limits->_attr##_min, \ + tunables->power_limits->_attr##_max, \ + &tunables->_attr, _wmi); \ + } \ + static ssize_t _attr##_current_value_show( \ + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ + { \ + struct rog_tunables *tunables = get_current_tunables(); \ + \ + if (!tunables) \ + return -ENODEV; \ + \ + return sysfs_emit(buf, "%u\n", tunables->_attr); \ + } \ + static struct kobj_attribute attr_##_attr##_current_value = \ + __ASUS_ATTR_RW(_attr, current_value) + +#define ASUS_ATTR_GROUP_ROG_TUNABLE(_attrname, _fsname, _wmi, _dispname) \ + __ROG_TUNABLE_RW(_attrname, _wmi); \ + __ROG_TUNABLE_SHOW_DEFAULT(_attrname); \ + __ROG_TUNABLE_SHOW(min_value, _attrname, _attrname##_min); \ + __ROG_TUNABLE_SHOW(max_value, _attrname, _attrname##_max); \ + __ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \ + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __ASUS_ATTR_RO_AS(type, int_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_default_value.attr, \ + &attr_##_attrname##_min_value.attr, \ + &attr_##_attrname##_max_value.attr, \ + &attr_##_attrname##_scalar_increment.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_type.attr, \ + NULL \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +/* Default is always the maximum value unless *_def is specified */ +struct power_limits { + u8 ppt_pl1_spl_min; + u8 ppt_pl1_spl_def; + u8 ppt_pl1_spl_max; + u8 ppt_pl2_sppt_min; + u8 ppt_pl2_sppt_def; + u8 ppt_pl2_sppt_max; + u8 ppt_pl3_fppt_min; + u8 ppt_pl3_fppt_def; + u8 ppt_pl3_fppt_max; + u8 ppt_apu_sppt_min; + u8 ppt_apu_sppt_def; + u8 ppt_apu_sppt_max; + u8 ppt_platform_sppt_min; + u8 ppt_platform_sppt_def; + u8 ppt_platform_sppt_max; + /* Nvidia GPU specific, default is always max */ + u8 nv_dynamic_boost_def; // unused. exists for macro + u8 nv_dynamic_boost_min; + u8 nv_dynamic_boost_max; + u8 nv_temp_target_def; // unused. exists for macro + u8 nv_temp_target_min; + u8 nv_temp_target_max; + u8 nv_tgp_def; // unused. exists for macro + u8 nv_tgp_min; + u8 nv_tgp_max; +}; + +struct power_data { + const struct power_limits *ac_data; + const struct power_limits *dc_data; + bool requires_fan_curve; +}; + +/* + * For each available attribute there must be a min and a max. + * _def is not required and will be assumed to be default == max if missing. + */ +static const struct dmi_system_id power_limits[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA401W"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 75, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 30, + .ppt_pl2_sppt_min = 31, + .ppt_pl2_sppt_max = 44, + .ppt_pl3_fppt_min = 45, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA507N"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA507R"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80 + }, + .dc_data = NULL, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA507X"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA507Z"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 105, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 15, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 45, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 60, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA607P"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 30, + .ppt_pl1_spl_def = 100, + .ppt_pl1_spl_max = 135, + .ppt_pl2_sppt_min = 30, + .ppt_pl2_sppt_def = 115, + .ppt_pl2_sppt_max = 135, + .ppt_pl3_fppt_min = 30, + .ppt_pl3_fppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_def = 60, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 25, + .ppt_pl3_fppt_max = 80, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA608WI"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 90, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 90, + .ppt_pl2_sppt_max = 90, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_def = 90, + .ppt_pl3_fppt_max = 90, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_def = 65, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA617NS"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_apu_sppt_min = 15, + .ppt_apu_sppt_max = 80, + .ppt_platform_sppt_min = 30, + .ppt_platform_sppt_max = 120, + }, + .dc_data = &(struct power_limits) { + .ppt_apu_sppt_min = 25, + .ppt_apu_sppt_max = 35, + .ppt_platform_sppt_min = 45, + .ppt_platform_sppt_max = 100, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA617NT"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_apu_sppt_min = 15, + .ppt_apu_sppt_max = 80, + .ppt_platform_sppt_min = 30, + .ppt_platform_sppt_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_apu_sppt_min = 15, + .ppt_apu_sppt_max = 45, + .ppt_platform_sppt_min = 30, + .ppt_platform_sppt_max = 50, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA617XS"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_apu_sppt_min = 15, + .ppt_apu_sppt_max = 80, + .ppt_platform_sppt_min = 30, + .ppt_platform_sppt_max = 120, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_apu_sppt_min = 25, + .ppt_apu_sppt_max = 35, + .ppt_platform_sppt_min = 45, + .ppt_platform_sppt_max = 100, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FX507VI"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 135, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 45, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 60, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FX507VV"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_def = 115, + .ppt_pl1_spl_max = 135, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 45, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 60, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FX507Z"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 15, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 45, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 60, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA401Q"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_max = 80, + }, + .dc_data = NULL, + }, + }, + { + .matches = { + // This model is full AMD. No Nvidia dGPU. + DMI_MATCH(DMI_BOARD_NAME, "GA402R"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_apu_sppt_min = 15, + .ppt_apu_sppt_max = 80, + .ppt_platform_sppt_min = 30, + .ppt_platform_sppt_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_apu_sppt_min = 25, + .ppt_apu_sppt_def = 30, + .ppt_apu_sppt_max = 45, + .ppt_platform_sppt_min = 40, + .ppt_platform_sppt_max = 60, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA402X"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 35, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_def = 65, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA403U"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 65, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA503R"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 35, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 65, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 25, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 60, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA605W"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 31, + .ppt_pl2_sppt_max = 44, + .ppt_pl3_fppt_min = 45, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU603Z"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 60, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 40, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 40, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + } + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU604V"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 65, + .ppt_pl1_spl_max = 120, + .ppt_pl2_sppt_min = 65, + .ppt_pl2_sppt_max = 150, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 40, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 40, + .ppt_pl2_sppt_max = 60, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU605CW"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 45, + .ppt_pl1_spl_max = 85, + .ppt_pl2_sppt_min = 56, + .ppt_pl2_sppt_max = 110, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 80, + .nv_tgp_def = 90, + .nv_tgp_max = 110, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 85, + .ppt_pl2_sppt_min = 32, + .ppt_pl2_sppt_max = 110, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU605CX"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 45, + .ppt_pl1_spl_max = 85, + .ppt_pl2_sppt_min = 56, + .ppt_pl2_sppt_max = 110, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 7, + .nv_temp_target_max = 87, + .nv_tgp_min = 95, + .nv_tgp_def = 100, + .nv_tgp_max = 110, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 85, + .ppt_pl2_sppt_min = 32, + .ppt_pl2_sppt_max = 110, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU605M"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 38, + .ppt_pl2_sppt_max = 53, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GV301Q"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 45, + .ppt_pl2_sppt_min = 65, + .ppt_pl2_sppt_max = 80, + }, + .dc_data = NULL, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GV301R"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 45, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 54, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GV601R"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 35, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 100, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_def = 80, + .ppt_pl3_fppt_max = 125, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 28, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 60, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_def = 80, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GV601V"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_def = 100, + .ppt_pl1_spl_max = 110, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 40, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 40, + .ppt_pl2_sppt_max = 60, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GX650P"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 110, + .ppt_pl1_spl_max = 130, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 125, + .ppt_pl2_sppt_max = 130, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_def = 125, + .ppt_pl3_fppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 25, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_def = 35, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_def = 42, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G513I"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + /* Yes this laptop is very limited */ + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_max = 80, + }, + .dc_data = NULL, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G513QM"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + /* Yes this laptop is very limited */ + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 100, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_max = 190, + }, + .dc_data = NULL, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G513R"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 35, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 54, + .ppt_pl2_sppt_max = 100, + .ppt_pl3_fppt_min = 54, + .ppt_pl3_fppt_max = 125, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 50, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 50, + .ppt_pl3_fppt_min = 28, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G614J"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 140, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 175, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 55, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 70, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G634J"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 140, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 175, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 55, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 70, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G713PV"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 30, + .ppt_pl1_spl_def = 120, + .ppt_pl1_spl_max = 130, + .ppt_pl2_sppt_min = 65, + .ppt_pl2_sppt_def = 125, + .ppt_pl2_sppt_max = 130, + .ppt_pl3_fppt_min = 65, + .ppt_pl3_fppt_def = 125, + .ppt_pl3_fppt_max = 130, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 75, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G733C"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 170, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 175, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 35, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G733P"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 30, + .ppt_pl1_spl_def = 100, + .ppt_pl1_spl_max = 130, + .ppt_pl2_sppt_min = 65, + .ppt_pl2_sppt_def = 125, + .ppt_pl2_sppt_max = 130, + .ppt_pl3_fppt_min = 65, + .ppt_pl3_fppt_def = 125, + .ppt_pl3_fppt_max = 130, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 75, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G814J"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 140, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 140, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 55, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 70, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G834J"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 140, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 175, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 55, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 70, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "H7606W"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 31, + .ppt_pl2_sppt_max = 44, + .ppt_pl3_fppt_min = 45, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "RC71"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 7, + .ppt_pl1_spl_max = 30, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_max = 43, + .ppt_pl3_fppt_min = 15, + .ppt_pl3_fppt_max = 53, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 7, + .ppt_pl1_spl_def = 15, + .ppt_pl1_spl_max = 25, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_def = 20, + .ppt_pl2_sppt_max = 30, + .ppt_pl3_fppt_min = 15, + .ppt_pl3_fppt_def = 25, + .ppt_pl3_fppt_max = 35, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "RC72"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 7, + .ppt_pl1_spl_max = 30, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_max = 43, + .ppt_pl3_fppt_min = 15, + .ppt_pl3_fppt_max = 53, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 7, + .ppt_pl1_spl_def = 17, + .ppt_pl1_spl_max = 25, + .ppt_pl2_sppt_min = 15, + .ppt_pl2_sppt_def = 24, + .ppt_pl2_sppt_max = 30, + .ppt_pl3_fppt_min = 15, + .ppt_pl3_fppt_def = 30, + .ppt_pl3_fppt_max = 35, + }, + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "RC73XA"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 7, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 14, + .ppt_pl2_sppt_max = 45, + .ppt_pl3_fppt_min = 19, + .ppt_pl3_fppt_max = 55, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 7, + .ppt_pl1_spl_def = 17, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 13, + .ppt_pl2_sppt_def = 21, + .ppt_pl2_sppt_max = 45, + .ppt_pl3_fppt_min = 19, + .ppt_pl3_fppt_def = 26, + .ppt_pl3_fppt_max = 55, + }, + }, + }, + {} +}; + #endif /* _ASUS_ARMOURY_H_ */ diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index e7c95e9d29db..419491d4abca 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -139,6 +139,9 @@ #define ASUS_WMI_DEVID_APU_MEM 0x000600C1 +#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099 +#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098 + /* gpu mux switch, 0 = dGPU, 1 = Optimus */ #define ASUS_WMI_DEVID_GPU_MUX 0x00090016 #define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026 -- cgit v1.2.3 From a75a5b148b4e1d7c0525359be455d5a54024b714 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 14 Nov 2025 19:37:55 +0100 Subject: usb: ohci-da8xx: remove unused platform data We no longer support any board files for DaVinci in mainline and so struct da8xx_ohci_root_hub is no longer used. Remove it together with all the code it's used for. Signed-off-by: Bartosz Golaszewski Acked-by: Alan Stern Link: https://patch.msgid.link/20251114-davinci-usb-v1-1-737380353a74@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-da8xx.c | 17 ----------------- include/linux/platform_data/usb-davinci.h | 22 ---------------------- 2 files changed, 39 deletions(-) delete mode 100644 include/linux/platform_data/usb-davinci.h (limited to 'include/linux/platform_data') diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 3c5ca2d7c92e..0938c0e7a8b6 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd) return 0; } -static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) -{ - struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); - - if (hub && hub->potpgt) - return 1; - - return 0; -} - static int ohci_da8xx_regulator_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -228,7 +216,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd) static int ohci_da8xx_reset(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); int result; u32 rh_a; @@ -266,10 +253,6 @@ static int ohci_da8xx_reset(struct usb_hcd *hcd) rh_a &= ~RH_A_NOCP; rh_a |= RH_A_OCPM; } - if (ohci_da8xx_has_potpgt(hcd)) { - rh_a &= ~RH_A_POTPGT; - rh_a |= hub->potpgt << 24; - } ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); return result; diff --git a/include/linux/platform_data/usb-davinci.h b/include/linux/platform_data/usb-davinci.h deleted file mode 100644 index 879f5c78b91a..000000000000 --- a/include/linux/platform_data/usb-davinci.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * USB related definitions - * - * Copyright (C) 2009 MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __ASM_ARCH_USB_H -#define __ASM_ARCH_USB_H - -/* Passed as the platform data to the OHCI driver */ -struct da8xx_ohci_root_hub { - /* Time from power on to power good (in 2 ms units) */ - u8 potpgt; -}; - -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); - -#endif /* ifndef __ASM_ARCH_USB_H */ -- cgit v1.2.3 From 2d45db63260c6ae3cf007361e04a1c41bd265084 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 10 Nov 2025 22:09:16 -0800 Subject: backlight: lp855x: Fix lp855x.h kernel-doc warnings Add a missing struct short description and a missing leading " *" to lp855x.h to avoid kernel-doc warnings: Warning: include/linux/platform_data/lp855x.h:126 missing initial short description on line: * struct lp855x_platform_data Warning: include/linux/platform_data/lp855x.h:131 bad line: Only valid when mode is PWM_BASED. Fixes: 7be865ab8634 ("backlight: new backlight driver for LP855x devices") Signed-off-by: Randy Dunlap Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20251111060916.1995920-1-rdunlap@infradead.org Signed-off-by: Lee Jones --- include/linux/platform_data/lp855x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/lp855x.h b/include/linux/platform_data/lp855x.h index ab222dd05bbc..3b4a891acefe 100644 --- a/include/linux/platform_data/lp855x.h +++ b/include/linux/platform_data/lp855x.h @@ -124,12 +124,12 @@ struct lp855x_rom_data { }; /** - * struct lp855x_platform_data + * struct lp855x_platform_data - lp855 platform-specific data * @name : Backlight driver name. If it is not defined, default name is set. * @device_control : value of DEVICE CONTROL register * @initial_brightness : initial value of backlight brightness * @period_ns : platform specific pwm period value. unit is nano. - Only valid when mode is PWM_BASED. + * Only valid when mode is PWM_BASED. * @size_program : total size of lp855x_rom_data * @rom_data : list of new eeprom/eprom registers */ -- cgit v1.2.3 From 611cf41ef6ac8301d23daadd8e78b013db0c5071 Mon Sep 17 00:00:00 2001 From: Yongxin Liu Date: Fri, 28 Nov 2025 18:24:38 +0800 Subject: platform/x86: intel_pmc_ipc: fix ACPI buffer memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intel_pmc_ipc() function uses ACPI_ALLOCATE_BUFFER to allocate memory for the ACPI evaluation result but never frees it, causing a 192-byte memory leak on each call. This leak is triggered during network interface initialization when the stmmac driver calls intel_mac_finish() -> intel_pmc_ipc(). unreferenced object 0xffff96a848d6ea80 (size 192): comm "dhcpcd", pid 541, jiffies 4294684345 hex dump (first 32 bytes): 04 00 00 00 05 00 00 00 98 ea d6 48 a8 96 ff ff ...........H.... 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ backtrace (crc b1564374): kmemleak_alloc+0x2d/0x40 __kmalloc_noprof+0x2fa/0x730 acpi_ut_initialize_buffer+0x83/0xc0 acpi_evaluate_object+0x29a/0x2f0 intel_pmc_ipc+0xfd/0x170 intel_mac_finish+0x168/0x230 stmmac_mac_finish+0x3d/0x50 phylink_major_config+0x22b/0x5b0 phylink_mac_initial_config.constprop.0+0xf1/0x1b0 phylink_start+0x8e/0x210 __stmmac_open+0x12c/0x2b0 stmmac_open+0x23c/0x380 __dev_open+0x11d/0x2c0 __dev_change_flags+0x1d2/0x250 netif_change_flags+0x2b/0x70 dev_change_flags+0x40/0xb0 Add __free(kfree) for ACPI object to properly release the allocated buffer. Cc: stable@vger.kernel.org Fixes: 7e2f7e25f6ff ("arch: x86: add IPC mailbox accessor function and add SoC register access") Signed-off-by: Yongxin Liu Link: https://patch.msgid.link/20251128102437.3412891-2-yongxin.liu@windriver.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/linux/platform_data/x86/intel_pmc_ipc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/x86/intel_pmc_ipc.h b/include/linux/platform_data/x86/intel_pmc_ipc.h index 1d34435b7001..85ea381e4a27 100644 --- a/include/linux/platform_data/x86/intel_pmc_ipc.h +++ b/include/linux/platform_data/x86/intel_pmc_ipc.h @@ -9,6 +9,7 @@ #ifndef INTEL_PMC_IPC_H #define INTEL_PMC_IPC_H #include +#include #define IPC_SOC_REGISTER_ACCESS 0xAA #define IPC_SOC_SUB_CMD_READ 0x00 @@ -48,7 +49,6 @@ static inline int intel_pmc_ipc(struct pmc_ipc_cmd *ipc_cmd, struct pmc_ipc_rbuf {.type = ACPI_TYPE_INTEGER,}, }; struct acpi_object_list arg_list = { PMC_IPCS_PARAM_COUNT, params }; - union acpi_object *obj; int status; if (!ipc_cmd || !rbuf) @@ -72,7 +72,7 @@ static inline int intel_pmc_ipc(struct pmc_ipc_cmd *ipc_cmd, struct pmc_ipc_rbuf if (ACPI_FAILURE(status)) return -ENODEV; - obj = buffer.pointer; + union acpi_object *obj __free(kfree) = buffer.pointer; if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count == VALID_IPC_RESPONSE) { -- cgit v1.2.3 From c888d0bd055b57688534a884f8f210a91d15e00f Mon Sep 17 00:00:00 2001 From: Ionut Nechita Date: Wed, 7 Jan 2026 17:42:22 +0200 Subject: HID: asus: Add WMI communication infrastructure Add infrastructure for the HID driver to communicate with the asus-wmi driver for handling special keys that require WMI communication. This includes: - Define ASUS_WMI_METHODID_NOTIF method ID in asus-wmi.h - Implement asus_wmi_send_event() function to send events to asus-wmi Reviewed-by: Denis Benato Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Ionut Nechita Signed-off-by: Jiri Kosina --- drivers/hid/hid-asus.c | 24 ++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 25 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 49ffed0f6e9e..f778387c3c55 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -23,6 +23,7 @@ /* */ +#include #include #include #include @@ -320,6 +321,29 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size) return 0; } +/* + * Send events to asus-wmi driver for handling special keys + */ +static int asus_wmi_send_event(struct asus_drvdata *drvdata, u8 code) +{ + int err; + u32 retval; + + err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, + ASUS_WMI_METHODID_NOTIF, code, &retval); + if (err) { + pr_warn("Failed to notify asus-wmi: %d\n", err); + return err; + } + + if (retval != 0) { + pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + static int asus_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 8a515179113d..e23b481ab118 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -27,6 +27,7 @@ #define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */ #define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */ #define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */ +#define ASUS_WMI_METHODID_NOTIF 0x00100021 /* Notify method */ #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE -- cgit v1.2.3 From 900131320bc9a9ec1d84702b2694b813c11c91b7 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 12 Jan 2026 23:15:54 +0200 Subject: ARM: s3c: remove a leftover hwmon-s3c.h header file The last user of defined structures s3c_hwmon_pdata and s3c_hwmon_chcfg was removed in commit 0d297df03890 ("ARM: s3c: simplify platform code"), thus the platform data header file itself can be removed also. Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260112211554.3755188-1-vz@mleia.com Signed-off-by: Krzysztof Kozlowski --- include/linux/platform_data/hwmon-s3c.h | 36 --------------------------------- 1 file changed, 36 deletions(-) delete mode 100644 include/linux/platform_data/hwmon-s3c.h (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/hwmon-s3c.h b/include/linux/platform_data/hwmon-s3c.h deleted file mode 100644 index 7d21e0c41037..000000000000 --- a/include/linux/platform_data/hwmon-s3c.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2005 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C - HWMon interface for ADC -*/ - -#ifndef __HWMON_S3C_H__ -#define __HWMON_S3C_H__ - -/** - * s3c_hwmon_chcfg - channel configuration - * @name: The name to give this channel. - * @mult: Multiply the ADC value read by this. - * @div: Divide the value from the ADC by this. - * - * The value read from the ADC is converted to a value that - * hwmon expects (mV) by result = (value_read * @mult) / @div. - */ -struct s3c_hwmon_chcfg { - const char *name; - unsigned int mult; - unsigned int div; -}; - -/** - * s3c_hwmon_pdata - HWMON platform data - * @in: One configuration for each possible channel used. - */ -struct s3c_hwmon_pdata { - struct s3c_hwmon_chcfg *in[8]; -}; - -#endif /* __HWMON_S3C_H__ */ -- cgit v1.2.3 From b8460480f62e16751876a1f367dc14fb62867463 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Jan 2026 18:44:12 +0200 Subject: i3c: mipi-i3c-hci: Allow for Multi-Bus Instances Add support for MIPI I3C Host Controllers with the Multi-Bus Instance capability. These controllers can host multiple I3C buses (up to 15) within a single hardware function (e.g., PCIe B/D/F), providing one indepedent HCI register set and corresponding I3C bus controller logic per bus. A separate platform device will represent each instance, but it is necessary to allow for shared resources. Multi-bus instances share the same MMIO address space, but the ranges are not guaranteed to be contiguous. To avoid overlapping mappings, pass base_regs from the parent mapping to child devices. Allow the IRQ to be shared among instances. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260106164416.67074-8-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 21 +++++++++++++++++---- include/linux/platform_data/mipi-i3c-hci.h | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 include/linux/platform_data/mipi-i3c-hci.h (limited to 'include/linux/platform_data') diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 3d6544a64188..6da5daf18166 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "hci.h" @@ -737,15 +738,27 @@ static int i3c_hci_init(struct i3c_hci *hci) static int i3c_hci_probe(struct platform_device *pdev) { + const struct mipi_i3c_hci_platform_data *pdata = pdev->dev.platform_data; struct i3c_hci *hci; int irq, ret; hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL); if (!hci) return -ENOMEM; - hci->base_regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(hci->base_regs)) - return PTR_ERR(hci->base_regs); + + /* + * Multi-bus instances share the same MMIO address range, but not + * necessarily in separate contiguous sub-ranges. To avoid overlapping + * mappings, provide base_regs from the parent mapping. + */ + if (pdata) + hci->base_regs = pdata->base_regs; + + if (!hci->base_regs) { + hci->base_regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hci->base_regs)) + return PTR_ERR(hci->base_regs); + } platform_set_drvdata(pdev, hci); /* temporary for dev_printk's, to be replaced in i3c_master_register */ @@ -759,7 +772,7 @@ static int i3c_hci_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler, - 0, NULL, hci); + IRQF_SHARED, NULL, hci); if (ret) return ret; diff --git a/include/linux/platform_data/mipi-i3c-hci.h b/include/linux/platform_data/mipi-i3c-hci.h new file mode 100644 index 000000000000..ab7395f455f9 --- /dev/null +++ b/include/linux/platform_data/mipi-i3c-hci.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H +#define INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H + +#include + +/** + * struct mipi_i3c_hci_platform_data - Platform-dependent data for mipi_i3c_hci + * @base_regs: Register set base address (to support multi-bus instances) + */ +struct mipi_i3c_hci_platform_data { + void __iomem *base_regs; +}; + +#endif -- cgit v1.2.3 From fac55d29581fcd4c3b66b9c2b9f7995c459c0064 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Thu, 22 Jan 2026 08:50:40 +0100 Subject: platform/x86: asus-wmi: Add support for multiple kbd led handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices, such as the Z13 have multiple Aura devices connected to them by USB. In addition, they might have a WMI interface for RGB. In Windows, Armoury Crate exposes a unified brightness slider for all of them, with 3 brightness levels. Therefore, to be synergistic in Linux, and support existing tooling such as UPower, allow adding listeners to the RGB device of the WMI interface. If WMI does not exist, lazy initialize the interface. Since hid-asus and asus-wmi can both interact with the led objects including from an atomic context, protect the brightness access with a spinlock and update the values from a workqueue. Use this workqueue to also process WMI keyboard events, so they are handled asynchronously. Acked-by: Benjamin Tissoires Signed-off-by: Antheas Kapenekakis Reviewed-by: Denis Benato Link: https://patch.msgid.link/20260122075044.5070-8-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 183 +++++++++++++++++++++++++---- include/linux/platform_data/x86/asus-wmi.h | 15 +++ 2 files changed, 173 insertions(+), 25 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 4aec7ec69250..c45846be3f99 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -31,13 +31,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -256,6 +256,9 @@ struct asus_wmi { int tpd_led_wk; struct led_classdev kbd_led; int kbd_led_wk; + bool kbd_led_notify; + bool kbd_led_avail; + bool kbd_led_registered; struct led_classdev lightbar_led; int lightbar_led_wk; struct led_classdev micmute_led; @@ -264,6 +267,7 @@ struct asus_wmi { struct work_struct tpd_led_work; struct work_struct wlan_led_work; struct work_struct lightbar_led_work; + struct work_struct kbd_led_work; struct asus_rfkill wlan; struct asus_rfkill bluetooth; @@ -1615,6 +1619,106 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus) /* LEDs ***********************************************************************/ +struct asus_hid_ref { + struct list_head listeners; + struct asus_wmi *asus; + /* Protects concurrent access from hid-asus and asus-wmi to leds */ + spinlock_t lock; +}; + +static struct asus_hid_ref asus_ref = { + .listeners = LIST_HEAD_INIT(asus_ref.listeners), + .asus = NULL, + /* + * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other + * asus variables are read-only after .asus is set. + * + * The led cdev device is not protected because it calls backlight_get + * during initialization, which would result in a nested lock attempt. + * + * The led cdev is safe to access without a lock because if + * kbd_led_avail is true it is initialized before .asus is set and never + * changed until .asus is dropped. If kbd_led_avail is false, the led + * cdev is registered by the workqueue, which is single-threaded and + * cancelled before asus-wmi would access the led cdev to unregister it. + * + * A spinlock is used, because the protected variables can be accessed + * from an IRQ context from asus-hid. + */ + .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock), +}; + +/* + * Allows registering hid-asus listeners that want to be notified of + * keyboard backlight changes. + */ +int asus_hid_register_listener(struct asus_hid_listener *bdev) +{ + struct asus_wmi *asus; + + guard(spinlock_irqsave)(&asus_ref.lock); + list_add_tail(&bdev->list, &asus_ref.listeners); + asus = asus_ref.asus; + if (asus) + queue_work(asus->led_workqueue, &asus->kbd_led_work); + return 0; +} +EXPORT_SYMBOL_GPL(asus_hid_register_listener); + +/* + * Allows unregistering hid-asus listeners that were added with + * asus_hid_register_listener(). + */ +void asus_hid_unregister_listener(struct asus_hid_listener *bdev) +{ + guard(spinlock_irqsave)(&asus_ref.lock); + list_del(&bdev->list); +} +EXPORT_SYMBOL_GPL(asus_hid_unregister_listener); + +static void do_kbd_led_set(struct led_classdev *led_cdev, int value); + +static void kbd_led_update_all(struct work_struct *work) +{ + struct asus_wmi *asus; + bool registered, notify; + int ret, value; + + asus = container_of(work, struct asus_wmi, kbd_led_work); + + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + registered = asus->kbd_led_registered; + value = asus->kbd_led_wk; + notify = asus->kbd_led_notify; + } + + if (!registered) { + /* + * This workqueue runs under asus-wmi, which means probe has + * completed and asus-wmi will keep running until it finishes. + * Therefore, we can safely register the LED without holding + * a spinlock. + */ + ret = devm_led_classdev_register(&asus->platform_device->dev, + &asus->kbd_led); + if (!ret) { + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_registered = true; + } else { + pr_warn("Failed to register keyboard backlight LED: %d\n", ret); + return; + } + } + + if (value >= 0) + do_kbd_led_set(&asus->kbd_led, value); + if (notify) { + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_notify = false; + led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value); + } +} + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1661,7 +1765,8 @@ static void kbd_led_update(struct asus_wmi *asus) { int ctrl_param = 0; - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); + scoped_guard(spinlock_irqsave, &asus_ref.lock) + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); } @@ -1694,14 +1799,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) static void do_kbd_led_set(struct led_classdev *led_cdev, int value) { + struct asus_hid_listener *listener; struct asus_wmi *asus; int max_level; asus = container_of(led_cdev, struct asus_wmi, kbd_led); max_level = asus->kbd_led.max_brightness; - asus->kbd_led_wk = clamp_val(value, 0, max_level); - kbd_led_update(asus); + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_wk = clamp_val(value, 0, max_level); + + if (asus->kbd_led_avail) + kbd_led_update(asus); + + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + list_for_each_entry(listener, &asus_ref.listeners, list) + listener->brightness_set(listener, asus->kbd_led_wk); + } } static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -1716,10 +1830,11 @@ static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value) { - struct led_classdev *led_cdev = &asus->kbd_led; - - do_kbd_led_set(led_cdev, value); - led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk); + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + asus->kbd_led_wk = value; + asus->kbd_led_notify = true; + } + queue_work(asus->led_workqueue, &asus->kbd_led_work); } static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) @@ -1729,10 +1844,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + if (!asus->kbd_led_avail) + return asus->kbd_led_wk; + } + retval = kbd_led_read(asus, &value, NULL); if (retval < 0) return retval; + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_wk = value; + return value; } @@ -1844,7 +1967,9 @@ static int camera_led_set(struct led_classdev *led_cdev, static void asus_wmi_led_exit(struct asus_wmi *asus) { - led_classdev_unregister(&asus->kbd_led); + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus_ref.asus = NULL; + led_classdev_unregister(&asus->tpd_led); led_classdev_unregister(&asus->wlan_led); led_classdev_unregister(&asus->lightbar_led); @@ -1882,22 +2007,26 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) { - pr_info("using asus-wmi for asus::kbd_backlight\n"); - asus->kbd_led_wk = led_val; - asus->kbd_led.name = "asus::kbd_backlight"; - asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; - asus->kbd_led.brightness_set_blocking = kbd_led_set; - asus->kbd_led.brightness_get = kbd_led_get; - asus->kbd_led.max_brightness = 3; + asus->kbd_led.name = "asus::kbd_backlight"; + asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; + asus->kbd_led.brightness_set_blocking = kbd_led_set; + asus->kbd_led.brightness_get = kbd_led_get; + asus->kbd_led.max_brightness = 3; + asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL); + INIT_WORK(&asus->kbd_led_work, kbd_led_update_all); + if (asus->kbd_led_avail) { + asus->kbd_led_wk = led_val; if (num_rgb_groups != 0) asus->kbd_led.groups = kbd_rgb_mode_groups; + } else { + asus->kbd_led_wk = -1; + } - rv = led_classdev_register(&asus->platform_device->dev, - &asus->kbd_led); - if (rv) - goto error; + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + asus_ref.asus = asus; + if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners)) + queue_work(asus->led_workqueue, &asus->kbd_led_work); } if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED) @@ -4372,6 +4501,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj) static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) { + enum led_brightness led_value; unsigned int key_value = 1; bool autorelease = 1; @@ -4388,19 +4518,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + scoped_guard(spinlock_irqsave, &asus_ref.lock) + led_value = asus->kbd_led_wk; + if (code == NOTIFY_KBD_BRTUP) { - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, led_value + 1); return; } if (code == NOTIFY_KBD_BRTDWN) { - kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); + kbd_led_set_by_kbd(asus, led_value - 1); return; } if (code == NOTIFY_KBD_BRTTOGGLE) { - if (asus->kbd_led_wk == asus->kbd_led.max_brightness) + if (led_value >= asus->kbd_led.max_brightness) kbd_led_set_by_kbd(asus, 0); else - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, led_value + 1); return; } diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 419491d4abca..d347cffd05d5 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -172,12 +172,20 @@ enum asus_ally_mcu_hack { ASUS_WMI_ALLY_MCU_HACK_DISABLED, }; +/* Used to notify hid-asus when asus-wmi changes keyboard backlight */ +struct asus_hid_listener { + struct list_head list; + void (*brightness_set)(struct asus_hid_listener *listener, int brightness); +}; + #if IS_REACHABLE(CONFIG_ASUS_WMI) void set_ally_mcu_hack(enum asus_ally_mcu_hack status); void set_ally_mcu_powersave(bool enabled); int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval); int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); +int asus_hid_register_listener(struct asus_hid_listener *cdev); +void asus_hid_unregister_listener(struct asus_hid_listener *cdev); #else static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status) { @@ -198,6 +206,13 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, { return -ENODEV; } +static inline int asus_hid_register_listener(struct asus_hid_listener *bdev) +{ + return -ENODEV; +} +static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev) +{ +} #endif #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ -- cgit v1.2.3 From d3133ccaf53694498b9827c5f9dbae52fc199d28 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Thu, 22 Jan 2026 08:50:42 +0100 Subject: platform/x86: asus-wmi: remove unused keyboard backlight quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quirk for selecting whether keyboard backlight should be controlled by HID or WMI is not needed anymore, so remove the file containing it. Reviewed-by: Denis Benato Acked-by: Benjamin Tissoires Signed-off-by: Antheas Kapenekakis Link: https://patch.msgid.link/20260122075044.5070-10-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../linux/platform_data/x86/asus-wmi-leds-ids.h | 50 ---------------------- 1 file changed, 50 deletions(-) delete mode 100644 include/linux/platform_data/x86/asus-wmi-leds-ids.h (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/x86/asus-wmi-leds-ids.h b/include/linux/platform_data/x86/asus-wmi-leds-ids.h deleted file mode 100644 index 034a039c4e37..000000000000 --- a/include/linux/platform_data/x86/asus-wmi-leds-ids.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H -#define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H - -#include -#include - -/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ -#if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS) -static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "GA403U"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "GU605M"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "RC71L"), - }, - }, - { }, -}; -#endif - -#endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */ -- cgit v1.2.3 From 7525566abd360ca425e43dd3f7f09fa3445dae17 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Thu, 22 Jan 2026 08:50:43 +0100 Subject: platform/x86: asus-wmi: add keyboard brightness event handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The keyboard brightness control of Asus WMI keyboards is handled in kernel, which leads to the shortcut going from brightness 0, to 1, to 2, and 3. However, for HID keyboards it is exposed as a key and handled by the user's desktop environment. For the toggle button, this means that brightness control becomes on/off. In addition, in the absence of a DE, the keyboard brightness does not work. Therefore, expose an event handler for the keyboard brightness control which can then be used by hid-asus. Since this handler is called from an interrupt context, defer the actual work to a workqueue. In the process, introduce ASUS_EV_MAX_BRIGHTNESS to hold the constant for maximum brightness since it is shared between hid-asus/asus-wmi. Reviewed-by: Luke D. Jones Tested-by: Luke D. Jones Acked-by: Benjamin Tissoires Reviewed-by: Denis Benato Signed-off-by: Antheas Kapenekakis Link: https://patch.msgid.link/20260122075044.5070-11-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 46 ++++++++++++++++++++++++++---- include/linux/platform_data/x86/asus-wmi.h | 13 +++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index c45846be3f99..4cbd7b9806dc 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1719,6 +1719,44 @@ static void kbd_led_update_all(struct work_struct *work) } } +/* + * This function is called from hid-asus to inform asus-wmi of brightness + * changes initiated by the keyboard backlight keys. + */ +int asus_hid_event(enum asus_hid_event event) +{ + struct asus_wmi *asus; + int brightness; + + guard(spinlock_irqsave)(&asus_ref.lock); + asus = asus_ref.asus; + if (!asus || !asus->kbd_led_registered) + return -EBUSY; + + brightness = asus->kbd_led_wk; + + switch (event) { + case ASUS_EV_BRTUP: + brightness += 1; + break; + case ASUS_EV_BRTDOWN: + brightness -= 1; + break; + case ASUS_EV_BRTTOGGLE: + if (brightness >= ASUS_EV_MAX_BRIGHTNESS) + brightness = 0; + else + brightness += 1; + break; + } + + asus->kbd_led_wk = clamp_val(brightness, 0, ASUS_EV_MAX_BRIGHTNESS); + asus->kbd_led_notify = true; + queue_work(asus->led_workqueue, &asus->kbd_led_work); + return 0; +} +EXPORT_SYMBOL_GPL(asus_hid_event); + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1801,13 +1839,11 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) { struct asus_hid_listener *listener; struct asus_wmi *asus; - int max_level; asus = container_of(led_cdev, struct asus_wmi, kbd_led); - max_level = asus->kbd_led.max_brightness; scoped_guard(spinlock_irqsave, &asus_ref.lock) - asus->kbd_led_wk = clamp_val(value, 0, max_level); + asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS); if (asus->kbd_led_avail) kbd_led_update(asus); @@ -2011,7 +2047,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; asus->kbd_led.brightness_set_blocking = kbd_led_set; asus->kbd_led.brightness_get = kbd_led_get; - asus->kbd_led.max_brightness = 3; + asus->kbd_led.max_brightness = ASUS_EV_MAX_BRIGHTNESS; asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL); INIT_WORK(&asus->kbd_led_work, kbd_led_update_all); @@ -4530,7 +4566,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } if (code == NOTIFY_KBD_BRTTOGGLE) { - if (led_value >= asus->kbd_led.max_brightness) + if (led_value >= ASUS_EV_MAX_BRIGHTNESS) kbd_led_set_by_kbd(asus, 0); else kbd_led_set_by_kbd(asus, led_value + 1); diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index d347cffd05d5..7b872b5d0960 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -178,6 +178,14 @@ struct asus_hid_listener { void (*brightness_set)(struct asus_hid_listener *listener, int brightness); }; +enum asus_hid_event { + ASUS_EV_BRTUP, + ASUS_EV_BRTDOWN, + ASUS_EV_BRTTOGGLE, +}; + +#define ASUS_EV_MAX_BRIGHTNESS 3 + #if IS_REACHABLE(CONFIG_ASUS_WMI) void set_ally_mcu_hack(enum asus_ally_mcu_hack status); void set_ally_mcu_powersave(bool enabled); @@ -186,6 +194,7 @@ int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); int asus_hid_register_listener(struct asus_hid_listener *cdev); void asus_hid_unregister_listener(struct asus_hid_listener *cdev); +int asus_hid_event(enum asus_hid_event event); #else static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status) { @@ -213,6 +222,10 @@ static inline int asus_hid_register_listener(struct asus_hid_listener *bdev) static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev) { } +static inline int asus_hid_event(enum asus_hid_event event) +{ + return -ENODEV; +} #endif #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ -- cgit v1.2.3 From 0077e9b985482e5c020468c6257f8508f68aa0b2 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 27 Jan 2026 19:27:02 -0800 Subject: iio: cros_ec: Allow enabling/disabling calibration mode 'calibrate' was a one-shot event sent to the sensor to calibrate itself. It is used on Bosch sensors (BMI160, BMA254). Light sensors work differently: They are first put in calibration mode, tests are run to collect information and calculate the calibration values to apply. Once done, the sensors are put back in normal mode. Accept boolean true and false (not just true) to enter/exit calibration state. Check "echo 0 > calibrate" is supported. Signed-off-by: Gwendal Grignou Reviewed-by: Nick Vaccaro Reviewed-by: Tzung-Bi Shih Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-cros-ec | 9 ++++++--- drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c | 6 +++--- include/linux/platform_data/cros_ec_commands.h | 12 +++++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec index 9e3926243797..3de1dfc98389 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec +++ b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec @@ -3,9 +3,12 @@ Date: July 2015 KernelVersion: 4.7 Contact: linux-iio@vger.kernel.org Description: - Writing '1' will perform a FOC (Fast Online Calibration). The - corresponding calibration offsets can be read from `*_calibbias` - entries. + Writing '1' either perform a FOC (Fast Online Calibration) or + enter calibration mode. + Writing '0` exits calibration mode. It is a NOP for FOC enabled + sensors. + The corresponding calibration offsets can be read from `*_calibbias` + entries. What: /sys/bus/iio/devices/iio:deviceX/id Date: September 2017 diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index ef53066b1735..5133755c2ea6 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -441,14 +441,14 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev, ret = kstrtobool(buf, &calibrate); if (ret < 0) return ret; - if (!calibrate) - return -EINVAL; mutex_lock(&st->cmd_lock); st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB; + st->param.perform_calib.enable = calibrate; ret = cros_ec_motion_send_host_cmd(st, 0); if (ret != 0) { - dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n"); + dev_warn(&indio_dev->dev, "Unable to calibrate sensor: %d\n", + ret); } else { /* Save values */ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++) diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 69294f79cc88..d363d60bb8a3 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2598,13 +2598,19 @@ struct ec_params_motion_sense { /* * Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA - * and MOTIONSENSE_CMD_PERFORM_CALIB. */ struct __ec_todo_unpacked { uint8_t sensor_num; - } info, info_3, data, fifo_flush, perform_calib, - list_activities; + } info, info_3, data, fifo_flush, list_activities; + /* + * Used for MOTIONSENSE_CMD_PERFORM_CALIB: + * Allow entering/exiting the calibration mode. + */ + struct __ec_todo_unpacked { + uint8_t sensor_num; + uint8_t enable; + } perform_calib; /* * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR * and MOTIONSENSE_CMD_SENSOR_RANGE. -- cgit v1.2.3 From 2d8251d98ce0be96d79aec708aa70cab2b4f6f17 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 30 Jan 2026 00:13:50 -0800 Subject: platform/chrome: lightbar: Report number of segments Add attribue `num_segments` to return the number of exposed LED segments in the lightbar. It can be smaller than the number of physical leds in the lightbar. Test: Check the attribute is present and returns a value when read. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20260130081351.487517-1-gwendal@google.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_lightbar.c | 43 ++++++++++++++++++++++++++ include/linux/platform_data/cros_ec_commands.h | 11 +++++++ 2 files changed, 54 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 3702baff5d4f..8ee761450394 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -180,6 +180,47 @@ static ssize_t version_show(struct device *dev, return sysfs_emit(buf, "%d %d\n", version, flags); } +static ssize_t num_segments_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ec_params_lightbar *param; + struct ec_response_lightbar *resp; + struct cros_ec_command *msg; + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + uint32_t num = 0; + int ret; + + ret = lb_throttle(); + if (ret) + return ret; + + msg = alloc_lightbar_cmd_msg(ec); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_lightbar *)msg->data; + param->cmd = LIGHTBAR_CMD_GET_PARAMS_V3; + msg->outsize = sizeof(param->cmd); + msg->insize = sizeof(resp->get_params_v3); + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0 && ret != -EINVAL) + goto exit; + + if (msg->result == EC_RES_SUCCESS) { + resp = (struct ec_response_lightbar *)msg->data; + num = resp->get_params_v3.reported_led_num; + } + + /* + * Anything else (ie, EC_RES_INVALID_COMMAND) - no direct control over + * LEDs, return that no leds are supported. + */ + ret = sysfs_emit(buf, "%u\n", num); +exit: + kfree(msg); + return ret; +} + static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -512,6 +553,7 @@ static ssize_t userspace_control_store(struct device *dev, /* Module initialization */ static DEVICE_ATTR_RW(interval_msec); +static DEVICE_ATTR_RO(num_segments); static DEVICE_ATTR_RO(version); static DEVICE_ATTR_WO(brightness); static DEVICE_ATTR_WO(led_rgb); @@ -521,6 +563,7 @@ static DEVICE_ATTR_RW(userspace_control); static struct attribute *__lb_cmds_attrs[] = { &dev_attr_interval_msec.attr, + &dev_attr_num_segments.attr, &dev_attr_version.attr, &dev_attr_brightness.attr, &dev_attr_led_rgb.attr, diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 69294f79cc88..9cbf024f56c3 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2005,6 +2005,14 @@ struct lightbar_params_v2_colors { struct rgb_s color[8]; /* 0-3 are Google colors */ } __ec_todo_packed; +struct lightbar_params_v3 { + /* + * Number of LEDs reported by the EC. + * May be less than the actual number of LEDs in the lightbar. + */ + uint8_t reported_led_num; +} __ec_todo_packed; + /* Lightbar program. */ #define EC_LB_PROG_LEN 192 struct lightbar_program { @@ -2086,6 +2094,8 @@ struct ec_response_lightbar { struct lightbar_params_v2_thresholds get_params_v2_thlds; struct lightbar_params_v2_colors get_params_v2_colors; + struct lightbar_params_v3 get_params_v3; + struct __ec_todo_unpacked { uint32_t num; uint32_t flags; @@ -2143,6 +2153,7 @@ enum lightbar_command { LIGHTBAR_CMD_SET_PARAMS_V2_THRESHOLDS = 31, LIGHTBAR_CMD_GET_PARAMS_V2_COLORS = 32, LIGHTBAR_CMD_SET_PARAMS_V2_COLORS = 33, + LIGHTBAR_CMD_GET_PARAMS_V3 = 34, LIGHTBAR_NUM_CMDS }; -- cgit v1.2.3 From 9600b8bdbfe48bb51865be743450160577d2bae2 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 30 Jan 2026 00:13:51 -0800 Subject: platform/chrome: lightbar: Add support for large sequence Current sequences are limited to 192 bytes. Increase support to whatever the EC support. If the sequence is too long, the EC will return an OVERFLOW error. Test: Check sending a large sequence is received by the EC. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20260130081351.487517-2-gwendal@google.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_lightbar.c | 88 ++++++++++++++++++-------- include/linux/platform_data/cros_ec_commands.h | 13 ++++ 2 files changed, 74 insertions(+), 27 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 8ee761450394..2d1aa6edda1a 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -37,6 +37,11 @@ static bool userspace_control; */ static bool has_manual_suspend; +/* + * Lightbar version + */ +static int lb_version; + static ssize_t interval_msec_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -93,11 +98,8 @@ out: static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec) { + int len = max(ec->ec_dev->max_response, ec->ec_dev->max_request); struct cros_ec_command *msg; - int len; - - len = max(sizeof(struct ec_params_lightbar), - sizeof(struct ec_response_lightbar)); msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); if (!msg) @@ -105,6 +107,11 @@ static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec) msg->version = 0; msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset; + /* + * Default sizes for regular commands. + * Can be set smaller to optimize transfer, + * larger when sending large light sequences. + */ msg->outsize = sizeof(struct ec_params_lightbar); msg->insize = sizeof(struct ec_response_lightbar); @@ -471,10 +478,11 @@ exit: static ssize_t program_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int extra_bytes, max_size, ret; + size_t extra_bytes, max_size; struct ec_params_lightbar *param; struct cros_ec_command *msg; struct cros_ec_dev *ec = to_cros_ec_dev(dev); + int ret; /* * We might need to reject the program for size reasons. The EC @@ -482,14 +490,22 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr, * and send a program that is too big for the protocol. In order * to ensure the latter, we also need to ensure we have extra bytes * to represent the rest of the packet. + * With V3, larger program can be sent, limited only by the EC. + * Only the protocol limit the payload size. */ - extra_bytes = sizeof(*param) - sizeof(param->set_program.data); - max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes); - if (count > max_size) { - dev_err(dev, "Program is %u bytes, too long to send (max: %u)", - (unsigned int)count, max_size); - - return -EINVAL; + if (lb_version < 3) { + extra_bytes = sizeof(*param) - sizeof(param->set_program.data); + max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes); + if (count > max_size) { + dev_err(dev, "Program is %zu bytes, too long to send (max: %zu)", + count, max_size); + + return -EINVAL; + } + } else { + extra_bytes = offsetof(typeof(*param), set_program_ex) + + sizeof(param->set_program_ex); + max_size = ec->ec_dev->max_request - extra_bytes; } msg = alloc_lightbar_cmd_msg(ec); @@ -499,26 +515,44 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr, ret = lb_throttle(); if (ret) goto exit; + param = (struct ec_params_lightbar *)msg->data; - dev_info(dev, "Copying %zu byte program to EC", count); + if (lb_version < 3) { + dev_info(dev, "Copying %zu byte program to EC", count); - param = (struct ec_params_lightbar *)msg->data; - param->cmd = LIGHTBAR_CMD_SET_PROGRAM; + param->cmd = LIGHTBAR_CMD_SET_PROGRAM; - param->set_program.size = count; - memcpy(param->set_program.data, buf, count); + param->set_program.size = count; + memcpy(param->set_program.data, buf, count); - /* - * We need to set the message size manually or else it will use - * EC_LB_PROG_LEN. This might be too long, and the program - * is unlikely to use all of the space. - */ - msg->outsize = count + extra_bytes; + /* + * We need to set the message size manually or else it will use + * EC_LB_PROG_LEN. This might be too long, and the program + * is unlikely to use all of the space. + */ + msg->outsize = count + extra_bytes; - ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); - if (ret < 0) - goto exit; + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) + goto exit; + } else { + size_t offset = 0; + size_t payload = 0; + + param->cmd = LIGHTBAR_CMD_SET_PROGRAM_EX; + while (offset < count) { + payload = min(max_size, count - offset); + param->set_program_ex.offset = offset; + param->set_program_ex.size = payload; + memcpy(param->set_program_ex.data, &buf[offset], payload); + msg->outsize = payload + extra_bytes; + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) + goto exit; + offset += payload; + } + } ret = count; exit: kfree(msg); @@ -596,7 +630,7 @@ static int cros_ec_lightbar_probe(struct platform_device *pd) * Ask then for the lightbar version, if it's 0 then the 'cros_ec' * doesn't have a lightbar. */ - if (!get_lightbar_version(ec_dev, NULL, NULL)) + if (!get_lightbar_version(ec_dev, &lb_version, NULL)) return -ENODEV; /* Take control of the lightbar from the EC. */ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 9cbf024f56c3..144243143034 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2020,6 +2020,17 @@ struct lightbar_program { uint8_t data[EC_LB_PROG_LEN]; } __ec_todo_unpacked; +/* + * Lightbar program for large sequences. Sequences are sent in pieces, with + * increasing offset. The sequences are still limited by the amount reserved in + * EC RAM. + */ +struct lightbar_program_ex { + uint16_t offset; + uint8_t size; + uint8_t data[0]; +} __ec_todo_unpacked; + struct ec_params_lightbar { uint8_t cmd; /* Command (see enum lightbar_command) */ union { @@ -2066,6 +2077,7 @@ struct ec_params_lightbar { struct lightbar_params_v2_colors set_v2par_colors; struct lightbar_program set_program; + struct lightbar_program_ex set_program_ex; }; } __ec_todo_packed; @@ -2154,6 +2166,7 @@ enum lightbar_command { LIGHTBAR_CMD_GET_PARAMS_V2_COLORS = 32, LIGHTBAR_CMD_SET_PARAMS_V2_COLORS = 33, LIGHTBAR_CMD_GET_PARAMS_V3 = 34, + LIGHTBAR_CMD_SET_PROGRAM_EX = 35, LIGHTBAR_NUM_CMDS }; -- cgit v1.2.3 From d095d2dbcc8df60ede1674e28ce0f0a478d37535 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 12 Jan 2026 17:47:58 -0500 Subject: MIPS: copy pic32.h header file from asm/mach-pic32/ to include/platform-data/ There are currently some pic32 MIPS drivers that are in tree, and are only configured to be compiled on the pic32 platform. There's a risk of breaking some of these drivers when migrating drivers away from legacy APIs. It happened to me with a pic32 clk driver. Let's go ahead and copy the MIPS pic32.h header to include/linux/platform_data/, and make a minor update to allow compiling this on other architectures. This will make it easier, and cleaner to enable COMPILE_TEST for some of these pic32 drivers. The asm variant of the header file will be dropped once all drivers have been updated. Link: https://lore.kernel.org/linux-clk/CABx5tq+eOocJ41X-GSgkGy6S+s+Am1yCS099wqP695NtwALTmg@mail.gmail.com/T/ Signed-off-by: Brian Masney Signed-off-by: Thomas Bogendoerfer --- include/linux/platform_data/pic32.h | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 include/linux/platform_data/pic32.h (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/pic32.h b/include/linux/platform_data/pic32.h new file mode 100644 index 000000000000..f0b395fdb784 --- /dev/null +++ b/include/linux/platform_data/pic32.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Joshua Henderson + * Copyright (C) 2015 Microchip Technology Inc. All rights reserved. + */ +#ifndef __LINUX_PLATFORM_DATA_PIC32_H +#define __LINUX_PLATFORM_DATA_PIC32_H + +#include + +/* + * PIC32 register offsets for SET/CLR/INV where supported. + */ +#define PIC32_CLR(_reg) ((_reg) + 0x04) +#define PIC32_SET(_reg) ((_reg) + 0x08) +#define PIC32_INV(_reg) ((_reg) + 0x0C) + +/* + * PIC32 Base Register Offsets + */ +#define PIC32_BASE_CONFIG 0x1f800000 +#define PIC32_BASE_OSC 0x1f801200 +#define PIC32_BASE_RESET 0x1f801240 +#define PIC32_BASE_PPS 0x1f801400 +#define PIC32_BASE_UART 0x1f822000 +#define PIC32_BASE_PORT 0x1f860000 +#define PIC32_BASE_DEVCFG2 0x1fc4ff44 + +#if defined(CONFIG_MACH_PIC32) +/* Register unlock sequence required for some register access. */ +void pic32_syskey_unlock_debug(const char *fn, const ulong ln); +#define pic32_syskey_unlock() \ + pic32_syskey_unlock_debug(__func__, __LINE__) +#else +/* COMPILE_TEST on all other architectures */ +#define pic32_syskey_unlock() +#endif + +#endif /* __LINUX_PLATFORM_DATA_PIC32_H */ -- cgit v1.2.3 From d55493f6d2dae3a8e8b50fe82e947781a7735c51 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Mon, 2 Feb 2026 02:06:21 -0800 Subject: platform/chrome: lightbar: Fix lightbar_program_ex alignment Make sure sub-command of lightbar command starts with a 8bit parameter to ensure alignment. Fixes: 9600b8bdbfe4 ("platform/chrome: lightbar: Add support for large sequence") Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20260202100621.3608437-1-gwendal@google.com Signed-off-by: Tzung-Bi Shih --- include/linux/platform_data/cros_ec_commands.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 144243143034..bddc7568158e 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2026,10 +2026,10 @@ struct lightbar_program { * EC RAM. */ struct lightbar_program_ex { - uint16_t offset; uint8_t size; + uint16_t offset; uint8_t data[0]; -} __ec_todo_unpacked; +} __ec_todo_packed; struct ec_params_lightbar { uint8_t cmd; /* Command (see enum lightbar_command) */ -- cgit v1.2.3 From 520e345dfdab89aed4a0ad98d5ec35086661a11a Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 3 Feb 2026 19:48:48 -0800 Subject: platform/chrome: lightbar: Use flexible array member Variable arrays should be defined as [], not [0], otherwise the kernel complains: memcpy : detected field-spanning write (size 9) of single field "param->set_program_ex.data" at drivers/platform/chrome/cros_ec_lightbar.c:603 (size 0) Fixes: 9600b8bdbfe4 ("platform/chrome: lightbar: Add support for large sequence") Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20260204034848.697033-1-gwendal@google.com Signed-off-by: Tzung-Bi Shih --- include/linux/platform_data/cros_ec_commands.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index bddc7568158e..8080a6fc6c8c 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2028,7 +2028,7 @@ struct lightbar_program { struct lightbar_program_ex { uint8_t size; uint16_t offset; - uint8_t data[0]; + uint8_t data[]; } __ec_todo_packed; struct ec_params_lightbar { -- cgit v1.2.3 From 9db327083f7e0da702e2ec0169f8a34f3576f371 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Mon, 2 Feb 2026 18:37:03 -0600 Subject: ASoC: ti: davinci-mcasp: Add asynchronous mode support McASP has dedicated clock & frame sync registers for both transmit and receive. Currently McASP driver only supports synchronous behavior and couples both TX & RX settings. Add logic that enables asynchronous mode via ti,async-mode property. In async mode, playback & record can be done simultaneously with different audio configurations (tdm slots, tdm width, audio bit depth). Note the ability to have different tx/rx DSP formats (i2s, dsp_a, etc.), while possible in hardware, remains to be a gap as it require changes to the corresponding machine driver interface. Existing IIS (sync mode) and DIT mode logic remains mostly unchanged. Exceptions are IIS mode logic that previously assumed sync mode, which has now been made aware of the distinction. And shared logic across all modes also now checks for McASP tx/rx-specific driver attributes. Those attributes have been populated according to the original extent, ensuring no divergence in functionality. Constraints no longer applicable for async mode are skipped. Clock selection options have also been added to include rx/tx-only clk_ids, exposing independent configuration via the machine driver as well. Note that asynchronous mode is not applicable for McASP in DIT mode, which is a transmitter-only mode to interface w/ self-clocking formats. Signed-off-by: Sen Wang Acked-by: Peter Ujfalusi Tested-by: Paresh Bhagat Link: https://patch.msgid.link/20260203003703.2334443-5-sen@ti.com Signed-off-by: Mark Brown --- include/linux/platform_data/davinci_asp.h | 3 +- sound/soc/ti/davinci-mcasp.c | 489 ++++++++++++++++++++++++------ sound/soc/ti/davinci-mcasp.h | 10 + 3 files changed, 401 insertions(+), 101 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index b9c8520b4bd3..509c5592aab0 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -59,7 +59,8 @@ struct davinci_mcasp_pdata { bool i2s_accurate_sck; /* McASP specific fields */ - int tdm_slots; + int tdm_slots_tx; + int tdm_slots_rx; u8 op_mode; u8 dismod; u8 num_serializer; diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 12b7d0c65624..2d260fbc9b83 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -71,6 +71,7 @@ struct davinci_mcasp_context { struct davinci_mcasp_ruledata { struct davinci_mcasp *mcasp; int serializers; + int stream; }; struct davinci_mcasp { @@ -88,21 +89,27 @@ struct davinci_mcasp { bool missing_audio_param; /* McASP specific data */ - int tdm_slots; + int tdm_slots_tx; + int tdm_slots_rx; u32 tdm_mask[2]; - int slot_width; + int slot_width_tx; + int slot_width_rx; u8 op_mode; u8 dismod; u8 num_serializer; u8 *serial_dir; u8 version; - u8 bclk_div; + u8 bclk_div_tx; + u8 bclk_div_rx; int streams; u32 irq_request[2]; - int sysclk_freq; + unsigned int sysclk_freq_tx; + unsigned int sysclk_freq_rx; bool bclk_master; - u32 auxclk_fs_ratio; + bool async_mode; + u32 auxclk_fs_ratio_tx; + u32 auxclk_fs_ratio_rx; unsigned long pdir; /* Pin direction bitfield */ @@ -204,6 +211,27 @@ static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) } } +static inline void mcasp_set_clk_pdir_stream(struct davinci_mcasp *mcasp, + int stream, bool enable) +{ + u32 bit, bit_end; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit = PIN_BIT_ACLKX; + bit_end = PIN_BIT_AFSX + 1; + } else { + bit = PIN_BIT_ACLKR; + bit_end = PIN_BIT_AFSR + 1; + } + + for_each_set_bit_from(bit, &mcasp->pdir, bit_end) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) { u32 bit; @@ -216,6 +244,36 @@ static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) } } +static inline int mcasp_get_tdm_slots(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->tdm_slots_tx : mcasp->tdm_slots_rx; +} + +static inline int mcasp_get_slot_width(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->slot_width_tx : mcasp->slot_width_rx; +} + +static inline unsigned int mcasp_get_sysclk_freq(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->sysclk_freq_tx : mcasp->sysclk_freq_rx; +} + +static inline unsigned int mcasp_get_bclk_div(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->bclk_div_tx : mcasp->bclk_div_rx; +} + +static inline unsigned int mcasp_get_auxclk_fs_ratio(struct davinci_mcasp *mcasp, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + mcasp->auxclk_fs_ratio_tx : mcasp->auxclk_fs_ratio_rx; +} + static void mcasp_start_rx(struct davinci_mcasp *mcasp) { if (mcasp->rxnumevt) { /* enable FIFO */ @@ -231,13 +289,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) /* * When ASYNC == 0 the transmit and receive sections operate * synchronously from the transmit clock and frame sync. We need to make - * sure that the TX signlas are enabled when starting reception. + * sure that the TX signals are enabled when starting reception, + * when the McASP is the producer. */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) { mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); } - mcasp_set_clk_pdir(mcasp, true); + if (mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir(mcasp, true); + else + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, true); /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -268,7 +330,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); - mcasp_set_clk_pdir(mcasp, true); + if (mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir(mcasp, true); + else + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, true); /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); @@ -311,9 +376,17 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) /* * In synchronous mode stop the TX clocks if no other stream is * running + * Otherwise in async mode only stop RX clocks */ - if (!mcasp->streams) + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_clk_pdir(mcasp, false); + else if (!mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, false); + /* + * When McASP is the producer and operating in synchronous mode, + * stop the transmit clocks if no other stream is running. As + * tx & rx operate synchronously from the transmit clock. + */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); @@ -338,11 +411,14 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) /* * In synchronous mode keep TX clocks running if the capture stream is * still running. + * Otherwise in async mode only stop TX clocks */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; - if (!mcasp->streams) + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_clk_pdir(mcasp, false); + else if (!mcasp_is_synchronous(mcasp)) + mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); @@ -626,13 +702,39 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, AHCLKRDIV(div - 1), AHCLKRDIV_MASK); break; + case MCASP_CLKDIV_AUXCLK_TXONLY: /* MCLK divider for TX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXDIV(div - 1), AHCLKXDIV_MASK); + break; + + case MCASP_CLKDIV_AUXCLK_RXONLY: /* MCLK divider for RX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRDIV(div - 1), AHCLKRDIV_MASK); + break; + case MCASP_CLKDIV_BCLK: /* BCLK divider */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXDIV(div - 1), ACLKXDIV_MASK); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, + ACLKRDIV(div - 1), ACLKRDIV_MASK); + if (explicit) { + mcasp->bclk_div_tx = div; + mcasp->bclk_div_rx = div; + } + break; + + case MCASP_CLKDIV_BCLK_TXONLY: /* BCLK divider for TX only */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, + ACLKXDIV(div - 1), ACLKXDIV_MASK); + if (explicit) + mcasp->bclk_div_tx = div; + break; + + case MCASP_CLKDIV_BCLK_RXONLY: /* BCLK divider for RX only */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRDIV(div - 1), ACLKRDIV_MASK); if (explicit) - mcasp->bclk_div = div; + mcasp->bclk_div_rx = div; break; case MCASP_CLKDIV_BCLK_FS_RATIO: @@ -646,11 +748,33 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, * tdm_slot width by dividing the ratio by the * number of configured tdm slots. */ - mcasp->slot_width = div / mcasp->tdm_slots; - if (div % mcasp->tdm_slots) + mcasp->slot_width_tx = div / mcasp->tdm_slots_tx; + if (div % mcasp->tdm_slots_tx) dev_warn(mcasp->dev, - "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", - __func__, div, mcasp->tdm_slots); + "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots", + __func__, div, mcasp->tdm_slots_tx); + + mcasp->slot_width_rx = div / mcasp->tdm_slots_rx; + if (div % mcasp->tdm_slots_rx) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots", + __func__, div, mcasp->tdm_slots_rx); + break; + + case MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY: + mcasp->slot_width_tx = div / mcasp->tdm_slots_tx; + if (div % mcasp->tdm_slots_tx) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots", + __func__, div, mcasp->tdm_slots_tx); + break; + + case MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY: + mcasp->slot_width_rx = div / mcasp->tdm_slots_rx; + if (div % mcasp->tdm_slots_rx) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots", + __func__, div, mcasp->tdm_slots_rx); break; default: @@ -684,6 +808,20 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + mcasp->sysclk_freq_rx = freq; + break; + case MCASP_CLK_HCLK_AHCLK_TXONLY: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + break; + case MCASP_CLK_HCLK_AHCLK_RXONLY: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + clear_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_rx = freq; break; case MCASP_CLK_HCLK_AUXCLK: mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, @@ -691,22 +829,56 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + mcasp->sysclk_freq_rx = freq; + break; + case MCASP_CLK_HCLK_AUXCLK_TXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + break; + case MCASP_CLK_HCLK_AUXCLK_RXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_rx = freq; break; default: dev_err(mcasp->dev, "Invalid clk id: %d\n", clk_id); goto out; } } else { - /* Select AUXCLK as HCLK */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + /* McASP is clock master, select AUXCLK as HCLK */ + switch (clk_id) { + case MCASP_CLK_HCLK_AUXCLK_TXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + break; + case MCASP_CLK_HCLK_AUXCLK_RXONLY: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_rx = freq; + break; + default: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + set_bit(PIN_BIT_AHCLKR, &mcasp->pdir); + mcasp->sysclk_freq_tx = freq; + mcasp->sysclk_freq_rx = freq; + break; + } } /* * When AHCLK X/R is selected to be output it means that the HCLK is * the same clock - coming via AUXCLK. */ - mcasp->sysclk_freq = freq; out: pm_runtime_put(mcasp->dev); return 0; @@ -718,9 +890,11 @@ static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, { struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; unsigned int *list = (unsigned int *) cl->list; - int slots = mcasp->tdm_slots; + int slots; int i, count = 0; + slots = mcasp_get_tdm_slots(mcasp, stream); + if (mcasp->tdm_mask[stream]) slots = hweight32(mcasp->tdm_mask[stream]); @@ -785,27 +959,42 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - mcasp->tdm_slots = slots; + if (mcasp->async_mode) { + if (tx_mask) { + mcasp->tdm_slots_tx = slots; + mcasp->slot_width_tx = slot_width; + } + if (rx_mask) { + mcasp->tdm_slots_rx = slots; + mcasp->slot_width_rx = slot_width; + } + } else { + mcasp->tdm_slots_tx = slots; + mcasp->tdm_slots_rx = slots; + mcasp->slot_width_tx = slot_width; + mcasp->slot_width_rx = slot_width; + } + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; - mcasp->slot_width = slot_width; return davinci_mcasp_set_ch_constraints(mcasp); } static int davinci_config_channel_size(struct davinci_mcasp *mcasp, - int sample_width) + int sample_width, int stream) { u32 fmt; u32 tx_rotate, rx_rotate, slot_width; u32 mask = (1ULL << sample_width) - 1; - if (mcasp->slot_width) - slot_width = mcasp->slot_width; - else if (mcasp->max_format_width) - slot_width = mcasp->max_format_width; - else - slot_width = sample_width; + slot_width = mcasp_get_slot_width(mcasp, stream); + if (!slot_width) { + if (mcasp->max_format_width) + slot_width = mcasp->max_format_width; + else + slot_width = sample_width; + } /* * TX rotation: * right aligned formats: rotate w/ slot_width @@ -828,17 +1017,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, fmt = (slot_width >> 1) - 1; if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), - RXSSZ(0x0F)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), - TXSSZ(0x0F)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), - TXROT(7)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), - RXROT(7)); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); + if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), + TXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), + TXROT(7)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); + } + if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_CAPTURE) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), + RXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), + RXROT(7)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); + } } else { /* + * DIT mode only use TX serializers * according to the TRM it should be TXROT=0, this one works: * 16 bit to 23-8 (TXROT=6, rotate 24 bits) * 24 bit to 23-0 (TXROT=0, rotate 0 bits) @@ -851,10 +1046,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, TXROT(7)); mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(15), TXSSZ(0x0F)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); } - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); - return 0; } @@ -865,11 +1059,13 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, int i; u8 tx_ser = 0; u8 rx_ser = 0; - u8 slots = mcasp->tdm_slots; + int slots; u8 max_active_serializers, max_rx_serializers, max_tx_serializers; int active_serializers, numevt; u32 reg; + slots = mcasp_get_tdm_slots(mcasp, stream); + /* In DIT mode we only allow maximum of one serializers for now */ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) max_active_serializers = 1; @@ -997,7 +1193,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, u32 mask = 0; u32 busel = 0; - total_slots = mcasp->tdm_slots; + total_slots = mcasp_get_tdm_slots(mcasp, stream); /* * If more than one serializer is needed, then use them with @@ -1028,7 +1224,10 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, mask |= (1 << i); } - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + if (mcasp->async_mode) + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); if (!mcasp->dat_port) busel = TXSEL; @@ -1127,16 +1326,33 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, unsigned int sysclk_freq, - unsigned int bclk_freq, bool set) + unsigned int bclk_freq, + int stream, + bool set) { - u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG); int div = sysclk_freq / bclk_freq; int rem = sysclk_freq % bclk_freq; int error_ppm; int aux_div = 1; + int bclk_div_id, auxclk_div_id; + bool auxclk_enabled; + + if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) { + auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE; + bclk_div_id = MCASP_CLKDIV_BCLK_RXONLY; + auxclk_div_id = MCASP_CLKDIV_AUXCLK_RXONLY; + } else if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_PLAYBACK) { + auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; + bclk_div_id = MCASP_CLKDIV_BCLK_TXONLY; + auxclk_div_id = MCASP_CLKDIV_AUXCLK_TXONLY; + } else { + auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE; + bclk_div_id = MCASP_CLKDIV_BCLK; + auxclk_div_id = MCASP_CLKDIV_AUXCLK; + } if (div > (ACLKXDIV_MASK + 1)) { - if (reg & AHCLKXE) { + if (auxclk_enabled) { aux_div = div / (ACLKXDIV_MASK + 1); if (div % (ACLKXDIV_MASK + 1)) aux_div++; @@ -1166,10 +1382,10 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", error_ppm); - __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0); - if (reg & AHCLKXE) - __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK, - aux_div, 0); + __davinci_mcasp_set_clkdiv(mcasp, bclk_div_id, div, false); + if (auxclk_enabled) + __davinci_mcasp_set_clkdiv(mcasp, auxclk_div_id, + aux_div, false); } return error_ppm; @@ -1220,6 +1436,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int channels = params_channels(params); int period_size = params_period_size(params); int ret; + unsigned int sysclk_freq = mcasp_get_sysclk_freq(mcasp, substream->stream); switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: @@ -1260,22 +1477,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, * If mcasp is BCLK master, and a BCLK divider was not provided by * the machine driver, we need to calculate the ratio. */ - if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - int slots = mcasp->tdm_slots; + if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 && + sysclk_freq) { + int slots, slot_width; int rate = params_rate(params); int sbits = params_width(params); unsigned int bclk_target; - if (mcasp->slot_width) - sbits = mcasp->slot_width; + slots = mcasp_get_tdm_slots(mcasp, substream->stream); + + slot_width = mcasp_get_slot_width(mcasp, substream->stream); + if (slot_width) + sbits = slot_width; if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) bclk_target = rate * sbits * slots; else bclk_target = rate * 128; - davinci_mcasp_calc_clk_div(mcasp, mcasp->sysclk_freq, - bclk_target, true); + davinci_mcasp_calc_clk_div(mcasp, sysclk_freq, + bclk_target, substream->stream, true); } ret = mcasp_common_hw_param(mcasp, substream->stream, @@ -1292,9 +1513,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - davinci_config_channel_size(mcasp, word_length); + davinci_config_channel_size(mcasp, word_length, substream->stream); - if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { + /* Channel constraints are disabled for async mode */ + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE && !mcasp->async_mode) { mcasp->channels = channels; if (!mcasp->max_format_width) mcasp->max_format_width = word_length; @@ -1338,7 +1560,7 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params, snd_pcm_format_t i; snd_mask_none(&nfmt); - slot_width = rd->mcasp->slot_width; + slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream); pcm_for_each_format(i) { if (snd_mask_test_format(fmt, i)) { @@ -1388,12 +1610,15 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval *ri = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); int sbits = params_width(params); - int slots = rd->mcasp->tdm_slots; + int slots, slot_width; struct snd_interval range; int i; - if (rd->mcasp->slot_width) - sbits = rd->mcasp->slot_width; + slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream); + + slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream); + if (slot_width) + sbits = slot_width; snd_interval_any(&range); range.empty = 1; @@ -1403,16 +1628,17 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, uint bclk_freq = sbits * slots * davinci_mcasp_dai_rates[i]; unsigned int sysclk_freq; + unsigned int ratio; int ppm; - if (rd->mcasp->auxclk_fs_ratio) - sysclk_freq = davinci_mcasp_dai_rates[i] * - rd->mcasp->auxclk_fs_ratio; + ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream); + if (ratio) + sysclk_freq = davinci_mcasp_dai_rates[i] * ratio; else - sysclk_freq = rd->mcasp->sysclk_freq; + sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream); ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq, - bclk_freq, false); + bclk_freq, rd->stream, false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { if (range.empty) { range.min = davinci_mcasp_dai_rates[i]; @@ -1438,30 +1664,34 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask nfmt; int rate = params_rate(params); - int slots = rd->mcasp->tdm_slots; + int slots; int count = 0; snd_pcm_format_t i; + slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream); + snd_mask_none(&nfmt); pcm_for_each_format(i) { if (snd_mask_test_format(fmt, i)) { uint sbits = snd_pcm_format_width(i); unsigned int sysclk_freq; - int ppm; + unsigned int ratio; + int ppm, slot_width; - if (rd->mcasp->auxclk_fs_ratio) - sysclk_freq = rate * - rd->mcasp->auxclk_fs_ratio; + ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream); + if (ratio) + sysclk_freq = rate * ratio; else - sysclk_freq = rd->mcasp->sysclk_freq; + sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream); - if (rd->mcasp->slot_width) - sbits = rd->mcasp->slot_width; + slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream); + if (slot_width) + sbits = slot_width; ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq, sbits * slots * rate, - false); + rd->stream, false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { snd_mask_set_format(&nfmt, i); count++; @@ -1498,7 +1728,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir, ret; - int tdm_slots = mcasp->tdm_slots; + int tdm_slots; u8 *numevt; /* Do not allow more then one stream per direction */ @@ -1507,6 +1737,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, mcasp->substreams[substream->stream] = substream; + tdm_slots = mcasp_get_tdm_slots(mcasp, substream->stream); + if (mcasp->tdm_mask[substream->stream]) tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); @@ -1528,6 +1760,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, } ruledata->serializers = max_channels; ruledata->mcasp = mcasp; + ruledata->stream = substream->stream; max_channels *= tdm_slots; /* * If the already active stream has less channels than the calculated @@ -1535,9 +1768,13 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, * is in use we need to use that as a constraint for the second stream. * Otherwise (first stream or less allowed channels or more than one * serializer in use) we use the calculated constraint. + * + * However, in async mode, TX and RX have independent clocks and can + * use different configurations, so don't apply the constraint. */ if (mcasp->channels && mcasp->channels < max_channels && - ruledata->serializers == 1) + ruledata->serializers == 1 && + !mcasp->async_mode) max_channels = mcasp->channels; /* * But we can always allow channels upto the amount of @@ -1554,10 +1791,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &mcasp->chconstr[substream->stream]); - if (mcasp->max_format_width) { + if (mcasp->max_format_width && !mcasp->async_mode) { /* * Only allow formats which require same amount of bits on the - * bus as the currently running stream + * bus as the currently running stream to ensure sync mode */ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, @@ -1566,8 +1803,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (ret) return ret; - } - else if (mcasp->slot_width) { + } else if (mcasp_get_slot_width(mcasp, substream->stream)) { /* Only allow formats require <= slot_width bits on the bus */ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, @@ -1582,7 +1818,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, * If we rely on implicit BCLK divider setting we should * set constraints based on what we can provide. */ - if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { + if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 && + mcasp_get_sysclk_freq(mcasp, substream->stream)) { ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, davinci_mcasp_hw_rule_rate, @@ -1759,8 +1996,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { .formats = DAVINCI_MCASP_PCM_FMTS, }, .ops = &davinci_mcasp_dai_ops, - - .symmetric_rate = 1, }, { .name = "davinci-mcasp.1", @@ -1918,18 +2153,33 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, goto out; } + /* Parse TX-specific TDM slot and use it as default for RX */ if (of_property_read_u32(np, "tdm-slots", &val) == 0) { if (val < 2 || val > 32) { - dev_err(&pdev->dev, "tdm-slots must be in rage [2-32]\n"); + dev_err(&pdev->dev, "tdm-slots must be in range [2-32]\n"); return -EINVAL; } - pdata->tdm_slots = val; + pdata->tdm_slots_tx = val; + pdata->tdm_slots_rx = val; } else if (pdata->op_mode == DAVINCI_MCASP_IIS_MODE) { mcasp->missing_audio_param = true; goto out; } + /* Parse RX-specific TDM slot count if provided */ + if (of_property_read_u32(np, "tdm-slots-rx", &val) == 0) { + if (val < 2 || val > 32) { + dev_err(&pdev->dev, "tdm-slots-rx must be in range [2-32]\n"); + return -EINVAL; + } + + pdata->tdm_slots_rx = val; + } + + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE) + mcasp->async_mode = of_property_read_bool(np, "ti,async-mode"); + of_serial_dir32 = of_get_property(np, "serial-dir", &val); val /= sizeof(u32); if (of_serial_dir32) { @@ -1955,8 +2205,15 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, if (of_property_read_u32(np, "rx-num-evt", &val) == 0) pdata->rxnumevt = val; - if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) - mcasp->auxclk_fs_ratio = val; + /* Parse TX-specific auxclk/fs ratio and use it as default for RX */ + if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) { + mcasp->auxclk_fs_ratio_tx = val; + mcasp->auxclk_fs_ratio_rx = val; + } + + /* Parse RX-specific auxclk/fs ratio if provided */ + if (of_property_read_u32(np, "auxclk-fs-ratio-rx", &val) == 0) + mcasp->auxclk_fs_ratio_rx = val; if (of_property_read_u32(np, "dismod", &val) == 0) { if (val == 0 || val == 2 || val == 3) { @@ -1985,19 +2242,51 @@ out: mcasp->op_mode = pdata->op_mode; /* sanity check for tdm slots parameter */ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { - if (pdata->tdm_slots < 2) { - dev_warn(&pdev->dev, "invalid tdm slots: %d\n", - pdata->tdm_slots); - mcasp->tdm_slots = 2; - } else if (pdata->tdm_slots > 32) { - dev_warn(&pdev->dev, "invalid tdm slots: %d\n", - pdata->tdm_slots); - mcasp->tdm_slots = 32; + if (pdata->tdm_slots_tx < 2) { + dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n", + pdata->tdm_slots_tx); + mcasp->tdm_slots_tx = 2; + } else if (pdata->tdm_slots_tx > 32) { + dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n", + pdata->tdm_slots_tx); + mcasp->tdm_slots_tx = 32; + } else { + mcasp->tdm_slots_tx = pdata->tdm_slots_tx; + } + + if (pdata->tdm_slots_rx < 2) { + dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n", + pdata->tdm_slots_rx); + mcasp->tdm_slots_rx = 2; + } else if (pdata->tdm_slots_rx > 32) { + dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n", + pdata->tdm_slots_rx); + mcasp->tdm_slots_rx = 32; } else { - mcasp->tdm_slots = pdata->tdm_slots; + mcasp->tdm_slots_rx = pdata->tdm_slots_rx; } } else { - mcasp->tdm_slots = 32; + mcasp->tdm_slots_tx = 32; + mcasp->tdm_slots_rx = 32; + } + + /* Different TX/RX slot counts require async mode */ + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE && + mcasp->tdm_slots_tx != mcasp->tdm_slots_rx && !mcasp->async_mode) { + dev_err(&pdev->dev, + "Different TX (%d) and RX (%d) TDM slots require ti,async-mode\n", + mcasp->tdm_slots_tx, mcasp->tdm_slots_rx); + return -EINVAL; + } + + /* Different TX/RX auxclk-fs-ratio require async mode */ + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE && + mcasp->auxclk_fs_ratio_tx && mcasp->auxclk_fs_ratio_rx && + mcasp->auxclk_fs_ratio_tx != mcasp->auxclk_fs_ratio_rx && !mcasp->async_mode) { + dev_err(&pdev->dev, + "Different TX (%d) and RX (%d) auxclk-fs-ratio require ti,async-mode\n", + mcasp->auxclk_fs_ratio_tx, mcasp->auxclk_fs_ratio_rx); + return -EINVAL; } mcasp->num_serializer = pdata->num_serializer; diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h index 5de2b8a31061..83b3c67f4a2b 100644 --- a/sound/soc/ti/davinci-mcasp.h +++ b/sound/soc/ti/davinci-mcasp.h @@ -298,10 +298,20 @@ /* Source of High-frequency transmit/receive clock */ #define MCASP_CLK_HCLK_AHCLK 0 /* AHCLKX/R */ #define MCASP_CLK_HCLK_AUXCLK 1 /* Internal functional clock */ +#define MCASP_CLK_HCLK_AHCLK_TXONLY 2 /* AHCLKX for TX only */ +#define MCASP_CLK_HCLK_AHCLK_RXONLY 3 /* AHCLKR for RX only */ +#define MCASP_CLK_HCLK_AUXCLK_TXONLY 4 /* AUXCLK for TX only */ +#define MCASP_CLK_HCLK_AUXCLK_RXONLY 5 /* AUXCLK for RX only */ /* clock divider IDs */ #define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */ #define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */ #define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */ +#define MCASP_CLKDIV_AUXCLK_TXONLY 3 /* AUXCLK divider for TX only */ +#define MCASP_CLKDIV_AUXCLK_RXONLY 4 /* AUXCLK divider for RX only */ +#define MCASP_CLKDIV_BCLK_TXONLY 5 /* BCLK divider for TX only */ +#define MCASP_CLKDIV_BCLK_RXONLY 6 /* BCLK divider for RX only */ +#define MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY 7 /* BCLK/FS ratio for TX only */ +#define MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY 8 /* BCLK/FS ratio for RX only*/ #endif /* DAVINCI_MCASP_H */ -- cgit v1.2.3 From 2a7b7652b1bb3fadc3bd47d622bfb127a93ab6b0 Mon Sep 17 00:00:00 2001 From: Leif Skunberg Date: Tue, 10 Feb 2026 14:21:29 +0100 Subject: platform/x86: int3472: Handle GPIO type 0x10 (DOVDD) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo ThinkPad X1 Fold 16 Gen 1 has an OV5675 sensor (ACPI HID OVTI5675) behind an INT3472 discrete PMIC controller. The INT3472 _DSM returns GPIO type 0x10 for one of the pins, which controls the DOVDD (digital I/O power) regulator enable. Type 0x10 is not currently handled by the driver, causing the GPIO to be ignored with a warning. Add INT3472_GPIO_TYPE_DOVDD (0x10) and handle it as a regulator with con_id "dovdd" to match the supply name used by sensor drivers (e.g. ov5675). Also increase GPIO_SUPPLY_NAME_LENGTH from 5 to 6 to accommodate the "dovdd" name (5 chars + null terminator). Signed-off-by: Leif Skunberg Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260210132129.17943-1-diamondback@cohunt.app Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 7 +++++++ include/linux/platform_data/x86/int3472.h | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 1455d9a7afca..1c65ce87cde0 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -223,6 +223,10 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 *con_id = "avdd"; *gpio_flags = GPIO_ACTIVE_HIGH; break; + case INT3472_GPIO_TYPE_DOVDD: + *con_id = "dovdd"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; case INT3472_GPIO_TYPE_HANDSHAKE: *con_id = "dvdd"; *gpio_flags = GPIO_ACTIVE_HIGH; @@ -251,6 +255,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 * 0x0b Power enable * 0x0c Clock enable * 0x0d Privacy LED + * 0x10 DOVDD (digital I/O voltage) * 0x13 Hotplug detect * * There are some known platform specific quirks where that does not quite @@ -332,6 +337,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: case INT3472_GPIO_TYPE_POWER_ENABLE: + case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags); if (IS_ERR(gpio)) { @@ -356,6 +362,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, case INT3472_GPIO_TYPE_POWER_ENABLE: second_sensor = int3472->quirks.avdd_second_sensor; fallthrough; + case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: ret = skl_int3472_register_regulator(int3472, gpio, enable_time_us, con_id, second_sensor); diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index b1b837583d54..dbe745dc88d5 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -26,6 +26,7 @@ #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d +#define INT3472_GPIO_TYPE_DOVDD 0x10 #define INT3472_GPIO_TYPE_HANDSHAKE 0x12 #define INT3472_GPIO_TYPE_HOTPLUG_DETECT 0x13 @@ -33,8 +34,8 @@ #define INT3472_MAX_SENSOR_GPIOS 3 #define INT3472_MAX_REGULATORS 3 -/* E.g. "avdd\0" */ -#define GPIO_SUPPLY_NAME_LENGTH 5 +/* E.g. "dovdd\0" */ +#define GPIO_SUPPLY_NAME_LENGTH 6 /* 12 chars for acpi_dev_name() + "-", e.g. "ABCD1234:00-" */ #define GPIO_REGULATOR_NAME_LENGTH (12 + GPIO_SUPPLY_NAME_LENGTH) /* lower- and upper-case mapping */ -- cgit v1.2.3 From c8e9b1d9febc83ee94944695a07cfd40a1b29743 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:12:20 -0800 Subject: dmaengine: fsl-edma: fix all kernel-doc warnings Use the correct kernel-doc format and struct member names to eliminate these kernel-doc warnings: Warning: include/linux/platform_data/dma-mcf-edma.h:35 struct member 'dma_channels' not described in 'mcf_edma_platform_data' Warning: include/linux/platform_data/dma-mcf-edma.h:35 struct member 'slave_map' not described in 'mcf_edma_platform_data' Warning: include/linux/platform_data/dma-mcf-edma.h:35 struct member 'slavecnt' not described in 'mcf_edma_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226051220.548566-1-rdunlap@infradead.org Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-mcf-edma.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dma-mcf-edma.h b/include/linux/platform_data/dma-mcf-edma.h index d718ccfa3421..0b31af66a1ac 100644 --- a/include/linux/platform_data/dma-mcf-edma.h +++ b/include/linux/platform_data/dma-mcf-edma.h @@ -26,8 +26,9 @@ bool mcf_edma_filter_fn(struct dma_chan *chan, void *param); /** * struct mcf_edma_platform_data - platform specific data for eDMA engine * - * @ver The eDMA module version. - * @dma_channels The number of eDMA channels. + * @dma_channels: The number of eDMA channels. + * @slave_map: Slave device map + * @slavecnt: Number of entries in @slave_map */ struct mcf_edma_platform_data { int dma_channels; -- cgit v1.2.3 From 3350c2b3f2b8a3b985a020a4ef4f2f050a4b6a1d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:12:29 -0800 Subject: platform_data/mlxreg: mlxreg.h: fix all kernel-doc warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the correct kernel-doc format & notation to eliminate kernel-doc warnings: Warning: include/linux/platform_data/mlxreg.h:24 Enum value 'MLX_WDT_TYPE1' not described in enum 'mlxreg_wdt_type' Warning: include/linux/platform_data/mlxreg.h:24 Enum value 'MLX_WDT_TYPE2' not described in enum 'mlxreg_wdt_type' Warning: include/linux/platform_data/mlxreg.h:24 Enum value 'MLX_WDT_TYPE3' not described in enum 'mlxreg_wdt_type' Warning: include/linux/platform_data/mlxreg.h:37 bad line: PHYs ready / unready state; Warning: include/linux/platform_data/mlxreg.h:153 struct member 'np' not described in 'mlxreg_core_data' Warning: include/linux/platform_data/mlxreg.h:153 struct member 'hpdev' not described in 'mlxreg_core_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226051232.549537-1-rdunlap@infradead.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/linux/platform_data/mlxreg.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index f6cca7a035c7..50b6be57da66 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -13,10 +13,10 @@ /** * enum mlxreg_wdt_type - type of HW watchdog * - * TYPE1 HW watchdog implementation exist in old systems. - * All new systems have TYPE2 HW watchdog. - * TYPE3 HW watchdog can exist on all systems with new CPLD. - * TYPE3 is selected by WD capability bit. + * @MLX_WDT_TYPE1: HW watchdog implementation in old systems. + * @MLX_WDT_TYPE2: All new systems have TYPE2 HW watchdog. + * @MLX_WDT_TYPE3: HW watchdog that can exist on all systems with new CPLD. + * TYPE3 is selected by WD capability bit. */ enum mlxreg_wdt_type { MLX_WDT_TYPE1, @@ -35,7 +35,7 @@ enum mlxreg_wdt_type { * @MLXREG_HOTPLUG_LC_SYNCED: entry for line card synchronization events, coming * after hardware-firmware synchronization handshake; * @MLXREG_HOTPLUG_LC_READY: entry for line card ready events, indicating line card - PHYs ready / unready state; + * PHYs ready / unready state; * @MLXREG_HOTPLUG_LC_ACTIVE: entry for line card active events, indicating firmware * availability / unavailability for the ports on line card; * @MLXREG_HOTPLUG_LC_THERMAL: entry for line card thermal shutdown events, positive @@ -123,8 +123,8 @@ struct mlxreg_hotplug_device { * @reg_pwr: attribute power register; * @reg_ena: attribute enable register; * @mode: access mode; - * @np - pointer to node platform associated with attribute; - * @hpdev - hotplug device data; + * @np: pointer to node platform associated with attribute; + * @hpdev: hotplug device data; * @notifier: pointer to event notifier block; * @health_cntr: dynamic device health indication counter; * @attached: true if device has been attached after good health indication; -- cgit v1.2.3 From 8021729acf21f4bf3c43866b8919b68968028478 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:12:57 -0800 Subject: iio: tsl2772: fix all kernel-doc warnings Use the correct kernel-doc notation for struct members to eliminate kernel-doc warnings: Warning: include/linux/platform_data/tsl2772.h:88 struct member 'prox_diode' not described in 'tsl2772_settings' Warning: include/linux/platform_data/tsl2772.h:88 struct member 'prox_power' not described in 'tsl2772_settings' Signed-off-by: Randy Dunlap Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/platform_data/tsl2772.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/tsl2772.h b/include/linux/platform_data/tsl2772.h index f8ade15a35e2..f042e82b39c3 100644 --- a/include/linux/platform_data/tsl2772.h +++ b/include/linux/platform_data/tsl2772.h @@ -61,9 +61,9 @@ struct tsl2772_lux { * @prox_pulse_count: Number if proximity emitter pulses. * @prox_max_samples_cal: The number of samples that are taken when performing * a proximity calibration. - * @prox_diode Which diode(s) to use for driving the external + * @prox_diode: Which diode(s) to use for driving the external * LED(s) for proximity sensing. - * @prox_power The amount of power to use for the external LED(s). + * @prox_power: The amount of power to use for the external LED(s). */ struct tsl2772_settings { int als_time; -- cgit v1.2.3 From de70eef32e10883fe74f6df635c616785b24b867 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:13:09 -0800 Subject: ARM: omap: fix all kernel-doc warnings Use the correct struct member names to avoid kernel-doc warnings: Warning: include/linux/platform_data/voltage-omap.h:27 struct member 'volt_nominal' not described in 'omap_volt_data' Warning: include/linux/platform_data/voltage-omap.h:27 struct member 'vp_errgain' not described in 'omap_volt_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226051309.556228-1-rdunlap@infradead.org Signed-off-by: Kevin Hilman --- include/linux/platform_data/voltage-omap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/voltage-omap.h b/include/linux/platform_data/voltage-omap.h index 6d74e507dbd2..2b48f2b0135d 100644 --- a/include/linux/platform_data/voltage-omap.h +++ b/include/linux/platform_data/voltage-omap.h @@ -10,14 +10,14 @@ /** * struct omap_volt_data - Omap voltage specific data. - * @voltage_nominal: The possible voltage value in uV + * @volt_nominal: The possible voltage value in uV * @sr_efuse_offs: The offset of the efuse register(from system * control module base address) from where to read * the n-target value for the smartreflex module. * @sr_errminlimit: Error min limit value for smartreflex. This value * differs at differnet opp and thus is linked * with voltage. - * @vp_errorgain: Error gain value for the voltage processor. This + * @vp_errgain: Error gain value for the voltage processor. This * field also differs according to the voltage/opp. */ struct omap_volt_data { -- cgit v1.2.3 From 356d4fbcf3defaff0f98d2b6b54f3b26f0ff189d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 16 Mar 2026 11:04:04 +0100 Subject: net: mdio-gpio: remove linux/platform_data/mdio-gpio.h Nobody defines struct mdio_gpio_platform_data. Remove platform data support from mdio-gpio and drop the header. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260316-gpio-mdio-hdr-cleanup-v1-2-2df696f74728@oss.qualcomm.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 - drivers/net/mdio/mdio-gpio.c | 7 ------- include/linux/platform_data/mdio-gpio.h | 14 -------------- 3 files changed, 22 deletions(-) delete mode 100644 include/linux/platform_data/mdio-gpio.h (limited to 'include/linux/platform_data') diff --git a/MAINTAINERS b/MAINTAINERS index 5d477fd592db..7d65f9435950 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9547,7 +9547,6 @@ F: include/linux/phy_fixed.h F: include/linux/phy_link_topology.h F: include/linux/phylib_stubs.h F: include/linux/platform_data/mdio-bcm-unimac.h -F: include/linux/platform_data/mdio-gpio.h F: include/net/phy/ F: include/trace/events/mdio.h F: include/uapi/linux/mdio.h diff --git a/drivers/net/mdio/mdio-gpio.c b/drivers/net/mdio/mdio-gpio.c index c99310889896..958d1c6608ab 100644 --- a/drivers/net/mdio/mdio-gpio.c +++ b/drivers/net/mdio/mdio-gpio.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -113,7 +112,6 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, struct mdio_gpio_info *bitbang, int bus_id) { - struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev); struct mii_bus *new_bus; bitbang->ctrl.ops = &mdio_gpio_ops; @@ -130,11 +128,6 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, else strscpy(new_bus->id, "gpio", sizeof(new_bus->id)); - if (pdata) { - new_bus->phy_mask = pdata->phy_mask; - new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask; - } - if (device_is_compatible(dev, "microchip,mdio-smi0")) { bitbang->ctrl.op_c22_read = 0; bitbang->ctrl.op_c22_write = 0; diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h deleted file mode 100644 index 13874fa6e767..000000000000 --- a/include/linux/platform_data/mdio-gpio.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * MDIO-GPIO bus platform data structure - */ - -#ifndef __LINUX_MDIO_GPIO_PDATA_H -#define __LINUX_MDIO_GPIO_PDATA_H - -struct mdio_gpio_platform_data { - u32 phy_mask; - u32 phy_ignore_ta_mask; -}; - -#endif /* __LINUX_MDIO_GPIO_PDATA_H */ -- cgit v1.2.3 From 331e5fd5bfd7aae3ab4eb947367b9d609ebb3fb3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 26 Mar 2026 10:30:00 +0100 Subject: hwmon: (ina2xx) drop unused platform data Nobody defines struct ina2xx_platform_data. Remove platform data support from the drivers which still have it (it's effectively dead code) and remove the header. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Acked-by: Jonathan Cameron Link: https://lore.kernel.org/r/20260326-drop-ina2xx-pdata-v1-1-c159437bb2df@oss.qualcomm.com [groeck: Fixed continuation line alignment] Signed-off-by: Guenter Roeck --- drivers/hwmon/ina209.c | 11 ++--------- drivers/iio/adc/ina2xx-adc.c | 14 ++------------ include/linux/platform_data/ina2xx.h | 16 ---------------- 3 files changed, 4 insertions(+), 37 deletions(-) delete mode 100644 include/linux/platform_data/ina2xx.h (limited to 'include/linux/platform_data') diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index bd7b3380d847..a116f1600e81 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -27,8 +27,6 @@ #include #include -#include - /* register definitions */ #define INA209_CONFIGURATION 0x00 #define INA209_STATUS 0x01 @@ -487,7 +485,6 @@ static void ina209_restore_conf(struct i2c_client *client, static int ina209_init_client(struct i2c_client *client, struct ina209_data *data) { - struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev); u32 shunt; int reg; @@ -501,12 +498,8 @@ static int ina209_init_client(struct i2c_client *client, return reg; data->config_orig = reg; - if (pdata) { - if (pdata->shunt_uohms <= 0) - return -EINVAL; - shunt = pdata->shunt_uohms; - } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor", - &shunt)) { + if (!of_property_read_u32(client->dev.of_node, "shunt-resistor", + &shunt)) { if (shunt == 0) return -EINVAL; } else { diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 857e1b69d6cd..c6cded508738 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -33,8 +33,6 @@ #include #include -#include - /* INA2XX registers definition */ #define INA2XX_CONFIG 0x00 #define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ @@ -980,16 +978,8 @@ static int ina2xx_probe(struct i2c_client *client) mutex_init(&chip->state_lock); - if (of_property_read_u32(client->dev.of_node, - "shunt-resistor", &val) < 0) { - struct ina2xx_platform_data *pdata = - dev_get_platdata(&client->dev); - - if (pdata) - val = pdata->shunt_uohms; - else - val = INA2XX_RSHUNT_DEFAULT; - } + if (of_property_read_u32(client->dev.of_node, "shunt-resistor", &val) < 0) + val = INA2XX_RSHUNT_DEFAULT; ret = set_shunt_resistor(chip, val); if (ret) diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h deleted file mode 100644 index 2aa5ee9a9050..000000000000 --- a/include/linux/platform_data/ina2xx.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Driver for Texas Instruments INA219, INA226 power monitor chips - * - * Copyright (C) 2012 Lothar Felten - * - * For further information, see the Documentation/hwmon/ina2xx.rst file. - */ - -/** - * struct ina2xx_platform_data - ina2xx info - * @shunt_uohms shunt resistance in microohms - */ -struct ina2xx_platform_data { - long shunt_uohms; -}; -- cgit v1.2.3 From 2d7ce8eb59ec880774c7500ac949f0100acba521 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:12:07 -0800 Subject: misc: apds990x: fix all kernel-doc warnings Move a #define so that it is not between kernel-doc and its struct declaration. Spell one struct member correctly. Warning: include/linux/platform_data/apds990x.h:33 #define APDS_PARAM_SCALE 4096; error: Cannot parse struct or union! Warning: include/linux/platform_data/apds990x.h:62 struct member 'pdrive' not described in 'apds990x_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226051207.547152-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_data/apds990x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/apds990x.h b/include/linux/platform_data/apds990x.h index 94dfbaa365e1..37684f68c04f 100644 --- a/include/linux/platform_data/apds990x.h +++ b/include/linux/platform_data/apds990x.h @@ -31,7 +31,6 @@ * itself. If the GA is zero, driver will use uncovered sensor default values * format: decimal value * APDS_PARAM_SCALE except df which is plain integer. */ -#define APDS_PARAM_SCALE 4096 struct apds990x_chip_factors { int ga; int cf1; @@ -40,11 +39,12 @@ struct apds990x_chip_factors { int irf2; int df; }; +#define APDS_PARAM_SCALE 4096 /** * struct apds990x_platform_data - platform data for apsd990x.c driver * @cf: chip factor data - * @pddrive: IR-led driving current + * @pdrive: IR-led driving current * @ppcount: number of IR pulses used for proximity estimation * @setup_resources: interrupt line setup call back function * @release_resources: interrupt line release call back function -- cgit v1.2.3 From b773b9935239e9bec86b96ce91b6ba2252c20b44 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 7 Apr 2026 00:21:55 +0300 Subject: net: dsa: remove struct platform_data This is not used anywhere in the kernel. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260406212158.721806-2-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- Documentation/networking/dsa/dsa.rst | 5 ----- include/linux/platform_data/dsa.h | 17 ----------------- 2 files changed, 22 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst index 5c79740a533b..fd3c254ced1d 100644 --- a/Documentation/networking/dsa/dsa.rst +++ b/Documentation/networking/dsa/dsa.rst @@ -383,11 +383,6 @@ DSA data structures are defined in ``include/net/dsa.h`` as well as well as various properties of its ports: names/labels, and finally a routing table indication (when cascading switches) -- ``dsa_platform_data``: platform device configuration data which can reference - a collection of dsa_chip_data structures if multiple switches are cascaded, - the conduit network device this switch tree is attached to needs to be - referenced - - ``dsa_switch_tree``: structure assigned to the conduit network device under ``dsa_ptr``, this structure references a dsa_platform_data structure as well as the tagging protocol supported by the switch tree, and which receive/transmit diff --git a/include/linux/platform_data/dsa.h b/include/linux/platform_data/dsa.h index d4d9bf2060a6..fec1ae5bddb9 100644 --- a/include/linux/platform_data/dsa.h +++ b/include/linux/platform_data/dsa.h @@ -48,21 +48,4 @@ struct dsa_chip_data { s8 rtable[DSA_MAX_SWITCHES]; }; -struct dsa_platform_data { - /* - * Reference to a Linux network interface that connects - * to the root switch chip of the tree. - */ - struct device *netdev; - struct net_device *of_netdev; - - /* - * Info structs describing each of the switch chips - * connected via this network interface. - */ - int nr_chips; - struct dsa_chip_data *chip; -}; - - #endif /* __DSA_PDATA_H */ -- cgit v1.2.3 From dc915f375e545cf72421d66ede983d88e298228f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 7 Apr 2026 00:21:56 +0300 Subject: net: dsa: clean up struct dsa_chip_data This has accumulated some fields which are no longer parsed by the core or set by any driver. Remove them. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260406212158.721806-3-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- include/linux/platform_data/dsa.h | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dsa.h b/include/linux/platform_data/dsa.h index fec1ae5bddb9..031f4cf83ae2 100644 --- a/include/linux/platform_data/dsa.h +++ b/include/linux/platform_data/dsa.h @@ -10,12 +10,6 @@ struct net_device; #define DSA_RTABLE_NONE -1 struct dsa_chip_data { - /* - * How to access the switch configuration registers. - */ - struct device *host_dev; - int sw_addr; - /* * Reference to network devices */ @@ -24,12 +18,6 @@ struct dsa_chip_data { /* set to size of eeprom if supported by the switch */ int eeprom_len; - /* Device tree node pointer for this specific switch chip - * used during switch setup in case additional properties - * and resources needs to be used - */ - struct device_node *of_node; - /* * The names of the switch's ports. Use "cpu" to * designate the switch port that the cpu is connected to, @@ -38,14 +26,6 @@ struct dsa_chip_data { * or any other string to indicate this is a physical port. */ char *port_names[DSA_MAX_PORTS]; - struct device_node *port_dn[DSA_MAX_PORTS]; - - /* - * An array of which element [a] indicates which port on this - * switch should be used to send packets to that are destined - * for switch a. Can be NULL if there is only one switch chip. - */ - s8 rtable[DSA_MAX_SWITCHES]; }; #endif /* __DSA_PDATA_H */ -- cgit v1.2.3 From c3b09190e658d3f1c3cd595df3a931962662f8f0 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 7 Apr 2026 00:21:57 +0300 Subject: net: dsa: remove unused platform_data definitions Pretty self-explanatory, nobody needs these. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260406212158.721806-4-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- include/linux/platform_data/dsa.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dsa.h b/include/linux/platform_data/dsa.h index 031f4cf83ae2..77424bb24723 100644 --- a/include/linux/platform_data/dsa.h +++ b/include/linux/platform_data/dsa.h @@ -3,11 +3,8 @@ #define __DSA_PDATA_H struct device; -struct net_device; -#define DSA_MAX_SWITCHES 4 #define DSA_MAX_PORTS 12 -#define DSA_RTABLE_NONE -1 struct dsa_chip_data { /* -- cgit v1.2.3 From 39237e3208209d1bb35d939d6fee1f36b642f562 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:36 +0200 Subject: platform/x86: int3472: Rename pled to led in LED registration code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the privacy LED type, struct member, and functions from "pled" to "led" in preparation for supporting additional LED types beyond just the privacy LED. No functional change. Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Signed-off-by: Marco Nenciarini Link: https://patch.msgid.link/20260401203638.1601661-3-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 4 ++-- drivers/platform/x86/intel/int3472/led.c | 14 +++++++------- include/linux/platform_data/x86/int3472.h | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 1c65ce87cde0..b03f61dab25f 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -354,7 +354,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: - ret = skl_int3472_register_pled(int3472, gpio); + ret = skl_int3472_register_led(int3472, gpio); if (ret) err_msg = "Failed to register LED\n"; @@ -429,7 +429,7 @@ void int3472_discrete_cleanup(struct int3472_discrete_device *int3472) gpiod_remove_lookup_table(&int3472->gpios); skl_int3472_unregister_clock(int3472); - skl_int3472_unregister_pled(int3472); + skl_int3472_unregister_led(int3472); skl_int3472_unregister_regulator(int3472); } EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE"); diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index 35abad900bf3..fe412cb938cf 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -6,17 +6,17 @@ #include #include -static int int3472_pled_set(struct led_classdev *led_cdev, enum led_brightness brightness) +static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct int3472_pled *led = container_of(led_cdev, struct int3472_pled, classdev); + struct int3472_led *led = container_of(led_cdev, struct int3472_led, classdev); gpiod_set_value_cansleep(led->gpio, brightness); return 0; } -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) { - struct int3472_pled *led = &int3472->pled; + struct int3472_led *led = &int3472->led; char *p; int ret; @@ -34,7 +34,7 @@ int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gp led->classdev.name = led->name; led->classdev.max_brightness = 1; - led->classdev.brightness_set_blocking = int3472_pled_set; + led->classdev.brightness_set_blocking = int3472_led_set; ret = led_classdev_register(int3472->dev, &led->classdev); if (ret) @@ -48,9 +48,9 @@ int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gp return 0; } -void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) +void skl_int3472_unregister_led(struct int3472_discrete_device *int3472) { - struct int3472_pled *led = &int3472->pled; + struct int3472_led *led = &int3472->led; if (IS_ERR_OR_NULL(led->classdev.dev)) return; diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index dbe745dc88d5..39a1938d77e1 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -122,12 +122,12 @@ struct int3472_discrete_device { u8 imgclk_index; } clock; - struct int3472_pled { + struct int3472_led { struct led_classdev classdev; struct led_lookup_data lookup; char name[INT3472_LED_MAX_NAME_LEN]; struct gpio_desc *gpio; - } pled; + } led; struct int3472_discrete_quirks quirks; @@ -161,7 +161,7 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); -void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472); +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); +void skl_int3472_unregister_led(struct int3472_discrete_device *int3472); #endif -- cgit v1.2.3 From 218d3c44f5f0a3cc1647bc61a4e4eac663b37aa5 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:37 +0200 Subject: platform/x86: int3472: Parameterize LED con_id in registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a con_id parameter to skl_int3472_register_led() to allow callers to specify both the LED name suffix and lookup con_id instead of hardcoding "privacy". This prepares for registering additional LED types with different names. While at it, rename the privacy LED's GPIO con_id from "privacy-led" to "privacy" in int3472_get_con_id_and_polarity() and pass it directly to skl_int3472_register_led(), reducing churn when adding new LED types. No functional change. Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Signed-off-by: Marco Nenciarini Link: https://patch.msgid.link/20260401203638.1601661-4-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 4 ++-- drivers/platform/x86/intel/int3472/led.c | 7 ++++--- include/linux/platform_data/x86/int3472.h | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index b03f61dab25f..637e3821b496 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -212,7 +212,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_PRIVACY_LED: - *con_id = "privacy-led"; + *con_id = "privacy"; *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_HOTPLUG_DETECT: @@ -354,7 +354,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: - ret = skl_int3472_register_led(int3472, gpio); + ret = skl_int3472_register_led(int3472, gpio, con_id); if (ret) err_msg = "Failed to register LED\n"; diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index fe412cb938cf..22d0d6c5e6ce 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -14,7 +14,8 @@ static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness br return 0; } -int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *con_id) { struct int3472_led *led = &int3472->led; char *p; @@ -27,7 +28,7 @@ int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpi /* Generate the name, replacing the ':' in the ACPI devname with '_' */ snprintf(led->name, sizeof(led->name), - "%s::privacy_led", acpi_dev_name(int3472->sensor)); + "%s::%s_led", acpi_dev_name(int3472->sensor), con_id); p = strchr(led->name, ':'); if (p) *p = '_'; @@ -42,7 +43,7 @@ int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpi led->lookup.provider = led->name; led->lookup.dev_id = int3472->sensor_name; - led->lookup.con_id = "privacy"; + led->lookup.con_id = con_id; led_add_lookup(&led->lookup); return 0; diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index 39a1938d77e1..ebf4d0637624 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -161,7 +161,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); -int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *con_id); void skl_int3472_unregister_led(struct int3472_discrete_device *int3472); #endif -- cgit v1.2.3 From 0ec7f158dc01e354ba83d808e46346dba826e353 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:38 +0200 Subject: platform/x86: int3472: Add support for GPIO type 0x02 (IR flood LED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for GPIO type 0x02, which controls an IR flood LED used for face authentication on some laptops (e.g. Dell Pro Max 16 Premium). Without this patch, the kernel logs "GPIO type 0x02 unknown; the sensor may not work" and IR sensors paired with a flood LED cannot function. The flood LED is registered through the LED subsystem like the existing privacy LED, including a lookup entry to allow future consumer drivers to find and control it via led_get(). To support multiple LEDs per INT3472 device, convert the single led struct member to an array with a counter. Signed-off-by: Marco Nenciarini Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260401203638.1601661-5-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 9 ++++++++- drivers/platform/x86/intel/int3472/led.c | 23 ++++++++++++----------- include/linux/platform_data/x86/int3472.h | 7 +++++-- 3 files changed, 25 insertions(+), 14 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 637e3821b496..115bb37577a1 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -215,6 +215,10 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 *con_id = "privacy"; *gpio_flags = GPIO_ACTIVE_HIGH; break; + case INT3472_GPIO_TYPE_STROBE: + *con_id = "ir_flood"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; case INT3472_GPIO_TYPE_HOTPLUG_DETECT: *con_id = "hpd"; *gpio_flags = GPIO_ACTIVE_HIGH; @@ -252,6 +256,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 * * 0x00 Reset * 0x01 Power down + * 0x02 Strobe * 0x0b Power enable * 0x0c Clock enable * 0x0d Privacy LED @@ -336,6 +341,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: + case INT3472_GPIO_TYPE_STROBE: case INT3472_GPIO_TYPE_POWER_ENABLE: case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: @@ -354,6 +360,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: + case INT3472_GPIO_TYPE_STROBE: ret = skl_int3472_register_led(int3472, gpio, con_id); if (ret) err_msg = "Failed to register LED\n"; @@ -429,7 +436,7 @@ void int3472_discrete_cleanup(struct int3472_discrete_device *int3472) gpiod_remove_lookup_table(&int3472->gpios); skl_int3472_unregister_clock(int3472); - skl_int3472_unregister_led(int3472); + skl_int3472_unregister_leds(int3472); skl_int3472_unregister_regulator(int3472); } EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE"); diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index 22d0d6c5e6ce..9b2573cc347b 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -17,13 +17,14 @@ static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness br int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, const char *con_id) { - struct int3472_led *led = &int3472->led; + struct int3472_led *led; char *p; int ret; - if (led->classdev.dev) - return -EBUSY; + if (int3472->n_leds >= INT3472_MAX_LEDS) + return -ENOSPC; + led = &int3472->leds[int3472->n_leds]; led->gpio = gpio; /* Generate the name, replacing the ':' in the ACPI devname with '_' */ @@ -46,17 +47,17 @@ int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpi led->lookup.con_id = con_id; led_add_lookup(&led->lookup); + int3472->n_leds++; return 0; } -void skl_int3472_unregister_led(struct int3472_discrete_device *int3472) +void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472) { - struct int3472_led *led = &int3472->led; + for (unsigned int i = 0; i < int3472->n_leds; i++) { + struct int3472_led *led = &int3472->leds[i]; - if (IS_ERR_OR_NULL(led->classdev.dev)) - return; - - led_remove_lookup(&led->lookup); - led_classdev_unregister(&led->classdev); - gpiod_put(led->gpio); + led_remove_lookup(&led->lookup); + led_classdev_unregister(&led->classdev); + gpiod_put(led->gpio); + } } diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index ebf4d0637624..93f1e1fe09b4 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -23,6 +23,7 @@ /* PMIC GPIO Types */ #define INT3472_GPIO_TYPE_RESET 0x00 #define INT3472_GPIO_TYPE_POWERDOWN 0x01 +#define INT3472_GPIO_TYPE_STROBE 0x02 #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d @@ -32,6 +33,7 @@ #define INT3472_PDEV_MAX_NAME_LEN 23 #define INT3472_MAX_SENSOR_GPIOS 3 +#define INT3472_MAX_LEDS 2 #define INT3472_MAX_REGULATORS 3 /* E.g. "dovdd\0" */ @@ -127,11 +129,12 @@ struct int3472_discrete_device { struct led_lookup_data lookup; char name[INT3472_LED_MAX_NAME_LEN]; struct gpio_desc *gpio; - } led; + } leds[INT3472_MAX_LEDS]; struct int3472_discrete_quirks quirks; unsigned int ngpios; /* how many GPIOs have we seen */ + unsigned int n_leds; /* how many LEDs have we registered */ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ unsigned int n_regulator_gpios; /* how many have we mapped to a regulator */ struct gpiod_lookup_table gpios; @@ -163,6 +166,6 @@ void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, const char *con_id); -void skl_int3472_unregister_led(struct int3472_discrete_device *int3472); +void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472); #endif -- cgit v1.2.3