From 5da094ac80cd4f6d4114ad220468f602f86d9980 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 24 Feb 2023 17:31:47 +0530 Subject: bus: mhi: host: Remove mhi_poll() API mhi_poll() API is not used within the MHI stack and also not by any client drivers in mainline. So let's remove it until any consumer is available. Reviewed-by: Jeffrey Hugo Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/main.c | 15 --------------- include/linux/mhi.h | 7 ------- 2 files changed, 22 deletions(-) diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index df0fbfee7b78..4fa0969472fc 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -1679,18 +1679,3 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) } } EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer); - -int mhi_poll(struct mhi_device *mhi_dev, u32 budget) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan = mhi_dev->dl_chan; - struct mhi_event *mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index]; - int ret; - - spin_lock_bh(&mhi_event->lock); - ret = mhi_event->process_event(mhi_cntrl, mhi_event, budget); - spin_unlock_bh(&mhi_event->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(mhi_poll); diff --git a/include/linux/mhi.h b/include/linux/mhi.h index a5441ad33c74..f6de4b6ecfc7 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -765,13 +765,6 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev); */ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev); -/** - * mhi_poll - Poll for any available data in DL direction - * @mhi_dev: Device associated with the channels - * @budget: # of events to process - */ -int mhi_poll(struct mhi_device *mhi_dev, u32 budget); - /** * mhi_queue_dma - Send or receive DMA mapped buffers from client device * over MHI channel -- cgit v1.2.3 From 94e19f4fbb1efc21cab860723662b3a9228b428f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Mar 2023 13:12:48 +0530 Subject: bus: mhi: ep: Demote unsupported channel error log to debug Not all MHI endpoints will support all available channels. Most of them support only a selected number of channels based on the implementations. In those cases, it is not needed to print error messages in the endpoint. So let's demote the error log to debug. Reviewed-by: Jeffrey Hugo Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/ep/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index a6a48e515478..600881808982 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -126,7 +126,7 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele /* Check if the channel is supported by the controller */ if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) { - dev_err(dev, "Channel (%u) not supported!\n", ch_id); + dev_dbg(dev, "Channel (%u) not supported!\n", ch_id); return -ENODEV; } @@ -702,7 +702,7 @@ static void mhi_ep_cmd_ring_worker(struct work_struct *work) el = &ring->ring_cache[ring->rd_offset]; ret = mhi_ep_process_cmd_ring(ring, el); - if (ret) + if (ret && ret != -ENODEV) dev_err(dev, "Error processing cmd ring element: %zu\n", ring->rd_offset); mhi_ep_ring_inc_index(ring); -- cgit v1.2.3 From 45581e663703b7d36da1bc884950f199976bc8b3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 14:16:25 -0600 Subject: bus: mhi: host: pci_generic: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since commit ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Reviewed-by: Jeffrey Hugo Link: https://lore.kernel.org/r/20230307201625.879567-1-helgaas@kernel.org Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/pci_generic.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index f39657f71483..6e13c43a84d1 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -8,7 +8,6 @@ * Copyright (C) 2020 Linaro Ltd */ -#include #include #include #include @@ -903,11 +902,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_pdev->pci_state = pci_store_saved_state(pdev); pci_load_saved_state(pdev, NULL); - pci_enable_pcie_error_reporting(pdev); - err = mhi_register_controller(mhi_cntrl, mhi_cntrl_config); if (err) - goto err_disable_reporting; + return err; /* MHI bus does not power up the controller by default */ err = mhi_prepare_for_power_up(mhi_cntrl); @@ -941,8 +938,6 @@ err_unprepare: mhi_unprepare_after_power_down(mhi_cntrl); err_unregister: mhi_unregister_controller(mhi_cntrl); -err_disable_reporting: - pci_disable_pcie_error_reporting(pdev); return err; } @@ -965,7 +960,6 @@ static void mhi_pci_remove(struct pci_dev *pdev) pm_runtime_get_noresume(&pdev->dev); mhi_unregister_controller(mhi_cntrl); - pci_disable_pcie_error_reporting(pdev); } static void mhi_pci_shutdown(struct pci_dev *pdev) -- cgit v1.2.3 From 3996954fa071aa20945c8ac7694684fa3c1fed68 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 22 Feb 2023 10:23:01 +0100 Subject: tty: ipwireless: move Kconfig entry to tty The ipwireless' Kconfig entry is currently sitting in char/pcmcia. But the driver is in tty/. So move the entry to tty/ too. First, it makes sense to have the entry along the driver. Second, whole char/pcmcia is about to pass away. Signed-off-by: Jiri Slaby (SUSE) Cc: Jiri Kosina Cc: David Sterba Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Acked-by: David Sterba Link: https://lore.kernel.org/r/20230222092302.6348-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/Kconfig | 9 --------- drivers/tty/Kconfig | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig index f5d589b2be44..26724990074c 100644 --- a/drivers/char/pcmcia/Kconfig +++ b/drivers/char/pcmcia/Kconfig @@ -55,14 +55,5 @@ config SCR24X If unsure say N. -config IPWIRELESS - tristate "IPWireless 3G UMTS PCMCIA card support" - depends on PCMCIA && NETDEVICES && TTY - select PPP - help - This is a driver for 3G UMTS PCMCIA card from IPWireless company. In - some countries (for example Czech Republic, T-Mobile ISP) this card - is shipped for service called UMTS 4G. - endmenu diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index d35fc068da74..4de4fb9ce61a 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -301,6 +301,15 @@ config GOLDFISH_TTY_EARLY_CONSOLE default y if GOLDFISH_TTY=y select SERIAL_EARLYCON +config IPWIRELESS + tristate "IPWireless 3G UMTS PCMCIA card support" + depends on PCMCIA && NETDEVICES + select PPP + help + This is a driver for 3G UMTS PCMCIA card from IPWireless company. In + some countries (for example Czech Republic, T-Mobile ISP) this card + is shipped for service called UMTS 4G. + config N_GSM tristate "GSM MUX line discipline support (EXPERIMENTAL)" depends on NET -- cgit v1.2.3 From 9b12f050c76f090cc6d0aebe0ef76fed79ec3f15 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 22 Feb 2023 10:23:02 +0100 Subject: char: pcmcia: remove all the drivers These char PCMCIA drivers are buggy[1] and receive only minimal care. It was concluded[2], that we should try to remove most pcmcia drivers completely. Let's start with these char broken one. Note that I also removed a UAPI header: include/uapi/linux/cm4000_cs.h. I found only coccinelle tests mentioning some ioctl constants from that file. But they are not actually used. Anyway, should someone complain, we may reintroduce the header (or its parts). [1] https://lore.kernel.org/all/f41c2765-80e0-48bc-b1e4-8cfd3230fd4a@www.fastmail.com/ [2] https://lore.kernel.org/all/c5b39544-a4fb-4796-a046-0b9be9853787@app.fastmail.com/ Signed-off-by: Jiri Slaby (SUSE) Cc: "Hyunwoo Kim" Cc: Harald Welte Cc: Lubomir Rintel Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Acked-by: Dominik Brodowski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230222092302.6348-2-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/process/magic-number.rst | 1 - .../translations/it_IT/process/magic-number.rst | 1 - .../translations/sp_SP/process/magic-number.rst | 1 - .../translations/zh_CN/process/magic-number.rst | 1 - .../translations/zh_TW/process/magic-number.rst | 1 - Documentation/userspace-api/ioctl/ioctl-number.rst | 1 - MAINTAINERS | 17 - arch/powerpc/configs/ppc6xx_defconfig | 2 - drivers/char/Kconfig | 2 - drivers/char/Makefile | 1 - drivers/char/pcmcia/Kconfig | 59 - drivers/char/pcmcia/Makefile | 11 - drivers/char/pcmcia/cm4000_cs.c | 1912 --------- drivers/char/pcmcia/cm4040_cs.c | 684 ---- drivers/char/pcmcia/cm4040_cs.h | 48 - drivers/char/pcmcia/scr24x_cs.c | 359 -- drivers/char/pcmcia/synclink_cs.c | 4290 -------------------- include/linux/cm4000_cs.h | 11 - include/uapi/linux/cm4000_cs.h | 64 - 19 files changed, 7466 deletions(-) delete mode 100644 drivers/char/pcmcia/Kconfig delete mode 100644 drivers/char/pcmcia/Makefile delete mode 100644 drivers/char/pcmcia/cm4000_cs.c delete mode 100644 drivers/char/pcmcia/cm4040_cs.c delete mode 100644 drivers/char/pcmcia/cm4040_cs.h delete mode 100644 drivers/char/pcmcia/scr24x_cs.c delete mode 100644 drivers/char/pcmcia/synclink_cs.c delete mode 100644 include/linux/cm4000_cs.h delete mode 100644 include/uapi/linux/cm4000_cs.h diff --git a/Documentation/process/magic-number.rst b/Documentation/process/magic-number.rst index 64b5948fc1d4..7029c3c084ee 100644 --- a/Documentation/process/magic-number.rst +++ b/Documentation/process/magic-number.rst @@ -72,7 +72,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c`` BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` diff --git a/Documentation/translations/it_IT/process/magic-number.rst b/Documentation/translations/it_IT/process/magic-number.rst index 02eb7eb2448e..ae92ab633c16 100644 --- a/Documentation/translations/it_IT/process/magic-number.rst +++ b/Documentation/translations/it_IT/process/magic-number.rst @@ -78,7 +78,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c`` BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` diff --git a/Documentation/translations/sp_SP/process/magic-number.rst b/Documentation/translations/sp_SP/process/magic-number.rst index 2b62cec34e8e..7c7dfb4ba80b 100644 --- a/Documentation/translations/sp_SP/process/magic-number.rst +++ b/Documentation/translations/sp_SP/process/magic-number.rst @@ -77,7 +77,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c`` BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` diff --git a/Documentation/translations/zh_CN/process/magic-number.rst b/Documentation/translations/zh_CN/process/magic-number.rst index 0617ce125e12..37ed33034134 100644 --- a/Documentation/translations/zh_CN/process/magic-number.rst +++ b/Documentation/translations/zh_CN/process/magic-number.rst @@ -61,7 +61,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c`` BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` diff --git a/Documentation/translations/zh_TW/process/magic-number.rst b/Documentation/translations/zh_TW/process/magic-number.rst index f3f7082e17c6..1d48e1bbf5f2 100644 --- a/Documentation/translations/zh_TW/process/magic-number.rst +++ b/Documentation/translations/zh_TW/process/magic-number.rst @@ -64,7 +64,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c`` BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 0a1882e296ae..176e8fc3f31b 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -222,7 +222,6 @@ Code Seq# Include File Comments 'b' 00-FF conflict! bit3 vme host bridge 'b' 00-0F linux/dma-buf.h conflict! -'c' all linux/cm4000_cs.h conflict! 'c' 00-7F linux/comstats.h conflict! 'c' 00-7F linux/coda.h conflict! 'c' 00-1F linux/chio.h conflict! diff --git a/MAINTAINERS b/MAINTAINERS index 8d5bc223f305..64068a601e59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15322,18 +15322,6 @@ S: Maintained F: Documentation/filesystems/omfs.rst F: fs/omfs/ -OMNIKEY CARDMAN 4000 DRIVER -M: Harald Welte -S: Maintained -F: drivers/char/pcmcia/cm4000_cs.c -F: include/linux/cm4000_cs.h -F: include/uapi/linux/cm4000_cs.h - -OMNIKEY CARDMAN 4040 DRIVER -M: Harald Welte -S: Maintained -F: drivers/char/pcmcia/cm4040_cs.* - OMNIVISION OG01A1B SENSOR DRIVER M: Shawn Tu L: linux-media@vger.kernel.org @@ -18609,11 +18597,6 @@ F: include/linux/wait.h F: include/uapi/linux/sched.h F: kernel/sched/ -SCR24X CHIP CARD INTERFACE DRIVER -M: Lubomir Rintel -S: Supported -F: drivers/char/pcmcia/scr24x_cs.c - SCSI RDMA PROTOCOL (SRP) INITIATOR M: Bart Van Assche L: linux-rdma@vger.kernel.org diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 110258277959..d8729b94400e 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -614,8 +614,6 @@ CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=m CONFIG_NVRAM=y CONFIG_DTLK=m -CONFIG_CARDMAN_4000=m -CONFIG_CARDMAN_4040=m CONFIG_IPWIRELESS=m CONFIG_I2C_CHARDEV=m CONFIG_I2C_HYDRA=m diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 30fe9848dac1..801d6c83f896 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -247,8 +247,6 @@ config SONYPI To compile this driver as a module, choose M here: the module will be called sonypi. -source "drivers/char/pcmcia/Kconfig" - config MWAVE tristate "ACP Modem (Mwave) support" depends on X86 && TTY diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 1b35d1724565..c5f532e412f1 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_TELCLOCK) += tlclk.o obj-$(CONFIG_MWAVE) += mwave/ obj-y += agp/ -obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig deleted file mode 100644 index 26724990074c..000000000000 --- a/drivers/char/pcmcia/Kconfig +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# PCMCIA character device configuration -# - -menu "PCMCIA character devices" - depends on PCMCIA!=n - -config SYNCLINK_CS - tristate "SyncLink PC Card support" - depends on PCMCIA && TTY - help - Enable support for the SyncLink PC Card serial adapter, running - asynchronous and HDLC communications up to 512Kbps. The port is - selectable for RS-232, V.35, RS-449, RS-530, and X.21 - - This driver may be built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called synclink_cs. If you want to do that, say M - here. - -config CARDMAN_4000 - tristate "Omnikey Cardman 4000 support" - depends on PCMCIA - select BITREVERSE - help - Enable support for the Omnikey Cardman 4000 PCMCIA Smartcard - reader. - - This kernel driver requires additional userspace support, either - by the vendor-provided PC/SC ifd_handler (http://www.omnikey.com/), - or via the cm4000 backend of OpenCT (http://www.opensc-project.org/opensc). - -config CARDMAN_4040 - tristate "Omnikey CardMan 4040 support" - depends on PCMCIA - help - Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard - reader. - - This card is basically a USB CCID device connected to a FIFO - in I/O space. To use the kernel driver, you will need either the - PC/SC ifdhandler provided from the Omnikey homepage - (http://www.omnikey.com/), or a current development version of OpenCT - (http://www.opensc-project.org/opensc). - -config SCR24X - tristate "SCR24x Chip Card Interface support" - depends on PCMCIA - help - Enable support for the SCR24x PCMCIA Chip Card Interface. - - To compile this driver as a module, choose M here. - The module will be called scr24x_cs.. - - If unsure say N. - -endmenu - diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile deleted file mode 100644 index 024eed1c4ca5..000000000000 --- a/drivers/char/pcmcia/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# drivers/char/pcmcia/Makefile -# -# Makefile for the Linux PCMCIA char device drivers. -# - -obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o -obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o -obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o -obj-$(CONFIG_SCR24X) += scr24x_cs.o diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c deleted file mode 100644 index e656f42a28ac..000000000000 --- a/drivers/char/pcmcia/cm4000_cs.c +++ /dev/null @@ -1,1912 +0,0 @@ - /* - * A driver for the PCMCIA Smartcard Reader "Omnikey CardMan Mobile 4000" - * - * cm4000_cs.c support.linux@omnikey.com - * - * Tue Oct 23 11:32:43 GMT 2001 herp - cleaned up header files - * Sun Jan 20 10:11:15 MET 2002 herp - added modversion header files - * Thu Nov 14 16:34:11 GMT 2002 mh - added PPS functionality - * Tue Nov 19 16:36:27 GMT 2002 mh - added SUSPEND/RESUME functionailty - * Wed Jul 28 12:55:01 CEST 2004 mh - kernel 2.6 adjustments - * - * current version: 2.4.0gm4 - * - * (C) 2000,2001,2002,2003,2004 Omnikey AG - * - * (C) 2005-2006 Harald Welte - * - Adhere to Kernel process/coding-style.rst - * - Port to 2.6.13 "new" style PCMCIA - * - Check for copy_{from,to}_user return values - * - Use nonseekable_open() - * - add class interface for udev device creation - * - * All rights reserved. Licensed under dual BSD/GPL license. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -/* #define ATR_CSUM */ - -#define reader_to_dev(x) (&x->p_dev->dev) - -/* n (debug level) is ignored */ -/* additional debug output may be enabled by re-compiling with - * CM4000_DEBUG set */ -/* #define CM4000_DEBUG */ -#define DEBUGP(n, rdr, x, args...) do { \ - dev_dbg(reader_to_dev(rdr), "%s:" x, \ - __func__ , ## args); \ - } while (0) - -static DEFINE_MUTEX(cmm_mutex); - -#define T_1SEC (HZ) -#define T_10MSEC msecs_to_jiffies(10) -#define T_20MSEC msecs_to_jiffies(20) -#define T_40MSEC msecs_to_jiffies(40) -#define T_50MSEC msecs_to_jiffies(50) -#define T_100MSEC msecs_to_jiffies(100) -#define T_500MSEC msecs_to_jiffies(500) - -static void cm4000_release(struct pcmcia_device *link); - -static int major; /* major number we get from the kernel */ - -/* note: the first state has to have number 0 always */ - -#define M_FETCH_ATR 0 -#define M_TIMEOUT_WAIT 1 -#define M_READ_ATR_LEN 2 -#define M_READ_ATR 3 -#define M_ATR_PRESENT 4 -#define M_BAD_CARD 5 -#define M_CARDOFF 6 - -#define LOCK_IO 0 -#define LOCK_MONITOR 1 - -#define IS_AUTOPPS_ACT 6 -#define IS_PROCBYTE_PRESENT 7 -#define IS_INVREV 8 -#define IS_ANY_T0 9 -#define IS_ANY_T1 10 -#define IS_ATR_PRESENT 11 -#define IS_ATR_VALID 12 -#define IS_CMM_ABSENT 13 -#define IS_BAD_LENGTH 14 -#define IS_BAD_CSUM 15 -#define IS_BAD_CARD 16 - -#define REG_FLAGS0(x) (x + 0) -#define REG_FLAGS1(x) (x + 1) -#define REG_NUM_BYTES(x) (x + 2) -#define REG_BUF_ADDR(x) (x + 3) -#define REG_BUF_DATA(x) (x + 4) -#define REG_NUM_SEND(x) (x + 5) -#define REG_BAUDRATE(x) (x + 6) -#define REG_STOPBITS(x) (x + 7) - -struct cm4000_dev { - struct pcmcia_device *p_dev; - - unsigned char atr[MAX_ATR]; - unsigned char rbuf[512]; - unsigned char sbuf[512]; - - wait_queue_head_t devq; /* when removing cardman must not be - zeroed! */ - - wait_queue_head_t ioq; /* if IO is locked, wait on this Q */ - wait_queue_head_t atrq; /* wait for ATR valid */ - wait_queue_head_t readq; /* used by write to wake blk.read */ - - /* warning: do not move this struct group. - * initialising to zero depends on it - see ZERO_DEV below. */ - struct_group(init, - unsigned char atr_csum; - unsigned char atr_len_retry; - unsigned short atr_len; - unsigned short rlen; /* bytes avail. after write */ - unsigned short rpos; /* latest read pos. write zeroes */ - unsigned char procbyte; /* T=0 procedure byte */ - unsigned char mstate; /* state of card monitor */ - unsigned char cwarn; /* slow down warning */ - unsigned char flags0; /* cardman IO-flags 0 */ - unsigned char flags1; /* cardman IO-flags 1 */ - unsigned int mdelay; /* variable monitor speeds, in jiffies */ - - unsigned int baudv; /* baud value for speed */ - unsigned char ta1; - unsigned char proto; /* T=0, T=1, ... */ - unsigned long flags; /* lock+flags (MONITOR,IO,ATR) * for concurrent - access */ - - unsigned char pts[4]; - - struct timer_list timer; /* used to keep monitor running */ - int monitor_running; - ); -}; - -#define ZERO_DEV(dev) memset(&((dev)->init), 0, sizeof((dev)->init)) - -static struct pcmcia_device *dev_table[CM4000_MAX_DEV]; -static struct class *cmm_class; - -/* This table doesn't use spaces after the comma between fields and thus - * violates process/coding-style.rst. However, I don't really think wrapping it around will - * make it any clearer to read -HW */ -static unsigned char fi_di_table[10][14] = { -/*FI 00 01 02 03 04 05 06 07 08 09 10 11 12 13 */ -/*DI */ -/* 0 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, -/* 1 */ {0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x91,0x11,0x11,0x11,0x11}, -/* 2 */ {0x02,0x12,0x22,0x32,0x11,0x11,0x11,0x11,0x11,0x92,0xA2,0xB2,0x11,0x11}, -/* 3 */ {0x03,0x13,0x23,0x33,0x43,0x53,0x63,0x11,0x11,0x93,0xA3,0xB3,0xC3,0xD3}, -/* 4 */ {0x04,0x14,0x24,0x34,0x44,0x54,0x64,0x11,0x11,0x94,0xA4,0xB4,0xC4,0xD4}, -/* 5 */ {0x00,0x15,0x25,0x35,0x45,0x55,0x65,0x11,0x11,0x95,0xA5,0xB5,0xC5,0xD5}, -/* 6 */ {0x06,0x16,0x26,0x36,0x46,0x56,0x66,0x11,0x11,0x96,0xA6,0xB6,0xC6,0xD6}, -/* 7 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, -/* 8 */ {0x08,0x11,0x28,0x38,0x48,0x58,0x68,0x11,0x11,0x98,0xA8,0xB8,0xC8,0xD8}, -/* 9 */ {0x09,0x19,0x29,0x39,0x49,0x59,0x69,0x11,0x11,0x99,0xA9,0xB9,0xC9,0xD9} -}; - -#ifndef CM4000_DEBUG -#define xoutb outb -#define xinb inb -#else -static inline void xoutb(unsigned char val, unsigned short port) -{ - pr_debug("outb(val=%.2x,port=%.4x)\n", val, port); - outb(val, port); -} -static inline unsigned char xinb(unsigned short port) -{ - unsigned char val; - - val = inb(port); - pr_debug("%.2x=inb(%.4x)\n", val, port); - - return val; -} -#endif - -static inline unsigned char invert_revert(unsigned char ch) -{ - return bitrev8(~ch); -} - -static void str_invert_revert(unsigned char *b, int len) -{ - int i; - - for (i = 0; i < len; i++) - b[i] = invert_revert(b[i]); -} - -#define ATRLENCK(dev,pos) \ - if (pos>=dev->atr_len || pos>=MAX_ATR) \ - goto return_0; - -static unsigned int calc_baudv(unsigned char fidi) -{ - unsigned int wcrcf, wbrcf, fi_rfu, di_rfu; - - fi_rfu = 372; - di_rfu = 1; - - /* FI */ - switch ((fidi >> 4) & 0x0F) { - case 0x00: - wcrcf = 372; - break; - case 0x01: - wcrcf = 372; - break; - case 0x02: - wcrcf = 558; - break; - case 0x03: - wcrcf = 744; - break; - case 0x04: - wcrcf = 1116; - break; - case 0x05: - wcrcf = 1488; - break; - case 0x06: - wcrcf = 1860; - break; - case 0x07: - wcrcf = fi_rfu; - break; - case 0x08: - wcrcf = fi_rfu; - break; - case 0x09: - wcrcf = 512; - break; - case 0x0A: - wcrcf = 768; - break; - case 0x0B: - wcrcf = 1024; - break; - case 0x0C: - wcrcf = 1536; - break; - case 0x0D: - wcrcf = 2048; - break; - default: - wcrcf = fi_rfu; - break; - } - - /* DI */ - switch (fidi & 0x0F) { - case 0x00: - wbrcf = di_rfu; - break; - case 0x01: - wbrcf = 1; - break; - case 0x02: - wbrcf = 2; - break; - case 0x03: - wbrcf = 4; - break; - case 0x04: - wbrcf = 8; - break; - case 0x05: - wbrcf = 16; - break; - case 0x06: - wbrcf = 32; - break; - case 0x07: - wbrcf = di_rfu; - break; - case 0x08: - wbrcf = 12; - break; - case 0x09: - wbrcf = 20; - break; - default: - wbrcf = di_rfu; - break; - } - - return (wcrcf / wbrcf); -} - -static unsigned short io_read_num_rec_bytes(unsigned int iobase, - unsigned short *s) -{ - unsigned short tmp; - - tmp = *s = 0; - do { - *s = tmp; - tmp = inb(REG_NUM_BYTES(iobase)) | - (inb(REG_FLAGS0(iobase)) & 4 ? 0x100 : 0); - } while (tmp != *s); - - return *s; -} - -static int parse_atr(struct cm4000_dev *dev) -{ - unsigned char any_t1, any_t0; - unsigned char ch, ifno; - int ix, done; - - DEBUGP(3, dev, "-> parse_atr: dev->atr_len = %i\n", dev->atr_len); - - if (dev->atr_len < 3) { - DEBUGP(5, dev, "parse_atr: atr_len < 3\n"); - return 0; - } - - if (dev->atr[0] == 0x3f) - set_bit(IS_INVREV, &dev->flags); - else - clear_bit(IS_INVREV, &dev->flags); - ix = 1; - ifno = 1; - ch = dev->atr[1]; - dev->proto = 0; /* XXX PROTO */ - any_t1 = any_t0 = done = 0; - dev->ta1 = 0x11; /* defaults to 9600 baud */ - do { - if (ifno == 1 && (ch & 0x10)) { - /* read first interface byte and TA1 is present */ - dev->ta1 = dev->atr[2]; - DEBUGP(5, dev, "Card says FiDi is 0x%.2x\n", dev->ta1); - ifno++; - } else if ((ifno == 2) && (ch & 0x10)) { /* TA(2) */ - dev->ta1 = 0x11; - ifno++; - } - - DEBUGP(5, dev, "Yi=%.2x\n", ch & 0xf0); - ix += ((ch & 0x10) >> 4) /* no of int.face chars */ - +((ch & 0x20) >> 5) - + ((ch & 0x40) >> 6) - + ((ch & 0x80) >> 7); - /* ATRLENCK(dev,ix); */ - if (ch & 0x80) { /* TDi */ - ch = dev->atr[ix]; - if ((ch & 0x0f)) { - any_t1 = 1; - DEBUGP(5, dev, "card is capable of T=1\n"); - } else { - any_t0 = 1; - DEBUGP(5, dev, "card is capable of T=0\n"); - } - } else - done = 1; - } while (!done); - - DEBUGP(5, dev, "ix=%d noHist=%d any_t1=%d\n", - ix, dev->atr[1] & 15, any_t1); - if (ix + 1 + (dev->atr[1] & 0x0f) + any_t1 != dev->atr_len) { - DEBUGP(5, dev, "length error\n"); - return 0; - } - if (any_t0) - set_bit(IS_ANY_T0, &dev->flags); - - if (any_t1) { /* compute csum */ - dev->atr_csum = 0; -#ifdef ATR_CSUM - for (i = 1; i < dev->atr_len; i++) - dev->atr_csum ^= dev->atr[i]; - if (dev->atr_csum) { - set_bit(IS_BAD_CSUM, &dev->flags); - DEBUGP(5, dev, "bad checksum\n"); - goto return_0; - } -#endif - if (any_t0 == 0) - dev->proto = 1; /* XXX PROTO */ - set_bit(IS_ANY_T1, &dev->flags); - } - - return 1; -} - -struct card_fixup { - char atr[12]; - u_int8_t atr_len; - u_int8_t stopbits; -}; - -static struct card_fixup card_fixups[] = { - { /* ACOS */ - .atr = { 0x3b, 0xb3, 0x11, 0x00, 0x00, 0x41, 0x01 }, - .atr_len = 7, - .stopbits = 0x03, - }, - { /* Motorola */ - .atr = {0x3b, 0x76, 0x13, 0x00, 0x00, 0x80, 0x62, 0x07, - 0x41, 0x81, 0x81 }, - .atr_len = 11, - .stopbits = 0x04, - }, -}; - -static void set_cardparameter(struct cm4000_dev *dev) -{ - int i; - unsigned int iobase = dev->p_dev->resource[0]->start; - u_int8_t stopbits = 0x02; /* ISO default */ - - DEBUGP(3, dev, "-> set_cardparameter\n"); - - dev->flags1 = dev->flags1 | (((dev->baudv - 1) & 0x0100) >> 8); - xoutb(dev->flags1, REG_FLAGS1(iobase)); - DEBUGP(5, dev, "flags1 = 0x%02x\n", dev->flags1); - - /* set baudrate */ - xoutb((unsigned char)((dev->baudv - 1) & 0xFF), REG_BAUDRATE(iobase)); - - DEBUGP(5, dev, "baudv = %i -> write 0x%02x\n", dev->baudv, - ((dev->baudv - 1) & 0xFF)); - - /* set stopbits */ - for (i = 0; i < ARRAY_SIZE(card_fixups); i++) { - if (!memcmp(dev->atr, card_fixups[i].atr, - card_fixups[i].atr_len)) - stopbits = card_fixups[i].stopbits; - } - xoutb(stopbits, REG_STOPBITS(iobase)); - - DEBUGP(3, dev, "<- set_cardparameter\n"); -} - -static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq) -{ - - unsigned long tmp, i; - unsigned short num_bytes_read; - unsigned char pts_reply[4]; - ssize_t rc; - unsigned int iobase = dev->p_dev->resource[0]->start; - - rc = 0; - - DEBUGP(3, dev, "-> set_protocol\n"); - DEBUGP(5, dev, "ptsreq->Protocol = 0x%.8x, ptsreq->Flags=0x%.8x, " - "ptsreq->pts1=0x%.2x, ptsreq->pts2=0x%.2x, " - "ptsreq->pts3=0x%.2x\n", (unsigned int)ptsreq->protocol, - (unsigned int)ptsreq->flags, ptsreq->pts1, ptsreq->pts2, - ptsreq->pts3); - - /* Fill PTS structure */ - dev->pts[0] = 0xff; - dev->pts[1] = 0x00; - tmp = ptsreq->protocol; - while ((tmp = (tmp >> 1)) > 0) - dev->pts[1]++; - dev->proto = dev->pts[1]; /* Set new protocol */ - dev->pts[1] = (0x01 << 4) | (dev->pts[1]); - - /* Correct Fi/Di according to CM4000 Fi/Di table */ - DEBUGP(5, dev, "Ta(1) from ATR is 0x%.2x\n", dev->ta1); - /* set Fi/Di according to ATR TA(1) */ - dev->pts[2] = fi_di_table[dev->ta1 & 0x0F][(dev->ta1 >> 4) & 0x0F]; - - /* Calculate PCK character */ - dev->pts[3] = dev->pts[0] ^ dev->pts[1] ^ dev->pts[2]; - - DEBUGP(5, dev, "pts0=%.2x, pts1=%.2x, pts2=%.2x, pts3=%.2x\n", - dev->pts[0], dev->pts[1], dev->pts[2], dev->pts[3]); - - /* check card convention */ - if (test_bit(IS_INVREV, &dev->flags)) - str_invert_revert(dev->pts, 4); - - /* reset SM */ - xoutb(0x80, REG_FLAGS0(iobase)); - - /* Enable access to the message buffer */ - DEBUGP(5, dev, "Enable access to the messages buffer\n"); - dev->flags1 = 0x20 /* T_Active */ - | (test_bit(IS_INVREV, &dev->flags) ? 0x02 : 0x00) /* inv parity */ - | ((dev->baudv >> 8) & 0x01); /* MSB-baud */ - xoutb(dev->flags1, REG_FLAGS1(iobase)); - - DEBUGP(5, dev, "Enable message buffer -> flags1 = 0x%.2x\n", - dev->flags1); - - /* write challenge to the buffer */ - DEBUGP(5, dev, "Write challenge to buffer: "); - for (i = 0; i < 4; i++) { - xoutb(i, REG_BUF_ADDR(iobase)); - xoutb(dev->pts[i], REG_BUF_DATA(iobase)); /* buf data */ -#ifdef CM4000_DEBUG - pr_debug("0x%.2x ", dev->pts[i]); - } - pr_debug("\n"); -#else - } -#endif - - /* set number of bytes to write */ - DEBUGP(5, dev, "Set number of bytes to write\n"); - xoutb(0x04, REG_NUM_SEND(iobase)); - - /* Trigger CARDMAN CONTROLLER */ - xoutb(0x50, REG_FLAGS0(iobase)); - - /* Monitor progress */ - /* wait for xmit done */ - DEBUGP(5, dev, "Waiting for NumRecBytes getting valid\n"); - - for (i = 0; i < 100; i++) { - if (inb(REG_FLAGS0(iobase)) & 0x08) { - DEBUGP(5, dev, "NumRecBytes is valid\n"); - break; - } - /* can not sleep as this is in atomic context */ - mdelay(10); - } - if (i == 100) { - DEBUGP(5, dev, "Timeout waiting for NumRecBytes getting " - "valid\n"); - rc = -EIO; - goto exit_setprotocol; - } - - DEBUGP(5, dev, "Reading NumRecBytes\n"); - for (i = 0; i < 100; i++) { - io_read_num_rec_bytes(iobase, &num_bytes_read); - if (num_bytes_read >= 4) { - DEBUGP(2, dev, "NumRecBytes = %i\n", num_bytes_read); - if (num_bytes_read > 4) { - rc = -EIO; - goto exit_setprotocol; - } - break; - } - /* can not sleep as this is in atomic context */ - mdelay(10); - } - - /* check whether it is a short PTS reply? */ - if (num_bytes_read == 3) - i = 0; - - if (i == 100) { - DEBUGP(5, dev, "Timeout reading num_bytes_read\n"); - rc = -EIO; - goto exit_setprotocol; - } - - DEBUGP(5, dev, "Reset the CARDMAN CONTROLLER\n"); - xoutb(0x80, REG_FLAGS0(iobase)); - - /* Read PPS reply */ - DEBUGP(5, dev, "Read PPS reply\n"); - for (i = 0; i < num_bytes_read; i++) { - xoutb(i, REG_BUF_ADDR(iobase)); - pts_reply[i] = inb(REG_BUF_DATA(iobase)); - } - -#ifdef CM4000_DEBUG - DEBUGP(2, dev, "PTSreply: "); - for (i = 0; i < num_bytes_read; i++) { - pr_debug("0x%.2x ", pts_reply[i]); - } - pr_debug("\n"); -#endif /* CM4000_DEBUG */ - - DEBUGP(5, dev, "Clear Tactive in Flags1\n"); - xoutb(0x20, REG_FLAGS1(iobase)); - - /* Compare ptsreq and ptsreply */ - if ((dev->pts[0] == pts_reply[0]) && - (dev->pts[1] == pts_reply[1]) && - (dev->pts[2] == pts_reply[2]) && (dev->pts[3] == pts_reply[3])) { - /* setcardparameter according to PPS */ - dev->baudv = calc_baudv(dev->pts[2]); - set_cardparameter(dev); - } else if ((dev->pts[0] == pts_reply[0]) && - ((dev->pts[1] & 0xef) == pts_reply[1]) && - ((pts_reply[0] ^ pts_reply[1]) == pts_reply[2])) { - /* short PTS reply, set card parameter to default values */ - dev->baudv = calc_baudv(0x11); - set_cardparameter(dev); - } else - rc = -EIO; - -exit_setprotocol: - DEBUGP(3, dev, "<- set_protocol\n"); - return rc; -} - -static int io_detect_cm4000(unsigned int iobase, struct cm4000_dev *dev) -{ - - /* note: statemachine is assumed to be reset */ - if (inb(REG_FLAGS0(iobase)) & 8) { - clear_bit(IS_ATR_VALID, &dev->flags); - set_bit(IS_CMM_ABSENT, &dev->flags); - return 0; /* detect CMM = 1 -> failure */ - } - /* xoutb(0x40, REG_FLAGS1(iobase)); detectCMM */ - xoutb(dev->flags1 | 0x40, REG_FLAGS1(iobase)); - if ((inb(REG_FLAGS0(iobase)) & 8) == 0) { - clear_bit(IS_ATR_VALID, &dev->flags); - set_bit(IS_CMM_ABSENT, &dev->flags); - return 0; /* detect CMM=0 -> failure */ - } - /* clear detectCMM again by restoring original flags1 */ - xoutb(dev->flags1, REG_FLAGS1(iobase)); - return 1; -} - -static void terminate_monitor(struct cm4000_dev *dev) -{ - - /* tell the monitor to stop and wait until - * it terminates. - */ - DEBUGP(3, dev, "-> terminate_monitor\n"); - wait_event_interruptible(dev->devq, - test_and_set_bit(LOCK_MONITOR, - (void *)&dev->flags)); - - /* now, LOCK_MONITOR has been set. - * allow a last cycle in the monitor. - * the monitor will indicate that it has - * finished by clearing this bit. - */ - DEBUGP(5, dev, "Now allow last cycle of monitor!\n"); - while (test_bit(LOCK_MONITOR, (void *)&dev->flags)) - msleep(25); - - DEBUGP(5, dev, "Delete timer\n"); - del_timer_sync(&dev->timer); -#ifdef CM4000_DEBUG - dev->monitor_running = 0; -#endif - - DEBUGP(3, dev, "<- terminate_monitor\n"); -} - -/* - * monitor the card every 50msec. as a side-effect, retrieve the - * atr once a card is inserted. another side-effect of retrieving the - * atr is that the card will be powered on, so there is no need to - * power on the card explicitly from the application: the driver - * is already doing that for you. - */ - -static void monitor_card(struct timer_list *t) -{ - struct cm4000_dev *dev = from_timer(dev, t, timer); - unsigned int iobase = dev->p_dev->resource[0]->start; - unsigned short s; - struct ptsreq ptsreq; - int i, atrc; - - DEBUGP(7, dev, "-> monitor_card\n"); - - /* if someone has set the lock for us: we're done! */ - if (test_and_set_bit(LOCK_MONITOR, &dev->flags)) { - DEBUGP(4, dev, "About to stop monitor\n"); - /* no */ - dev->rlen = - dev->rpos = - dev->atr_csum = dev->atr_len_retry = dev->cwarn = 0; - dev->mstate = M_FETCH_ATR; - clear_bit(LOCK_MONITOR, &dev->flags); - /* close et al. are sleeping on devq, so wake it */ - wake_up_interruptible(&dev->devq); - DEBUGP(2, dev, "<- monitor_card (we are done now)\n"); - return; - } - - /* try to lock io: if it is already locked, just add another timer */ - if (test_and_set_bit(LOCK_IO, (void *)&dev->flags)) { - DEBUGP(4, dev, "Couldn't get IO lock\n"); - goto return_with_timer; - } - - /* is a card/a reader inserted at all ? */ - dev->flags0 = xinb(REG_FLAGS0(iobase)); - DEBUGP(7, dev, "dev->flags0 = 0x%2x\n", dev->flags0); - DEBUGP(7, dev, "smartcard present: %s\n", - dev->flags0 & 1 ? "yes" : "no"); - DEBUGP(7, dev, "cardman present: %s\n", - dev->flags0 == 0xff ? "no" : "yes"); - - if ((dev->flags0 & 1) == 0 /* no smartcard inserted */ - || dev->flags0 == 0xff) { /* no cardman inserted */ - /* no */ - dev->rlen = - dev->rpos = - dev->atr_csum = dev->atr_len_retry = dev->cwarn = 0; - dev->mstate = M_FETCH_ATR; - - dev->flags &= 0x000000ff; /* only keep IO and MONITOR locks */ - - if (dev->flags0 == 0xff) { - DEBUGP(4, dev, "set IS_CMM_ABSENT bit\n"); - set_bit(IS_CMM_ABSENT, &dev->flags); - } else if (test_bit(IS_CMM_ABSENT, &dev->flags)) { - DEBUGP(4, dev, "clear IS_CMM_ABSENT bit " - "(card is removed)\n"); - clear_bit(IS_CMM_ABSENT, &dev->flags); - } - - goto release_io; - } else if ((dev->flags0 & 1) && test_bit(IS_CMM_ABSENT, &dev->flags)) { - /* cardman and card present but cardman was absent before - * (after suspend with inserted card) */ - DEBUGP(4, dev, "clear IS_CMM_ABSENT bit (card is inserted)\n"); - clear_bit(IS_CMM_ABSENT, &dev->flags); - } - - if (test_bit(IS_ATR_VALID, &dev->flags) == 1) { - DEBUGP(7, dev, "believe ATR is already valid (do nothing)\n"); - goto release_io; - } - - switch (dev->mstate) { - case M_CARDOFF: { - unsigned char flags0; - - DEBUGP(4, dev, "M_CARDOFF\n"); - flags0 = inb(REG_FLAGS0(iobase)); - if (flags0 & 0x02) { - /* wait until Flags0 indicate power is off */ - dev->mdelay = T_10MSEC; - } else { - /* Flags0 indicate power off and no card inserted now; - * Reset CARDMAN CONTROLLER */ - xoutb(0x80, REG_FLAGS0(iobase)); - - /* prepare for fetching ATR again: after card off ATR - * is read again automatically */ - dev->rlen = - dev->rpos = - dev->atr_csum = - dev->atr_len_retry = dev->cwarn = 0; - dev->mstate = M_FETCH_ATR; - - /* minimal gap between CARDOFF and read ATR is 50msec */ - dev->mdelay = T_50MSEC; - } - break; - } - case M_FETCH_ATR: - DEBUGP(4, dev, "M_FETCH_ATR\n"); - xoutb(0x80, REG_FLAGS0(iobase)); - DEBUGP(4, dev, "Reset BAUDV to 9600\n"); - dev->baudv = 0x173; /* 9600 */ - xoutb(0x02, REG_STOPBITS(iobase)); /* stopbits=2 */ - xoutb(0x73, REG_BAUDRATE(iobase)); /* baud value */ - xoutb(0x21, REG_FLAGS1(iobase)); /* T_Active=1, baud - value */ - /* warm start vs. power on: */ - xoutb(dev->flags0 & 2 ? 0x46 : 0x44, REG_FLAGS0(iobase)); - dev->mdelay = T_40MSEC; - dev->mstate = M_TIMEOUT_WAIT; - break; - case M_TIMEOUT_WAIT: - DEBUGP(4, dev, "M_TIMEOUT_WAIT\n"); - /* numRecBytes */ - io_read_num_rec_bytes(iobase, &dev->atr_len); - dev->mdelay = T_10MSEC; - dev->mstate = M_READ_ATR_LEN; - break; - case M_READ_ATR_LEN: - DEBUGP(4, dev, "M_READ_ATR_LEN\n"); - /* infinite loop possible, since there is no timeout */ - -#define MAX_ATR_LEN_RETRY 100 - - if (dev->atr_len == io_read_num_rec_bytes(iobase, &s)) { - if (dev->atr_len_retry++ >= MAX_ATR_LEN_RETRY) { /* + XX msec */ - dev->mdelay = T_10MSEC; - dev->mstate = M_READ_ATR; - } - } else { - dev->atr_len = s; - dev->atr_len_retry = 0; /* set new timeout */ - } - - DEBUGP(4, dev, "Current ATR_LEN = %i\n", dev->atr_len); - break; - case M_READ_ATR: - DEBUGP(4, dev, "M_READ_ATR\n"); - xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */ - for (i = 0; i < dev->atr_len; i++) { - xoutb(i, REG_BUF_ADDR(iobase)); - dev->atr[i] = inb(REG_BUF_DATA(iobase)); - } - /* Deactivate T_Active flags */ - DEBUGP(4, dev, "Deactivate T_Active flags\n"); - dev->flags1 = 0x01; - xoutb(dev->flags1, REG_FLAGS1(iobase)); - - /* atr is present (which doesn't mean it's valid) */ - set_bit(IS_ATR_PRESENT, &dev->flags); - if (dev->atr[0] == 0x03) - str_invert_revert(dev->atr, dev->atr_len); - atrc = parse_atr(dev); - if (atrc == 0) { /* atr invalid */ - dev->mdelay = 0; - dev->mstate = M_BAD_CARD; - } else { - dev->mdelay = T_50MSEC; - dev->mstate = M_ATR_PRESENT; - set_bit(IS_ATR_VALID, &dev->flags); - } - - if (test_bit(IS_ATR_VALID, &dev->flags) == 1) { - DEBUGP(4, dev, "monitor_card: ATR valid\n"); - /* if ta1 == 0x11, no PPS necessary (default values) */ - /* do not do PPS with multi protocol cards */ - if ((test_bit(IS_AUTOPPS_ACT, &dev->flags) == 0) && - (dev->ta1 != 0x11) && - !(test_bit(IS_ANY_T0, &dev->flags) && - test_bit(IS_ANY_T1, &dev->flags))) { - DEBUGP(4, dev, "Perform AUTOPPS\n"); - set_bit(IS_AUTOPPS_ACT, &dev->flags); - ptsreq.protocol = (0x01 << dev->proto); - ptsreq.flags = 0x01; - ptsreq.pts1 = 0x00; - ptsreq.pts2 = 0x00; - ptsreq.pts3 = 0x00; - if (set_protocol(dev, &ptsreq) == 0) { - DEBUGP(4, dev, "AUTOPPS ret SUCC\n"); - clear_bit(IS_AUTOPPS_ACT, &dev->flags); - wake_up_interruptible(&dev->atrq); - } else { - DEBUGP(4, dev, "AUTOPPS failed: " - "repower using defaults\n"); - /* prepare for repowering */ - clear_bit(IS_ATR_PRESENT, &dev->flags); - clear_bit(IS_ATR_VALID, &dev->flags); - dev->rlen = - dev->rpos = - dev->atr_csum = - dev->atr_len_retry = dev->cwarn = 0; - dev->mstate = M_FETCH_ATR; - - dev->mdelay = T_50MSEC; - } - } else { - /* for cards which use slightly different - * params (extra guard time) */ - set_cardparameter(dev); - if (test_bit(IS_AUTOPPS_ACT, &dev->flags) == 1) - DEBUGP(4, dev, "AUTOPPS already active " - "2nd try:use default values\n"); - if (dev->ta1 == 0x11) - DEBUGP(4, dev, "No AUTOPPS necessary " - "TA(1)==0x11\n"); - if (test_bit(IS_ANY_T0, &dev->flags) - && test_bit(IS_ANY_T1, &dev->flags)) - DEBUGP(4, dev, "Do NOT perform AUTOPPS " - "with multiprotocol cards\n"); - clear_bit(IS_AUTOPPS_ACT, &dev->flags); - wake_up_interruptible(&dev->atrq); - } - } else { - DEBUGP(4, dev, "ATR invalid\n"); - wake_up_interruptible(&dev->atrq); - } - break; - case M_BAD_CARD: - DEBUGP(4, dev, "M_BAD_CARD\n"); - /* slow down warning, but prompt immediately after insertion */ - if (dev->cwarn == 0 || dev->cwarn == 10) { - set_bit(IS_BAD_CARD, &dev->flags); - dev_warn(&dev->p_dev->dev, MODULE_NAME ": "); - if (test_bit(IS_BAD_CSUM, &dev->flags)) { - DEBUGP(4, dev, "ATR checksum (0x%.2x, should " - "be zero) failed\n", dev->atr_csum); - } -#ifdef CM4000_DEBUG - else if (test_bit(IS_BAD_LENGTH, &dev->flags)) { - DEBUGP(4, dev, "ATR length error\n"); - } else { - DEBUGP(4, dev, "card damaged or wrong way " - "inserted\n"); - } -#endif - dev->cwarn = 0; - wake_up_interruptible(&dev->atrq); /* wake open */ - } - dev->cwarn++; - dev->mdelay = T_100MSEC; - dev->mstate = M_FETCH_ATR; - break; - default: - DEBUGP(7, dev, "Unknown action\n"); - break; /* nothing */ - } - -release_io: - DEBUGP(7, dev, "release_io\n"); - clear_bit(LOCK_IO, &dev->flags); - wake_up_interruptible(&dev->ioq); /* whoever needs IO */ - -return_with_timer: - DEBUGP(7, dev, "<- monitor_card (returns with timer)\n"); - mod_timer(&dev->timer, jiffies + dev->mdelay); - clear_bit(LOCK_MONITOR, &dev->flags); -} - -/* Interface to userland (file_operations) */ - -static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, - loff_t *ppos) -{ - struct cm4000_dev *dev = filp->private_data; - unsigned int iobase = dev->p_dev->resource[0]->start; - ssize_t rc; - int i, j, k; - - DEBUGP(2, dev, "-> cmm_read(%s,%d)\n", current->comm, current->pid); - - if (count == 0) /* according to manpage */ - return 0; - - if (!pcmcia_dev_present(dev->p_dev) || /* device removed */ - test_bit(IS_CMM_ABSENT, &dev->flags)) - return -ENODEV; - - if (test_bit(IS_BAD_CSUM, &dev->flags)) - return -EIO; - - /* also see the note about this in cmm_write */ - if (wait_event_interruptible - (dev->atrq, - ((filp->f_flags & O_NONBLOCK) - || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; - } - - if (test_bit(IS_ATR_VALID, &dev->flags) == 0) - return -EIO; - - /* this one implements blocking IO */ - if (wait_event_interruptible - (dev->readq, - ((filp->f_flags & O_NONBLOCK) || (dev->rpos < dev->rlen)))) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; - } - - /* lock io */ - if (wait_event_interruptible - (dev->ioq, - ((filp->f_flags & O_NONBLOCK) - || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; - } - - rc = 0; - dev->flags0 = inb(REG_FLAGS0(iobase)); - if ((dev->flags0 & 1) == 0 /* no smartcard inserted */ - || dev->flags0 == 0xff) { /* no cardman inserted */ - clear_bit(IS_ATR_VALID, &dev->flags); - if (dev->flags0 & 1) { - set_bit(IS_CMM_ABSENT, &dev->flags); - rc = -ENODEV; - } else { - rc = -EIO; - } - goto release_io; - } - - DEBUGP(4, dev, "begin read answer\n"); - j = min(count, (size_t)(dev->rlen - dev->rpos)); - k = dev->rpos; - if (k + j > 255) - j = 256 - k; - DEBUGP(4, dev, "read1 j=%d\n", j); - for (i = 0; i < j; i++) { - xoutb(k++, REG_BUF_ADDR(iobase)); - dev->rbuf[i] = xinb(REG_BUF_DATA(iobase)); - } - j = min(count, (size_t)(dev->rlen - dev->rpos)); - if (k + j > 255) { - DEBUGP(4, dev, "read2 j=%d\n", j); - dev->flags1 |= 0x10; /* MSB buf addr set */ - xoutb(dev->flags1, REG_FLAGS1(iobase)); - for (; i < j; i++) { - xoutb(k++, REG_BUF_ADDR(iobase)); - dev->rbuf[i] = xinb(REG_BUF_DATA(iobase)); - } - } - - if (dev->proto == 0 && count > dev->rlen - dev->rpos && i) { - DEBUGP(4, dev, "T=0 and count > buffer\n"); - dev->rbuf[i] = dev->rbuf[i - 1]; - dev->rbuf[i - 1] = dev->procbyte; - j++; - } - count = j; - - dev->rpos = dev->rlen + 1; - - /* Clear T1Active */ - DEBUGP(4, dev, "Clear T1Active\n"); - dev->flags1 &= 0xdf; - xoutb(dev->flags1, REG_FLAGS1(iobase)); - - xoutb(0, REG_FLAGS1(iobase)); /* clear detectCMM */ - /* last check before exit */ - if (!io_detect_cm4000(iobase, dev)) { - rc = -ENODEV; - goto release_io; - } - - if (test_bit(IS_INVREV, &dev->flags) && count > 0) - str_invert_revert(dev->rbuf, count); - - if (copy_to_user(buf, dev->rbuf, count)) - rc = -EFAULT; - -release_io: - clear_bit(LOCK_IO, &dev->flags); - wake_up_interruptible(&dev->ioq); - - DEBUGP(2, dev, "<- cmm_read returns: rc = %zi\n", - (rc < 0 ? rc : count)); - return rc < 0 ? rc : count; -} - -static ssize_t cmm_write(struct file *filp, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct cm4000_dev *dev = filp->private_data; - unsigned int iobase = dev->p_dev->resource[0]->start; - unsigned short s; - unsigned char infolen; - unsigned char sendT0; - unsigned short nsend; - unsigned short nr; - ssize_t rc; - int i; - - DEBUGP(2, dev, "-> cmm_write(%s,%d)\n", current->comm, current->pid); - - if (count == 0) /* according to manpage */ - return 0; - - if (dev->proto == 0 && count < 4) { - /* T0 must have at least 4 bytes */ - DEBUGP(4, dev, "T0 short write\n"); - return -EIO; - } - - nr = count & 0x1ff; /* max bytes to write */ - - sendT0 = dev->proto ? 0 : nr > 5 ? 0x08 : 0; - - if (!pcmcia_dev_present(dev->p_dev) || /* device removed */ - test_bit(IS_CMM_ABSENT, &dev->flags)) - return -ENODEV; - - if (test_bit(IS_BAD_CSUM, &dev->flags)) { - DEBUGP(4, dev, "bad csum\n"); - return -EIO; - } - - /* - * wait for atr to become valid. - * note: it is important to lock this code. if we dont, the monitor - * could be run between test_bit and the call to sleep on the - * atr-queue. if *then* the monitor detects atr valid, it will wake up - * any process on the atr-queue, *but* since we have been interrupted, - * we do not yet sleep on this queue. this would result in a missed - * wake_up and the calling process would sleep forever (until - * interrupted). also, do *not* restore_flags before sleep_on, because - * this could result in the same situation! - */ - if (wait_event_interruptible - (dev->atrq, - ((filp->f_flags & O_NONBLOCK) - || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; - } - - if (test_bit(IS_ATR_VALID, &dev->flags) == 0) { /* invalid atr */ - DEBUGP(4, dev, "invalid ATR\n"); - return -EIO; - } - - /* lock io */ - if (wait_event_interruptible - (dev->ioq, - ((filp->f_flags & O_NONBLOCK) - || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; - } - - if (copy_from_user(dev->sbuf, buf, ((count > 512) ? 512 : count))) - return -EFAULT; - - rc = 0; - dev->flags0 = inb(REG_FLAGS0(iobase)); - if ((dev->flags0 & 1) == 0 /* no smartcard inserted */ - || dev->flags0 == 0xff) { /* no cardman inserted */ - clear_bit(IS_ATR_VALID, &dev->flags); - if (dev->flags0 & 1) { - set_bit(IS_CMM_ABSENT, &dev->flags); - rc = -ENODEV; - } else { - DEBUGP(4, dev, "IO error\n"); - rc = -EIO; - } - goto release_io; - } - - xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */ - - if (!io_detect_cm4000(iobase, dev)) { - rc = -ENODEV; - goto release_io; - } - - /* reflect T=0 send/read mode in flags1 */ - dev->flags1 |= (sendT0); - - set_cardparameter(dev); - - /* dummy read, reset flag procedure received */ - inb(REG_FLAGS1(iobase)); - - dev->flags1 = 0x20 /* T_Active */ - | (sendT0) - | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)/* inverse parity */ - | (((dev->baudv - 1) & 0x0100) >> 8); /* MSB-Baud */ - DEBUGP(1, dev, "set dev->flags1 = 0x%.2x\n", dev->flags1); - xoutb(dev->flags1, REG_FLAGS1(iobase)); - - /* xmit data */ - DEBUGP(4, dev, "Xmit data\n"); - for (i = 0; i < nr; i++) { - if (i >= 256) { - dev->flags1 = 0x20 /* T_Active */ - | (sendT0) /* SendT0 */ - /* inverse parity: */ - | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0) - | (((dev->baudv - 1) & 0x0100) >> 8) /* MSB-Baud */ - | 0x10; /* set address high */ - DEBUGP(4, dev, "dev->flags = 0x%.2x - set address " - "high\n", dev->flags1); - xoutb(dev->flags1, REG_FLAGS1(iobase)); - } - if (test_bit(IS_INVREV, &dev->flags)) { - DEBUGP(4, dev, "Apply inverse convention for 0x%.2x " - "-> 0x%.2x\n", (unsigned char)dev->sbuf[i], - invert_revert(dev->sbuf[i])); - xoutb(i, REG_BUF_ADDR(iobase)); - xoutb(invert_revert(dev->sbuf[i]), - REG_BUF_DATA(iobase)); - } else { - xoutb(i, REG_BUF_ADDR(iobase)); - xoutb(dev->sbuf[i], REG_BUF_DATA(iobase)); - } - } - DEBUGP(4, dev, "Xmit done\n"); - - if (dev->proto == 0) { - /* T=0 proto: 0 byte reply */ - if (nr == 4) { - DEBUGP(4, dev, "T=0 assumes 0 byte reply\n"); - xoutb(i, REG_BUF_ADDR(iobase)); - if (test_bit(IS_INVREV, &dev->flags)) - xoutb(0xff, REG_BUF_DATA(iobase)); - else - xoutb(0x00, REG_BUF_DATA(iobase)); - } - - /* numSendBytes */ - if (sendT0) - nsend = nr; - else { - if (nr == 4) - nsend = 5; - else { - nsend = 5 + (unsigned char)dev->sbuf[4]; - if (dev->sbuf[4] == 0) - nsend += 0x100; - } - } - } else - nsend = nr; - - /* T0: output procedure byte */ - if (test_bit(IS_INVREV, &dev->flags)) { - DEBUGP(4, dev, "T=0 set Procedure byte (inverse-reverse) " - "0x%.2x\n", invert_revert(dev->sbuf[1])); - xoutb(invert_revert(dev->sbuf[1]), REG_NUM_BYTES(iobase)); - } else { - DEBUGP(4, dev, "T=0 set Procedure byte 0x%.2x\n", dev->sbuf[1]); - xoutb(dev->sbuf[1], REG_NUM_BYTES(iobase)); - } - - DEBUGP(1, dev, "set NumSendBytes = 0x%.2x\n", - (unsigned char)(nsend & 0xff)); - xoutb((unsigned char)(nsend & 0xff), REG_NUM_SEND(iobase)); - - DEBUGP(1, dev, "Trigger CARDMAN CONTROLLER (0x%.2x)\n", - 0x40 /* SM_Active */ - | (dev->flags0 & 2 ? 0 : 4) /* power on if needed */ - |(dev->proto ? 0x10 : 0x08) /* T=1/T=0 */ - |(nsend & 0x100) >> 8 /* MSB numSendBytes */ ); - xoutb(0x40 /* SM_Active */ - | (dev->flags0 & 2 ? 0 : 4) /* power on if needed */ - |(dev->proto ? 0x10 : 0x08) /* T=1/T=0 */ - |(nsend & 0x100) >> 8, /* MSB numSendBytes */ - REG_FLAGS0(iobase)); - - /* wait for xmit done */ - if (dev->proto == 1) { - DEBUGP(4, dev, "Wait for xmit done\n"); - for (i = 0; i < 1000; i++) { - if (inb(REG_FLAGS0(iobase)) & 0x08) - break; - msleep_interruptible(10); - } - if (i == 1000) { - DEBUGP(4, dev, "timeout waiting for xmit done\n"); - rc = -EIO; - goto release_io; - } - } - - /* T=1: wait for infoLen */ - - infolen = 0; - if (dev->proto) { - /* wait until infoLen is valid */ - for (i = 0; i < 6000; i++) { /* max waiting time of 1 min */ - io_read_num_rec_bytes(iobase, &s); - if (s >= 3) { - infolen = inb(REG_FLAGS1(iobase)); - DEBUGP(4, dev, "infolen=%d\n", infolen); - break; - } - msleep_interruptible(10); - } - if (i == 6000) { - DEBUGP(4, dev, "timeout waiting for infoLen\n"); - rc = -EIO; - goto release_io; - } - } else - clear_bit(IS_PROCBYTE_PRESENT, &dev->flags); - - /* numRecBytes | bit9 of numRecytes */ - io_read_num_rec_bytes(iobase, &dev->rlen); - for (i = 0; i < 600; i++) { /* max waiting time of 2 sec */ - if (dev->proto) { - if (dev->rlen >= infolen + 4) - break; - } - msleep_interruptible(10); - /* numRecBytes | bit9 of numRecytes */ - io_read_num_rec_bytes(iobase, &s); - if (s > dev->rlen) { - DEBUGP(1, dev, "NumRecBytes inc (reset timeout)\n"); - i = 0; /* reset timeout */ - dev->rlen = s; - } - /* T=0: we are done when numRecBytes doesn't - * increment any more and NoProcedureByte - * is set and numRecBytes == bytes sent + 6 - * (header bytes + data + 1 for sw2) - * except when the card replies an error - * which means, no data will be sent back. - */ - else if (dev->proto == 0) { - if ((inb(REG_BUF_ADDR(iobase)) & 0x80)) { - /* no procedure byte received since last read */ - DEBUGP(1, dev, "NoProcedure byte set\n"); - /* i=0; */ - } else { - /* procedure byte received since last read */ - DEBUGP(1, dev, "NoProcedure byte unset " - "(reset timeout)\n"); - dev->procbyte = inb(REG_FLAGS1(iobase)); - DEBUGP(1, dev, "Read procedure byte 0x%.2x\n", - dev->procbyte); - i = 0; /* resettimeout */ - } - if (inb(REG_FLAGS0(iobase)) & 0x08) { - DEBUGP(1, dev, "T0Done flag (read reply)\n"); - break; - } - } - if (dev->proto) - infolen = inb(REG_FLAGS1(iobase)); - } - if (i == 600) { - DEBUGP(1, dev, "timeout waiting for numRecBytes\n"); - rc = -EIO; - goto release_io; - } else { - if (dev->proto == 0) { - DEBUGP(1, dev, "Wait for T0Done bit to be set\n"); - for (i = 0; i < 1000; i++) { - if (inb(REG_FLAGS0(iobase)) & 0x08) - break; - msleep_interruptible(10); - } - if (i == 1000) { - DEBUGP(1, dev, "timeout waiting for T0Done\n"); - rc = -EIO; - goto release_io; - } - - dev->procbyte = inb(REG_FLAGS1(iobase)); - DEBUGP(4, dev, "Read procedure byte 0x%.2x\n", - dev->procbyte); - - io_read_num_rec_bytes(iobase, &dev->rlen); - DEBUGP(4, dev, "Read NumRecBytes = %i\n", dev->rlen); - - } - } - /* T=1: read offset=zero, T=0: read offset=after challenge */ - dev->rpos = dev->proto ? 0 : nr == 4 ? 5 : nr > dev->rlen ? 5 : nr; - DEBUGP(4, dev, "dev->rlen = %i, dev->rpos = %i, nr = %i\n", - dev->rlen, dev->rpos, nr); - -release_io: - DEBUGP(4, dev, "Reset SM\n"); - xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */ - - if (rc < 0) { - DEBUGP(4, dev, "Write failed but clear T_Active\n"); - dev->flags1 &= 0xdf; - xoutb(dev->flags1, REG_FLAGS1(iobase)); - } - - clear_bit(LOCK_IO, &dev->flags); - wake_up_interruptible(&dev->ioq); - wake_up_interruptible(&dev->readq); /* tell read we have data */ - - /* ITSEC E2: clear write buffer */ - memset((char *)dev->sbuf, 0, 512); - - /* return error or actually written bytes */ - DEBUGP(2, dev, "<- cmm_write\n"); - return rc < 0 ? rc : nr; -} - -static void start_monitor(struct cm4000_dev *dev) -{ - DEBUGP(3, dev, "-> start_monitor\n"); - if (!dev->monitor_running) { - DEBUGP(5, dev, "create, init and add timer\n"); - timer_setup(&dev->timer, monitor_card, 0); - dev->monitor_running = 1; - mod_timer(&dev->timer, jiffies); - } else - DEBUGP(5, dev, "monitor already running\n"); - DEBUGP(3, dev, "<- start_monitor\n"); -} - -static void stop_monitor(struct cm4000_dev *dev) -{ - DEBUGP(3, dev, "-> stop_monitor\n"); - if (dev->monitor_running) { - DEBUGP(5, dev, "stopping monitor\n"); - terminate_monitor(dev); - /* reset monitor SM */ - clear_bit(IS_ATR_VALID, &dev->flags); - clear_bit(IS_ATR_PRESENT, &dev->flags); - } else - DEBUGP(5, dev, "monitor already stopped\n"); - DEBUGP(3, dev, "<- stop_monitor\n"); -} - -static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct cm4000_dev *dev = filp->private_data; - unsigned int iobase = dev->p_dev->resource[0]->start; - struct inode *inode = file_inode(filp); - struct pcmcia_device *link; - int rc; - void __user *argp = (void __user *)arg; -#ifdef CM4000_DEBUG - char *ioctl_names[CM_IOC_MAXNR + 1] = { - [_IOC_NR(CM_IOCGSTATUS)] "CM_IOCGSTATUS", - [_IOC_NR(CM_IOCGATR)] "CM_IOCGATR", - [_IOC_NR(CM_IOCARDOFF)] "CM_IOCARDOFF", - [_IOC_NR(CM_IOCSPTS)] "CM_IOCSPTS", - [_IOC_NR(CM_IOSDBGLVL)] "CM4000_DBGLVL", - }; - DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode), - iminor(inode), ioctl_names[_IOC_NR(cmd)]); -#endif - - mutex_lock(&cmm_mutex); - rc = -ENODEV; - link = dev_table[iminor(inode)]; - if (!pcmcia_dev_present(link)) { - DEBUGP(4, dev, "DEV_OK false\n"); - goto out; - } - - if (test_bit(IS_CMM_ABSENT, &dev->flags)) { - DEBUGP(4, dev, "CMM_ABSENT flag set\n"); - goto out; - } - rc = -EINVAL; - - if (_IOC_TYPE(cmd) != CM_IOC_MAGIC) { - DEBUGP(4, dev, "ioctype mismatch\n"); - goto out; - } - if (_IOC_NR(cmd) > CM_IOC_MAXNR) { - DEBUGP(4, dev, "iocnr mismatch\n"); - goto out; - } - rc = 0; - - switch (cmd) { - case CM_IOCGSTATUS: - DEBUGP(4, dev, " ... in CM_IOCGSTATUS\n"); - { - int status; - - /* clear other bits, but leave inserted & powered as - * they are */ - status = dev->flags0 & 3; - if (test_bit(IS_ATR_PRESENT, &dev->flags)) - status |= CM_ATR_PRESENT; - if (test_bit(IS_ATR_VALID, &dev->flags)) - status |= CM_ATR_VALID; - if (test_bit(IS_CMM_ABSENT, &dev->flags)) - status |= CM_NO_READER; - if (test_bit(IS_BAD_CARD, &dev->flags)) - status |= CM_BAD_CARD; - if (copy_to_user(argp, &status, sizeof(int))) - rc = -EFAULT; - } - break; - case CM_IOCGATR: - DEBUGP(4, dev, "... in CM_IOCGATR\n"); - { - struct atreq __user *atreq = argp; - int tmp; - /* allow nonblocking io and being interrupted */ - if (wait_event_interruptible - (dev->atrq, - ((filp->f_flags & O_NONBLOCK) - || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) - != 0)))) { - if (filp->f_flags & O_NONBLOCK) - rc = -EAGAIN; - else - rc = -ERESTARTSYS; - break; - } - - rc = -EFAULT; - if (test_bit(IS_ATR_VALID, &dev->flags) == 0) { - tmp = -1; - if (copy_to_user(&(atreq->atr_len), &tmp, - sizeof(int))) - break; - } else { - if (copy_to_user(atreq->atr, dev->atr, - dev->atr_len)) - break; - - tmp = dev->atr_len; - if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int))) - break; - } - rc = 0; - break; - } - case CM_IOCARDOFF: - -#ifdef CM4000_DEBUG - DEBUGP(4, dev, "... in CM_IOCARDOFF\n"); - if (dev->flags0 & 0x01) { - DEBUGP(4, dev, " Card inserted\n"); - } else { - DEBUGP(2, dev, " No card inserted\n"); - } - if (dev->flags0 & 0x02) { - DEBUGP(4, dev, " Card powered\n"); - } else { - DEBUGP(2, dev, " Card not powered\n"); - } -#endif - - /* is a card inserted and powered? */ - if ((dev->flags0 & 0x01) && (dev->flags0 & 0x02)) { - - /* get IO lock */ - if (wait_event_interruptible - (dev->ioq, - ((filp->f_flags & O_NONBLOCK) - || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) - == 0)))) { - if (filp->f_flags & O_NONBLOCK) - rc = -EAGAIN; - else - rc = -ERESTARTSYS; - break; - } - /* Set Flags0 = 0x42 */ - DEBUGP(4, dev, "Set Flags0=0x42 \n"); - xoutb(0x42, REG_FLAGS0(iobase)); - clear_bit(IS_ATR_PRESENT, &dev->flags); - clear_bit(IS_ATR_VALID, &dev->flags); - dev->mstate = M_CARDOFF; - clear_bit(LOCK_IO, &dev->flags); - if (wait_event_interruptible - (dev->atrq, - ((filp->f_flags & O_NONBLOCK) - || (test_bit(IS_ATR_VALID, (void *)&dev->flags) != - 0)))) { - if (filp->f_flags & O_NONBLOCK) - rc = -EAGAIN; - else - rc = -ERESTARTSYS; - break; - } - } - /* release lock */ - clear_bit(LOCK_IO, &dev->flags); - wake_up_interruptible(&dev->ioq); - - rc = 0; - break; - case CM_IOCSPTS: - { - struct ptsreq krnptsreq; - - if (copy_from_user(&krnptsreq, argp, - sizeof(struct ptsreq))) { - rc = -EFAULT; - break; - } - - rc = 0; - DEBUGP(4, dev, "... in CM_IOCSPTS\n"); - /* wait for ATR to get valid */ - if (wait_event_interruptible - (dev->atrq, - ((filp->f_flags & O_NONBLOCK) - || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) - != 0)))) { - if (filp->f_flags & O_NONBLOCK) - rc = -EAGAIN; - else - rc = -ERESTARTSYS; - break; - } - /* get IO lock */ - if (wait_event_interruptible - (dev->ioq, - ((filp->f_flags & O_NONBLOCK) - || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) - == 0)))) { - if (filp->f_flags & O_NONBLOCK) - rc = -EAGAIN; - else - rc = -ERESTARTSYS; - break; - } - - if ((rc = set_protocol(dev, &krnptsreq)) != 0) { - /* auto power_on again */ - dev->mstate = M_FETCH_ATR; - clear_bit(IS_ATR_VALID, &dev->flags); - } - /* release lock */ - clear_bit(LOCK_IO, &dev->flags); - wake_up_interruptible(&dev->ioq); - - } - break; -#ifdef CM4000_DEBUG - case CM_IOSDBGLVL: - rc = -ENOTTY; - break; -#endif - default: - DEBUGP(4, dev, "... in default (unknown IOCTL code)\n"); - rc = -ENOTTY; - } -out: - mutex_unlock(&cmm_mutex); - return rc; -} - -static int cmm_open(struct inode *inode, struct file *filp) -{ - struct cm4000_dev *dev; - struct pcmcia_device *link; - int minor = iminor(inode); - int ret; - - if (minor >= CM4000_MAX_DEV) - return -ENODEV; - - mutex_lock(&cmm_mutex); - link = dev_table[minor]; - if (link == NULL || !pcmcia_dev_present(link)) { - ret = -ENODEV; - goto out; - } - - if (link->open) { - ret = -EBUSY; - goto out; - } - - dev = link->priv; - filp->private_data = dev; - - DEBUGP(2, dev, "-> cmm_open(device=%d.%d process=%s,%d)\n", - imajor(inode), minor, current->comm, current->pid); - - /* init device variables, they may be "polluted" after close - * or, the device may never have been closed (i.e. open failed) - */ - - ZERO_DEV(dev); - - /* opening will always block since the - * monitor will be started by open, which - * means we have to wait for ATR becoming - * valid = block until valid (or card - * inserted) - */ - if (filp->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - goto out; - } - - dev->mdelay = T_50MSEC; - - /* start monitoring the cardstatus */ - start_monitor(dev); - - link->open = 1; /* only one open per device */ - - DEBUGP(2, dev, "<- cmm_open\n"); - ret = stream_open(inode, filp); -out: - mutex_unlock(&cmm_mutex); - return ret; -} - -static int cmm_close(struct inode *inode, struct file *filp) -{ - struct cm4000_dev *dev; - struct pcmcia_device *link; - int minor = iminor(inode); - - if (minor >= CM4000_MAX_DEV) - return -ENODEV; - - link = dev_table[minor]; - if (link == NULL) - return -ENODEV; - - dev = link->priv; - - DEBUGP(2, dev, "-> cmm_close(maj/min=%d.%d)\n", - imajor(inode), minor); - - stop_monitor(dev); - - ZERO_DEV(dev); - - link->open = 0; /* only one open per device */ - wake_up(&dev->devq); /* socket removed? */ - - DEBUGP(2, dev, "cmm_close\n"); - return 0; -} - -static void cmm_cm4000_release(struct pcmcia_device * link) -{ - struct cm4000_dev *dev = link->priv; - - /* dont terminate the monitor, rather rely on - * close doing that for us. - */ - DEBUGP(3, dev, "-> cmm_cm4000_release\n"); - while (link->open) { - printk(KERN_INFO MODULE_NAME ": delaying release until " - "process has terminated\n"); - /* note: don't interrupt us: - * close the applications which own - * the devices _first_ ! - */ - wait_event(dev->devq, (link->open == 0)); - } - /* dev->devq=NULL; this cannot be zeroed earlier */ - DEBUGP(3, dev, "<- cmm_cm4000_release\n"); - return; -} - -/*==== Interface to PCMCIA Layer =======================================*/ - -static int cm4000_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - return pcmcia_request_io(p_dev); -} - -static int cm4000_config(struct pcmcia_device * link, int devno) -{ - link->config_flags |= CONF_AUTO_SET_IO; - - /* read the config-tuples */ - if (pcmcia_loop_config(link, cm4000_config_check, NULL)) - goto cs_release; - - if (pcmcia_enable_device(link)) - goto cs_release; - - return 0; - -cs_release: - cm4000_release(link); - return -ENODEV; -} - -static int cm4000_suspend(struct pcmcia_device *link) -{ - struct cm4000_dev *dev; - - dev = link->priv; - stop_monitor(dev); - - return 0; -} - -static int cm4000_resume(struct pcmcia_device *link) -{ - struct cm4000_dev *dev; - - dev = link->priv; - if (link->open) - start_monitor(dev); - - return 0; -} - -static void cm4000_release(struct pcmcia_device *link) -{ - cmm_cm4000_release(link); /* delay release until device closed */ - pcmcia_disable_device(link); -} - -static int cm4000_probe(struct pcmcia_device *link) -{ - struct cm4000_dev *dev; - int i, ret; - - for (i = 0; i < CM4000_MAX_DEV; i++) - if (dev_table[i] == NULL) - break; - - if (i == CM4000_MAX_DEV) { - printk(KERN_NOTICE MODULE_NAME ": all devices in use\n"); - return -ENODEV; - } - - /* create a new cm4000_cs device */ - dev = kzalloc(sizeof(struct cm4000_dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; - - dev->p_dev = link; - link->priv = dev; - dev_table[i] = link; - - init_waitqueue_head(&dev->devq); - init_waitqueue_head(&dev->ioq); - init_waitqueue_head(&dev->atrq); - init_waitqueue_head(&dev->readq); - - ret = cm4000_config(link, i); - if (ret) { - dev_table[i] = NULL; - kfree(dev); - return ret; - } - - device_create(cmm_class, NULL, MKDEV(major, i), NULL, "cmm%d", i); - - return 0; -} - -static void cm4000_detach(struct pcmcia_device *link) -{ - struct cm4000_dev *dev = link->priv; - int devno; - - /* find device */ - for (devno = 0; devno < CM4000_MAX_DEV; devno++) - if (dev_table[devno] == link) - break; - if (devno == CM4000_MAX_DEV) - return; - - stop_monitor(dev); - - cm4000_release(link); - - dev_table[devno] = NULL; - kfree(dev); - - device_destroy(cmm_class, MKDEV(major, devno)); - - return; -} - -static const struct file_operations cm4000_fops = { - .owner = THIS_MODULE, - .read = cmm_read, - .write = cmm_write, - .unlocked_ioctl = cmm_ioctl, - .open = cmm_open, - .release= cmm_close, - .llseek = no_llseek, -}; - -static const struct pcmcia_device_id cm4000_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0002), - PCMCIA_DEVICE_PROD_ID12("CardMan", "4000", 0x2FB368CA, 0xA2BD8C39), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, cm4000_ids); - -static struct pcmcia_driver cm4000_driver = { - .owner = THIS_MODULE, - .name = "cm4000_cs", - .probe = cm4000_probe, - .remove = cm4000_detach, - .suspend = cm4000_suspend, - .resume = cm4000_resume, - .id_table = cm4000_ids, -}; - -static int __init cmm_init(void) -{ - int rc; - - cmm_class = class_create(THIS_MODULE, "cardman_4000"); - if (IS_ERR(cmm_class)) - return PTR_ERR(cmm_class); - - major = register_chrdev(0, DEVICE_NAME, &cm4000_fops); - if (major < 0) { - printk(KERN_WARNING MODULE_NAME - ": could not get major number\n"); - class_destroy(cmm_class); - return major; - } - - rc = pcmcia_register_driver(&cm4000_driver); - if (rc < 0) { - unregister_chrdev(major, DEVICE_NAME); - class_destroy(cmm_class); - return rc; - } - - return 0; -} - -static void __exit cmm_exit(void) -{ - pcmcia_unregister_driver(&cm4000_driver); - unregister_chrdev(major, DEVICE_NAME); - class_destroy(cmm_class); -}; - -module_init(cmm_init); -module_exit(cmm_exit); -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c deleted file mode 100644 index 827711911da4..000000000000 --- a/drivers/char/pcmcia/cm4040_cs.c +++ /dev/null @@ -1,684 +0,0 @@ -/* - * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040 - * - * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/) - * - * (C) 2005-2006 Harald Welte - * - add support for poll() - * - driver cleanup - * - add waitqueues - * - adhere to linux kernel coding style and policies - * - support 2.6.13 "new style" pcmcia interface - * - add class interface for udev device creation - * - * The device basically is a USB CCID compliant device that has been - * attached to an I/O-Mapped FIFO. - * - * All rights reserved, Dual BSD/GPL Licensed. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "cm4040_cs.h" - - -#define reader_to_dev(x) (&x->p_dev->dev) - -/* n (debug level) is ignored */ -/* additional debug output may be enabled by re-compiling with - * CM4040_DEBUG set */ -/* #define CM4040_DEBUG */ -#define DEBUGP(n, rdr, x, args...) do { \ - dev_dbg(reader_to_dev(rdr), "%s:" x, \ - __func__ , ## args); \ - } while (0) - -static DEFINE_MUTEX(cm4040_mutex); - -#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ) -#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ) -#define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ) -#define READ_WRITE_BUFFER_SIZE 512 -#define POLL_LOOP_COUNT 1000 - -/* how often to poll for fifo status change */ -#define POLL_PERIOD msecs_to_jiffies(10) - -static void reader_release(struct pcmcia_device *link); - -static int major; -static struct class *cmx_class; - -#define BS_READABLE 0x01 -#define BS_WRITABLE 0x02 - -struct reader_dev { - struct pcmcia_device *p_dev; - wait_queue_head_t devq; - wait_queue_head_t poll_wait; - wait_queue_head_t read_wait; - wait_queue_head_t write_wait; - unsigned long buffer_status; - unsigned long timeout; - unsigned char s_buf[READ_WRITE_BUFFER_SIZE]; - unsigned char r_buf[READ_WRITE_BUFFER_SIZE]; - struct timer_list poll_timer; -}; - -static struct pcmcia_device *dev_table[CM_MAX_DEV]; - -#ifndef CM4040_DEBUG -#define xoutb outb -#define xinb inb -#else -static inline void xoutb(unsigned char val, unsigned short port) -{ - pr_debug("outb(val=%.2x,port=%.4x)\n", val, port); - outb(val, port); -} - -static inline unsigned char xinb(unsigned short port) -{ - unsigned char val; - - val = inb(port); - pr_debug("%.2x=inb(%.4x)\n", val, port); - return val; -} -#endif - -/* poll the device fifo status register. not to be confused with - * the poll syscall. */ -static void cm4040_do_poll(struct timer_list *t) -{ - struct reader_dev *dev = from_timer(dev, t, poll_timer); - unsigned int obs = xinb(dev->p_dev->resource[0]->start - + REG_OFFSET_BUFFER_STATUS); - - if ((obs & BSR_BULK_IN_FULL)) { - set_bit(BS_READABLE, &dev->buffer_status); - DEBUGP(4, dev, "waking up read_wait\n"); - wake_up_interruptible(&dev->read_wait); - } else - clear_bit(BS_READABLE, &dev->buffer_status); - - if (!(obs & BSR_BULK_OUT_FULL)) { - set_bit(BS_WRITABLE, &dev->buffer_status); - DEBUGP(4, dev, "waking up write_wait\n"); - wake_up_interruptible(&dev->write_wait); - } else - clear_bit(BS_WRITABLE, &dev->buffer_status); - - if (dev->buffer_status) - wake_up_interruptible(&dev->poll_wait); - - mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD); -} - -static void cm4040_stop_poll(struct reader_dev *dev) -{ - del_timer_sync(&dev->poll_timer); -} - -static int wait_for_bulk_out_ready(struct reader_dev *dev) -{ - int i, rc; - int iobase = dev->p_dev->resource[0]->start; - - for (i = 0; i < POLL_LOOP_COUNT; i++) { - if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS) - & BSR_BULK_OUT_FULL) == 0) { - DEBUGP(4, dev, "BulkOut empty (i=%d)\n", i); - return 1; - } - } - - DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n", - dev->timeout); - rc = wait_event_interruptible_timeout(dev->write_wait, - test_and_clear_bit(BS_WRITABLE, - &dev->buffer_status), - dev->timeout); - - if (rc > 0) - DEBUGP(4, dev, "woke up: BulkOut empty\n"); - else if (rc == 0) - DEBUGP(4, dev, "woke up: BulkOut full, returning 0 :(\n"); - else if (rc < 0) - DEBUGP(4, dev, "woke up: signal arrived\n"); - - return rc; -} - -/* Write to Sync Control Register */ -static int write_sync_reg(unsigned char val, struct reader_dev *dev) -{ - int iobase = dev->p_dev->resource[0]->start; - int rc; - - rc = wait_for_bulk_out_ready(dev); - if (rc <= 0) - return rc; - - xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL); - rc = wait_for_bulk_out_ready(dev); - if (rc <= 0) - return rc; - - return 1; -} - -static int wait_for_bulk_in_ready(struct reader_dev *dev) -{ - int i, rc; - int iobase = dev->p_dev->resource[0]->start; - - for (i = 0; i < POLL_LOOP_COUNT; i++) { - if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS) - & BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) { - DEBUGP(3, dev, "BulkIn full (i=%d)\n", i); - return 1; - } - } - - DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n", - dev->timeout); - rc = wait_event_interruptible_timeout(dev->read_wait, - test_and_clear_bit(BS_READABLE, - &dev->buffer_status), - dev->timeout); - if (rc > 0) - DEBUGP(4, dev, "woke up: BulkIn full\n"); - else if (rc == 0) - DEBUGP(4, dev, "woke up: BulkIn not full, returning 0 :(\n"); - else if (rc < 0) - DEBUGP(4, dev, "woke up: signal arrived\n"); - - return rc; -} - -static ssize_t cm4040_read(struct file *filp, char __user *buf, - size_t count, loff_t *ppos) -{ - struct reader_dev *dev = filp->private_data; - int iobase = dev->p_dev->resource[0]->start; - size_t bytes_to_read; - unsigned long i; - size_t min_bytes_to_read; - int rc; - - DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid); - - if (count == 0) - return 0; - - if (count < 10) - return -EFAULT; - - if (filp->f_flags & O_NONBLOCK) { - DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); - DEBUGP(2, dev, "<- cm4040_read (failure)\n"); - return -EAGAIN; - } - - if (!pcmcia_dev_present(dev->p_dev)) - return -ENODEV; - - for (i = 0; i < 5; i++) { - rc = wait_for_bulk_in_ready(dev); - if (rc <= 0) { - DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc); - DEBUGP(2, dev, "<- cm4040_read (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - return -EIO; - } - dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN); -#ifdef CM4040_DEBUG - pr_debug("%lu:%2x ", i, dev->r_buf[i]); - } - pr_debug("\n"); -#else - } -#endif - - bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]); - - DEBUGP(6, dev, "BytesToRead=%zu\n", bytes_to_read); - - min_bytes_to_read = min(count, bytes_to_read + 5); - min_bytes_to_read = min_t(size_t, min_bytes_to_read, READ_WRITE_BUFFER_SIZE); - - DEBUGP(6, dev, "Min=%zu\n", min_bytes_to_read); - - for (i = 0; i < (min_bytes_to_read-5); i++) { - rc = wait_for_bulk_in_ready(dev); - if (rc <= 0) { - DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc); - DEBUGP(2, dev, "<- cm4040_read (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - return -EIO; - } - dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN); -#ifdef CM4040_DEBUG - pr_debug("%lu:%2x ", i, dev->r_buf[i]); - } - pr_debug("\n"); -#else - } -#endif - - *ppos = min_bytes_to_read; - if (copy_to_user(buf, dev->r_buf, min_bytes_to_read)) - return -EFAULT; - - rc = wait_for_bulk_in_ready(dev); - if (rc <= 0) { - DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc); - DEBUGP(2, dev, "<- cm4040_read (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - return -EIO; - } - - rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev); - if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2x\n", rc); - DEBUGP(2, dev, "<- cm4040_read (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - else - return -EIO; - } - - xinb(iobase + REG_OFFSET_BULK_IN); - - DEBUGP(2, dev, "<- cm4040_read (successfully)\n"); - return min_bytes_to_read; -} - -static ssize_t cm4040_write(struct file *filp, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct reader_dev *dev = filp->private_data; - int iobase = dev->p_dev->resource[0]->start; - ssize_t rc; - int i; - unsigned int bytes_to_write; - - DEBUGP(2, dev, "-> cm4040_write(%s,%d)\n", current->comm, current->pid); - - if (count == 0) { - DEBUGP(2, dev, "<- cm4040_write empty read (successfully)\n"); - return 0; - } - - if ((count < 5) || (count > READ_WRITE_BUFFER_SIZE)) { - DEBUGP(2, dev, "<- cm4040_write buffersize=%zd < 5\n", count); - return -EIO; - } - - if (filp->f_flags & O_NONBLOCK) { - DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); - DEBUGP(4, dev, "<- cm4040_write (failure)\n"); - return -EAGAIN; - } - - if (!pcmcia_dev_present(dev->p_dev)) - return -ENODEV; - - bytes_to_write = count; - if (copy_from_user(dev->s_buf, buf, bytes_to_write)) - return -EFAULT; - - switch (dev->s_buf[0]) { - case CMD_PC_TO_RDR_XFRBLOCK: - case CMD_PC_TO_RDR_SECURE: - case CMD_PC_TO_RDR_TEST_SECURE: - case CMD_PC_TO_RDR_OK_SECURE: - dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT; - break; - - case CMD_PC_TO_RDR_ICCPOWERON: - dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT; - break; - - case CMD_PC_TO_RDR_GETSLOTSTATUS: - case CMD_PC_TO_RDR_ICCPOWEROFF: - case CMD_PC_TO_RDR_GETPARAMETERS: - case CMD_PC_TO_RDR_RESETPARAMETERS: - case CMD_PC_TO_RDR_SETPARAMETERS: - case CMD_PC_TO_RDR_ESCAPE: - case CMD_PC_TO_RDR_ICCCLOCK: - default: - dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT; - break; - } - - rc = write_sync_reg(SCR_HOST_TO_READER_START, dev); - if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc); - DEBUGP(2, dev, "<- cm4040_write (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - else - return -EIO; - } - - DEBUGP(4, dev, "start \n"); - - for (i = 0; i < bytes_to_write; i++) { - rc = wait_for_bulk_out_ready(dev); - if (rc <= 0) { - DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n", - rc); - DEBUGP(2, dev, "<- cm4040_write (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - else - return -EIO; - } - - xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT); - } - DEBUGP(4, dev, "end\n"); - - rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev); - - if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc); - DEBUGP(2, dev, "<- cm4040_write (failed)\n"); - if (rc == -ERESTARTSYS) - return rc; - else - return -EIO; - } - - DEBUGP(2, dev, "<- cm4040_write (successfully)\n"); - return count; -} - -static __poll_t cm4040_poll(struct file *filp, poll_table *wait) -{ - struct reader_dev *dev = filp->private_data; - __poll_t mask = 0; - - poll_wait(filp, &dev->poll_wait, wait); - - if (test_and_clear_bit(BS_READABLE, &dev->buffer_status)) - mask |= EPOLLIN | EPOLLRDNORM; - if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status)) - mask |= EPOLLOUT | EPOLLWRNORM; - - DEBUGP(2, dev, "<- cm4040_poll(%u)\n", mask); - - return mask; -} - -static int cm4040_open(struct inode *inode, struct file *filp) -{ - struct reader_dev *dev; - struct pcmcia_device *link; - int minor = iminor(inode); - int ret; - - if (minor >= CM_MAX_DEV) - return -ENODEV; - - mutex_lock(&cm4040_mutex); - link = dev_table[minor]; - if (link == NULL || !pcmcia_dev_present(link)) { - ret = -ENODEV; - goto out; - } - - if (link->open) { - ret = -EBUSY; - goto out; - } - - dev = link->priv; - filp->private_data = dev; - - if (filp->f_flags & O_NONBLOCK) { - DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); - ret = -EAGAIN; - goto out; - } - - link->open = 1; - - mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD); - - DEBUGP(2, dev, "<- cm4040_open (successfully)\n"); - ret = nonseekable_open(inode, filp); -out: - mutex_unlock(&cm4040_mutex); - return ret; -} - -static int cm4040_close(struct inode *inode, struct file *filp) -{ - struct reader_dev *dev = filp->private_data; - struct pcmcia_device *link; - int minor = iminor(inode); - - DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode), - iminor(inode)); - - if (minor >= CM_MAX_DEV) - return -ENODEV; - - link = dev_table[minor]; - if (link == NULL) - return -ENODEV; - - cm4040_stop_poll(dev); - - link->open = 0; - wake_up(&dev->devq); - - DEBUGP(2, dev, "<- cm4040_close\n"); - return 0; -} - -static void cm4040_reader_release(struct pcmcia_device *link) -{ - struct reader_dev *dev = link->priv; - - DEBUGP(3, dev, "-> cm4040_reader_release\n"); - while (link->open) { - DEBUGP(3, dev, MODULE_NAME ": delaying release " - "until process has terminated\n"); - wait_event(dev->devq, (link->open == 0)); - } - DEBUGP(3, dev, "<- cm4040_reader_release\n"); - return; -} - -static int cm4040_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - return pcmcia_request_io(p_dev); -} - - -static int reader_config(struct pcmcia_device *link, int devno) -{ - struct reader_dev *dev; - int fail_rc; - - link->config_flags |= CONF_AUTO_SET_IO; - - if (pcmcia_loop_config(link, cm4040_config_check, NULL)) - goto cs_release; - - fail_rc = pcmcia_enable_device(link); - if (fail_rc != 0) { - dev_info(&link->dev, "pcmcia_enable_device failed 0x%x\n", - fail_rc); - goto cs_release; - } - - dev = link->priv; - - DEBUGP(2, dev, "device " DEVICE_NAME "%d at %pR\n", devno, - link->resource[0]); - DEBUGP(2, dev, "<- reader_config (succ)\n"); - - return 0; - -cs_release: - reader_release(link); - return -ENODEV; -} - -static void reader_release(struct pcmcia_device *link) -{ - cm4040_reader_release(link); - pcmcia_disable_device(link); -} - -static int reader_probe(struct pcmcia_device *link) -{ - struct reader_dev *dev; - int i, ret; - - for (i = 0; i < CM_MAX_DEV; i++) { - if (dev_table[i] == NULL) - break; - } - - if (i == CM_MAX_DEV) - return -ENODEV; - - dev = kzalloc(sizeof(struct reader_dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; - - dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT; - dev->buffer_status = 0; - - link->priv = dev; - dev->p_dev = link; - - dev_table[i] = link; - - init_waitqueue_head(&dev->devq); - init_waitqueue_head(&dev->poll_wait); - init_waitqueue_head(&dev->read_wait); - init_waitqueue_head(&dev->write_wait); - timer_setup(&dev->poll_timer, cm4040_do_poll, 0); - - ret = reader_config(link, i); - if (ret) { - dev_table[i] = NULL; - kfree(dev); - return ret; - } - - device_create(cmx_class, NULL, MKDEV(major, i), NULL, "cmx%d", i); - - return 0; -} - -static void reader_detach(struct pcmcia_device *link) -{ - struct reader_dev *dev = link->priv; - int devno; - - /* find device */ - for (devno = 0; devno < CM_MAX_DEV; devno++) { - if (dev_table[devno] == link) - break; - } - if (devno == CM_MAX_DEV) - return; - - reader_release(link); - - dev_table[devno] = NULL; - kfree(dev); - - device_destroy(cmx_class, MKDEV(major, devno)); - - return; -} - -static const struct file_operations reader_fops = { - .owner = THIS_MODULE, - .read = cm4040_read, - .write = cm4040_write, - .open = cm4040_open, - .release = cm4040_close, - .poll = cm4040_poll, - .llseek = no_llseek, -}; - -static const struct pcmcia_device_id cm4040_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200), - PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040", - 0xE32CDD8C, 0x8F23318B), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, cm4040_ids); - -static struct pcmcia_driver reader_driver = { - .owner = THIS_MODULE, - .name = "cm4040_cs", - .probe = reader_probe, - .remove = reader_detach, - .id_table = cm4040_ids, -}; - -static int __init cm4040_init(void) -{ - int rc; - - cmx_class = class_create(THIS_MODULE, "cardman_4040"); - if (IS_ERR(cmx_class)) - return PTR_ERR(cmx_class); - - major = register_chrdev(0, DEVICE_NAME, &reader_fops); - if (major < 0) { - printk(KERN_WARNING MODULE_NAME - ": could not get major number\n"); - class_destroy(cmx_class); - return major; - } - - rc = pcmcia_register_driver(&reader_driver); - if (rc < 0) { - unregister_chrdev(major, DEVICE_NAME); - class_destroy(cmx_class); - return rc; - } - - return 0; -} - -static void __exit cm4040_exit(void) -{ - pcmcia_unregister_driver(&reader_driver); - unregister_chrdev(major, DEVICE_NAME); - class_destroy(cmx_class); -} - -module_init(cm4040_init); -module_exit(cm4040_exit); -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/char/pcmcia/cm4040_cs.h b/drivers/char/pcmcia/cm4040_cs.h deleted file mode 100644 index e2ffff995d51..000000000000 --- a/drivers/char/pcmcia/cm4040_cs.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _CM4040_H_ -#define _CM4040_H_ - -#define CM_MAX_DEV 4 - -#define DEVICE_NAME "cmx" -#define MODULE_NAME "cm4040_cs" - -#define REG_OFFSET_BULK_OUT 0 -#define REG_OFFSET_BULK_IN 0 -#define REG_OFFSET_BUFFER_STATUS 1 -#define REG_OFFSET_SYNC_CONTROL 2 - -#define BSR_BULK_IN_FULL 0x02 -#define BSR_BULK_OUT_FULL 0x01 - -#define SCR_HOST_TO_READER_START 0x80 -#define SCR_ABORT 0x40 -#define SCR_EN_NOTIFY 0x20 -#define SCR_ACK_NOTIFY 0x10 -#define SCR_READER_TO_HOST_DONE 0x08 -#define SCR_HOST_TO_READER_DONE 0x04 -#define SCR_PULSE_INTERRUPT 0x02 -#define SCR_POWER_DOWN 0x01 - - -#define CMD_PC_TO_RDR_ICCPOWERON 0x62 -#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65 -#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63 -#define CMD_PC_TO_RDR_SECURE 0x69 -#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C -#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D -#define CMD_PC_TO_RDR_SETPARAMETERS 0x61 -#define CMD_PC_TO_RDR_XFRBLOCK 0x6F -#define CMD_PC_TO_RDR_ESCAPE 0x6B -#define CMD_PC_TO_RDR_ICCCLOCK 0x6E -#define CMD_PC_TO_RDR_TEST_SECURE 0x74 -#define CMD_PC_TO_RDR_OK_SECURE 0x89 - - -#define CMD_RDR_TO_PC_SLOTSTATUS 0x81 -#define CMD_RDR_TO_PC_DATABLOCK 0x80 -#define CMD_RDR_TO_PC_PARAMETERS 0x82 -#define CMD_RDR_TO_PC_ESCAPE 0x83 -#define CMD_RDR_TO_PC_OK_SECURE 0x89 - -#endif /* _CM4040_H_ */ diff --git a/drivers/char/pcmcia/scr24x_cs.c b/drivers/char/pcmcia/scr24x_cs.c deleted file mode 100644 index 1bdce08fae3d..000000000000 --- a/drivers/char/pcmcia/scr24x_cs.c +++ /dev/null @@ -1,359 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SCR24x PCMCIA Smart Card Reader Driver - * - * Copyright (C) 2005-2006 TL Sudheendran - * Copyright (C) 2016 Lubomir Rintel - * - * Derived from "scr24x_v4.2.6_Release.tar.gz" driver by TL Sudheendran. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define CCID_HEADER_SIZE 10 -#define CCID_LENGTH_OFFSET 1 -#define CCID_MAX_LEN 271 - -#define SCR24X_DATA(n) (1 + n) -#define SCR24X_CMD_STATUS 7 -#define CMD_START 0x40 -#define CMD_WRITE_BYTE 0x41 -#define CMD_READ_BYTE 0x42 -#define STATUS_BUSY 0x80 - -struct scr24x_dev { - struct device *dev; - struct cdev c_dev; - unsigned char buf[CCID_MAX_LEN]; - int devno; - struct mutex lock; - struct kref refcnt; - u8 __iomem *regs; -}; - -#define SCR24X_DEVS 8 -static DECLARE_BITMAP(scr24x_minors, SCR24X_DEVS); - -static struct class *scr24x_class; -static dev_t scr24x_devt; - -static void scr24x_delete(struct kref *kref) -{ - struct scr24x_dev *dev = container_of(kref, struct scr24x_dev, - refcnt); - - kfree(dev); -} - -static int scr24x_wait_ready(struct scr24x_dev *dev) -{ - u_char status; - int timeout = 100; - - do { - status = ioread8(dev->regs + SCR24X_CMD_STATUS); - if (!(status & STATUS_BUSY)) - return 0; - - msleep(20); - } while (--timeout); - - return -EIO; -} - -static int scr24x_open(struct inode *inode, struct file *filp) -{ - struct scr24x_dev *dev = container_of(inode->i_cdev, - struct scr24x_dev, c_dev); - - kref_get(&dev->refcnt); - filp->private_data = dev; - - return stream_open(inode, filp); -} - -static int scr24x_release(struct inode *inode, struct file *filp) -{ - struct scr24x_dev *dev = filp->private_data; - - /* We must not take the dev->lock here as scr24x_delete() - * might be called to remove the dev structure altogether. - * We don't need the lock anyway, since after the reference - * acquired in probe() is released in remove() the chrdev - * is already unregistered and noone can possibly acquire - * a reference via open() anymore. */ - kref_put(&dev->refcnt, scr24x_delete); - return 0; -} - -static int read_chunk(struct scr24x_dev *dev, size_t offset, size_t limit) -{ - size_t i, y; - int ret; - - for (i = offset; i < limit; i += 5) { - iowrite8(CMD_READ_BYTE, dev->regs + SCR24X_CMD_STATUS); - ret = scr24x_wait_ready(dev); - if (ret < 0) - return ret; - - for (y = 0; y < 5 && i + y < limit; y++) - dev->buf[i + y] = ioread8(dev->regs + SCR24X_DATA(y)); - } - - return 0; -} - -static ssize_t scr24x_read(struct file *filp, char __user *buf, size_t count, - loff_t *ppos) -{ - struct scr24x_dev *dev = filp->private_data; - int ret; - int len; - - if (count < CCID_HEADER_SIZE) - return -EINVAL; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - - if (!dev->dev) { - ret = -ENODEV; - goto out; - } - - ret = scr24x_wait_ready(dev); - if (ret < 0) - goto out; - len = CCID_HEADER_SIZE; - ret = read_chunk(dev, 0, len); - if (ret < 0) - goto out; - - len += le32_to_cpu(*(__le32 *)(&dev->buf[CCID_LENGTH_OFFSET])); - if (len > sizeof(dev->buf)) { - ret = -EIO; - goto out; - } - ret = read_chunk(dev, CCID_HEADER_SIZE, len); - if (ret < 0) - goto out; - - if (len < count) - count = len; - - if (copy_to_user(buf, dev->buf, count)) { - ret = -EFAULT; - goto out; - } - - ret = count; -out: - mutex_unlock(&dev->lock); - return ret; -} - -static ssize_t scr24x_write(struct file *filp, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct scr24x_dev *dev = filp->private_data; - size_t i, y; - int ret; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - - if (!dev->dev) { - ret = -ENODEV; - goto out; - } - - if (count > sizeof(dev->buf)) { - ret = -EINVAL; - goto out; - } - - if (copy_from_user(dev->buf, buf, count)) { - ret = -EFAULT; - goto out; - } - - ret = scr24x_wait_ready(dev); - if (ret < 0) - goto out; - - iowrite8(CMD_START, dev->regs + SCR24X_CMD_STATUS); - ret = scr24x_wait_ready(dev); - if (ret < 0) - goto out; - - for (i = 0; i < count; i += 5) { - for (y = 0; y < 5 && i + y < count; y++) - iowrite8(dev->buf[i + y], dev->regs + SCR24X_DATA(y)); - - iowrite8(CMD_WRITE_BYTE, dev->regs + SCR24X_CMD_STATUS); - ret = scr24x_wait_ready(dev); - if (ret < 0) - goto out; - } - - ret = count; -out: - mutex_unlock(&dev->lock); - return ret; -} - -static const struct file_operations scr24x_fops = { - .owner = THIS_MODULE, - .read = scr24x_read, - .write = scr24x_write, - .open = scr24x_open, - .release = scr24x_release, - .llseek = no_llseek, -}; - -static int scr24x_config_check(struct pcmcia_device *link, void *priv_data) -{ - if (resource_size(link->resource[PCMCIA_IOPORT_0]) != 0x11) - return -ENODEV; - return pcmcia_request_io(link); -} - -static int scr24x_probe(struct pcmcia_device *link) -{ - struct scr24x_dev *dev; - int ret; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->devno = find_first_zero_bit(scr24x_minors, SCR24X_DEVS); - if (dev->devno >= SCR24X_DEVS) { - ret = -EBUSY; - goto err; - } - - mutex_init(&dev->lock); - kref_init(&dev->refcnt); - - link->priv = dev; - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - ret = pcmcia_loop_config(link, scr24x_config_check, NULL); - if (ret < 0) - goto err; - - dev->dev = &link->dev; - dev->regs = devm_ioport_map(&link->dev, - link->resource[PCMCIA_IOPORT_0]->start, - resource_size(link->resource[PCMCIA_IOPORT_0])); - if (!dev->regs) { - ret = -EIO; - goto err; - } - - cdev_init(&dev->c_dev, &scr24x_fops); - dev->c_dev.owner = THIS_MODULE; - ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1); - if (ret < 0) - goto err; - - ret = pcmcia_enable_device(link); - if (ret < 0) { - pcmcia_disable_device(link); - goto err; - } - - device_create(scr24x_class, NULL, MKDEV(MAJOR(scr24x_devt), dev->devno), - NULL, "scr24x%d", dev->devno); - - dev_info(&link->dev, "SCR24x Chip Card Interface\n"); - return 0; - -err: - if (dev->devno < SCR24X_DEVS) - clear_bit(dev->devno, scr24x_minors); - kfree (dev); - return ret; -} - -static void scr24x_remove(struct pcmcia_device *link) -{ - struct scr24x_dev *dev = (struct scr24x_dev *)link->priv; - - device_destroy(scr24x_class, MKDEV(MAJOR(scr24x_devt), dev->devno)); - mutex_lock(&dev->lock); - pcmcia_disable_device(link); - cdev_del(&dev->c_dev); - clear_bit(dev->devno, scr24x_minors); - dev->dev = NULL; - mutex_unlock(&dev->lock); - - kref_put(&dev->refcnt, scr24x_delete); -} - -static const struct pcmcia_device_id scr24x_ids[] = { - PCMCIA_DEVICE_PROD_ID12("HP", "PC Card Smart Card Reader", - 0x53cb94f9, 0xbfdf89a5), - PCMCIA_DEVICE_PROD_ID1("SCR241 PCMCIA", 0x6271efa3), - PCMCIA_DEVICE_PROD_ID1("SCR243 PCMCIA", 0x2054e8de), - PCMCIA_DEVICE_PROD_ID1("SCR24x PCMCIA", 0x54a33665), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, scr24x_ids); - -static struct pcmcia_driver scr24x_driver = { - .owner = THIS_MODULE, - .name = "scr24x_cs", - .probe = scr24x_probe, - .remove = scr24x_remove, - .id_table = scr24x_ids, -}; - -static int __init scr24x_init(void) -{ - int ret; - - scr24x_class = class_create(THIS_MODULE, "scr24x"); - if (IS_ERR(scr24x_class)) - return PTR_ERR(scr24x_class); - - ret = alloc_chrdev_region(&scr24x_devt, 0, SCR24X_DEVS, "scr24x"); - if (ret < 0) { - class_destroy(scr24x_class); - return ret; - } - - ret = pcmcia_register_driver(&scr24x_driver); - if (ret < 0) { - unregister_chrdev_region(scr24x_devt, SCR24X_DEVS); - class_destroy(scr24x_class); - } - - return ret; -} - -static void __exit scr24x_exit(void) -{ - pcmcia_unregister_driver(&scr24x_driver); - unregister_chrdev_region(scr24x_devt, SCR24X_DEVS); - class_destroy(scr24x_class); -} - -module_init(scr24x_init); -module_exit(scr24x_exit); - -MODULE_AUTHOR("Lubomir Rintel"); -MODULE_DESCRIPTION("SCR24x PCMCIA Smart Card Reader Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c deleted file mode 100644 index 6ddfeb2fe98f..000000000000 --- a/drivers/char/pcmcia/synclink_cs.c +++ /dev/null @@ -1,4290 +0,0 @@ -/* - * linux/drivers/char/pcmcia/synclink_cs.c - * - * $Id: synclink_cs.c,v 4.34 2005/09/08 13:20:54 paulkf Exp $ - * - * Device driver for Microgate SyncLink PC Card - * multiprotocol serial adapter. - * - * written by Paul Fulghum for Microgate Corporation - * paulkf@microgate.com - * - * Microgate and SyncLink are trademarks of Microgate Corporation - * - * This code is released under the GNU General Public License (GPL) - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) -#if defined(__i386__) -# define BREAKPOINT() asm(" int $3"); -#else -# define BREAKPOINT() { } -#endif - -#define MAX_DEVICE_COUNT 4 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_CS_MODULE)) -#define SYNCLINK_GENERIC_HDLC 1 -#else -#define SYNCLINK_GENERIC_HDLC 0 -#endif - -#define GET_USER(error,value,addr) error = get_user(value,addr) -#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 -#define PUT_USER(error,value,addr) error = put_user(value,addr) -#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 - -#include - -static MGSL_PARAMS default_params = { - MGSL_MODE_HDLC, /* unsigned long mode */ - 0, /* unsigned char loopback; */ - HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ - HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ - 0, /* unsigned long clock_speed; */ - 0xff, /* unsigned char addr_filter; */ - HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ - HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ - HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ - 9600, /* unsigned long data_rate; */ - 8, /* unsigned char data_bits; */ - 1, /* unsigned char stop_bits; */ - ASYNC_PARITY_NONE /* unsigned char parity; */ -}; - -typedef struct { - int count; - unsigned char status; - char data[1]; -} RXBUF; - -/* The queue of BH actions to be performed */ - -#define BH_RECEIVE 1 -#define BH_TRANSMIT 2 -#define BH_STATUS 4 - -#define IO_PIN_SHUTDOWN_LIMIT 100 - -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - -struct _input_signal_events { - int ri_up; - int ri_down; - int dsr_up; - int dsr_down; - int dcd_up; - int dcd_down; - int cts_up; - int cts_down; -}; - - -/* - * Device instance data structure - */ - -typedef struct _mgslpc_info { - struct tty_port port; - void *if_ptr; /* General purpose pointer (used by SPPP) */ - int magic; - int line; - - struct mgsl_icount icount; - - int timeout; - int x_char; /* xon/xoff character */ - unsigned char read_status_mask; - unsigned char ignore_status_mask; - - unsigned char *tx_buf; - int tx_put; - int tx_get; - int tx_count; - - /* circular list of fixed length rx buffers */ - - unsigned char *rx_buf; /* memory allocated for all rx buffers */ - int rx_buf_total_size; /* size of memory allocated for rx buffers */ - int rx_put; /* index of next empty rx buffer */ - int rx_get; /* index of next full rx buffer */ - int rx_buf_size; /* size in bytes of single rx buffer */ - int rx_buf_count; /* total number of rx buffers */ - int rx_frame_count; /* number of full rx buffers */ - - wait_queue_head_t status_event_wait_q; - wait_queue_head_t event_wait_q; - struct timer_list tx_timer; /* HDLC transmit timeout timer */ - struct _mgslpc_info *next_device; /* device list link */ - - unsigned short imra_value; - unsigned short imrb_value; - unsigned char pim_value; - - spinlock_t lock; - struct work_struct task; /* task structure for scheduling bh */ - - u32 max_frame_size; - - u32 pending_bh; - - bool bh_running; - bool bh_requested; - - int dcd_chkcount; /* check counts to prevent */ - int cts_chkcount; /* too many IRQs if a signal */ - int dsr_chkcount; /* is floating */ - int ri_chkcount; - - bool rx_enabled; - bool rx_overflow; - - bool tx_enabled; - bool tx_active; - bool tx_aborting; - u32 idle_mode; - - int if_mode; /* serial interface selection (RS-232, v.35 etc) */ - - char device_name[25]; /* device instance name */ - - unsigned int io_base; /* base I/O address of adapter */ - unsigned int irq_level; - - MGSL_PARAMS params; /* communications parameters */ - - unsigned char serial_signals; /* current serial signal states */ - - bool irq_occurred; /* for diagnostics use */ - char testing_irq; - unsigned int init_error; /* startup error (DIAGS) */ - - char *flag_buf; - bool drop_rts_on_tx_done; - - struct _input_signal_events input_signal_events; - - /* PCMCIA support */ - struct pcmcia_device *p_dev; - int stop; - - /* SPPP/Cisco HDLC device parts */ - int netcount; - spinlock_t netlock; - -#if SYNCLINK_GENERIC_HDLC - struct net_device *netdev; -#endif - -} MGSLPC_INFO; - -#define MGSLPC_MAGIC 0x5402 - -/* - * The size of the serial xmit buffer is 1 page, or 4096 bytes - */ -#define TXBUFSIZE 4096 - - -#define CHA 0x00 /* channel A offset */ -#define CHB 0x40 /* channel B offset */ - -/* - * FIXME: PPC has PVR defined in asm/reg.h. For now we just undef it. - */ -#undef PVR - -#define RXFIFO 0 -#define TXFIFO 0 -#define STAR 0x20 -#define CMDR 0x20 -#define RSTA 0x21 -#define PRE 0x21 -#define MODE 0x22 -#define TIMR 0x23 -#define XAD1 0x24 -#define XAD2 0x25 -#define RAH1 0x26 -#define RAH2 0x27 -#define DAFO 0x27 -#define RAL1 0x28 -#define RFC 0x28 -#define RHCR 0x29 -#define RAL2 0x29 -#define RBCL 0x2a -#define XBCL 0x2a -#define RBCH 0x2b -#define XBCH 0x2b -#define CCR0 0x2c -#define CCR1 0x2d -#define CCR2 0x2e -#define CCR3 0x2f -#define VSTR 0x34 -#define BGR 0x34 -#define RLCR 0x35 -#define AML 0x36 -#define AMH 0x37 -#define GIS 0x38 -#define IVA 0x38 -#define IPC 0x39 -#define ISR 0x3a -#define IMR 0x3a -#define PVR 0x3c -#define PIS 0x3d -#define PIM 0x3d -#define PCR 0x3e -#define CCR4 0x3f - -// IMR/ISR - -#define IRQ_BREAK_ON BIT15 // rx break detected -#define IRQ_DATAOVERRUN BIT14 // receive data overflow -#define IRQ_ALLSENT BIT13 // all sent -#define IRQ_UNDERRUN BIT12 // transmit data underrun -#define IRQ_TIMER BIT11 // timer interrupt -#define IRQ_CTS BIT10 // CTS status change -#define IRQ_TXREPEAT BIT9 // tx message repeat -#define IRQ_TXFIFO BIT8 // transmit pool ready -#define IRQ_RXEOM BIT7 // receive message end -#define IRQ_EXITHUNT BIT6 // receive frame start -#define IRQ_RXTIME BIT6 // rx char timeout -#define IRQ_DCD BIT2 // carrier detect status change -#define IRQ_OVERRUN BIT1 // receive frame overflow -#define IRQ_RXFIFO BIT0 // receive pool full - -// STAR - -#define XFW BIT6 // transmit FIFO write enable -#define CEC BIT2 // command executing -#define CTS BIT1 // CTS state - -#define PVR_DTR BIT0 -#define PVR_DSR BIT1 -#define PVR_RI BIT2 -#define PVR_AUTOCTS BIT3 -#define PVR_RS232 0x20 /* 0010b */ -#define PVR_V35 0xe0 /* 1110b */ -#define PVR_RS422 0x40 /* 0100b */ - -/* Register access functions */ - -#define write_reg(info, reg, val) outb((val),(info)->io_base + (reg)) -#define read_reg(info, reg) inb((info)->io_base + (reg)) - -#define read_reg16(info, reg) inw((info)->io_base + (reg)) -#define write_reg16(info, reg, val) outw((val), (info)->io_base + (reg)) - -#define set_reg_bits(info, reg, mask) \ - write_reg(info, (reg), \ - (unsigned char) (read_reg(info, (reg)) | (mask))) -#define clear_reg_bits(info, reg, mask) \ - write_reg(info, (reg), \ - (unsigned char) (read_reg(info, (reg)) & ~(mask))) -/* - * interrupt enable/disable routines - */ -static void irq_disable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask) -{ - if (channel == CHA) { - info->imra_value |= mask; - write_reg16(info, CHA + IMR, info->imra_value); - } else { - info->imrb_value |= mask; - write_reg16(info, CHB + IMR, info->imrb_value); - } -} -static void irq_enable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask) -{ - if (channel == CHA) { - info->imra_value &= ~mask; - write_reg16(info, CHA + IMR, info->imra_value); - } else { - info->imrb_value &= ~mask; - write_reg16(info, CHB + IMR, info->imrb_value); - } -} - -#define port_irq_disable(info, mask) \ - { info->pim_value |= (mask); write_reg(info, PIM, info->pim_value); } - -#define port_irq_enable(info, mask) \ - { info->pim_value &= ~(mask); write_reg(info, PIM, info->pim_value); } - -static void rx_start(MGSLPC_INFO *info); -static void rx_stop(MGSLPC_INFO *info); - -static void tx_start(MGSLPC_INFO *info, struct tty_struct *tty); -static void tx_stop(MGSLPC_INFO *info); -static void tx_set_idle(MGSLPC_INFO *info); - -static void get_signals(MGSLPC_INFO *info); -static void set_signals(MGSLPC_INFO *info); - -static void reset_device(MGSLPC_INFO *info); - -static void hdlc_mode(MGSLPC_INFO *info); -static void async_mode(MGSLPC_INFO *info); - -static void tx_timeout(struct timer_list *t); - -static bool carrier_raised(struct tty_port *port); -static void dtr_rts(struct tty_port *port, bool active); - -#if SYNCLINK_GENERIC_HDLC -#define dev_to_port(D) (dev_to_hdlc(D)->priv) -static void hdlcdev_tx_done(MGSLPC_INFO *info); -static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size); -static int hdlcdev_init(MGSLPC_INFO *info); -static void hdlcdev_exit(MGSLPC_INFO *info); -#endif - -static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit); - -static bool register_test(MGSLPC_INFO *info); -static bool irq_test(MGSLPC_INFO *info); -static int adapter_test(MGSLPC_INFO *info); - -static int claim_resources(MGSLPC_INFO *info); -static void release_resources(MGSLPC_INFO *info); -static int mgslpc_add_device(MGSLPC_INFO *info); -static void mgslpc_remove_device(MGSLPC_INFO *info); - -static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty); -static void rx_reset_buffers(MGSLPC_INFO *info); -static int rx_alloc_buffers(MGSLPC_INFO *info); -static void rx_free_buffers(MGSLPC_INFO *info); - -static irqreturn_t mgslpc_isr(int irq, void *dev_id); - -/* - * Bottom half interrupt handlers - */ -static void bh_handler(struct work_struct *work); -static void bh_transmit(MGSLPC_INFO *info, struct tty_struct *tty); -static void bh_status(MGSLPC_INFO *info); - -/* - * ioctl handlers - */ -static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int get_stats(MGSLPC_INFO *info, struct mgsl_icount __user *user_icount); -static int get_params(MGSLPC_INFO *info, MGSL_PARAMS __user *user_params); -static int set_params(MGSLPC_INFO *info, MGSL_PARAMS __user *new_params, struct tty_struct *tty); -static int get_txidle(MGSLPC_INFO *info, int __user *idle_mode); -static int set_txidle(MGSLPC_INFO *info, int idle_mode); -static int set_txenable(MGSLPC_INFO *info, int enable, struct tty_struct *tty); -static int tx_abort(MGSLPC_INFO *info); -static int set_rxenable(MGSLPC_INFO *info, int enable); -static int wait_events(MGSLPC_INFO *info, int __user *mask); - -static MGSLPC_INFO *mgslpc_device_list = NULL; -static int mgslpc_device_count = 0; - -/* - * Set this param to non-zero to load eax with the - * .text section address and breakpoint on module load. - * This is useful for use with gdb and add-symbol-file command. - */ -static bool break_on_load; - -/* - * Driver major number, defaults to zero to get auto - * assigned major number. May be forced as module parameter. - */ -static int ttymajor=0; - -static int debug_level = 0; -static int maxframe[MAX_DEVICE_COUNT] = {0,}; - -module_param(break_on_load, bool, 0); -module_param(ttymajor, int, 0); -module_param(debug_level, int, 0); -module_param_array(maxframe, int, NULL, 0); - -MODULE_LICENSE("GPL"); - -static char *driver_name = "SyncLink PC Card driver"; -static char *driver_version = "$Revision: 4.34 $"; - -static struct tty_driver *serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty); -static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout); - -/* PCMCIA prototypes */ - -static int mgslpc_config(struct pcmcia_device *link); -static void mgslpc_release(u_long arg); -static void mgslpc_detach(struct pcmcia_device *p_dev); - -/* - * 1st function defined in .text section. Calling this function in - * init_module() followed by a breakpoint allows a remote debugger - * (gdb) to get the .text address for the add-symbol-file command. - * This allows remote debugging of dynamically loadable modules. - */ -static void* mgslpc_get_text_ptr(void) -{ - return mgslpc_get_text_ptr; -} - -/* - * line discipline callback wrappers - * - * The wrappers maintain line discipline references - * while calling into the line discipline. - * - * ldisc_receive_buf - pass receive data to line discipline - */ - -static void ldisc_receive_buf(struct tty_struct *tty, - const __u8 *data, char *flags, int count) -{ - struct tty_ldisc *ld; - if (!tty) - return; - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->receive_buf) - ld->ops->receive_buf(tty, data, flags, count); - tty_ldisc_deref(ld); - } -} - -static const struct tty_port_operations mgslpc_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts -}; - -static int mgslpc_probe(struct pcmcia_device *link) -{ - MGSLPC_INFO *info; - int ret; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgslpc_attach\n"); - - info = kzalloc(sizeof(MGSLPC_INFO), GFP_KERNEL); - if (!info) { - printk("Error can't allocate device instance data\n"); - return -ENOMEM; - } - - info->magic = MGSLPC_MAGIC; - tty_port_init(&info->port); - info->port.ops = &mgslpc_port_ops; - INIT_WORK(&info->task, bh_handler); - info->max_frame_size = 4096; - init_waitqueue_head(&info->status_event_wait_q); - init_waitqueue_head(&info->event_wait_q); - spin_lock_init(&info->lock); - spin_lock_init(&info->netlock); - memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; - info->imra_value = 0xffff; - info->imrb_value = 0xffff; - info->pim_value = 0xff; - - info->p_dev = link; - link->priv = info; - - /* Initialize the struct pcmcia_device structure */ - - ret = mgslpc_config(link); - if (ret != 0) - goto failed; - - ret = mgslpc_add_device(info); - if (ret != 0) - goto failed_release; - - return 0; - -failed_release: - mgslpc_release((u_long)link); -failed: - tty_port_destroy(&info->port); - kfree(info); - return ret; -} - -/* Card has been inserted. - */ - -static int mgslpc_ioprobe(struct pcmcia_device *p_dev, void *priv_data) -{ - return pcmcia_request_io(p_dev); -} - -static int mgslpc_config(struct pcmcia_device *link) -{ - MGSLPC_INFO *info = link->priv; - int ret; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgslpc_config(0x%p)\n", link); - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - ret = pcmcia_loop_config(link, mgslpc_ioprobe, NULL); - if (ret != 0) - goto failed; - - link->config_index = 8; - link->config_regs = PRESENT_OPTION; - - ret = pcmcia_request_irq(link, mgslpc_isr); - if (ret) - goto failed; - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - info->io_base = link->resource[0]->start; - info->irq_level = link->irq; - return 0; - -failed: - mgslpc_release((u_long)link); - return -ENODEV; -} - -/* Card has been removed. - * Unregister device and release PCMCIA configuration. - * If device is open, postpone until it is closed. - */ -static void mgslpc_release(u_long arg) -{ - struct pcmcia_device *link = (struct pcmcia_device *)arg; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgslpc_release(0x%p)\n", link); - - pcmcia_disable_device(link); -} - -static void mgslpc_detach(struct pcmcia_device *link) -{ - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgslpc_detach(0x%p)\n", link); - - ((MGSLPC_INFO *)link->priv)->stop = 1; - mgslpc_release((u_long)link); - - mgslpc_remove_device((MGSLPC_INFO *)link->priv); -} - -static int mgslpc_suspend(struct pcmcia_device *link) -{ - MGSLPC_INFO *info = link->priv; - - info->stop = 1; - - return 0; -} - -static int mgslpc_resume(struct pcmcia_device *link) -{ - MGSLPC_INFO *info = link->priv; - - info->stop = 0; - - return 0; -} - - -static inline bool mgslpc_paranoia_check(MGSLPC_INFO *info, - char *name, const char *routine) -{ -#ifdef MGSLPC_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for mgsl struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null mgslpc_info for (%s) in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return true; - } - if (info->magic != MGSLPC_MAGIC) { - printk(badmagic, name, routine); - return true; - } -#else - if (!info) - return true; -#endif - return false; -} - - -#define CMD_RXFIFO BIT7 // release current rx FIFO -#define CMD_RXRESET BIT6 // receiver reset -#define CMD_RXFIFO_READ BIT5 -#define CMD_START_TIMER BIT4 -#define CMD_TXFIFO BIT3 // release current tx FIFO -#define CMD_TXEOM BIT1 // transmit end message -#define CMD_TXRESET BIT0 // transmit reset - -static bool wait_command_complete(MGSLPC_INFO *info, unsigned char channel) -{ - int i = 0; - /* wait for command completion */ - while (read_reg(info, (unsigned char)(channel+STAR)) & BIT2) { - udelay(1); - if (i++ == 1000) - return false; - } - return true; -} - -static void issue_command(MGSLPC_INFO *info, unsigned char channel, unsigned char cmd) -{ - wait_command_complete(info, channel); - write_reg(info, (unsigned char) (channel + CMDR), cmd); -} - -static void tx_pause(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (mgslpc_paranoia_check(info, tty->name, "tx_pause")) - return; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("tx_pause(%s)\n", info->device_name); - - spin_lock_irqsave(&info->lock, flags); - if (info->tx_enabled) - tx_stop(info); - spin_unlock_irqrestore(&info->lock, flags); -} - -static void tx_release(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (mgslpc_paranoia_check(info, tty->name, "tx_release")) - return; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("tx_release(%s)\n", info->device_name); - - spin_lock_irqsave(&info->lock, flags); - if (!info->tx_enabled) - tx_start(info, tty); - spin_unlock_irqrestore(&info->lock, flags); -} - -/* Return next bottom half action to perform. - * or 0 if nothing to do. - */ -static int bh_action(MGSLPC_INFO *info) -{ - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&info->lock, flags); - - if (info->pending_bh & BH_RECEIVE) { - info->pending_bh &= ~BH_RECEIVE; - rc = BH_RECEIVE; - } else if (info->pending_bh & BH_TRANSMIT) { - info->pending_bh &= ~BH_TRANSMIT; - rc = BH_TRANSMIT; - } else if (info->pending_bh & BH_STATUS) { - info->pending_bh &= ~BH_STATUS; - rc = BH_STATUS; - } - - if (!rc) { - /* Mark BH routine as complete */ - info->bh_running = false; - info->bh_requested = false; - } - - spin_unlock_irqrestore(&info->lock, flags); - - return rc; -} - -static void bh_handler(struct work_struct *work) -{ - MGSLPC_INFO *info = container_of(work, MGSLPC_INFO, task); - struct tty_struct *tty; - int action; - - if (debug_level >= DEBUG_LEVEL_BH) - printk("%s(%d):bh_handler(%s) entry\n", - __FILE__,__LINE__,info->device_name); - - info->bh_running = true; - tty = tty_port_tty_get(&info->port); - - while((action = bh_action(info)) != 0) { - - /* Process work item */ - if (debug_level >= DEBUG_LEVEL_BH) - printk("%s(%d):bh_handler() work item action=%d\n", - __FILE__,__LINE__,action); - - switch (action) { - - case BH_RECEIVE: - while(rx_get_frame(info, tty)); - break; - case BH_TRANSMIT: - bh_transmit(info, tty); - break; - case BH_STATUS: - bh_status(info); - break; - default: - /* unknown work item ID */ - printk("Unknown work item ID=%08X!\n", action); - break; - } - } - - tty_kref_put(tty); - if (debug_level >= DEBUG_LEVEL_BH) - printk("%s(%d):bh_handler(%s) exit\n", - __FILE__,__LINE__,info->device_name); -} - -static void bh_transmit(MGSLPC_INFO *info, struct tty_struct *tty) -{ - if (debug_level >= DEBUG_LEVEL_BH) - printk("bh_transmit() entry on %s\n", info->device_name); - - if (tty) - tty_wakeup(tty); -} - -static void bh_status(MGSLPC_INFO *info) -{ - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - info->dcd_chkcount = 0; - info->cts_chkcount = 0; -} - -/* eom: non-zero = end of frame */ -static void rx_ready_hdlc(MGSLPC_INFO *info, int eom) -{ - unsigned char data[2]; - unsigned char fifo_count, read_count, i; - RXBUF *buf = (RXBUF*)(info->rx_buf + (info->rx_put * info->rx_buf_size)); - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):rx_ready_hdlc(eom=%d)\n", __FILE__, __LINE__, eom); - - if (!info->rx_enabled) - return; - - if (info->rx_frame_count >= info->rx_buf_count) { - /* no more free buffers */ - issue_command(info, CHA, CMD_RXRESET); - info->pending_bh |= BH_RECEIVE; - info->rx_overflow = true; - info->icount.buf_overrun++; - return; - } - - if (eom) { - /* end of frame, get FIFO count from RBCL register */ - fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f); - if (fifo_count == 0) - fifo_count = 32; - } else - fifo_count = 32; - - do { - if (fifo_count == 1) { - read_count = 1; - data[0] = read_reg(info, CHA + RXFIFO); - } else { - read_count = 2; - *((unsigned short *) data) = read_reg16(info, CHA + RXFIFO); - } - fifo_count -= read_count; - if (!fifo_count && eom) - buf->status = data[--read_count]; - - for (i = 0; i < read_count; i++) { - if (buf->count >= info->max_frame_size) { - /* frame too large, reset receiver and reset current buffer */ - issue_command(info, CHA, CMD_RXRESET); - buf->count = 0; - return; - } - *(buf->data + buf->count) = data[i]; - buf->count++; - } - } while (fifo_count); - - if (eom) { - info->pending_bh |= BH_RECEIVE; - info->rx_frame_count++; - info->rx_put++; - if (info->rx_put >= info->rx_buf_count) - info->rx_put = 0; - } - issue_command(info, CHA, CMD_RXFIFO); -} - -static void rx_ready_async(MGSLPC_INFO *info, int tcd) -{ - struct tty_port *port = &info->port; - unsigned char data, status, flag; - int fifo_count; - int work = 0; - struct mgsl_icount *icount = &info->icount; - - if (tcd) { - /* early termination, get FIFO count from RBCL register */ - fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f); - - /* Zero fifo count could mean 0 or 32 bytes available. - * If BIT5 of STAR is set then at least 1 byte is available. - */ - if (!fifo_count && (read_reg(info,CHA+STAR) & BIT5)) - fifo_count = 32; - } else - fifo_count = 32; - - tty_buffer_request_room(port, fifo_count); - /* Flush received async data to receive data buffer. */ - while (fifo_count) { - data = read_reg(info, CHA + RXFIFO); - status = read_reg(info, CHA + RXFIFO); - fifo_count -= 2; - - icount->rx++; - flag = TTY_NORMAL; - - // if no frameing/crc error then save data - // BIT7:parity error - // BIT6:framing error - - if (status & (BIT7 | BIT6)) { - if (status & BIT7) - icount->parity++; - else - icount->frame++; - - /* discard char if tty control flags say so */ - if (status & info->ignore_status_mask) - continue; - - status &= info->read_status_mask; - - if (status & BIT7) - flag = TTY_PARITY; - else if (status & BIT6) - flag = TTY_FRAME; - } - work += tty_insert_flip_char(port, data, flag); - } - issue_command(info, CHA, CMD_RXFIFO); - - if (debug_level >= DEBUG_LEVEL_ISR) { - printk("%s(%d):rx_ready_async", - __FILE__,__LINE__); - printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n", - __FILE__,__LINE__,icount->rx,icount->brk, - icount->parity,icount->frame,icount->overrun); - } - - if (work) - tty_flip_buffer_push(port); -} - - -static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty) -{ - if (!info->tx_active) - return; - - info->tx_active = false; - info->tx_aborting = false; - - if (info->params.mode == MGSL_MODE_ASYNC) - return; - - info->tx_count = info->tx_put = info->tx_get = 0; - del_timer(&info->tx_timer); - - if (info->drop_rts_on_tx_done) { - get_signals(info); - if (info->serial_signals & SerialSignal_RTS) { - info->serial_signals &= ~SerialSignal_RTS; - set_signals(info); - } - info->drop_rts_on_tx_done = false; - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - { - if (tty && (tty->flow.stopped || tty->hw_stopped)) { - tx_stop(info); - return; - } - info->pending_bh |= BH_TRANSMIT; - } -} - -static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty) -{ - unsigned char fifo_count = 32; - int c; - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):tx_ready(%s)\n", __FILE__, __LINE__, info->device_name); - - if (info->params.mode == MGSL_MODE_HDLC) { - if (!info->tx_active) - return; - } else { - if (tty && (tty->flow.stopped || tty->hw_stopped)) { - tx_stop(info); - return; - } - if (!info->tx_count) - info->tx_active = false; - } - - if (!info->tx_count) - return; - - while (info->tx_count && fifo_count) { - c = min(2, min_t(int, fifo_count, min(info->tx_count, TXBUFSIZE - info->tx_get))); - - if (c == 1) { - write_reg(info, CHA + TXFIFO, *(info->tx_buf + info->tx_get)); - } else { - write_reg16(info, CHA + TXFIFO, - *((unsigned short*)(info->tx_buf + info->tx_get))); - } - info->tx_count -= c; - info->tx_get = (info->tx_get + c) & (TXBUFSIZE - 1); - fifo_count -= c; - } - - if (info->params.mode == MGSL_MODE_ASYNC) { - if (info->tx_count < WAKEUP_CHARS) - info->pending_bh |= BH_TRANSMIT; - issue_command(info, CHA, CMD_TXFIFO); - } else { - if (info->tx_count) - issue_command(info, CHA, CMD_TXFIFO); - else - issue_command(info, CHA, CMD_TXFIFO + CMD_TXEOM); - } -} - -static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) -{ - get_signals(info); - if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - irq_disable(info, CHB, IRQ_CTS); - info->icount.cts++; - if (info->serial_signals & SerialSignal_CTS) - info->input_signal_events.cts_up++; - else - info->input_signal_events.cts_down++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - if (tty && tty_port_cts_enabled(&info->port)) { - if (tty->hw_stopped) { - if (info->serial_signals & SerialSignal_CTS) { - if (debug_level >= DEBUG_LEVEL_ISR) - printk("CTS tx start..."); - tty->hw_stopped = 0; - tx_start(info, tty); - info->pending_bh |= BH_TRANSMIT; - return; - } - } else { - if (!(info->serial_signals & SerialSignal_CTS)) { - if (debug_level >= DEBUG_LEVEL_ISR) - printk("CTS tx stop..."); - tty->hw_stopped = 1; - tx_stop(info); - } - } - } - info->pending_bh |= BH_STATUS; -} - -static void dcd_change(MGSLPC_INFO *info, struct tty_struct *tty) -{ - get_signals(info); - if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - irq_disable(info, CHB, IRQ_DCD); - info->icount.dcd++; - if (info->serial_signals & SerialSignal_DCD) { - info->input_signal_events.dcd_up++; - } - else - info->input_signal_events.dcd_down++; -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) { - if (info->serial_signals & SerialSignal_DCD) - netif_carrier_on(info->netdev); - else - netif_carrier_off(info->netdev); - } -#endif - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - if (tty_port_check_carrier(&info->port)) { - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s CD now %s...", info->device_name, - (info->serial_signals & SerialSignal_DCD) ? "on" : "off"); - if (info->serial_signals & SerialSignal_DCD) - wake_up_interruptible(&info->port.open_wait); - else { - if (debug_level >= DEBUG_LEVEL_ISR) - printk("doing serial hangup..."); - if (tty) - tty_hangup(tty); - } - } - info->pending_bh |= BH_STATUS; -} - -static void dsr_change(MGSLPC_INFO *info) -{ - get_signals(info); - if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - port_irq_disable(info, PVR_DSR); - info->icount.dsr++; - if (info->serial_signals & SerialSignal_DSR) - info->input_signal_events.dsr_up++; - else - info->input_signal_events.dsr_down++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; -} - -static void ri_change(MGSLPC_INFO *info) -{ - get_signals(info); - if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - port_irq_disable(info, PVR_RI); - info->icount.rng++; - if (info->serial_signals & SerialSignal_RI) - info->input_signal_events.ri_up++; - else - info->input_signal_events.ri_down++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; -} - -/* Interrupt service routine entry point. - * - * Arguments: - * - * irq interrupt number that caused interrupt - * dev_id device ID supplied during interrupt registration - */ -static irqreturn_t mgslpc_isr(int dummy, void *dev_id) -{ - MGSLPC_INFO *info = dev_id; - struct tty_struct *tty; - unsigned short isr; - unsigned char gis, pis; - int count=0; - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("mgslpc_isr(%d) entry.\n", info->irq_level); - - if (!(info->p_dev->_locked)) - return IRQ_HANDLED; - - tty = tty_port_tty_get(&info->port); - - spin_lock(&info->lock); - - while ((gis = read_reg(info, CHA + GIS))) { - if (debug_level >= DEBUG_LEVEL_ISR) - printk("mgslpc_isr %s gis=%04X\n", info->device_name,gis); - - if ((gis & 0x70) || count > 1000) { - printk("synclink_cs:hardware failed or ejected\n"); - break; - } - count++; - - if (gis & (BIT1 | BIT0)) { - isr = read_reg16(info, CHB + ISR); - if (isr & IRQ_DCD) - dcd_change(info, tty); - if (isr & IRQ_CTS) - cts_change(info, tty); - } - if (gis & (BIT3 | BIT2)) - { - isr = read_reg16(info, CHA + ISR); - if (isr & IRQ_TIMER) { - info->irq_occurred = true; - irq_disable(info, CHA, IRQ_TIMER); - } - - /* receive IRQs */ - if (isr & IRQ_EXITHUNT) { - info->icount.exithunt++; - wake_up_interruptible(&info->event_wait_q); - } - if (isr & IRQ_BREAK_ON) { - info->icount.brk++; - if (info->port.flags & ASYNC_SAK) - do_SAK(tty); - } - if (isr & IRQ_RXTIME) { - issue_command(info, CHA, CMD_RXFIFO_READ); - } - if (isr & (IRQ_RXEOM | IRQ_RXFIFO)) { - if (info->params.mode == MGSL_MODE_HDLC) - rx_ready_hdlc(info, isr & IRQ_RXEOM); - else - rx_ready_async(info, isr & IRQ_RXEOM); - } - - /* transmit IRQs */ - if (isr & IRQ_UNDERRUN) { - if (info->tx_aborting) - info->icount.txabort++; - else - info->icount.txunder++; - tx_done(info, tty); - } - else if (isr & IRQ_ALLSENT) { - info->icount.txok++; - tx_done(info, tty); - } - else if (isr & IRQ_TXFIFO) - tx_ready(info, tty); - } - if (gis & BIT7) { - pis = read_reg(info, CHA + PIS); - if (pis & BIT1) - dsr_change(info); - if (pis & BIT2) - ri_change(info); - } - } - - /* Request bottom half processing if there's something - * for it to do and the bh is not already running - */ - - if (info->pending_bh && !info->bh_running && !info->bh_requested) { - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):%s queueing bh task.\n", - __FILE__,__LINE__,info->device_name); - schedule_work(&info->task); - info->bh_requested = true; - } - - spin_unlock(&info->lock); - tty_kref_put(tty); - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):mgslpc_isr(%d)exit.\n", - __FILE__, __LINE__, info->irq_level); - - return IRQ_HANDLED; -} - -/* Initialize and start device. - */ -static int startup(MGSLPC_INFO * info, struct tty_struct *tty) -{ - int retval = 0; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):startup(%s)\n", __FILE__, __LINE__, info->device_name); - - if (tty_port_initialized(&info->port)) - return 0; - - if (!info->tx_buf) { - /* allocate a page of memory for a transmit buffer */ - info->tx_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - if (!info->tx_buf) { - printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", - __FILE__, __LINE__, info->device_name); - return -ENOMEM; - } - } - - info->pending_bh = 0; - - memset(&info->icount, 0, sizeof(info->icount)); - - timer_setup(&info->tx_timer, tx_timeout, 0); - - /* Allocate and claim adapter resources */ - retval = claim_resources(info); - - /* perform existence check and diagnostics */ - if (!retval) - retval = adapter_test(info); - - if (retval) { - if (capable(CAP_SYS_ADMIN) && tty) - set_bit(TTY_IO_ERROR, &tty->flags); - release_resources(info); - return retval; - } - - /* program hardware for current parameters */ - mgslpc_change_params(info, tty); - - if (tty) - clear_bit(TTY_IO_ERROR, &tty->flags); - - tty_port_set_initialized(&info->port, true); - - return 0; -} - -/* Called by mgslpc_close() and mgslpc_hangup() to shutdown hardware - */ -static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty) -{ - unsigned long flags; - - if (!tty_port_initialized(&info->port)) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_shutdown(%s)\n", - __FILE__, __LINE__, info->device_name); - - /* clear status wait queue because status changes */ - /* can't happen after shutting down the hardware */ - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - del_timer_sync(&info->tx_timer); - - if (info->tx_buf) { - free_page((unsigned long) info->tx_buf); - info->tx_buf = NULL; - } - - spin_lock_irqsave(&info->lock, flags); - - rx_stop(info); - tx_stop(info); - - /* TODO:disable interrupts instead of reset to preserve signal states */ - reset_device(info); - - if (!tty || C_HUPCL(tty)) { - info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_signals(info); - } - - spin_unlock_irqrestore(&info->lock, flags); - - release_resources(info); - - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - - tty_port_set_initialized(&info->port, false); -} - -static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty) -{ - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - - rx_stop(info); - tx_stop(info); - info->tx_count = info->tx_put = info->tx_get = 0; - - if (info->params.mode == MGSL_MODE_HDLC || info->netcount) - hdlc_mode(info); - else - async_mode(info); - - set_signals(info); - - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - - irq_enable(info, CHB, IRQ_DCD | IRQ_CTS); - port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI); - get_signals(info); - - if (info->netcount || (tty && C_CREAD(tty))) - rx_start(info); - - spin_unlock_irqrestore(&info->lock, flags); -} - -/* Reconfigure adapter based on new parameters - */ -static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty) -{ - unsigned cflag; - int bits_per_char; - - if (!tty) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_change_params(%s)\n", - __FILE__, __LINE__, info->device_name); - - cflag = tty->termios.c_cflag; - - /* if B0 rate (hangup) specified then negate RTS and DTR */ - /* otherwise assert RTS and DTR */ - if (cflag & CBAUD) - info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; - else - info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - - /* byte size and parity */ - if ((cflag & CSIZE) != CS8) { - cflag &= ~CSIZE; - cflag |= CS7; - tty->termios.c_cflag = cflag; - } - info->params.data_bits = tty_get_char_size(cflag); - - if (cflag & CSTOPB) - info->params.stop_bits = 2; - else - info->params.stop_bits = 1; - - info->params.parity = ASYNC_PARITY_NONE; - if (cflag & PARENB) { - if (cflag & PARODD) - info->params.parity = ASYNC_PARITY_ODD; - else - info->params.parity = ASYNC_PARITY_EVEN; - if (cflag & CMSPAR) - info->params.parity = ASYNC_PARITY_SPACE; - } - - /* calculate number of jiffies to transmit a full - * FIFO (32 bytes) at specified data rate - */ - bits_per_char = info->params.data_bits + - info->params.stop_bits + 1; - - /* if port data rate is set to 460800 or less then - * allow tty settings to override, otherwise keep the - * current data rate. - */ - if (info->params.data_rate <= 460800) { - info->params.data_rate = tty_get_baud_rate(tty); - } - - if (info->params.data_rate) { - info->timeout = (32*HZ*bits_per_char) / - info->params.data_rate; - } - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); - tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); - - /* process tty input control flags */ - - info->read_status_mask = 0; - if (I_INPCK(tty)) - info->read_status_mask |= BIT7 | BIT6; - if (I_IGNPAR(tty)) - info->ignore_status_mask |= BIT7 | BIT6; - - mgslpc_program_hw(info, tty); -} - -/* Add a character to the transmit buffer - */ -static int mgslpc_put_char(struct tty_struct *tty, unsigned char ch) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) { - printk("%s(%d):mgslpc_put_char(%d) on %s\n", - __FILE__, __LINE__, ch, info->device_name); - } - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_put_char")) - return 0; - - if (!info->tx_buf) - return 0; - - spin_lock_irqsave(&info->lock, flags); - - if (info->params.mode == MGSL_MODE_ASYNC || !info->tx_active) { - if (info->tx_count < TXBUFSIZE - 1) { - info->tx_buf[info->tx_put++] = ch; - info->tx_put &= TXBUFSIZE-1; - info->tx_count++; - } - } - - spin_unlock_irqrestore(&info->lock, flags); - return 1; -} - -/* Enable transmitter so remaining characters in the - * transmit buffer are sent. - */ -static void mgslpc_flush_chars(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_flush_chars() entry on %s tx_count=%d\n", - __FILE__, __LINE__, info->device_name, info->tx_count); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_flush_chars")) - return; - - if (info->tx_count <= 0 || tty->flow.stopped || - tty->hw_stopped || !info->tx_buf) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_flush_chars() entry on %s starting transmitter\n", - __FILE__, __LINE__, info->device_name); - - spin_lock_irqsave(&info->lock, flags); - if (!info->tx_active) - tx_start(info, tty); - spin_unlock_irqrestore(&info->lock, flags); -} - -/* Send a block of data - * - * Arguments: - * - * tty pointer to tty information structure - * buf pointer to buffer containing send data - * count size of send data in bytes - * - * Returns: number of characters written - */ -static int mgslpc_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_write(%s) count=%d\n", - __FILE__, __LINE__, info->device_name, count); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_write") || - !info->tx_buf) - goto cleanup; - - if (info->params.mode == MGSL_MODE_HDLC) { - if (count > TXBUFSIZE) { - ret = -EIO; - goto cleanup; - } - if (info->tx_active) - goto cleanup; - else if (info->tx_count) - goto start; - } - - for (;;) { - c = min(count, - min(TXBUFSIZE - info->tx_count - 1, - TXBUFSIZE - info->tx_put)); - if (c <= 0) - break; - - memcpy(info->tx_buf + info->tx_put, buf, c); - - spin_lock_irqsave(&info->lock, flags); - info->tx_put = (info->tx_put + c) & (TXBUFSIZE-1); - info->tx_count += c; - spin_unlock_irqrestore(&info->lock, flags); - - buf += c; - count -= c; - ret += c; - } -start: - if (info->tx_count && !tty->flow.stopped && !tty->hw_stopped) { - spin_lock_irqsave(&info->lock, flags); - if (!info->tx_active) - tx_start(info, tty); - spin_unlock_irqrestore(&info->lock, flags); - } -cleanup: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_write(%s) returning=%d\n", - __FILE__, __LINE__, info->device_name, ret); - return ret; -} - -/* Return the count of free bytes in transmit buffer - */ -static unsigned int mgslpc_write_room(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - int ret; - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_write_room")) - return 0; - - if (info->params.mode == MGSL_MODE_HDLC) { - /* HDLC (frame oriented) mode */ - if (info->tx_active) - return 0; - else - return HDLC_MAX_FRAME_SIZE; - } else { - ret = TXBUFSIZE - info->tx_count - 1; - if (ret < 0) - ret = 0; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_write_room(%s)=%d\n", - __FILE__, __LINE__, info->device_name, ret); - return ret; -} - -/* Return the count of bytes in transmit buffer - */ -static unsigned int mgslpc_chars_in_buffer(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned int rc; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_chars_in_buffer(%s)\n", - __FILE__, __LINE__, info->device_name); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_chars_in_buffer")) - return 0; - - if (info->params.mode == MGSL_MODE_HDLC) - rc = info->tx_active ? info->max_frame_size : 0; - else - rc = info->tx_count; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_chars_in_buffer(%s)=%u\n", - __FILE__, __LINE__, info->device_name, rc); - - return rc; -} - -/* Discard all data in the send buffer - */ -static void mgslpc_flush_buffer(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_flush_buffer(%s) entry\n", - __FILE__, __LINE__, info->device_name); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_flush_buffer")) - return; - - spin_lock_irqsave(&info->lock, flags); - info->tx_count = info->tx_put = info->tx_get = 0; - del_timer(&info->tx_timer); - spin_unlock_irqrestore(&info->lock, flags); - - wake_up_interruptible(&tty->write_wait); - tty_wakeup(tty); -} - -/* Send a high-priority XON/XOFF character - */ -static void mgslpc_send_xchar(struct tty_struct *tty, char ch) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_send_xchar(%s,%d)\n", - __FILE__, __LINE__, info->device_name, ch); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_send_xchar")) - return; - - info->x_char = ch; - if (ch) { - spin_lock_irqsave(&info->lock, flags); - if (!info->tx_enabled) - tx_start(info, tty); - spin_unlock_irqrestore(&info->lock, flags); - } -} - -/* Signal remote device to throttle send data (our receive data) - */ -static void mgslpc_throttle(struct tty_struct * tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_throttle(%s) entry\n", - __FILE__, __LINE__, info->device_name); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_throttle")) - return; - - if (I_IXOFF(tty)) - mgslpc_send_xchar(tty, STOP_CHAR(tty)); - - if (C_CRTSCTS(tty)) { - spin_lock_irqsave(&info->lock, flags); - info->serial_signals &= ~SerialSignal_RTS; - set_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - } -} - -/* Signal remote device to stop throttling send data (our receive data) - */ -static void mgslpc_unthrottle(struct tty_struct * tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_unthrottle(%s) entry\n", - __FILE__, __LINE__, info->device_name); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - mgslpc_send_xchar(tty, START_CHAR(tty)); - } - - if (C_CRTSCTS(tty)) { - spin_lock_irqsave(&info->lock, flags); - info->serial_signals |= SerialSignal_RTS; - set_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - } -} - -/* get the current serial statistics - */ -static int get_stats(MGSLPC_INFO * info, struct mgsl_icount __user *user_icount) -{ - int err; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("get_params(%s)\n", info->device_name); - if (!user_icount) { - memset(&info->icount, 0, sizeof(info->icount)); - } else { - COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount)); - if (err) - return -EFAULT; - } - return 0; -} - -/* get the current serial parameters - */ -static int get_params(MGSLPC_INFO * info, MGSL_PARAMS __user *user_params) -{ - int err; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("get_params(%s)\n", info->device_name); - COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); - if (err) - return -EFAULT; - return 0; -} - -/* set the serial parameters - * - * Arguments: - * - * info pointer to device instance data - * new_params user buffer containing new serial params - * - * Returns: 0 if success, otherwise error code - */ -static int set_params(MGSLPC_INFO * info, MGSL_PARAMS __user *new_params, struct tty_struct *tty) -{ - unsigned long flags; - MGSL_PARAMS tmp_params; - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):set_params %s\n", __FILE__,__LINE__, - info->device_name); - COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); - if (err) { - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):set_params(%s) user buffer copy failed\n", - __FILE__, __LINE__, info->device_name); - return -EFAULT; - } - - spin_lock_irqsave(&info->lock, flags); - memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); - spin_unlock_irqrestore(&info->lock, flags); - - mgslpc_change_params(info, tty); - - return 0; -} - -static int get_txidle(MGSLPC_INFO * info, int __user *idle_mode) -{ - int err; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("get_txidle(%s)=%d\n", info->device_name, info->idle_mode); - COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); - if (err) - return -EFAULT; - return 0; -} - -static int set_txidle(MGSLPC_INFO * info, int idle_mode) -{ - unsigned long flags; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("set_txidle(%s,%d)\n", info->device_name, idle_mode); - spin_lock_irqsave(&info->lock, flags); - info->idle_mode = idle_mode; - tx_set_idle(info); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int get_interface(MGSLPC_INFO * info, int __user *if_mode) -{ - int err; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("get_interface(%s)=%d\n", info->device_name, info->if_mode); - COPY_TO_USER(err,if_mode, &info->if_mode, sizeof(int)); - if (err) - return -EFAULT; - return 0; -} - -static int set_interface(MGSLPC_INFO * info, int if_mode) -{ - unsigned long flags; - unsigned char val; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("set_interface(%s,%d)\n", info->device_name, if_mode); - spin_lock_irqsave(&info->lock, flags); - info->if_mode = if_mode; - - val = read_reg(info, PVR) & 0x0f; - switch (info->if_mode) - { - case MGSL_INTERFACE_RS232: val |= PVR_RS232; break; - case MGSL_INTERFACE_V35: val |= PVR_V35; break; - case MGSL_INTERFACE_RS422: val |= PVR_RS422; break; - } - write_reg(info, PVR, val); - - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int set_txenable(MGSLPC_INFO * info, int enable, struct tty_struct *tty) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("set_txenable(%s,%d)\n", info->device_name, enable); - - spin_lock_irqsave(&info->lock, flags); - if (enable) { - if (!info->tx_enabled) - tx_start(info, tty); - } else { - if (info->tx_enabled) - tx_stop(info); - } - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int tx_abort(MGSLPC_INFO * info) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("tx_abort(%s)\n", info->device_name); - - spin_lock_irqsave(&info->lock, flags); - if (info->tx_active && info->tx_count && - info->params.mode == MGSL_MODE_HDLC) { - /* clear data count so FIFO is not filled on next IRQ. - * This results in underrun and abort transmission. - */ - info->tx_count = info->tx_put = info->tx_get = 0; - info->tx_aborting = true; - } - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int set_rxenable(MGSLPC_INFO * info, int enable) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("set_rxenable(%s,%d)\n", info->device_name, enable); - - spin_lock_irqsave(&info->lock, flags); - if (enable) { - if (!info->rx_enabled) - rx_start(info); - } else { - if (info->rx_enabled) - rx_stop(info); - } - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -/* wait for specified event to occur - * - * Arguments: info pointer to device instance data - * mask pointer to bitmask of events to wait for - * Return Value: 0 if successful and bit mask updated with - * of events triggerred, - * otherwise error code - */ -static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr) -{ - unsigned long flags; - int s; - int rc=0; - struct mgsl_icount cprev, cnow; - int events; - int mask; - struct _input_signal_events oldsigs, newsigs; - DECLARE_WAITQUEUE(wait, current); - - COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); - if (rc) - return -EFAULT; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("wait_events(%s,%d)\n", info->device_name, mask); - - spin_lock_irqsave(&info->lock, flags); - - /* return immediately if state matches requested events */ - get_signals(info); - s = info->serial_signals; - events = mask & - ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + - ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + - ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + - ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); - if (events) { - spin_unlock_irqrestore(&info->lock, flags); - goto exit; - } - - /* save current irq counts */ - cprev = info->icount; - oldsigs = info->input_signal_events; - - if ((info->params.mode == MGSL_MODE_HDLC) && - (mask & MgslEvent_ExitHuntMode)) - irq_enable(info, CHA, IRQ_EXITHUNT); - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&info->event_wait_q, &wait); - - spin_unlock_irqrestore(&info->lock, flags); - - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get current irq counts */ - spin_lock_irqsave(&info->lock, flags); - cnow = info->icount; - newsigs = info->input_signal_events; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock, flags); - - /* if no change, wait aborted for some reason */ - if (newsigs.dsr_up == oldsigs.dsr_up && - newsigs.dsr_down == oldsigs.dsr_down && - newsigs.dcd_up == oldsigs.dcd_up && - newsigs.dcd_down == oldsigs.dcd_down && - newsigs.cts_up == oldsigs.cts_up && - newsigs.cts_down == oldsigs.cts_down && - newsigs.ri_up == oldsigs.ri_up && - newsigs.ri_down == oldsigs.ri_down && - cnow.exithunt == cprev.exithunt && - cnow.rxidle == cprev.rxidle) { - rc = -EIO; - break; - } - - events = mask & - ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + - (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + - (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + - (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + - (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + - (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + - (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + - (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + - (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + - (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); - if (events) - break; - - cprev = cnow; - oldsigs = newsigs; - } - - remove_wait_queue(&info->event_wait_q, &wait); - set_current_state(TASK_RUNNING); - - if (mask & MgslEvent_ExitHuntMode) { - spin_lock_irqsave(&info->lock, flags); - if (!waitqueue_active(&info->event_wait_q)) - irq_disable(info, CHA, IRQ_EXITHUNT); - spin_unlock_irqrestore(&info->lock, flags); - } -exit: - if (rc == 0) - PUT_USER(rc, events, mask_ptr); - return rc; -} - -static int modem_input_wait(MGSLPC_INFO *info,int arg) -{ - unsigned long flags; - int rc; - struct mgsl_icount cprev, cnow; - DECLARE_WAITQUEUE(wait, current); - - /* save current irq counts */ - spin_lock_irqsave(&info->lock, flags); - cprev = info->icount; - add_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock, flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get new irq counts */ - spin_lock_irqsave(&info->lock, flags); - cnow = info->icount; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock, flags); - - /* if no change, wait aborted for some reason */ - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - rc = -EIO; - break; - } - - /* check for change in caller specified modem input */ - if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || - (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || - (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || - (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { - rc = 0; - break; - } - - cprev = cnow; - } - remove_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_RUNNING); - return rc; -} - -/* return the state of the serial control and status signals - */ -static int tiocmget(struct tty_struct *tty) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - - result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + - ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + - ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + - ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + - ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + - ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tiocmget() value=%08X\n", - __FILE__, __LINE__, info->device_name, result); - return result; -} - -/* set modem control signals (DTR/RTS) - */ -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tiocmset(%x,%x)\n", - __FILE__, __LINE__, info->device_name, set, clear); - - if (set & TIOCM_RTS) - info->serial_signals |= SerialSignal_RTS; - if (set & TIOCM_DTR) - info->serial_signals |= SerialSignal_DTR; - if (clear & TIOCM_RTS) - info->serial_signals &= ~SerialSignal_RTS; - if (clear & TIOCM_DTR) - info->serial_signals &= ~SerialSignal_DTR; - - spin_lock_irqsave(&info->lock, flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - - return 0; -} - -/* Set or clear transmit break condition - * - * Arguments: tty pointer to tty instance data - * break_state -1=set break condition, 0=clear - */ -static int mgslpc_break(struct tty_struct *tty, int break_state) -{ - MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_break(%s,%d)\n", - __FILE__, __LINE__, info->device_name, break_state); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break")) - return -EINVAL; - - spin_lock_irqsave(&info->lock, flags); - if (break_state == -1) - set_reg_bits(info, CHA+DAFO, BIT6); - else - clear_reg_bits(info, CHA+DAFO, BIT6); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int mgslpc_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; - struct mgsl_icount cnow; /* kernel counter temps */ - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->lock, flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - -/* Service an IOCTL request - * - * Arguments: - * - * tty pointer to tty instance data - * cmd IOCTL command code - * arg command argument/context - * - * Return Value: 0 if success, otherwise error code - */ -static int mgslpc_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; - void __user *argp = (void __user *)arg; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__, __LINE__, - info->device_name, cmd); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_ioctl")) - return -ENODEV; - - if (cmd != TIOCMIWAIT) { - if (tty_io_error(tty)) - return -EIO; - } - - switch (cmd) { - case MGSL_IOCGPARAMS: - return get_params(info, argp); - case MGSL_IOCSPARAMS: - return set_params(info, argp, tty); - case MGSL_IOCGTXIDLE: - return get_txidle(info, argp); - case MGSL_IOCSTXIDLE: - return set_txidle(info, (int)arg); - case MGSL_IOCGIF: - return get_interface(info, argp); - case MGSL_IOCSIF: - return set_interface(info,(int)arg); - case MGSL_IOCTXENABLE: - return set_txenable(info,(int)arg, tty); - case MGSL_IOCRXENABLE: - return set_rxenable(info,(int)arg); - case MGSL_IOCTXABORT: - return tx_abort(info); - case MGSL_IOCGSTATS: - return get_stats(info, argp); - case MGSL_IOCWAITEVENT: - return wait_events(info, argp); - case TIOCMIWAIT: - return modem_input_wait(info,(int)arg); - default: - return -ENOIOCTLCMD; - } - return 0; -} - -/* Set new termios settings - * - * Arguments: - * - * tty pointer to tty structure - * termios pointer to buffer to hold returned old termios - */ -static void mgslpc_set_termios(struct tty_struct *tty, - const struct ktermios *old_termios) -{ - MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_set_termios %s\n", __FILE__, __LINE__, - tty->driver->name); - - /* just return if nothing has changed */ - if ((tty->termios.c_cflag == old_termios->c_cflag) - && (RELEVANT_IFLAG(tty->termios.c_iflag) - == RELEVANT_IFLAG(old_termios->c_iflag))) - return; - - mgslpc_change_params(info, tty); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { - info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - spin_lock_irqsave(&info->lock, flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { - info->serial_signals |= SerialSignal_DTR; - if (!C_CRTSCTS(tty) || !tty_throttled(tty)) - info->serial_signals |= SerialSignal_RTS; - spin_lock_irqsave(&info->lock, flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - } - - /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && !C_CRTSCTS(tty)) { - tty->hw_stopped = 0; - tx_release(tty); - } -} - -static void mgslpc_close(struct tty_struct *tty, struct file * filp) -{ - MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; - struct tty_port *port = &info->port; - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_close")) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_close(%s) entry, count=%d\n", - __FILE__, __LINE__, info->device_name, port->count); - - if (tty_port_close_start(port, tty, filp) == 0) - goto cleanup; - - if (tty_port_initialized(port)) - mgslpc_wait_until_sent(tty, info->timeout); - - mgslpc_flush_buffer(tty); - - tty_ldisc_flush(tty); - shutdown(info, tty); - - tty_port_close_end(port, tty); - tty_port_tty_set(port, NULL); -cleanup: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_close(%s) exit, count=%d\n", __FILE__, __LINE__, - tty->driver->name, port->count); -} - -/* Wait until the transmitter is empty. - */ -static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout) -{ - MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; - unsigned long orig_jiffies, char_time; - - if (!info) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_wait_until_sent(%s) entry\n", - __FILE__, __LINE__, info->device_name); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_wait_until_sent")) - return; - - if (!tty_port_initialized(&info->port)) - goto exit; - - orig_jiffies = jiffies; - - /* Set check interval to 1/5 of estimated time to - * send a character, and make it at least 1. The check - * interval should also be less than the timeout. - * Note: use tight timings here to satisfy the NIST-PCTS. - */ - - if (info->params.data_rate) { - char_time = info->timeout/(32 * 5); - if (!char_time) - char_time++; - } else - char_time = 1; - - if (timeout) - char_time = min_t(unsigned long, char_time, timeout); - - if (info->params.mode == MGSL_MODE_HDLC) { - while (info->tx_active) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - } else { - while ((info->tx_count || info->tx_active) && - info->tx_enabled) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - } - -exit: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_wait_until_sent(%s) exit\n", - __FILE__, __LINE__, info->device_name); -} - -/* Called by tty_hangup() when a hangup is signaled. - * This is the same as closing all open files for the port. - */ -static void mgslpc_hangup(struct tty_struct *tty) -{ - MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_hangup(%s)\n", - __FILE__, __LINE__, info->device_name); - - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_hangup")) - return; - - mgslpc_flush_buffer(tty); - shutdown(info, tty); - tty_port_hangup(&info->port); -} - -static bool carrier_raised(struct tty_port *port) -{ - MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port); - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - - return info->serial_signals & SerialSignal_DCD; -} - -static void dtr_rts(struct tty_port *port, bool active) -{ - MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port); - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - if (active) - info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; - else - info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_signals(info); - spin_unlock_irqrestore(&info->lock, flags); -} - - -static int mgslpc_open(struct tty_struct *tty, struct file * filp) -{ - MGSLPC_INFO *info; - struct tty_port *port; - int retval, line; - unsigned long flags; - - /* verify range of specified line number */ - line = tty->index; - if (line >= mgslpc_device_count) { - printk("%s(%d):mgslpc_open with invalid line #%d.\n", - __FILE__, __LINE__, line); - return -ENODEV; - } - - /* find the info structure for the specified line */ - info = mgslpc_device_list; - while(info && info->line != line) - info = info->next_device; - if (mgslpc_paranoia_check(info, tty->name, "mgslpc_open")) - return -ENODEV; - - port = &info->port; - tty->driver_data = info; - tty_port_tty_set(port, tty); - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_open(%s), old ref count = %d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - spin_lock_irqsave(&info->netlock, flags); - if (info->netcount) { - retval = -EBUSY; - spin_unlock_irqrestore(&info->netlock, flags); - goto cleanup; - } - spin_lock(&port->lock); - port->count++; - spin_unlock(&port->lock); - spin_unlock_irqrestore(&info->netlock, flags); - - if (port->count == 1) { - /* 1st open on this device, init hardware */ - retval = startup(info, tty); - if (retval < 0) - goto cleanup; - } - - retval = tty_port_block_til_ready(&info->port, tty, filp); - if (retval) { - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):block_til_ready(%s) returned %d\n", - __FILE__, __LINE__, info->device_name, retval); - goto cleanup; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgslpc_open(%s) success\n", - __FILE__, __LINE__, info->device_name); - retval = 0; - -cleanup: - return retval; -} - -/* - * /proc fs routines.... - */ - -static inline void line_info(struct seq_file *m, MGSLPC_INFO *info) -{ - char stat_buf[30]; - unsigned long flags; - - seq_printf(m, "%s:io:%04X irq:%d", - info->device_name, info->io_base, info->irq_level); - - /* output current serial signal states */ - spin_lock_irqsave(&info->lock, flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (info->serial_signals & SerialSignal_RTS) - strcat(stat_buf, "|RTS"); - if (info->serial_signals & SerialSignal_CTS) - strcat(stat_buf, "|CTS"); - if (info->serial_signals & SerialSignal_DTR) - strcat(stat_buf, "|DTR"); - if (info->serial_signals & SerialSignal_DSR) - strcat(stat_buf, "|DSR"); - if (info->serial_signals & SerialSignal_DCD) - strcat(stat_buf, "|CD"); - if (info->serial_signals & SerialSignal_RI) - strcat(stat_buf, "|RI"); - - if (info->params.mode == MGSL_MODE_HDLC) { - seq_printf(m, " HDLC txok:%d rxok:%d", - info->icount.txok, info->icount.rxok); - if (info->icount.txunder) - seq_printf(m, " txunder:%d", info->icount.txunder); - if (info->icount.txabort) - seq_printf(m, " txabort:%d", info->icount.txabort); - if (info->icount.rxshort) - seq_printf(m, " rxshort:%d", info->icount.rxshort); - if (info->icount.rxlong) - seq_printf(m, " rxlong:%d", info->icount.rxlong); - if (info->icount.rxover) - seq_printf(m, " rxover:%d", info->icount.rxover); - if (info->icount.rxcrc) - seq_printf(m, " rxcrc:%d", info->icount.rxcrc); - } else { - seq_printf(m, " ASYNC tx:%d rx:%d", - info->icount.tx, info->icount.rx); - if (info->icount.frame) - seq_printf(m, " fe:%d", info->icount.frame); - if (info->icount.parity) - seq_printf(m, " pe:%d", info->icount.parity); - if (info->icount.brk) - seq_printf(m, " brk:%d", info->icount.brk); - if (info->icount.overrun) - seq_printf(m, " oe:%d", info->icount.overrun); - } - - /* Append serial signal status to end */ - seq_printf(m, " %s\n", stat_buf+1); - - seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", - info->tx_active,info->bh_requested,info->bh_running, - info->pending_bh); -} - -/* Called to print information about devices - */ -static int mgslpc_proc_show(struct seq_file *m, void *v) -{ - MGSLPC_INFO *info; - - seq_printf(m, "synclink driver:%s\n", driver_version); - - info = mgslpc_device_list; - while (info) { - line_info(m, info); - info = info->next_device; - } - return 0; -} - -static int rx_alloc_buffers(MGSLPC_INFO *info) -{ - /* each buffer has header and data */ - info->rx_buf_size = sizeof(RXBUF) + info->max_frame_size; - - /* calculate total allocation size for 8 buffers */ - info->rx_buf_total_size = info->rx_buf_size * 8; - - /* limit total allocated memory */ - if (info->rx_buf_total_size > 0x10000) - info->rx_buf_total_size = 0x10000; - - /* calculate number of buffers */ - info->rx_buf_count = info->rx_buf_total_size / info->rx_buf_size; - - info->rx_buf = kmalloc(info->rx_buf_total_size, GFP_KERNEL); - if (info->rx_buf == NULL) - return -ENOMEM; - - /* unused flag buffer to satisfy receive_buf calling interface */ - info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL); - if (!info->flag_buf) { - kfree(info->rx_buf); - info->rx_buf = NULL; - return -ENOMEM; - } - - rx_reset_buffers(info); - return 0; -} - -static void rx_free_buffers(MGSLPC_INFO *info) -{ - kfree(info->rx_buf); - info->rx_buf = NULL; - kfree(info->flag_buf); - info->flag_buf = NULL; -} - -static int claim_resources(MGSLPC_INFO *info) -{ - if (rx_alloc_buffers(info) < 0) { - printk("Can't allocate rx buffer %s\n", info->device_name); - release_resources(info); - return -ENODEV; - } - return 0; -} - -static void release_resources(MGSLPC_INFO *info) -{ - if (debug_level >= DEBUG_LEVEL_INFO) - printk("release_resources(%s)\n", info->device_name); - rx_free_buffers(info); -} - -/* Add the specified device instance data structure to the - * global linked list of devices and increment the device count. - * - * Arguments: info pointer to device instance data - */ -static int mgslpc_add_device(MGSLPC_INFO *info) -{ - MGSLPC_INFO *current_dev = NULL; - struct device *tty_dev; - int ret; - - info->next_device = NULL; - info->line = mgslpc_device_count; - sprintf(info->device_name,"ttySLP%d",info->line); - - if (info->line < MAX_DEVICE_COUNT) { - if (maxframe[info->line]) - info->max_frame_size = maxframe[info->line]; - } - - mgslpc_device_count++; - - if (!mgslpc_device_list) - mgslpc_device_list = info; - else { - current_dev = mgslpc_device_list; - while (current_dev->next_device) - current_dev = current_dev->next_device; - current_dev->next_device = info; - } - - if (info->max_frame_size < 4096) - info->max_frame_size = 4096; - else if (info->max_frame_size > 65535) - info->max_frame_size = 65535; - - printk("SyncLink PC Card %s:IO=%04X IRQ=%d\n", - info->device_name, info->io_base, info->irq_level); - -#if SYNCLINK_GENERIC_HDLC - ret = hdlcdev_init(info); - if (ret != 0) - goto failed; -#endif - - tty_dev = tty_port_register_device(&info->port, serial_driver, info->line, - &info->p_dev->dev); - if (IS_ERR(tty_dev)) { - ret = PTR_ERR(tty_dev); -#if SYNCLINK_GENERIC_HDLC - hdlcdev_exit(info); -#endif - goto failed; - } - - return 0; - -failed: - if (current_dev) - current_dev->next_device = NULL; - else - mgslpc_device_list = NULL; - mgslpc_device_count--; - return ret; -} - -static void mgslpc_remove_device(MGSLPC_INFO *remove_info) -{ - MGSLPC_INFO *info = mgslpc_device_list; - MGSLPC_INFO *last = NULL; - - while(info) { - if (info == remove_info) { - if (last) - last->next_device = info->next_device; - else - mgslpc_device_list = info->next_device; - tty_unregister_device(serial_driver, info->line); -#if SYNCLINK_GENERIC_HDLC - hdlcdev_exit(info); -#endif - release_resources(info); - tty_port_destroy(&info->port); - kfree(info); - mgslpc_device_count--; - return; - } - last = info; - info = info->next_device; - } -} - -static const struct pcmcia_device_id mgslpc_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x02c5, 0x0050), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, mgslpc_ids); - -static struct pcmcia_driver mgslpc_driver = { - .owner = THIS_MODULE, - .name = "synclink_cs", - .probe = mgslpc_probe, - .remove = mgslpc_detach, - .id_table = mgslpc_ids, - .suspend = mgslpc_suspend, - .resume = mgslpc_resume, -}; - -static const struct tty_operations mgslpc_ops = { - .open = mgslpc_open, - .close = mgslpc_close, - .write = mgslpc_write, - .put_char = mgslpc_put_char, - .flush_chars = mgslpc_flush_chars, - .write_room = mgslpc_write_room, - .chars_in_buffer = mgslpc_chars_in_buffer, - .flush_buffer = mgslpc_flush_buffer, - .ioctl = mgslpc_ioctl, - .throttle = mgslpc_throttle, - .unthrottle = mgslpc_unthrottle, - .send_xchar = mgslpc_send_xchar, - .break_ctl = mgslpc_break, - .wait_until_sent = mgslpc_wait_until_sent, - .set_termios = mgslpc_set_termios, - .stop = tx_pause, - .start = tx_release, - .hangup = mgslpc_hangup, - .tiocmget = tiocmget, - .tiocmset = tiocmset, - .get_icount = mgslpc_get_icount, - .proc_show = mgslpc_proc_show, -}; - -static int __init synclink_cs_init(void) -{ - int rc; - - if (break_on_load) { - mgslpc_get_text_ptr(); - BREAKPOINT(); - } - - serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(serial_driver)) { - rc = PTR_ERR(serial_driver); - goto err; - } - - /* Initialize the tty_driver structure */ - serial_driver->driver_name = "synclink_cs"; - serial_driver->name = "ttySLP"; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty_set_operations(serial_driver, &mgslpc_ops); - - rc = tty_register_driver(serial_driver); - if (rc < 0) { - printk(KERN_ERR "%s(%d):Couldn't register serial driver\n", - __FILE__, __LINE__); - goto err_put_tty; - } - - rc = pcmcia_register_driver(&mgslpc_driver); - if (rc < 0) - goto err_unreg_tty; - - printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version, - serial_driver->major); - - return 0; -err_unreg_tty: - tty_unregister_driver(serial_driver); -err_put_tty: - tty_driver_kref_put(serial_driver); -err: - return rc; -} - -static void __exit synclink_cs_exit(void) -{ - pcmcia_unregister_driver(&mgslpc_driver); - tty_unregister_driver(serial_driver); - tty_driver_kref_put(serial_driver); -} - -module_init(synclink_cs_init); -module_exit(synclink_cs_exit); - -static void mgslpc_set_rate(MGSLPC_INFO *info, unsigned char channel, unsigned int rate) -{ - unsigned int M, N; - unsigned char val; - - /* note:standard BRG mode is broken in V3.2 chip - * so enhanced mode is always used - */ - - if (rate) { - N = 3686400 / rate; - if (!N) - N = 1; - N >>= 1; - for (M = 1; N > 64 && M < 16; M++) - N >>= 1; - N--; - - /* BGR[5..0] = N - * BGR[9..6] = M - * BGR[7..0] contained in BGR register - * BGR[9..8] contained in CCR2[7..6] - * divisor = (N+1)*2^M - * - * Note: M *must* not be zero (causes asymetric duty cycle) - */ - write_reg(info, (unsigned char) (channel + BGR), - (unsigned char) ((M << 6) + N)); - val = read_reg(info, (unsigned char) (channel + CCR2)) & 0x3f; - val |= ((M << 4) & 0xc0); - write_reg(info, (unsigned char) (channel + CCR2), val); - } -} - -/* Enabled the AUX clock output at the specified frequency. - */ -static void enable_auxclk(MGSLPC_INFO *info) -{ - unsigned char val; - - /* MODE - * - * 07..06 MDS[1..0] 10 = transparent HDLC mode - * 05 ADM Address Mode, 0 = no addr recognition - * 04 TMD Timer Mode, 0 = external - * 03 RAC Receiver Active, 0 = inactive - * 02 RTS 0=RTS active during xmit, 1=RTS always active - * 01 TRS Timer Resolution, 1=512 - * 00 TLP Test Loop, 0 = no loop - * - * 1000 0010 - */ - val = 0x82; - - /* channel B RTS is used to enable AUXCLK driver on SP505 */ - if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed) - val |= BIT2; - write_reg(info, CHB + MODE, val); - - /* CCR0 - * - * 07 PU Power Up, 1=active, 0=power down - * 06 MCE Master Clock Enable, 1=enabled - * 05 Reserved, 0 - * 04..02 SC[2..0] Encoding - * 01..00 SM[1..0] Serial Mode, 00=HDLC - * - * 11000000 - */ - write_reg(info, CHB + CCR0, 0xc0); - - /* CCR1 - * - * 07 SFLG Shared Flag, 0 = disable shared flags - * 06 GALP Go Active On Loop, 0 = not used - * 05 GLP Go On Loop, 0 = not used - * 04 ODS Output Driver Select, 1=TxD is push-pull output - * 03 ITF Interframe Time Fill, 0=mark, 1=flag - * 02..00 CM[2..0] Clock Mode - * - * 0001 0111 - */ - write_reg(info, CHB + CCR1, 0x17); - - /* CCR2 (Channel B) - * - * 07..06 BGR[9..8] Baud rate bits 9..8 - * 05 BDF Baud rate divisor factor, 0=1, 1=BGR value - * 04 SSEL Clock source select, 1=submode b - * 03 TOE 0=TxCLK is input, 1=TxCLK is output - * 02 RWX Read/Write Exchange 0=disabled - * 01 C32, CRC select, 0=CRC-16, 1=CRC-32 - * 00 DIV, data inversion 0=disabled, 1=enabled - * - * 0011 1000 - */ - if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed) - write_reg(info, CHB + CCR2, 0x38); - else - write_reg(info, CHB + CCR2, 0x30); - - /* CCR4 - * - * 07 MCK4 Master Clock Divide by 4, 1=enabled - * 06 EBRG Enhanced Baud Rate Generator Mode, 1=enabled - * 05 TST1 Test Pin, 0=normal operation - * 04 ICD Ivert Carrier Detect, 1=enabled (active low) - * 03..02 Reserved, must be 0 - * 01..00 RFT[1..0] RxFIFO Threshold 00=32 bytes - * - * 0101 0000 - */ - write_reg(info, CHB + CCR4, 0x50); - - /* if auxclk not enabled, set internal BRG so - * CTS transitions can be detected (requires TxC) - */ - if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed) - mgslpc_set_rate(info, CHB, info->params.clock_speed); - else - mgslpc_set_rate(info, CHB, 921600); -} - -static void loopback_enable(MGSLPC_INFO *info) -{ - unsigned char val; - - /* CCR1:02..00 CM[2..0] Clock Mode = 111 (clock mode 7) */ - val = read_reg(info, CHA + CCR1) | (BIT2 | BIT1 | BIT0); - write_reg(info, CHA + CCR1, val); - - /* CCR2:04 SSEL Clock source select, 1=submode b */ - val = read_reg(info, CHA + CCR2) | (BIT4 | BIT5); - write_reg(info, CHA + CCR2, val); - - /* set LinkSpeed if available, otherwise default to 2Mbps */ - if (info->params.clock_speed) - mgslpc_set_rate(info, CHA, info->params.clock_speed); - else - mgslpc_set_rate(info, CHA, 1843200); - - /* MODE:00 TLP Test Loop, 1=loopback enabled */ - val = read_reg(info, CHA + MODE) | BIT0; - write_reg(info, CHA + MODE, val); -} - -static void hdlc_mode(MGSLPC_INFO *info) -{ - unsigned char val; - unsigned char clkmode, clksubmode; - - /* disable all interrupts */ - irq_disable(info, CHA, 0xffff); - irq_disable(info, CHB, 0xffff); - port_irq_disable(info, 0xff); - - /* assume clock mode 0a, rcv=RxC xmt=TxC */ - clkmode = clksubmode = 0; - if (info->params.flags & HDLC_FLAG_RXC_DPLL - && info->params.flags & HDLC_FLAG_TXC_DPLL) { - /* clock mode 7a, rcv = DPLL, xmt = DPLL */ - clkmode = 7; - } else if (info->params.flags & HDLC_FLAG_RXC_BRG - && info->params.flags & HDLC_FLAG_TXC_BRG) { - /* clock mode 7b, rcv = BRG, xmt = BRG */ - clkmode = 7; - clksubmode = 1; - } else if (info->params.flags & HDLC_FLAG_RXC_DPLL) { - if (info->params.flags & HDLC_FLAG_TXC_BRG) { - /* clock mode 6b, rcv = DPLL, xmt = BRG/16 */ - clkmode = 6; - clksubmode = 1; - } else { - /* clock mode 6a, rcv = DPLL, xmt = TxC */ - clkmode = 6; - } - } else if (info->params.flags & HDLC_FLAG_TXC_BRG) { - /* clock mode 0b, rcv = RxC, xmt = BRG */ - clksubmode = 1; - } - - /* MODE - * - * 07..06 MDS[1..0] 10 = transparent HDLC mode - * 05 ADM Address Mode, 0 = no addr recognition - * 04 TMD Timer Mode, 0 = external - * 03 RAC Receiver Active, 0 = inactive - * 02 RTS 0=RTS active during xmit, 1=RTS always active - * 01 TRS Timer Resolution, 1=512 - * 00 TLP Test Loop, 0 = no loop - * - * 1000 0010 - */ - val = 0x82; - if (info->params.loopback) - val |= BIT0; - - /* preserve RTS state */ - if (info->serial_signals & SerialSignal_RTS) - val |= BIT2; - write_reg(info, CHA + MODE, val); - - /* CCR0 - * - * 07 PU Power Up, 1=active, 0=power down - * 06 MCE Master Clock Enable, 1=enabled - * 05 Reserved, 0 - * 04..02 SC[2..0] Encoding - * 01..00 SM[1..0] Serial Mode, 00=HDLC - * - * 11000000 - */ - val = 0xc0; - switch (info->params.encoding) - { - case HDLC_ENCODING_NRZI: - val |= BIT3; - break; - case HDLC_ENCODING_BIPHASE_SPACE: - val |= BIT4; - break; // FM0 - case HDLC_ENCODING_BIPHASE_MARK: - val |= BIT4 | BIT2; - break; // FM1 - case HDLC_ENCODING_BIPHASE_LEVEL: - val |= BIT4 | BIT3; - break; // Manchester - } - write_reg(info, CHA + CCR0, val); - - /* CCR1 - * - * 07 SFLG Shared Flag, 0 = disable shared flags - * 06 GALP Go Active On Loop, 0 = not used - * 05 GLP Go On Loop, 0 = not used - * 04 ODS Output Driver Select, 1=TxD is push-pull output - * 03 ITF Interframe Time Fill, 0=mark, 1=flag - * 02..00 CM[2..0] Clock Mode - * - * 0001 0000 - */ - val = 0x10 + clkmode; - write_reg(info, CHA + CCR1, val); - - /* CCR2 - * - * 07..06 BGR[9..8] Baud rate bits 9..8 - * 05 BDF Baud rate divisor factor, 0=1, 1=BGR value - * 04 SSEL Clock source select, 1=submode b - * 03 TOE 0=TxCLK is input, 0=TxCLK is input - * 02 RWX Read/Write Exchange 0=disabled - * 01 C32, CRC select, 0=CRC-16, 1=CRC-32 - * 00 DIV, data inversion 0=disabled, 1=enabled - * - * 0000 0000 - */ - val = 0x00; - if (clkmode == 2 || clkmode == 3 || clkmode == 6 - || clkmode == 7 || (clkmode == 0 && clksubmode == 1)) - val |= BIT5; - if (clksubmode) - val |= BIT4; - if (info->params.crc_type == HDLC_CRC_32_CCITT) - val |= BIT1; - if (info->params.encoding == HDLC_ENCODING_NRZB) - val |= BIT0; - write_reg(info, CHA + CCR2, val); - - /* CCR3 - * - * 07..06 PRE[1..0] Preamble count 00=1, 01=2, 10=4, 11=8 - * 05 EPT Enable preamble transmission, 1=enabled - * 04 RADD Receive address pushed to FIFO, 0=disabled - * 03 CRL CRC Reset Level, 0=FFFF - * 02 RCRC Rx CRC 0=On 1=Off - * 01 TCRC Tx CRC 0=On 1=Off - * 00 PSD DPLL Phase Shift Disable - * - * 0000 0000 - */ - val = 0x00; - if (info->params.crc_type == HDLC_CRC_NONE) - val |= BIT2 | BIT1; - if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) - val |= BIT5; - switch (info->params.preamble_length) - { - case HDLC_PREAMBLE_LENGTH_16BITS: - val |= BIT6; - break; - case HDLC_PREAMBLE_LENGTH_32BITS: - val |= BIT6; - break; - case HDLC_PREAMBLE_LENGTH_64BITS: - val |= BIT7 | BIT6; - break; - } - write_reg(info, CHA + CCR3, val); - - /* PRE - Preamble pattern */ - val = 0; - switch (info->params.preamble) - { - case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break; - case HDLC_PREAMBLE_PATTERN_10: val = 0xaa; break; - case HDLC_PREAMBLE_PATTERN_01: val = 0x55; break; - case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break; - } - write_reg(info, CHA + PRE, val); - - /* CCR4 - * - * 07 MCK4 Master Clock Divide by 4, 1=enabled - * 06 EBRG Enhanced Baud Rate Generator Mode, 1=enabled - * 05 TST1 Test Pin, 0=normal operation - * 04 ICD Ivert Carrier Detect, 1=enabled (active low) - * 03..02 Reserved, must be 0 - * 01..00 RFT[1..0] RxFIFO Threshold 00=32 bytes - * - * 0101 0000 - */ - val = 0x50; - write_reg(info, CHA + CCR4, val); - if (info->params.flags & HDLC_FLAG_RXC_DPLL) - mgslpc_set_rate(info, CHA, info->params.clock_speed * 16); - else - mgslpc_set_rate(info, CHA, info->params.clock_speed); - - /* RLCR Receive length check register - * - * 7 1=enable receive length check - * 6..0 Max frame length = (RL + 1) * 32 - */ - write_reg(info, CHA + RLCR, 0); - - /* XBCH Transmit Byte Count High - * - * 07 DMA mode, 0 = interrupt driven - * 06 NRM, 0=ABM (ignored) - * 05 CAS Carrier Auto Start - * 04 XC Transmit Continuously (ignored) - * 03..00 XBC[10..8] Transmit byte count bits 10..8 - * - * 0000 0000 - */ - val = 0x00; - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - val |= BIT5; - write_reg(info, CHA + XBCH, val); - enable_auxclk(info); - if (info->params.loopback || info->testing_irq) - loopback_enable(info); - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - { - irq_enable(info, CHB, IRQ_CTS); - /* PVR[3] 1=AUTO CTS active */ - set_reg_bits(info, CHA + PVR, BIT3); - } else - clear_reg_bits(info, CHA + PVR, BIT3); - - irq_enable(info, CHA, - IRQ_RXEOM | IRQ_RXFIFO | IRQ_ALLSENT | - IRQ_UNDERRUN | IRQ_TXFIFO); - issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET); - wait_command_complete(info, CHA); - read_reg16(info, CHA + ISR); /* clear pending IRQs */ - - /* Master clock mode enabled above to allow reset commands - * to complete even if no data clocks are present. - * - * Disable master clock mode for normal communications because - * V3.2 of the ESCC2 has a bug that prevents the transmit all sent - * IRQ when in master clock mode. - * - * Leave master clock mode enabled for IRQ test because the - * timer IRQ used by the test can only happen in master clock mode. - */ - if (!info->testing_irq) - clear_reg_bits(info, CHA + CCR0, BIT6); - - tx_set_idle(info); - - tx_stop(info); - rx_stop(info); -} - -static void rx_stop(MGSLPC_INFO *info) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):rx_stop(%s)\n", - __FILE__, __LINE__, info->device_name); - - /* MODE:03 RAC Receiver Active, 0=inactive */ - clear_reg_bits(info, CHA + MODE, BIT3); - - info->rx_enabled = false; - info->rx_overflow = false; -} - -static void rx_start(MGSLPC_INFO *info) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):rx_start(%s)\n", - __FILE__, __LINE__, info->device_name); - - rx_reset_buffers(info); - info->rx_enabled = false; - info->rx_overflow = false; - - /* MODE:03 RAC Receiver Active, 1=active */ - set_reg_bits(info, CHA + MODE, BIT3); - - info->rx_enabled = true; -} - -static void tx_start(MGSLPC_INFO *info, struct tty_struct *tty) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):tx_start(%s)\n", - __FILE__, __LINE__, info->device_name); - - if (info->tx_count) { - /* If auto RTS enabled and RTS is inactive, then assert */ - /* RTS and set a flag indicating that the driver should */ - /* negate RTS when the transmission completes. */ - info->drop_rts_on_tx_done = false; - - if (info->params.flags & HDLC_FLAG_AUTO_RTS) { - get_signals(info); - if (!(info->serial_signals & SerialSignal_RTS)) { - info->serial_signals |= SerialSignal_RTS; - set_signals(info); - info->drop_rts_on_tx_done = true; - } - } - - if (info->params.mode == MGSL_MODE_ASYNC) { - if (!info->tx_active) { - info->tx_active = true; - tx_ready(info, tty); - } - } else { - info->tx_active = true; - tx_ready(info, tty); - mod_timer(&info->tx_timer, jiffies + - msecs_to_jiffies(5000)); - } - } - - if (!info->tx_enabled) - info->tx_enabled = true; -} - -static void tx_stop(MGSLPC_INFO *info) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):tx_stop(%s)\n", - __FILE__, __LINE__, info->device_name); - - del_timer(&info->tx_timer); - - info->tx_enabled = false; - info->tx_active = false; -} - -/* Reset the adapter to a known state and prepare it for further use. - */ -static void reset_device(MGSLPC_INFO *info) -{ - /* power up both channels (set BIT7) */ - write_reg(info, CHA + CCR0, 0x80); - write_reg(info, CHB + CCR0, 0x80); - write_reg(info, CHA + MODE, 0); - write_reg(info, CHB + MODE, 0); - - /* disable all interrupts */ - irq_disable(info, CHA, 0xffff); - irq_disable(info, CHB, 0xffff); - port_irq_disable(info, 0xff); - - /* PCR Port Configuration Register - * - * 07..04 DEC[3..0] Serial I/F select outputs - * 03 output, 1=AUTO CTS control enabled - * 02 RI Ring Indicator input 0=active - * 01 DSR input 0=active - * 00 DTR output 0=active - * - * 0000 0110 - */ - write_reg(info, PCR, 0x06); - - /* PVR Port Value Register - * - * 07..04 DEC[3..0] Serial I/F select (0000=disabled) - * 03 AUTO CTS output 1=enabled - * 02 RI Ring Indicator input - * 01 DSR input - * 00 DTR output (1=inactive) - * - * 0000 0001 - */ -// write_reg(info, PVR, PVR_DTR); - - /* IPC Interrupt Port Configuration - * - * 07 VIS 1=Masked interrupts visible - * 06..05 Reserved, 0 - * 04..03 SLA Slave address, 00 ignored - * 02 CASM Cascading Mode, 1=daisy chain - * 01..00 IC[1..0] Interrupt Config, 01=push-pull output, active low - * - * 0000 0101 - */ - write_reg(info, IPC, 0x05); -} - -static void async_mode(MGSLPC_INFO *info) -{ - unsigned char val; - - /* disable all interrupts */ - irq_disable(info, CHA, 0xffff); - irq_disable(info, CHB, 0xffff); - port_irq_disable(info, 0xff); - - /* MODE - * - * 07 Reserved, 0 - * 06 FRTS RTS State, 0=active - * 05 FCTS Flow Control on CTS - * 04 FLON Flow Control Enable - * 03 RAC Receiver Active, 0 = inactive - * 02 RTS 0=Auto RTS, 1=manual RTS - * 01 TRS Timer Resolution, 1=512 - * 00 TLP Test Loop, 0 = no loop - * - * 0000 0110 - */ - val = 0x06; - if (info->params.loopback) - val |= BIT0; - - /* preserve RTS state */ - if (!(info->serial_signals & SerialSignal_RTS)) - val |= BIT6; - write_reg(info, CHA + MODE, val); - - /* CCR0 - * - * 07 PU Power Up, 1=active, 0=power down - * 06 MCE Master Clock Enable, 1=enabled - * 05 Reserved, 0 - * 04..02 SC[2..0] Encoding, 000=NRZ - * 01..00 SM[1..0] Serial Mode, 11=Async - * - * 1000 0011 - */ - write_reg(info, CHA + CCR0, 0x83); - - /* CCR1 - * - * 07..05 Reserved, 0 - * 04 ODS Output Driver Select, 1=TxD is push-pull output - * 03 BCR Bit Clock Rate, 1=16x - * 02..00 CM[2..0] Clock Mode, 111=BRG - * - * 0001 1111 - */ - write_reg(info, CHA + CCR1, 0x1f); - - /* CCR2 (channel A) - * - * 07..06 BGR[9..8] Baud rate bits 9..8 - * 05 BDF Baud rate divisor factor, 0=1, 1=BGR value - * 04 SSEL Clock source select, 1=submode b - * 03 TOE 0=TxCLK is input, 0=TxCLK is input - * 02 RWX Read/Write Exchange 0=disabled - * 01 Reserved, 0 - * 00 DIV, data inversion 0=disabled, 1=enabled - * - * 0001 0000 - */ - write_reg(info, CHA + CCR2, 0x10); - - /* CCR3 - * - * 07..01 Reserved, 0 - * 00 PSD DPLL Phase Shift Disable - * - * 0000 0000 - */ - write_reg(info, CHA + CCR3, 0); - - /* CCR4 - * - * 07 MCK4 Master Clock Divide by 4, 1=enabled - * 06 EBRG Enhanced Baud Rate Generator Mode, 1=enabled - * 05 TST1 Test Pin, 0=normal operation - * 04 ICD Ivert Carrier Detect, 1=enabled (active low) - * 03..00 Reserved, must be 0 - * - * 0101 0000 - */ - write_reg(info, CHA + CCR4, 0x50); - mgslpc_set_rate(info, CHA, info->params.data_rate * 16); - - /* DAFO Data Format - * - * 07 Reserved, 0 - * 06 XBRK transmit break, 0=normal operation - * 05 Stop bits (0=1, 1=2) - * 04..03 PAR[1..0] Parity (01=odd, 10=even) - * 02 PAREN Parity Enable - * 01..00 CHL[1..0] Character Length (00=8, 01=7) - * - */ - val = 0x00; - if (info->params.data_bits != 8) - val |= BIT0; /* 7 bits */ - if (info->params.stop_bits != 1) - val |= BIT5; - if (info->params.parity != ASYNC_PARITY_NONE) - { - val |= BIT2; /* Parity enable */ - if (info->params.parity == ASYNC_PARITY_ODD) - val |= BIT3; - else - val |= BIT4; - } - write_reg(info, CHA + DAFO, val); - - /* RFC Rx FIFO Control - * - * 07 Reserved, 0 - * 06 DPS, 1=parity bit not stored in data byte - * 05 DXS, 0=all data stored in FIFO (including XON/XOFF) - * 04 RFDF Rx FIFO Data Format, 1=status byte stored in FIFO - * 03..02 RFTH[1..0], rx threshold, 11=16 status + 16 data byte - * 01 Reserved, 0 - * 00 TCDE Terminate Char Detect Enable, 0=disabled - * - * 0101 1100 - */ - write_reg(info, CHA + RFC, 0x5c); - - /* RLCR Receive length check register - * - * Max frame length = (RL + 1) * 32 - */ - write_reg(info, CHA + RLCR, 0); - - /* XBCH Transmit Byte Count High - * - * 07 DMA mode, 0 = interrupt driven - * 06 NRM, 0=ABM (ignored) - * 05 CAS Carrier Auto Start - * 04 XC Transmit Continuously (ignored) - * 03..00 XBC[10..8] Transmit byte count bits 10..8 - * - * 0000 0000 - */ - val = 0x00; - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - val |= BIT5; - write_reg(info, CHA + XBCH, val); - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - irq_enable(info, CHA, IRQ_CTS); - - /* MODE:03 RAC Receiver Active, 1=active */ - set_reg_bits(info, CHA + MODE, BIT3); - enable_auxclk(info); - if (info->params.flags & HDLC_FLAG_AUTO_CTS) { - irq_enable(info, CHB, IRQ_CTS); - /* PVR[3] 1=AUTO CTS active */ - set_reg_bits(info, CHA + PVR, BIT3); - } else - clear_reg_bits(info, CHA + PVR, BIT3); - irq_enable(info, CHA, - IRQ_RXEOM | IRQ_RXFIFO | IRQ_BREAK_ON | IRQ_RXTIME | - IRQ_ALLSENT | IRQ_TXFIFO); - issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET); - wait_command_complete(info, CHA); - read_reg16(info, CHA + ISR); /* clear pending IRQs */ -} - -/* Set the HDLC idle mode for the transmitter. - */ -static void tx_set_idle(MGSLPC_INFO *info) -{ - /* Note: ESCC2 only supports flags and one idle modes */ - if (info->idle_mode == HDLC_TXIDLE_FLAGS) - set_reg_bits(info, CHA + CCR1, BIT3); - else - clear_reg_bits(info, CHA + CCR1, BIT3); -} - -/* get state of the V24 status (input) signals. - */ -static void get_signals(MGSLPC_INFO *info) -{ - unsigned char status = 0; - - /* preserve RTS and DTR */ - info->serial_signals &= SerialSignal_RTS | SerialSignal_DTR; - - if (read_reg(info, CHB + VSTR) & BIT7) - info->serial_signals |= SerialSignal_DCD; - if (read_reg(info, CHB + STAR) & BIT1) - info->serial_signals |= SerialSignal_CTS; - - status = read_reg(info, CHA + PVR); - if (!(status & PVR_RI)) - info->serial_signals |= SerialSignal_RI; - if (!(status & PVR_DSR)) - info->serial_signals |= SerialSignal_DSR; -} - -/* Set the state of RTS and DTR based on contents of - * serial_signals member of device extension. - */ -static void set_signals(MGSLPC_INFO *info) -{ - unsigned char val; - - val = read_reg(info, CHA + MODE); - if (info->params.mode == MGSL_MODE_ASYNC) { - if (info->serial_signals & SerialSignal_RTS) - val &= ~BIT6; - else - val |= BIT6; - } else { - if (info->serial_signals & SerialSignal_RTS) - val |= BIT2; - else - val &= ~BIT2; - } - write_reg(info, CHA + MODE, val); - - if (info->serial_signals & SerialSignal_DTR) - clear_reg_bits(info, CHA + PVR, PVR_DTR); - else - set_reg_bits(info, CHA + PVR, PVR_DTR); -} - -static void rx_reset_buffers(MGSLPC_INFO *info) -{ - RXBUF *buf; - int i; - - info->rx_put = 0; - info->rx_get = 0; - info->rx_frame_count = 0; - for (i=0 ; i < info->rx_buf_count ; i++) { - buf = (RXBUF*)(info->rx_buf + (i * info->rx_buf_size)); - buf->status = buf->count = 0; - } -} - -/* Attempt to return a received HDLC frame - * Only frames received without errors are returned. - * - * Returns true if frame returned, otherwise false - */ -static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty) -{ - unsigned short status; - RXBUF *buf; - unsigned int framesize = 0; - unsigned long flags; - bool return_frame = false; - - if (info->rx_frame_count == 0) - return false; - - buf = (RXBUF*)(info->rx_buf + (info->rx_get * info->rx_buf_size)); - - status = buf->status; - - /* 07 VFR 1=valid frame - * 06 RDO 1=data overrun - * 05 CRC 1=OK, 0=error - * 04 RAB 1=frame aborted - */ - if ((status & 0xf0) != 0xA0) { - if (!(status & BIT7) || (status & BIT4)) - info->icount.rxabort++; - else if (status & BIT6) - info->icount.rxover++; - else if (!(status & BIT5)) { - info->icount.rxcrc++; - if (info->params.crc_type & HDLC_CRC_RETURN_EX) - return_frame = true; - } - framesize = 0; -#if SYNCLINK_GENERIC_HDLC - { - info->netdev->stats.rx_errors++; - info->netdev->stats.rx_frame_errors++; - } -#endif - } else - return_frame = true; - - if (return_frame) - framesize = buf->count; - - if (debug_level >= DEBUG_LEVEL_BH) - printk("%s(%d):rx_get_frame(%s) status=%04X size=%d\n", - __FILE__, __LINE__, info->device_name, status, framesize); - - if (debug_level >= DEBUG_LEVEL_DATA) - trace_block(info, buf->data, framesize, 0); - - if (framesize) { - if ((info->params.crc_type & HDLC_CRC_RETURN_EX && - framesize+1 > info->max_frame_size) || - framesize > info->max_frame_size) - info->icount.rxlong++; - else { - if (status & BIT5) - info->icount.rxok++; - - if (info->params.crc_type & HDLC_CRC_RETURN_EX) { - *(buf->data + framesize) = status & BIT5 ? RX_OK:RX_CRC_ERROR; - ++framesize; - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_rx(info, buf->data, framesize); - else -#endif - ldisc_receive_buf(tty, buf->data, info->flag_buf, framesize); - } - } - - spin_lock_irqsave(&info->lock, flags); - buf->status = buf->count = 0; - info->rx_frame_count--; - info->rx_get++; - if (info->rx_get >= info->rx_buf_count) - info->rx_get = 0; - spin_unlock_irqrestore(&info->lock, flags); - - return true; -} - -static bool register_test(MGSLPC_INFO *info) -{ - static unsigned char patterns[] = - { 0x00, 0xff, 0xaa, 0x55, 0x69, 0x96, 0x0f }; - static unsigned int count = ARRAY_SIZE(patterns); - unsigned int i; - bool rc = true; - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - reset_device(info); - - for (i = 0; i < count; i++) { - write_reg(info, XAD1, patterns[i]); - write_reg(info, XAD2, patterns[(i + 1) % count]); - if ((read_reg(info, XAD1) != patterns[i]) || - (read_reg(info, XAD2) != patterns[(i + 1) % count])) { - rc = false; - break; - } - } - - spin_unlock_irqrestore(&info->lock, flags); - return rc; -} - -static bool irq_test(MGSLPC_INFO *info) -{ - unsigned long end_time; - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - reset_device(info); - - info->testing_irq = true; - hdlc_mode(info); - - info->irq_occurred = false; - - /* init hdlc mode */ - - irq_enable(info, CHA, IRQ_TIMER); - write_reg(info, CHA + TIMR, 0); /* 512 cycles */ - issue_command(info, CHA, CMD_START_TIMER); - - spin_unlock_irqrestore(&info->lock, flags); - - end_time=100; - while(end_time-- && !info->irq_occurred) { - msleep_interruptible(10); - } - - info->testing_irq = false; - - spin_lock_irqsave(&info->lock, flags); - reset_device(info); - spin_unlock_irqrestore(&info->lock, flags); - - return info->irq_occurred; -} - -static int adapter_test(MGSLPC_INFO *info) -{ - if (!register_test(info)) { - info->init_error = DiagStatus_AddressFailure; - printk("%s(%d):Register test failure for device %s Addr=%04X\n", - __FILE__, __LINE__, info->device_name, (unsigned short)(info->io_base)); - return -ENODEV; - } - - if (!irq_test(info)) { - info->init_error = DiagStatus_IrqFailure; - printk("%s(%d):Interrupt test failure for device %s IRQ=%d\n", - __FILE__, __LINE__, info->device_name, (unsigned short)(info->irq_level)); - return -ENODEV; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):device %s passed diagnostics\n", - __FILE__, __LINE__, info->device_name); - return 0; -} - -static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit) -{ - int i; - int linecount; - if (xmit) - printk("%s tx data:\n", info->device_name); - else - printk("%s rx data:\n", info->device_name); - - while(count) { - if (count > 16) - linecount = 16; - else - linecount = count; - - for(i=0;i=040 && data[i]<=0176) - printk("%c", data[i]); - else - printk("."); - } - printk("\n"); - - data += linecount; - count -= linecount; - } -} - -/* HDLC frame time out - * update stats and do tx completion processing - */ -static void tx_timeout(struct timer_list *t) -{ - MGSLPC_INFO *info = from_timer(info, t, tx_timer); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):tx_timeout(%s)\n", - __FILE__, __LINE__, info->device_name); - if (info->tx_active && - info->params.mode == MGSL_MODE_HDLC) { - info->icount.txtimeout++; - } - spin_lock_irqsave(&info->lock, flags); - info->tx_active = false; - info->tx_count = info->tx_put = info->tx_get = 0; - - spin_unlock_irqrestore(&info->lock, flags); - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - { - struct tty_struct *tty = tty_port_tty_get(&info->port); - bh_transmit(info, tty); - tty_kref_put(tty); - } -} - -#if SYNCLINK_GENERIC_HDLC - -/* - * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) - * set encoding and frame check sequence (FCS) options - * - * dev pointer to network device structure - * encoding serial encoding setting - * parity FCS setting - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, - unsigned short parity) -{ - MGSLPC_INFO *info = dev_to_port(dev); - struct tty_struct *tty; - unsigned char new_encoding; - unsigned short new_crctype; - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - switch (encoding) - { - case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; - case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; - case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; - case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; - case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; - default: return -EINVAL; - } - - switch (parity) - { - case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; - case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; - case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; - default: return -EINVAL; - } - - info->params.encoding = new_encoding; - info->params.crc_type = new_crctype; - - /* if network interface up, reprogram hardware */ - if (info->netcount) { - tty = tty_port_tty_get(&info->port); - mgslpc_program_hw(info, tty); - tty_kref_put(tty); - } - - return 0; -} - -/* - * called by generic HDLC layer to send frame - * - * skb socket buffer containing HDLC frame - * dev pointer to network device structure - */ -static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - MGSLPC_INFO *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk(KERN_INFO "%s:hdlc_xmit(%s)\n", __FILE__, dev->name); - - /* stop sending until this frame completes */ - netif_stop_queue(dev); - - /* copy data to device buffers */ - skb_copy_from_linear_data(skb, info->tx_buf, skb->len); - info->tx_get = 0; - info->tx_put = info->tx_count = skb->len; - - /* update network statistics */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - /* done with socket buffer, so free it */ - dev_kfree_skb(skb); - - /* save start time for transmit timeout detection */ - netif_trans_update(dev); - - /* start hardware transmitter if necessary */ - spin_lock_irqsave(&info->lock, flags); - if (!info->tx_active) { - struct tty_struct *tty = tty_port_tty_get(&info->port); - tx_start(info, tty); - tty_kref_put(tty); - } - spin_unlock_irqrestore(&info->lock, flags); - - return NETDEV_TX_OK; -} - -/* - * called by network layer when interface enabled - * claim resources and initialize hardware - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_open(struct net_device *dev) -{ - MGSLPC_INFO *info = dev_to_port(dev); - struct tty_struct *tty; - int rc; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_open(%s)\n", __FILE__, dev->name); - - /* generic HDLC layer open processing */ - rc = hdlc_open(dev); - if (rc != 0) - return rc; - - /* arbitrate between network and tty opens */ - spin_lock_irqsave(&info->netlock, flags); - if (info->port.count != 0 || info->netcount != 0) { - printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name); - spin_unlock_irqrestore(&info->netlock, flags); - return -EBUSY; - } - info->netcount=1; - spin_unlock_irqrestore(&info->netlock, flags); - - tty = tty_port_tty_get(&info->port); - /* claim resources and init adapter */ - rc = startup(info, tty); - if (rc != 0) { - tty_kref_put(tty); - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return rc; - } - /* assert RTS and DTR, apply hardware settings */ - info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; - mgslpc_program_hw(info, tty); - tty_kref_put(tty); - - /* enable network layer transmit */ - netif_trans_update(dev); - netif_start_queue(dev); - - /* inform generic HDLC layer of current DCD status */ - spin_lock_irqsave(&info->lock, flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - if (info->serial_signals & SerialSignal_DCD) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - return 0; -} - -/* - * called by network layer when interface is disabled - * shutdown hardware and release resources - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_close(struct net_device *dev) -{ - MGSLPC_INFO *info = dev_to_port(dev); - struct tty_struct *tty = tty_port_tty_get(&info->port); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_close(%s)\n", __FILE__, dev->name); - - netif_stop_queue(dev); - - /* shutdown adapter and release resources */ - shutdown(info, tty); - tty_kref_put(tty); - hdlc_close(dev); - - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - - return 0; -} - -/* - * called by network layer to process IOCTL call to network device - * - * dev pointer to network device structure - * ifs pointer to network interface settings structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_wan_ioctl(struct net_device *dev, struct if_settings *ifs) -{ - const size_t size = sizeof(sync_serial_settings); - sync_serial_settings new_line; - sync_serial_settings __user *line = ifs->ifs_ifsu.sync; - MGSLPC_INFO *info = dev_to_port(dev); - unsigned int flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_ioctl(%s)\n", __FILE__, dev->name); - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - memset(&new_line, 0, size); - - switch (ifs->type) { - case IF_GET_IFACE: /* return current sync_serial_settings */ - - ifs->type = IF_IFACE_SYNC_SERIAL; - if (ifs->size < size) { - ifs->size = size; /* data size wanted */ - return -ENOBUFS; - } - - flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - - switch (flags){ - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; - case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; - default: new_line.clock_type = CLOCK_DEFAULT; - } - - new_line.clock_rate = info->params.clock_speed; - new_line.loopback = info->params.loopback ? 1:0; - - if (copy_to_user(line, &new_line, size)) - return -EFAULT; - return 0; - - case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&new_line, line, size)) - return -EFAULT; - - switch (new_line.clock_type) - { - case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; - case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; - case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; - case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; - case CLOCK_DEFAULT: flags = info->params.flags & - (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; - default: return -EINVAL; - } - - if (new_line.loopback != 0 && new_line.loopback != 1) - return -EINVAL; - - info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - info->params.flags |= flags; - - info->params.loopback = new_line.loopback; - - if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) - info->params.clock_speed = new_line.clock_rate; - else - info->params.clock_speed = 0; - - /* if network interface up, reprogram hardware */ - if (info->netcount) { - struct tty_struct *tty = tty_port_tty_get(&info->port); - mgslpc_program_hw(info, tty); - tty_kref_put(tty); - } - return 0; - default: - return hdlc_ioctl(dev, ifs); - } -} - -/* - * called by network layer when transmit timeout is detected - * - * dev pointer to network device structure - */ -static void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - MGSLPC_INFO *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_tx_timeout(%s)\n", dev->name); - - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - - spin_lock_irqsave(&info->lock, flags); - tx_stop(info); - spin_unlock_irqrestore(&info->lock, flags); - - netif_wake_queue(dev); -} - -/* - * called by device driver when transmit completes - * reenable network layer transmit if stopped - * - * info pointer to device instance information - */ -static void hdlcdev_tx_done(MGSLPC_INFO *info) -{ - if (netif_queue_stopped(info->netdev)) - netif_wake_queue(info->netdev); -} - -/* - * called by device driver when frame received - * pass frame to network layer - * - * info pointer to device instance information - * buf pointer to buffer contianing frame data - * size count of data bytes in buf - */ -static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size) -{ - struct sk_buff *skb = dev_alloc_skb(size); - struct net_device *dev = info->netdev; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_rx(%s)\n", dev->name); - - if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - - skb_put_data(skb, buf, size); - - skb->protocol = hdlc_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - - netif_rx(skb); -} - -static const struct net_device_ops hdlcdev_ops = { - .ndo_open = hdlcdev_open, - .ndo_stop = hdlcdev_close, - .ndo_start_xmit = hdlc_start_xmit, - .ndo_siocwandev = hdlcdev_wan_ioctl, - .ndo_tx_timeout = hdlcdev_tx_timeout, -}; - -/* - * called by device driver when adding device instance - * do generic HDLC initialization - * - * info pointer to device instance information - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_init(MGSLPC_INFO *info) -{ - int rc; - struct net_device *dev; - hdlc_device *hdlc; - - /* allocate and initialize network and HDLC layer objects */ - - dev = alloc_hdlcdev(info); - if (dev == NULL) { - printk(KERN_ERR "%s:hdlc device allocation failure\n", __FILE__); - return -ENOMEM; - } - - /* for network layer reporting purposes only */ - dev->base_addr = info->io_base; - dev->irq = info->irq_level; - - /* network layer callbacks and settings */ - dev->netdev_ops = &hdlcdev_ops; - dev->watchdog_timeo = 10 * HZ; - dev->tx_queue_len = 50; - - /* generic HDLC layer callbacks and settings */ - hdlc = dev_to_hdlc(dev); - hdlc->attach = hdlcdev_attach; - hdlc->xmit = hdlcdev_xmit; - - /* register objects with HDLC layer */ - rc = register_hdlc_device(dev); - if (rc) { - printk(KERN_WARNING "%s:unable to register hdlc device\n", __FILE__); - free_netdev(dev); - return rc; - } - - info->netdev = dev; - return 0; -} - -/* - * called by device driver when removing device instance - * do generic HDLC cleanup - * - * info pointer to device instance information - */ -static void hdlcdev_exit(MGSLPC_INFO *info) -{ - unregister_hdlc_device(info->netdev); - free_netdev(info->netdev); - info->netdev = NULL; -} - -#endif /* CONFIG_HDLC */ - diff --git a/include/linux/cm4000_cs.h b/include/linux/cm4000_cs.h deleted file mode 100644 index ea4958e07a14..000000000000 --- a/include/linux/cm4000_cs.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _CM4000_H_ -#define _CM4000_H_ - -#include - - -#define DEVICE_NAME "cmm" -#define MODULE_NAME "cm4000_cs" - -#endif /* _CM4000_H_ */ diff --git a/include/uapi/linux/cm4000_cs.h b/include/uapi/linux/cm4000_cs.h deleted file mode 100644 index c70a62ec8a49..000000000000 --- a/include/uapi/linux/cm4000_cs.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef _UAPI_CM4000_H_ -#define _UAPI_CM4000_H_ - -#include -#include - -#define MAX_ATR 33 - -#define CM4000_MAX_DEV 4 - -/* those two structures are passed via ioctl() from/to userspace. They are - * used by existing userspace programs, so I kepth the awkward "bIFSD" naming - * not to break compilation of userspace apps. -HW */ - -typedef struct atreq { - __s32 atr_len; - unsigned char atr[64]; - __s32 power_act; - unsigned char bIFSD; - unsigned char bIFSC; -} atreq_t; - - -/* what is particularly stupid in the original driver is the arch-dependent - * member sizes. This leads to CONFIG_COMPAT breakage, since 32bit userspace - * will lay out the structure members differently than the 64bit kernel. - * - * I've changed "ptsreq.protocol" from "unsigned long" to "__u32". - * On 32bit this will make no difference. With 64bit kernels, it will make - * 32bit apps work, too. - */ - -typedef struct ptsreq { - __u32 protocol; /*T=0: 2^0, T=1: 2^1*/ - unsigned char flags; - unsigned char pts1; - unsigned char pts2; - unsigned char pts3; -} ptsreq_t; - -#define CM_IOC_MAGIC 'c' -#define CM_IOC_MAXNR 255 - -#define CM_IOCGSTATUS _IOR (CM_IOC_MAGIC, 0, unsigned char *) -#define CM_IOCGATR _IOWR(CM_IOC_MAGIC, 1, atreq_t *) -#define CM_IOCSPTS _IOW (CM_IOC_MAGIC, 2, ptsreq_t *) -#define CM_IOCSRDR _IO (CM_IOC_MAGIC, 3) -#define CM_IOCARDOFF _IO (CM_IOC_MAGIC, 4) - -#define CM_IOSDBGLVL _IOW(CM_IOC_MAGIC, 250, int*) - -/* card and device states */ -#define CM_CARD_INSERTED 0x01 -#define CM_CARD_POWERED 0x02 -#define CM_ATR_PRESENT 0x04 -#define CM_ATR_VALID 0x08 -#define CM_STATE_VALID 0x0f -/* extra info only from CM4000 */ -#define CM_NO_READER 0x10 -#define CM_BAD_CARD 0x20 - - -#endif /* _UAPI_CM4000_H_ */ -- cgit v1.2.3 From 81773c98a808ee9881e2c503d6b9a5316c130694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 Feb 2023 03:17:37 +0000 Subject: firmware: dmi-sysfs: make kobj_type structures constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ee6d3dd4ed48 ("driver core: make kobj_type constant.") the driver core allows the usage of const struct kobj_type. Take advantage of this to constify the structure definitions to prevent modification at runtime. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20230227-kobj_type-firmware-dmi-v1-1-d1131d4e7a2b@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dmi-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index ed5aff0a4204..03708ab64e56 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -304,7 +304,7 @@ static struct attribute *dmi_sysfs_sel_attrs[] = { }; ATTRIBUTE_GROUPS(dmi_sysfs_sel); -static struct kobj_type dmi_system_event_log_ktype = { +static const struct kobj_type dmi_system_event_log_ktype = { .release = dmi_entry_free, .sysfs_ops = &dmi_sysfs_specialize_attr_ops, .default_groups = dmi_sysfs_sel_groups, @@ -563,7 +563,7 @@ static void dmi_sysfs_entry_release(struct kobject *kobj) kfree(entry); } -static struct kobj_type dmi_sysfs_entry_ktype = { +static const struct kobj_type dmi_sysfs_entry_ktype = { .release = dmi_sysfs_entry_release, .sysfs_ops = &dmi_sysfs_attr_ops, .default_groups = dmi_sysfs_entry_groups, -- cgit v1.2.3 From 7ef1a2c1c9dffa177ecc3ea50b7f5ee63a621137 Mon Sep 17 00:00:00 2001 From: Marco Pagani Date: Wed, 1 Mar 2023 15:03:08 +0100 Subject: fpga: bridge: fix kernel-doc parameter description Fix the kernel-doc description for the "struct fpga_image_info *info" parameter of the fpga_bridge_get() function. Fixes: 060ac5c8fa7b ("fpga: bridge: kernel-doc fixes") Signed-off-by: Marco Pagani Reviewed-by: Tom Rix Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20230301140309.512578-1-marpagan@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/fpga-bridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 5cd40acab5bf..ff4470067ed5 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -115,7 +115,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data) /** * fpga_bridge_get - get an exclusive reference to an fpga bridge * @dev: parent device that fpga bridge was registered with - * @info: fpga manager info + * @info: fpga image specific information * * Given a device, get an exclusive reference to an fpga bridge. * -- cgit v1.2.3 From 9263271a61fcb07b8a30379e785391415d10aa72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 10:12:51 +0100 Subject: misc: sram: Improve and simplify clk handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code tries to get an associated clk, ignores any errors in the process and if there is a clock enables it unconditionally for the whole lifetime of the sram device. Instead use an "optional" variant of devm_clk_get() which handles the case where no clk is needed for the sram device and do proper error handling for the remaining error cases. Also use an "enabled" variant of devm_clk_get() to simplify. With that .probe() is the only function using struct sram_dev::clk, so it can be replaced by a local variable. Signed-off-by: Uwe Kleine-König Reviewed-by: Philipp Zabel Link: https://lore.kernel.org/r/20230302091251.1852454-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 17 +++++------------ drivers/misc/sram.h | 1 - 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index f0e7f02605eb..dec92580a42c 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -381,6 +381,7 @@ static int sram_probe(struct platform_device *pdev) struct sram_dev *sram; int ret; struct resource *res; + struct clk *clk; config = of_device_get_match_data(&pdev->dev); @@ -409,16 +410,14 @@ static int sram_probe(struct platform_device *pdev) return PTR_ERR(sram->pool); } - sram->clk = devm_clk_get(sram->dev, NULL); - if (IS_ERR(sram->clk)) - sram->clk = NULL; - else - clk_prepare_enable(sram->clk); + clk = devm_clk_get_optional_enabled(sram->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); ret = sram_reserve_regions(sram, platform_get_resource(pdev, IORESOURCE_MEM, 0)); if (ret) - goto err_disable_clk; + return ret; platform_set_drvdata(pdev, sram); @@ -436,9 +435,6 @@ static int sram_probe(struct platform_device *pdev) err_free_partitions: sram_free_partitions(sram); -err_disable_clk: - if (sram->clk) - clk_disable_unprepare(sram->clk); return ret; } @@ -452,9 +448,6 @@ static int sram_remove(struct platform_device *pdev) if (sram->pool && gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) dev_err(sram->dev, "removed while SRAM allocated\n"); - if (sram->clk) - clk_disable_unprepare(sram->clk); - return 0; } diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h index d2058d8c8f1d..397205b8bf6f 100644 --- a/drivers/misc/sram.h +++ b/drivers/misc/sram.h @@ -27,7 +27,6 @@ struct sram_dev { bool no_memory_wc; struct gen_pool *pool; - struct clk *clk; struct sram_partition *partition; u32 partitions; -- cgit v1.2.3 From b085dbf6c955c34f735cac433f8551832138e154 Mon Sep 17 00:00:00 2001 From: Luis Chamberlain Date: Thu, 2 Mar 2023 12:46:10 -0800 Subject: sgi-xp: simplify sysctl registration Although this driver is a good use case for having a directory that is not other directories and then subdirectories with more entries, the usage of register_sysctl_table() can recurse and increases complexity so to avoid that just split out the registration to each directory with its own entries. register_sysctl_table() is a deprecated compatibility wrapper. register_sysctl() can do the directory creation for you so just use that. Signed-off-by: Luis Chamberlain Reviewed-by: Steve Wahl Link: https://lore.kernel.org/r/20230302204612.782387-6-mcgrof@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-xp/xpc_main.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index b2c3c22fc13c..6da509d692bb 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -93,7 +93,7 @@ int xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT; static int xpc_disengage_min_timelimit; /* = 0 */ static int xpc_disengage_max_timelimit = 120; -static struct ctl_table xpc_sys_xpc_hb_dir[] = { +static struct ctl_table xpc_sys_xpc_hb[] = { { .procname = "hb_interval", .data = &xpc_hb_interval, @@ -112,11 +112,7 @@ static struct ctl_table xpc_sys_xpc_hb_dir[] = { .extra2 = &xpc_hb_check_max_interval}, {} }; -static struct ctl_table xpc_sys_xpc_dir[] = { - { - .procname = "hb", - .mode = 0555, - .child = xpc_sys_xpc_hb_dir}, +static struct ctl_table xpc_sys_xpc[] = { { .procname = "disengage_timelimit", .data = &xpc_disengage_timelimit, @@ -127,14 +123,9 @@ static struct ctl_table xpc_sys_xpc_dir[] = { .extra2 = &xpc_disengage_max_timelimit}, {} }; -static struct ctl_table xpc_sys_dir[] = { - { - .procname = "xpc", - .mode = 0555, - .child = xpc_sys_xpc_dir}, - {} -}; + static struct ctl_table_header *xpc_sysctl; +static struct ctl_table_header *xpc_sysctl_hb; /* non-zero if any remote partition disengage was timed out */ int xpc_disengage_timedout; @@ -1041,6 +1032,8 @@ xpc_do_exit(enum xp_retval reason) if (xpc_sysctl) unregister_sysctl_table(xpc_sysctl); + if (xpc_sysctl_hb) + unregister_sysctl_table(xpc_sysctl_hb); xpc_teardown_partitions(); @@ -1243,7 +1236,8 @@ xpc_init(void) goto out_1; } - xpc_sysctl = register_sysctl_table(xpc_sys_dir); + xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc); + xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb); /* * Fill the partition reserved page with the information needed by @@ -1308,6 +1302,8 @@ out_3: (void)unregister_die_notifier(&xpc_die_notifier); (void)unregister_reboot_notifier(&xpc_reboot_notifier); out_2: + if (xpc_sysctl_hb) + unregister_sysctl_table(xpc_sysctl_hb); if (xpc_sysctl) unregister_sysctl_table(xpc_sysctl); -- cgit v1.2.3 From 8efc52743ecb5c69d2e4faba965a3e14418c2494 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 15:38:16 -0600 Subject: misc: alcor_pci: Use PCI core to manage ASPM instead of open-coding "priv->ext_config_dev_aspm" was never set to a non-zero value. Therefore, alcor_pci_aspm_ctrl(priv, 1) did nothing, and alcor_pci_aspm_ctrl(priv, 0) always disabled ASPM in the device and the upstream bridge. The driver disabled ASPM in alcor_pci_probe() and alcor_resume(), so it's possible the device doesn't work well when ASPM is enabled. Remove all the ASPM-related code and replace the alcor_pci_aspm_ctrl(0) calls with pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1), which asks the PCI core to disable ASPM. Signed-off-by: Bjorn Helgaas Cc: Oleksij Rempel Reviewed-by: Oleksij Rempel Link: https://lore.kernel.org/r/20230307213816.886308-1-helgaas@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/alcor_pci.c | 144 +----------------------------------- include/linux/alcor_pci.h | 7 -- 2 files changed, 4 insertions(+), 147 deletions(-) diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 9080f9f150a2..5b637171c46c 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -95,137 +95,6 @@ u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr) } EXPORT_SYMBOL_GPL(alcor_read32be); -static int alcor_pci_find_cap_offset(struct alcor_pci_priv *priv, - struct pci_dev *pci) -{ - int where; - u8 val8; - u32 val32; - - where = ALCOR_CAP_START_OFFSET; - pci_read_config_byte(pci, where, &val8); - if (!val8) - return 0; - - where = (int)val8; - while (1) { - pci_read_config_dword(pci, where, &val32); - if (val32 == 0xffffffff) { - dev_dbg(priv->dev, "find_cap_offset invalid value %x.\n", - val32); - return 0; - } - - if ((val32 & 0xff) == 0x10) { - dev_dbg(priv->dev, "pcie cap offset: %x\n", where); - return where; - } - - if ((val32 & 0xff00) == 0x00) { - dev_dbg(priv->dev, "pci_find_cap_offset invalid value %x.\n", - val32); - break; - } - where = (int)((val32 >> 8) & 0xff); - } - - return 0; -} - -static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv) -{ - struct pci_dev *pci; - int where; - u32 val32; - - priv->pdev_cap_off = alcor_pci_find_cap_offset(priv, priv->pdev); - /* - * A device might be attached to root complex directly and - * priv->parent_pdev will be NULL. In this case we don't check its - * capability and disable ASPM completely. - */ - if (priv->parent_pdev) - priv->parent_cap_off = alcor_pci_find_cap_offset(priv, - priv->parent_pdev); - - if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) { - dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n", - priv->pdev_cap_off, priv->parent_cap_off); - return; - } - - /* link capability */ - pci = priv->pdev; - where = priv->pdev_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET; - pci_read_config_dword(pci, where, &val32); - priv->pdev_aspm_cap = (u8)(val32 >> 10) & 0x03; - - pci = priv->parent_pdev; - where = priv->parent_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET; - pci_read_config_dword(pci, where, &val32); - priv->parent_aspm_cap = (u8)(val32 >> 10) & 0x03; - - if (priv->pdev_aspm_cap != priv->parent_aspm_cap) { - u8 aspm_cap; - - dev_dbg(priv->dev, "pdev_aspm_cap: %x, parent_aspm_cap: %x\n", - priv->pdev_aspm_cap, priv->parent_aspm_cap); - aspm_cap = priv->pdev_aspm_cap & priv->parent_aspm_cap; - priv->pdev_aspm_cap = aspm_cap; - priv->parent_aspm_cap = aspm_cap; - } - - dev_dbg(priv->dev, "ext_config_dev_aspm: %x, pdev_aspm_cap: %x\n", - priv->ext_config_dev_aspm, priv->pdev_aspm_cap); - priv->ext_config_dev_aspm &= priv->pdev_aspm_cap; -} - -static void alcor_pci_aspm_ctrl(struct alcor_pci_priv *priv, u8 aspm_enable) -{ - struct pci_dev *pci; - u8 aspm_ctrl, i; - int where; - u32 val32; - - if ((!priv->pdev_cap_off) || (!priv->parent_cap_off)) { - dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n", - priv->pdev_cap_off, priv->parent_cap_off); - return; - } - - if (!priv->pdev_aspm_cap) - return; - - aspm_ctrl = 0; - if (aspm_enable) { - aspm_ctrl = priv->ext_config_dev_aspm; - - if (!aspm_ctrl) { - dev_dbg(priv->dev, "aspm_ctrl == 0\n"); - return; - } - } - - for (i = 0; i < 2; i++) { - - if (i) { - pci = priv->parent_pdev; - where = priv->parent_cap_off - + ALCOR_PCIE_LINK_CTRL_OFFSET; - } else { - pci = priv->pdev; - where = priv->pdev_cap_off - + ALCOR_PCIE_LINK_CTRL_OFFSET; - } - - pci_read_config_dword(pci, where, &val32); - val32 &= (~0x03); - val32 |= (aspm_ctrl & priv->pdev_aspm_cap); - pci_write_config_byte(pci, where, (u8)val32); - } - -} - static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv) { alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); @@ -308,7 +177,6 @@ static int alcor_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_set_drvdata(pdev, priv); - alcor_pci_init_check_aspm(priv); for (i = 0; i < ARRAY_SIZE(alcor_pci_cells); i++) { alcor_pci_cells[i].platform_data = priv; @@ -319,7 +187,7 @@ static int alcor_pci_probe(struct pci_dev *pdev, if (ret < 0) goto error_clear_drvdata; - alcor_pci_aspm_ctrl(priv, 0); + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); return 0; @@ -339,8 +207,6 @@ static void alcor_pci_remove(struct pci_dev *pdev) priv = pci_get_drvdata(pdev); - alcor_pci_aspm_ctrl(priv, 1); - mfd_remove_devices(&pdev->dev); ida_free(&alcor_pci_idr, priv->id); @@ -353,18 +219,16 @@ static void alcor_pci_remove(struct pci_dev *pdev) #ifdef CONFIG_PM_SLEEP static int alcor_suspend(struct device *dev) { - struct alcor_pci_priv *priv = dev_get_drvdata(dev); - - alcor_pci_aspm_ctrl(priv, 1); return 0; } static int alcor_resume(struct device *dev) { - struct alcor_pci_priv *priv = dev_get_drvdata(dev); - alcor_pci_aspm_ctrl(priv, 0); + pci_disable_link_state(priv->pdev, + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); + return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/include/linux/alcor_pci.h b/include/linux/alcor_pci.h index 8274ed525e9f..c4a0b23846d8 100644 --- a/include/linux/alcor_pci.h +++ b/include/linux/alcor_pci.h @@ -268,13 +268,6 @@ struct alcor_pci_priv { unsigned long id; /* idr id */ struct alcor_dev_cfg *cfg; - - /* PCI ASPM related vars */ - int pdev_cap_off; - u8 pdev_aspm_cap; - int parent_cap_off; - u8 parent_aspm_cap; - u8 ext_config_dev_aspm; }; void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr); -- cgit v1.2.3 From 1a0aae883093f304c4dac441d527a893eb840c5d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 14:24:35 -0600 Subject: misc: genwqe: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20230307202435.880921-1-helgaas@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 5b63d179b24e..4829bc2208d6 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -1099,7 +1098,6 @@ static int genwqe_pci_setup(struct genwqe_dev *cd) } pci_set_master(pci_dev); - pci_enable_pcie_error_reporting(pci_dev); /* EEH recovery requires PCIe fundamental reset */ pci_dev->needs_freset = 1; -- cgit v1.2.3 From 8f118f61540e23fb0e4ec84aec361f6758dbe93a Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Fri, 24 Feb 2023 17:37:37 +0530 Subject: firmware: xilinx: Add pm api function for PL config reg readback Adds PM API for performing Programmable Logic(PL) configuration register readback. It provides an interface to the firmware(pmufw) to readback the FPGA configuration register. Signed-off-by: Nava kishore Manne Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20230224120738.329416-2-nava.kishore.manne@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 33 +++++++++++++++++++++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 11 +++++++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index acd83d29c866..3b3ffb223c4c 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -971,6 +971,39 @@ int zynqmp_pm_fpga_get_status(u32 *value) } EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_status); +/** + * zynqmp_pm_fpga_get_config_status - Get the FPGA configuration status. + * @value: Buffer to store FPGA configuration status. + * + * This function provides access to the pmufw to get the FPGA configuration + * status + * + * Return: 0 on success, a negative value on error + */ +int zynqmp_pm_fpga_get_config_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 buf, lower_addr, upper_addr; + int ret; + + if (!value) + return -EINVAL; + + lower_addr = lower_32_bits((u64)&buf); + upper_addr = upper_32_bits((u64)&buf); + + ret = zynqmp_pm_invoke_fn(PM_FPGA_READ, + XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET, + lower_addr, upper_addr, + XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG, + ret_payload); + + *value = ret_payload[1]; + + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_config_status); + /** * zynqmp_pm_pinctrl_request - Request Pin from firmware * @pin: Pin number to request diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 0e4c70987e6a..f5da51677069 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -71,6 +71,10 @@ #define XILINX_ZYNQMP_PM_FPGA_FULL 0x0U #define XILINX_ZYNQMP_PM_FPGA_PARTIAL BIT(0) +/* FPGA Status Reg */ +#define XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET 7U +#define XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG 0U + /* * Node IDs for the Error Events. */ @@ -124,6 +128,7 @@ enum pm_api_id { PM_CLOCK_GETRATE = 42, PM_CLOCK_SETPARENT = 43, PM_CLOCK_GETPARENT = 44, + PM_FPGA_READ = 46, PM_SECURE_AES = 47, PM_FEATURE_CHECK = 63, }; @@ -519,6 +524,7 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out); int zynqmp_pm_sha_hash(const u64 address, const u32 size, const u32 flags); int zynqmp_pm_fpga_load(const u64 address, const u32 size, const u32 flags); int zynqmp_pm_fpga_get_status(u32 *value); +int zynqmp_pm_fpga_get_config_status(u32 *value); int zynqmp_pm_write_ggs(u32 index, u32 value); int zynqmp_pm_read_ggs(u32 index, u32 *value); int zynqmp_pm_write_pggs(u32 index, u32 value); @@ -725,6 +731,11 @@ static inline int zynqmp_pm_fpga_get_status(u32 *value) return -ENODEV; } +static inline int zynqmp_pm_fpga_get_config_status(u32 *value) +{ + return -ENODEV; +} + static inline int zynqmp_pm_write_ggs(u32 index, u32 value) { return -ENODEV; -- cgit v1.2.3 From 995a3bb72101e7492572d48936cea0fa14c8b794 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Fri, 24 Feb 2023 17:37:38 +0530 Subject: fpga: zynqmp-fpga: Adds status interface Adds status interface for zynqmp-fpga. It's a read only interface which allows the user to get the Programmable Logic(PL) configuration status. Usage: To read the Programmable Logic(PL) configuration status. cat /sys/bus/platform/drivers/zynqmp_fpga_manager/firmware:zynqmp-firmware:pcap/status Signed-off-by: Nava kishore Manne Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20230224120738.329416-3-nava.kishore.manne@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-driver-zynqmp-fpga | 73 ++++++++++++++++++++++ drivers/fpga/zynqmp-fpga.c | 21 +++++++ 2 files changed, 94 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-zynqmp-fpga diff --git a/Documentation/ABI/testing/sysfs-driver-zynqmp-fpga b/Documentation/ABI/testing/sysfs-driver-zynqmp-fpga new file mode 100644 index 000000000000..8f93d27b6d91 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-zynqmp-fpga @@ -0,0 +1,73 @@ +What: /sys/bus/platform/drivers/zynqmp_fpga_manager/firmware:zynqmp-firmware:pcap/status +Date: February 2023 +KernelVersion: 6.4 +Contact: Nava kishore Manne +Description: (RO) Read fpga status. + Read returns a hexadecimal value that tells the current status + of the FPGA device. Each bit position in the status value is + described Below(see ug570 chapter 9). + https://docs.xilinx.com/v/u/en-US/ug570-ultrascale-configuration + + ====================== ============================================== + BIT(0) 0: No CRC error + 1: CRC error + + BIT(1) 0: Decryptor security not set + 1: Decryptor security set + + BIT(2) 0: MMCMs/PLLs are not locked + 1: MMCMs/PLLs are locked + + BIT(3) 0: DCI not matched + 1: DCI matched + + BIT(4) 0: Start-up sequence has not finished + 1: Start-up sequence has finished + + BIT(5) 0: All I/Os are placed in High-Z state + 1: All I/Os behave as configured + + BIT(6) 0: Flip-flops and block RAM are write disabled + 1: Flip-flops and block RAM are write enabled + + BIT(7) 0: GHIGH_B_STATUS asserted + 1: GHIGH_B_STATUS deasserted + + BIT(8) to BIT(10) Status of the mode pins + + BIT(11) 0: Initialization has not finished + 1: Initialization finished + + BIT(12) Value on INIT_B_PIN pin + + BIT(13) 0: Signal not released + 1: Signal released + + BIT(14) Value on DONE_PIN pin. + + BIT(15) 0: No IDCODE_ERROR + 1: IDCODE_ERROR + + BIT(16) 0: No SECURITY_ERROR + 1: SECURITY_ERROR + + BIT(17) System Monitor over-temperature if set + + BIT(18) to BIT(20) Start-up state machine (0 to 7) + Phase 0 = 000 + Phase 1 = 001 + Phase 2 = 011 + Phase 3 = 010 + Phase 4 = 110 + Phase 5 = 111 + Phase 6 = 101 + Phase 7 = 100 + + BIT(25) to BIT(26) Indicates the detected bus width + 00 = x1 + 01 = x8 + 10 = x16 + 11 = x32 + ====================== ============================================== + + The other bits are reserved. diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c index c60f20949c47..f3434e2c487b 100644 --- a/drivers/fpga/zynqmp-fpga.c +++ b/drivers/fpga/zynqmp-fpga.c @@ -77,6 +77,26 @@ static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr) return FPGA_MGR_STATE_UNKNOWN; } +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 status; + int ret; + + ret = zynqmp_pm_fpga_get_config_status(&status); + if (ret) + return ret; + + return sysfs_emit(buf, "0x%x\n", status); +} +static DEVICE_ATTR_RO(status); + +static struct attribute *zynqmp_fpga_attrs[] = { + &dev_attr_status.attr, + NULL, +}; +ATTRIBUTE_GROUPS(zynqmp_fpga); + static const struct fpga_manager_ops zynqmp_fpga_ops = { .state = zynqmp_fpga_ops_state, .write_init = zynqmp_fpga_ops_write_init, @@ -113,6 +133,7 @@ static struct platform_driver zynqmp_fpga_driver = { .driver = { .name = "zynqmp_fpga_manager", .of_match_table = of_match_ptr(zynqmp_fpga_of_match), + .dev_groups = zynqmp_fpga_groups, }, }; -- cgit v1.2.3 From 1274b37ab31211beb0f1c6e12e059d6b58a6e40a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 Feb 2023 14:32:52 +0100 Subject: misc: microchip: pci1xxxx: Convert to immutable irqchip Convert the driver to immutable irq-chip with a bit of intuition. Cc: linux-gpio@vger.kernel.org Cc: Marc Zyngier Signed-off-by: Linus Walleij Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20230223133252.2257276-1-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 3389803cb281..e616e3ec2b42 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -175,9 +175,13 @@ static void pci1xxxx_gpio_irq_set_mask(struct irq_data *data, bool set) unsigned int gpio = irqd_to_hwirq(data); unsigned long flags; + if (!set) + gpiochip_enable_irq(chip, gpio); spin_lock_irqsave(&priv->lock, flags); pci1xxx_assign_bit(priv->reg_base, INTR_MASK_OFFSET(gpio), (gpio % 32), set); spin_unlock_irqrestore(&priv->lock, flags); + if (set) + gpiochip_disable_irq(chip, gpio); } static void pci1xxxx_gpio_irq_mask(struct irq_data *data) @@ -283,12 +287,14 @@ static irqreturn_t pci1xxxx_gpio_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irq_chip pci1xxxx_gpio_irqchip = { +static const struct irq_chip pci1xxxx_gpio_irqchip = { .name = "pci1xxxx_gpio", .irq_ack = pci1xxxx_gpio_irq_ack, .irq_mask = pci1xxxx_gpio_irq_mask, .irq_unmask = pci1xxxx_gpio_irq_unmask, .irq_set_type = pci1xxxx_gpio_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int pci1xxxx_gpio_suspend(struct device *dev) @@ -351,7 +357,7 @@ static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) return retval; girq = &priv->gpio.irq; - girq->chip = &pci1xxxx_gpio_irqchip; + gpio_irq_chip_set_chip(girq, &pci1xxxx_gpio_irqchip); girq->parent_handler = NULL; girq->num_parents = 0; girq->parents = NULL; -- cgit v1.2.3 From f0312cbd2af1c458a8441bb7364133d8242446b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 Feb 2023 03:14:02 +0000 Subject: edd: make kobj_type structure constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ee6d3dd4ed48 ("driver core: make kobj_type constant.") the driver core allows the usage of const struct kobj_type. Take advantage of this to constify the structure definition to prevent modification at runtime. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20230227-kobj_type-firmware-edd-v1-1-c626afe5bd05@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/edd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c index 5cc238916551..55dec4eb2c00 100644 --- a/drivers/firmware/edd.c +++ b/drivers/firmware/edd.c @@ -608,7 +608,7 @@ static void edd_release(struct kobject * kobj) kfree(dev); } -static struct kobj_type edd_ktype = { +static const struct kobj_type edd_ktype = { .release = edd_release, .sysfs_ops = &edd_attr_ops, }; -- cgit v1.2.3 From b0f64c80cb8c1adcdae59fa09bbc9d34adc29911 Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Fri, 10 Mar 2023 15:34:15 +0700 Subject: misc: smpro-errmon: Add DIMM 2x Refresh rate event In high temperature condition, JEDEC spec requires memory controller to double the refresh rate. This commit adds event_dimm_2x_refresh sysfs to report that events for all memory channels. Signed-off-by: Quan Nguyen Link: https://lore.kernel.org/r/20230310083416.3670980-2-quan@os.amperecomputing.com Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-platform-devices-ampere-smpro | 9 ++++++--- drivers/misc/smpro-errmon.c | 5 +++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro b/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro index ca93c215ef99..14b58c893df5 100644 --- a/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro +++ b/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro @@ -234,8 +234,8 @@ Description: For details, see section `5.10 RAS Internal Error Register Definitions, Altra Family Soc BMC Interface Specification`. -What: /sys/bus/platform/devices/smpro-errmon.*/event_[vrd_warn_fault|vrd_hot|dimm_hot] -KernelVersion: 6.1 +What: /sys/bus/platform/devices/smpro-errmon.*/event_[vrd_warn_fault|vrd_hot|dimm_hot|dimm_2x_refresh] +KernelVersion: 6.1 (event_[vrd_warn_fault|vrd_hot|dimm_hot]), 6.4 (event_dimm_2x_refresh) Contact: Quan Nguyen Description: (RO) Contains the detail information in case of VRD/DIMM warning/hot events @@ -258,8 +258,11 @@ Description: +---------------+---------------------------------------------------------------+---------------------+ | DIMM HOT | /sys/bus/platform/devices/smpro-errmon.*/event_dimm_hot | DIMM Hot | +---------------+---------------------------------------------------------------+---------------------+ + | DIMM 2X | /sys/bus/platform/devices/smpro-errmon.*/event_dimm_2x_refresh| DIMM 2x refresh rate| + | REFRESH RATE | | event in high temp | + +---------------+---------------------------------------------------------------+---------------------+ - For more details, see section `5.7 GPI Status Registers, + For more details, see section `5.7 GPI Status Registers and 5.9 Memory Error Register Definitions, Altra Family Soc BMC Interface Specification`. What: /sys/bus/platform/devices/smpro-misc.*/boot_progress diff --git a/drivers/misc/smpro-errmon.c b/drivers/misc/smpro-errmon.c index d1431d419aa4..1635e881aefb 100644 --- a/drivers/misc/smpro-errmon.c +++ b/drivers/misc/smpro-errmon.c @@ -67,6 +67,7 @@ #define VRD_WARN_FAULT_EVENT_DATA 0x78 #define VRD_HOT_EVENT_DATA 0x79 #define DIMM_HOT_EVENT_DATA 0x7A +#define DIMM_2X_REFRESH_EVENT_DATA 0x96 #define MAX_READ_BLOCK_LENGTH 48 @@ -190,6 +191,7 @@ enum EVENT_TYPES { VRD_WARN_FAULT_EVENT, VRD_HOT_EVENT, DIMM_HOT_EVENT, + DIMM_2X_REFRESH_EVENT, NUM_EVENTS_TYPE, }; @@ -198,6 +200,7 @@ static u8 smpro_event_table[NUM_EVENTS_TYPE] = { VRD_WARN_FAULT_EVENT_DATA, VRD_HOT_EVENT_DATA, DIMM_HOT_EVENT_DATA, + DIMM_2X_REFRESH_EVENT_DATA, }; static ssize_t smpro_event_data_read(struct device *dev, @@ -463,6 +466,7 @@ static DEVICE_ATTR_RO(warn_pmpro); EVENT_RO(vrd_warn_fault, VRD_WARN_FAULT_EVENT); EVENT_RO(vrd_hot, VRD_HOT_EVENT); EVENT_RO(dimm_hot, DIMM_HOT_EVENT); +EVENT_RO(dimm_2x_refresh, DIMM_2X_REFRESH_EVENT); static struct attribute *smpro_errmon_attrs[] = { &dev_attr_overflow_core_ce.attr, @@ -488,6 +492,7 @@ static struct attribute *smpro_errmon_attrs[] = { &dev_attr_event_vrd_warn_fault.attr, &dev_attr_event_vrd_hot.attr, &dev_attr_event_dimm_hot.attr, + &dev_attr_event_dimm_2x_refresh.attr, NULL }; -- cgit v1.2.3 From c2c99326cc0176cbc3e7c11e97db11ca56077d10 Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Fri, 10 Mar 2023 15:34:16 +0700 Subject: misc: smpro-errmon: Add dimm training failure syndrome Adds event_dimm[0-15]_syndrome sysfs to report the failure syndrome to BMC when DIMM training failed. Signed-off-by: Quan Nguyen Link: https://lore.kernel.org/r/20230310083416.3670980-3-quan@os.amperecomputing.com Signed-off-by: Greg Kroah-Hartman --- .../sysfs-bus-platform-devices-ampere-smpro | 10 +++ drivers/misc/smpro-errmon.c | 77 ++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro b/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro index 14b58c893df5..fead760dcf77 100644 --- a/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro +++ b/Documentation/ABI/testing/sysfs-bus-platform-devices-ampere-smpro @@ -265,6 +265,16 @@ Description: For more details, see section `5.7 GPI Status Registers and 5.9 Memory Error Register Definitions, Altra Family Soc BMC Interface Specification`. +What: /sys/bus/platform/devices/smpro-errmon.*/event_dimm[0-15]_syndrome +KernelVersion: 6.4 +Contact: Quan Nguyen +Description: + (RO) The sysfs returns the 2-byte DIMM failure syndrome data for slot + 0-15 if it failed to initialize. + + For more details, see section `5.11 Boot Stage Register Definitions, + Altra Family Soc BMC Interface Specification`. + What: /sys/bus/platform/devices/smpro-misc.*/boot_progress KernelVersion: 6.1 Contact: Quan Nguyen diff --git a/drivers/misc/smpro-errmon.c b/drivers/misc/smpro-errmon.c index 1635e881aefb..a1f0b2c77fac 100644 --- a/drivers/misc/smpro-errmon.c +++ b/drivers/misc/smpro-errmon.c @@ -47,6 +47,12 @@ #define WARN_PMPRO_INFO_LO 0xAC #define WARN_PMPRO_INFO_HI 0xAD +/* Boot Stage Register */ +#define BOOTSTAGE 0xB0 +#define DIMM_SYNDROME_SEL 0xB4 +#define DIMM_SYNDROME_ERR 0xB5 +#define DIMM_SYNDROME_STAGE 4 + /* PCIE Error Registers */ #define PCIE_CE_ERR_CNT 0xC0 #define PCIE_CE_ERR_LEN 0xC1 @@ -468,6 +474,61 @@ EVENT_RO(vrd_hot, VRD_HOT_EVENT); EVENT_RO(dimm_hot, DIMM_HOT_EVENT); EVENT_RO(dimm_2x_refresh, DIMM_2X_REFRESH_EVENT); +static ssize_t smpro_dimm_syndrome_read(struct device *dev, struct device_attribute *da, + char *buf, unsigned int slot) +{ + struct smpro_errmon *errmon = dev_get_drvdata(dev); + unsigned int data; + int ret; + + ret = regmap_read(errmon->regmap, BOOTSTAGE, &data); + if (ret) + return ret; + + /* check for valid stage */ + data = (data >> 8) & 0xff; + if (data != DIMM_SYNDROME_STAGE) + return ret; + + /* Write the slot ID to retrieve Error Syndrome */ + ret = regmap_write(errmon->regmap, DIMM_SYNDROME_SEL, slot); + if (ret) + return ret; + + /* Read the Syndrome error */ + ret = regmap_read(errmon->regmap, DIMM_SYNDROME_ERR, &data); + if (ret || !data) + return ret; + + return sysfs_emit(buf, "%04x\n", data); +} + +#define EVENT_DIMM_SYNDROME(_slot) \ + static ssize_t event_dimm##_slot##_syndrome_show(struct device *dev, \ + struct device_attribute *da, \ + char *buf) \ + { \ + return smpro_dimm_syndrome_read(dev, da, buf, _slot); \ + } \ + static DEVICE_ATTR_RO(event_dimm##_slot##_syndrome) + +EVENT_DIMM_SYNDROME(0); +EVENT_DIMM_SYNDROME(1); +EVENT_DIMM_SYNDROME(2); +EVENT_DIMM_SYNDROME(3); +EVENT_DIMM_SYNDROME(4); +EVENT_DIMM_SYNDROME(5); +EVENT_DIMM_SYNDROME(6); +EVENT_DIMM_SYNDROME(7); +EVENT_DIMM_SYNDROME(8); +EVENT_DIMM_SYNDROME(9); +EVENT_DIMM_SYNDROME(10); +EVENT_DIMM_SYNDROME(11); +EVENT_DIMM_SYNDROME(12); +EVENT_DIMM_SYNDROME(13); +EVENT_DIMM_SYNDROME(14); +EVENT_DIMM_SYNDROME(15); + static struct attribute *smpro_errmon_attrs[] = { &dev_attr_overflow_core_ce.attr, &dev_attr_overflow_core_ue.attr, @@ -493,6 +554,22 @@ static struct attribute *smpro_errmon_attrs[] = { &dev_attr_event_vrd_hot.attr, &dev_attr_event_dimm_hot.attr, &dev_attr_event_dimm_2x_refresh.attr, + &dev_attr_event_dimm0_syndrome.attr, + &dev_attr_event_dimm1_syndrome.attr, + &dev_attr_event_dimm2_syndrome.attr, + &dev_attr_event_dimm3_syndrome.attr, + &dev_attr_event_dimm4_syndrome.attr, + &dev_attr_event_dimm5_syndrome.attr, + &dev_attr_event_dimm6_syndrome.attr, + &dev_attr_event_dimm7_syndrome.attr, + &dev_attr_event_dimm8_syndrome.attr, + &dev_attr_event_dimm9_syndrome.attr, + &dev_attr_event_dimm10_syndrome.attr, + &dev_attr_event_dimm11_syndrome.attr, + &dev_attr_event_dimm12_syndrome.attr, + &dev_attr_event_dimm13_syndrome.attr, + &dev_attr_event_dimm14_syndrome.attr, + &dev_attr_event_dimm15_syndrome.attr, NULL }; -- cgit v1.2.3 From 14853a0676c126aad836eb249499552fa9d6e85a Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 10 Mar 2023 18:17:15 +0800 Subject: bus: mhi: host: pci_generic: Revert "Add a secondary AT port to Telit FN990" This reverts commit 2d5253a096c6057bbf7caa5520856dcdf7eca8bb. There are 2 commits with commit message "Add a secondary AT port to Telit FN990": commit 2d5253a096c6 ("bus: mhi: host: pci_generic: Add a secondary AT port to Telit FN990") commit 479aa3b0ec2e ("bus: mhi: host: pci_generic: Add a secondary AT port to Telit FN990") This turned out to be due to the patch getting applied through different trees and git settled on a resolution while applying it second time. But the second AT port of Foxconn devices don't work in PCIe mode. So the second commit needs to be reverted. Cc: stable@vger.kernel.org # 6.2 Fixes: 2d5253a096c6 ("bus: mhi: host: pci_generic: Add a secondary AT port to Telit FN990") Signed-off-by: Slark Xiao Reviewed-by: Fabio Porcedda Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20230310101715.69209-1-slark_xiao@163.com [mani: massaged the commit message a bit, added fixes tag and CCed stable] Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/pci_generic.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 6e13c43a84d1..91d02f84ba21 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -343,8 +343,6 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0), MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0), MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), - MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1), - MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1), MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2), MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), }; -- cgit v1.2.3 From 8b6f18a0e20daee13be77970acf410a7fda6d0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 29 Jan 2023 14:20:20 +0100 Subject: iio: chemical: sps030: Reformat comment in a more readable way MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's easier to see the (lack of) difference between the lines when they are visually aligned. Signed-off-by: Jonathan Neuschäfer Link: https://lore.kernel.org/r/20230129132020.1352368-1-j.neuschaefer@gmx.net Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sps30_i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index 2aed483a2fde..0cb5d9b65d62 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -68,10 +68,10 @@ static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size /* * Internally sensor stores measurements in a following manner: * - * PM1: upper two bytes, crc8, lower two bytes, crc8 + * PM1: upper two bytes, crc8, lower two bytes, crc8 * PM2P5: upper two bytes, crc8, lower two bytes, crc8 - * PM4: upper two bytes, crc8, lower two bytes, crc8 - * PM10: upper two bytes, crc8, lower two bytes, crc8 + * PM4: upper two bytes, crc8, lower two bytes, crc8 + * PM10: upper two bytes, crc8, lower two bytes, crc8 * * What follows next are number concentration measurements and * typical particle size measurement which we omit. -- cgit v1.2.3 From e932fb64c017dccbee44b7ee781584dbc65449ed Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sun, 29 Jan 2023 14:37:46 +0100 Subject: iio: accel: kionix-kx022a: Remove the unused member g_range The g_range member of the driver data struct is not used and should therefore be removed. Signed-off-by: Mehdi Djait Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/e884d53fed0d8322d8be1de2593d4f5f8975af9c.1674996464.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index f866859855cd..9f8c7631f098 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -162,7 +162,6 @@ struct kx022a_data { int inc_reg; int ien_reg; - unsigned int g_range; unsigned int state; unsigned int odr_ns; -- cgit v1.2.3 From cd62d4f39a0de0c2936ecaae938ba444d77c902a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 24 Oct 2022 13:34:15 +0200 Subject: dt-bindings: iio: adc: renesas,rcar-gyroadc: Miscellaneous improvements Set limits on the number of power-domains and resets, and make them required. Simplify the example, and update it to match reality: - Convert from obsolete MSTP to CPG/MSSR bindings, - Examples should use #{address,size}-cells = <1>, - Add missing resets property, - Drop soc container and pinctrl properties, which are not needed in examples. Signed-off-by: Geert Uytterhoeven Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/7b7a13680fa24282c3407e12b5943a66a2ed9068.1666611184.git.geert+renesas@glider.be Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/renesas,rcar-gyroadc.yaml | 60 +++++++++++----------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml index c115e2e99bd9..1c7aee5ed3e0 100644 --- a/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml @@ -34,9 +34,11 @@ properties: clock-names: const: fck - power-domains: true + power-domains: + maxItems: 1 - resets: true + resets: + maxItems: 1 "#address-cells": const: 1 @@ -51,6 +53,8 @@ required: - reg - clocks - clock-names + - power-domains + - resets - "#address-cells" - "#size-cells" @@ -108,36 +112,30 @@ patternProperties: examples: - | - #include + #include #include - soc { - #address-cells = <2>; - #size-cells = <2>; - - adc@e6e54000 { - compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc"; - reg = <0 0xe6e54000 0 64>; - clocks = <&mstp9_clks R8A7791_CLK_GYROADC>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - - pinctrl-0 = <&adc_pins>; - pinctrl-names = "default"; - - #address-cells = <1>; - #size-cells = <0>; - - adc@0 { - reg = <0>; - compatible = "maxim,max1162"; - vref-supply = <&vref_max1162>; - }; - - adc@1 { - reg = <1>; - compatible = "maxim,max1162"; - vref-supply = <&vref_max1162>; - }; + + adc@e6e54000 { + compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc"; + reg = <0xe6e54000 64>; + clocks = <&cpg CPG_MOD 901>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 901>; + + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + reg = <0>; + compatible = "maxim,max1162"; + vref-supply = <&vref_max1162>; + }; + + adc@1 { + reg = <1>; + compatible = "maxim,max1162"; + vref-supply = <&vref_max1162>; }; }; ... -- cgit v1.2.3 From 390738596ccf565047f1db0c3c17753018da5196 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 17 Nov 2022 11:01:30 +0200 Subject: drivers: iio: dac: ad5592r: add gpio_chip names Add array of explicit gpio names for the `gpiochip` structure of ad5592r, mainly for debug purposes. Since the gpios are configurable via the dts, generic names are used. Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20221117090130.51702-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5592r-base.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 7a9b5fc1e579..076bc9ecfb49 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -124,6 +124,10 @@ static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } +static const char * const ad5592r_gpio_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", +}; + static int ad5592r_gpio_init(struct ad5592r_state *st) { if (!st->gpio_map) @@ -140,6 +144,7 @@ static int ad5592r_gpio_init(struct ad5592r_state *st) st->gpiochip.set = ad5592r_gpio_set; st->gpiochip.request = ad5592r_gpio_request; st->gpiochip.owner = THIS_MODULE; + st->gpiochip.names = ad5592r_gpio_names; mutex_init(&st->gpio_lock); -- cgit v1.2.3 From f22ed8d9e804e403b204d1926138490d6ad35ed2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 14 Jan 2023 17:29:28 +0000 Subject: iio: proximity: sx_common: Drop documentation of non existent struct element. struct sx_common_data doesn't have a num_default_regs element so drop the documentation for it. Signed-off-by: Jonathan Cameron Cc: Gwendal Grignou Cc: Stephen Boyd Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20230114172928.80414-1-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx_common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index 49d4517103b0..a2bbdef200d1 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -101,7 +101,6 @@ struct sx_common_chip_info { * @client: I2C client structure. * @trig: IIO trigger object. * @regmap: Register map. - * @num_default_regs: Number of default registers to set at init. * @chan_prox_stat: Last reading of the proximity status for each channel. * We only send an event to user space when this changes. * @trigger_enabled: True when the device trigger is enabled. -- cgit v1.2.3 From 6d5f85de2d610d652ab7e90fea19a85c58b8df04 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 29 Jan 2023 16:08:05 +0000 Subject: staging: iio: meter: Drop ade7854 driver This driver is so far from making correct use of the IIO infrastructure and ABI that if someone wanted to make the driver suitable for moving out of staging, they would more or less be starting from scratch. As such there is little point in keeping the existing code in staging. Note this was only user of the meter.h header so that is dropped. There are no other drivers in the staging/iio/meter directory so drop the build system files as well. Signed-off-by: Jonathan Cameron Acked-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20230129160805.747745-1-jic23@kernel.org --- drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/meter/Kconfig | 37 --- drivers/staging/iio/meter/Makefile | 8 - drivers/staging/iio/meter/ade7854-i2c.c | 153 --------- drivers/staging/iio/meter/ade7854-spi.c | 160 --------- drivers/staging/iio/meter/ade7854.c | 556 -------------------------------- drivers/staging/iio/meter/ade7854.h | 173 ---------- drivers/staging/iio/meter/meter.h | 398 ----------------------- 9 files changed, 1487 deletions(-) delete mode 100644 drivers/staging/iio/meter/Kconfig delete mode 100644 drivers/staging/iio/meter/Makefile delete mode 100644 drivers/staging/iio/meter/ade7854-i2c.c delete mode 100644 drivers/staging/iio/meter/ade7854-spi.c delete mode 100644 drivers/staging/iio/meter/ade7854.c delete mode 100644 drivers/staging/iio/meter/ade7854.h delete mode 100644 drivers/staging/iio/meter/meter.h diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index afd05bf3345e..d3968fe2ebb8 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -10,7 +10,6 @@ source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" -source "drivers/staging/iio/meter/Kconfig" source "drivers/staging/iio/resolver/Kconfig" endmenu diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 5ed56fe57e14..c50f1019f829 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -8,5 +8,4 @@ obj-y += adc/ obj-y += addac/ obj-y += frequency/ obj-y += impedance-analyzer/ -obj-y += meter/ obj-y += resolver/ diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig deleted file mode 100644 index aa6a3e7f6cdb..000000000000 --- a/drivers/staging/iio/meter/Kconfig +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# IIO meter drivers configuration -# -menu "Active energy metering IC" - -config ADE7854 - tristate "Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver" - depends on SPI || I2C - help - Say yes here to build support for Analog Devices ADE7854/58/68/78 Polyphase - Multifunction Energy Metering IC Driver. - - To compile this driver as a module, choose M here: the - module will be called ade7854. - -config ADE7854_I2C - tristate "support I2C bus connection" - depends on ADE7854 && I2C - default y - help - Say Y here if you have ADE7854/58/68/78 hooked to an I2C bus. - - To compile this driver as a module, choose M here: the - module will be called ade7854-i2c. - -config ADE7854_SPI - tristate "support SPI bus connection" - depends on ADE7854 && SPI - default y - help - Say Y here if you have ADE7854/58/68/78 hooked to a SPI bus. - - To compile this driver as a module, choose M here: the - module will be called ade7854-spi. - -endmenu diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile deleted file mode 100644 index ed4547e38f3a..000000000000 --- a/drivers/staging/iio/meter/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for metering ic drivers -# - -obj-$(CONFIG_ADE7854) += ade7854.o -obj-$(CONFIG_ADE7854_I2C) += ade7854-i2c.o -obj-$(CONFIG_ADE7854_SPI) += ade7854-spi.o diff --git a/drivers/staging/iio/meter/ade7854-i2c.c b/drivers/staging/iio/meter/ade7854-i2c.c deleted file mode 100644 index 572d714eb0dd..000000000000 --- a/drivers/staging/iio/meter/ade7854-i2c.c +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (I2C Bus) - * - * Copyright 2010 Analog Devices Inc. - */ - -#include -#include -#include -#include -#include - -#include -#include "ade7854.h" - -static int ade7854_i2c_write_reg(struct device *dev, - u16 reg_address, - u32 val, - int bits) -{ - int ret; - int count; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - mutex_lock(&st->buf_lock); - st->tx[0] = (reg_address >> 8) & 0xFF; - st->tx[1] = reg_address & 0xFF; - - switch (bits) { - case 8: - st->tx[2] = val & 0xFF; - count = 3; - break; - case 16: - st->tx[2] = (val >> 8) & 0xFF; - st->tx[3] = val & 0xFF; - count = 4; - break; - case 24: - st->tx[2] = (val >> 16) & 0xFF; - st->tx[3] = (val >> 8) & 0xFF; - st->tx[4] = val & 0xFF; - count = 5; - break; - case 32: - st->tx[2] = (val >> 24) & 0xFF; - st->tx[3] = (val >> 16) & 0xFF; - st->tx[4] = (val >> 8) & 0xFF; - st->tx[5] = val & 0xFF; - count = 6; - break; - default: - ret = -EINVAL; - goto unlock; - } - - ret = i2c_master_send(st->i2c, st->tx, count); - -unlock: - mutex_unlock(&st->buf_lock); - - if (ret < 0) - return ret; - - return 0; -} - -static int ade7854_i2c_read_reg(struct device *dev, - u16 reg_address, - u32 *val, - int bits) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - int ret; - - mutex_lock(&st->buf_lock); - st->tx[0] = (reg_address >> 8) & 0xFF; - st->tx[1] = reg_address & 0xFF; - - ret = i2c_master_send(st->i2c, st->tx, 2); - if (ret < 0) - goto unlock; - - ret = i2c_master_recv(st->i2c, st->rx, bits); - if (ret < 0) - goto unlock; - - switch (bits) { - case 8: - *val = st->rx[0]; - break; - case 16: - *val = (st->rx[0] << 8) | st->rx[1]; - break; - case 24: - *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; - break; - case 32: - *val = (st->rx[0] << 24) | (st->rx[1] << 16) | - (st->rx[2] << 8) | st->rx[3]; - break; - default: - ret = -EINVAL; - goto unlock; - } - -unlock: - mutex_unlock(&st->buf_lock); - return ret; -} - -static int ade7854_i2c_probe(struct i2c_client *client) -{ - struct ade7854_state *st; - struct iio_dev *indio_dev; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); - if (!indio_dev) - return -ENOMEM; - st = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - st->read_reg = ade7854_i2c_read_reg; - st->write_reg = ade7854_i2c_write_reg; - st->i2c = client; - st->irq = client->irq; - - return ade7854_probe(indio_dev, &client->dev); -} - -static const struct i2c_device_id ade7854_id[] = { - { "ade7854", 0 }, - { "ade7858", 0 }, - { "ade7868", 0 }, - { "ade7878", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ade7854_id); - -static struct i2c_driver ade7854_i2c_driver = { - .driver = { - .name = "ade7854", - }, - .probe_new = ade7854_i2c_probe, - .id_table = ade7854_id, -}; -module_i2c_driver(ade7854_i2c_driver); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC I2C Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7854-spi.c b/drivers/staging/iio/meter/ade7854-spi.c deleted file mode 100644 index f12a6c8b3e88..000000000000 --- a/drivers/staging/iio/meter/ade7854-spi.c +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (SPI Bus) - * - * Copyright 2010 Analog Devices Inc. - */ - -#include -#include -#include -#include -#include - -#include -#include "ade7854.h" - -static int ade7854_spi_write_reg(struct device *dev, - u16 reg_address, - u32 val, - int bits) -{ - int ret; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - struct spi_transfer xfer = { - .tx_buf = st->tx, - .bits_per_word = 8, - .len = 4, - }; - - mutex_lock(&st->buf_lock); - st->tx[0] = ADE7854_WRITE_REG; - st->tx[1] = (reg_address >> 8) & 0xFF; - st->tx[2] = reg_address & 0xFF; - switch (bits) { - case 8: - st->tx[3] = val & 0xFF; - break; - case 16: - xfer.len = 5; - st->tx[3] = (val >> 8) & 0xFF; - st->tx[4] = val & 0xFF; - break; - case 24: - xfer.len = 6; - st->tx[3] = (val >> 16) & 0xFF; - st->tx[4] = (val >> 8) & 0xFF; - st->tx[5] = val & 0xFF; - break; - case 32: - xfer.len = 7; - st->tx[3] = (val >> 24) & 0xFF; - st->tx[4] = (val >> 16) & 0xFF; - st->tx[5] = (val >> 8) & 0xFF; - st->tx[6] = val & 0xFF; - break; - default: - ret = -EINVAL; - goto unlock; - } - - ret = spi_sync_transfer(st->spi, &xfer, 1); -unlock: - mutex_unlock(&st->buf_lock); - - return ret; -} - -static int ade7854_spi_read_reg(struct device *dev, - u16 reg_address, - u32 *val, - int bits) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - int ret; - struct spi_transfer xfers[] = { - { - .tx_buf = st->tx, - .bits_per_word = 8, - .len = 3, - }, { - .rx_buf = st->rx, - .bits_per_word = 8, - .len = bits, - } - }; - - mutex_lock(&st->buf_lock); - - st->tx[0] = ADE7854_READ_REG; - st->tx[1] = (reg_address >> 8) & 0xFF; - st->tx[2] = reg_address & 0xFF; - - ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); - if (ret < 0) { - dev_err(&st->spi->dev, "problem when reading register 0x%02X", - reg_address); - goto unlock; - } - - switch (bits) { - case 8: - *val = st->rx[0]; - break; - case 16: - *val = be16_to_cpup((const __be16 *)st->rx); - break; - case 24: - *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; - break; - case 32: - *val = be32_to_cpup((const __be32 *)st->rx); - break; - } - -unlock: - mutex_unlock(&st->buf_lock); - return ret; -} - -static int ade7854_spi_probe(struct spi_device *spi) -{ - struct ade7854_state *st; - struct iio_dev *indio_dev; - - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (!indio_dev) - return -ENOMEM; - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - st->read_reg = ade7854_spi_read_reg; - st->write_reg = ade7854_spi_write_reg; - st->irq = spi->irq; - st->spi = spi; - - return ade7854_probe(indio_dev, &spi->dev); -} - -static const struct spi_device_id ade7854_id[] = { - { "ade7854", 0 }, - { "ade7858", 0 }, - { "ade7868", 0 }, - { "ade7878", 0 }, - { } -}; -MODULE_DEVICE_TABLE(spi, ade7854_id); - -static struct spi_driver ade7854_driver = { - .driver = { - .name = "ade7854", - }, - .probe = ade7854_spi_probe, - .id_table = ade7854_id, -}; -module_spi_driver(ade7854_driver); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 SPI Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c deleted file mode 100644 index 68da6ecde6a3..000000000000 --- a/drivers/staging/iio/meter/ade7854.c +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver - * - * Copyright 2010 Analog Devices Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "meter.h" -#include "ade7854.h" - -static ssize_t ade7854_read_8bit(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - int ret; - u32 val = 0; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - - ret = st->read_reg(dev, this_attr->address, &val, 8); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", val); -} - -static ssize_t ade7854_read_16bit(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - int ret; - u32 val = 0; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - - ret = st->read_reg(dev, this_attr->address, &val, 16); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", val); -} - -static ssize_t ade7854_read_24bit(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - int ret; - u32 val; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - - ret = st->read_reg(dev, this_attr->address, &val, 24); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", val); -} - -static ssize_t ade7854_read_32bit(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - int ret; - u32 val = 0; - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - ret = st->read_reg(dev, this_attr->address, &val, 32); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", val); -} - -static ssize_t ade7854_write_8bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - int ret; - u8 val; - - ret = kstrtou8(buf, 10, &val); - if (ret) - goto error_ret; - ret = st->write_reg(dev, this_attr->address, val, 8); - -error_ret: - return ret ? ret : len; -} - -static ssize_t ade7854_write_16bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - int ret; - u16 val; - - ret = kstrtou16(buf, 10, &val); - if (ret) - goto error_ret; - ret = st->write_reg(dev, this_attr->address, val, 16); - -error_ret: - return ret ? ret : len; -} - -static ssize_t ade7854_write_24bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - int ret; - u32 val; - - ret = kstrtou32(buf, 10, &val); - if (ret) - goto error_ret; - ret = st->write_reg(dev, this_attr->address, val, 24); - -error_ret: - return ret ? ret : len; -} - -static ssize_t ade7854_write_32bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - int ret; - u32 val; - - ret = kstrtou32(buf, 10, &val); - if (ret) - goto error_ret; - ret = st->write_reg(dev, this_attr->address, val, 32); - -error_ret: - return ret ? ret : len; -} - -static int ade7854_reset(struct device *dev) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - u32 val; - - st->read_reg(dev, ADE7854_CONFIG, &val, 16); - val |= BIT(7); /* Software Chip Reset */ - - return st->write_reg(dev, ADE7854_CONFIG, val, 16); -} - -static IIO_DEV_ATTR_AIGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_AIGAIN); -static IIO_DEV_ATTR_BIGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_BIGAIN); -static IIO_DEV_ATTR_CIGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_CIGAIN); -static IIO_DEV_ATTR_NIGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_NIGAIN); -static IIO_DEV_ATTR_AVGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_AVGAIN); -static IIO_DEV_ATTR_BVGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_BVGAIN); -static IIO_DEV_ATTR_CVGAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_CVGAIN); -static IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_AVAGAIN); -static IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_BVAGAIN); -static IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_CVAGAIN); -static IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_AWATTOS); -static IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_BWATTOS); -static IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_CWATTOS); -static IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_AVARGAIN); -static IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_BVARGAIN); -static IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_CVARGAIN); -static IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_AVAROS); -static IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_BVAROS); -static IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(0644, - ade7854_read_24bit, - ade7854_write_24bit, - ADE7854_CVAROS); -static IIO_DEV_ATTR_VPEAK(0644, - ade7854_read_32bit, - ade7854_write_32bit, - ADE7854_VPEAK); -static IIO_DEV_ATTR_IPEAK(0644, - ade7854_read_32bit, - ade7854_write_32bit, - ADE7854_IPEAK); -static IIO_DEV_ATTR_APHCAL(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_APHCAL); -static IIO_DEV_ATTR_BPHCAL(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_BPHCAL); -static IIO_DEV_ATTR_CPHCAL(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_CPHCAL); -static IIO_DEV_ATTR_CF1DEN(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_CF1DEN); -static IIO_DEV_ATTR_CF2DEN(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_CF2DEN); -static IIO_DEV_ATTR_CF3DEN(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_CF3DEN); -static IIO_DEV_ATTR_LINECYC(0644, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_LINECYC); -static IIO_DEV_ATTR_SAGCYC(0644, - ade7854_read_8bit, - ade7854_write_8bit, - ADE7854_SAGCYC); -static IIO_DEV_ATTR_CFCYC(0644, - ade7854_read_8bit, - ade7854_write_8bit, - ADE7854_CFCYC); -static IIO_DEV_ATTR_PEAKCYC(0644, - ade7854_read_8bit, - ade7854_write_8bit, - ADE7854_PEAKCYC); -static IIO_DEV_ATTR_CHKSUM(ade7854_read_24bit, - ADE7854_CHECKSUM); -static IIO_DEV_ATTR_ANGLE0(ade7854_read_24bit, - ADE7854_ANGLE0); -static IIO_DEV_ATTR_ANGLE1(ade7854_read_24bit, - ADE7854_ANGLE1); -static IIO_DEV_ATTR_ANGLE2(ade7854_read_24bit, - ADE7854_ANGLE2); -static IIO_DEV_ATTR_AIRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_AIRMS); -static IIO_DEV_ATTR_BIRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_BIRMS); -static IIO_DEV_ATTR_CIRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_CIRMS); -static IIO_DEV_ATTR_NIRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_NIRMS); -static IIO_DEV_ATTR_AVRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_AVRMS); -static IIO_DEV_ATTR_BVRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_BVRMS); -static IIO_DEV_ATTR_CVRMS(0444, - ade7854_read_24bit, - NULL, - ADE7854_CVRMS); -static IIO_DEV_ATTR_AIRMSOS(0444, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_AIRMSOS); -static IIO_DEV_ATTR_BIRMSOS(0444, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_BIRMSOS); -static IIO_DEV_ATTR_CIRMSOS(0444, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_CIRMSOS); -static IIO_DEV_ATTR_AVRMSOS(0444, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_AVRMSOS); -static IIO_DEV_ATTR_BVRMSOS(0444, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_BVRMSOS); -static IIO_DEV_ATTR_CVRMSOS(0444, - ade7854_read_16bit, - ade7854_write_16bit, - ADE7854_CVRMSOS); -static IIO_DEV_ATTR_VOLT_A(ade7854_read_24bit, - ADE7854_VAWV); -static IIO_DEV_ATTR_VOLT_B(ade7854_read_24bit, - ADE7854_VBWV); -static IIO_DEV_ATTR_VOLT_C(ade7854_read_24bit, - ADE7854_VCWV); -static IIO_DEV_ATTR_CURRENT_A(ade7854_read_24bit, - ADE7854_IAWV); -static IIO_DEV_ATTR_CURRENT_B(ade7854_read_24bit, - ADE7854_IBWV); -static IIO_DEV_ATTR_CURRENT_C(ade7854_read_24bit, - ADE7854_ICWV); -static IIO_DEV_ATTR_AWATTHR(ade7854_read_32bit, - ADE7854_AWATTHR); -static IIO_DEV_ATTR_BWATTHR(ade7854_read_32bit, - ADE7854_BWATTHR); -static IIO_DEV_ATTR_CWATTHR(ade7854_read_32bit, - ADE7854_CWATTHR); -static IIO_DEV_ATTR_AFWATTHR(ade7854_read_32bit, - ADE7854_AFWATTHR); -static IIO_DEV_ATTR_BFWATTHR(ade7854_read_32bit, - ADE7854_BFWATTHR); -static IIO_DEV_ATTR_CFWATTHR(ade7854_read_32bit, - ADE7854_CFWATTHR); -static IIO_DEV_ATTR_AVARHR(ade7854_read_32bit, - ADE7854_AVARHR); -static IIO_DEV_ATTR_BVARHR(ade7854_read_32bit, - ADE7854_BVARHR); -static IIO_DEV_ATTR_CVARHR(ade7854_read_32bit, - ADE7854_CVARHR); -static IIO_DEV_ATTR_AVAHR(ade7854_read_32bit, - ADE7854_AVAHR); -static IIO_DEV_ATTR_BVAHR(ade7854_read_32bit, - ADE7854_BVAHR); -static IIO_DEV_ATTR_CVAHR(ade7854_read_32bit, - ADE7854_CVAHR); - -static int ade7854_set_irq(struct device *dev, bool enable) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ade7854_state *st = iio_priv(indio_dev); - - int ret; - u32 irqen; - - ret = st->read_reg(dev, ADE7854_MASK0, &irqen, 32); - if (ret < 0) - return ret; - - if (enable) - irqen |= BIT(17); /* 1: interrupt enabled when all periodical - * (at 8 kHz rate) DSP computations finish. - */ - else - irqen &= ~BIT(17); - - return st->write_reg(dev, ADE7854_MASK0, irqen, 32); -} - -static int ade7854_initial_setup(struct iio_dev *indio_dev) -{ - int ret; - struct device *dev = &indio_dev->dev; - - /* Disable IRQ */ - ret = ade7854_set_irq(dev, false); - if (ret) { - dev_err(dev, "disable irq failed"); - goto err_ret; - } - - ade7854_reset(dev); - usleep_range(ADE7854_STARTUP_DELAY, ADE7854_STARTUP_DELAY + 100); - -err_ret: - return ret; -} - -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("8000"); - -static IIO_CONST_ATTR(name, "ade7854"); - -static struct attribute *ade7854_attributes[] = { - &iio_dev_attr_aigain.dev_attr.attr, - &iio_dev_attr_bigain.dev_attr.attr, - &iio_dev_attr_cigain.dev_attr.attr, - &iio_dev_attr_nigain.dev_attr.attr, - &iio_dev_attr_avgain.dev_attr.attr, - &iio_dev_attr_bvgain.dev_attr.attr, - &iio_dev_attr_cvgain.dev_attr.attr, - &iio_dev_attr_linecyc.dev_attr.attr, - &iio_dev_attr_sagcyc.dev_attr.attr, - &iio_dev_attr_cfcyc.dev_attr.attr, - &iio_dev_attr_peakcyc.dev_attr.attr, - &iio_dev_attr_chksum.dev_attr.attr, - &iio_dev_attr_apparent_power_a_gain.dev_attr.attr, - &iio_dev_attr_apparent_power_b_gain.dev_attr.attr, - &iio_dev_attr_apparent_power_c_gain.dev_attr.attr, - &iio_dev_attr_active_power_a_offset.dev_attr.attr, - &iio_dev_attr_active_power_b_offset.dev_attr.attr, - &iio_dev_attr_active_power_c_offset.dev_attr.attr, - &iio_dev_attr_reactive_power_a_gain.dev_attr.attr, - &iio_dev_attr_reactive_power_b_gain.dev_attr.attr, - &iio_dev_attr_reactive_power_c_gain.dev_attr.attr, - &iio_dev_attr_reactive_power_a_offset.dev_attr.attr, - &iio_dev_attr_reactive_power_b_offset.dev_attr.attr, - &iio_dev_attr_reactive_power_c_offset.dev_attr.attr, - &iio_dev_attr_awatthr.dev_attr.attr, - &iio_dev_attr_bwatthr.dev_attr.attr, - &iio_dev_attr_cwatthr.dev_attr.attr, - &iio_dev_attr_afwatthr.dev_attr.attr, - &iio_dev_attr_bfwatthr.dev_attr.attr, - &iio_dev_attr_cfwatthr.dev_attr.attr, - &iio_dev_attr_avarhr.dev_attr.attr, - &iio_dev_attr_bvarhr.dev_attr.attr, - &iio_dev_attr_cvarhr.dev_attr.attr, - &iio_dev_attr_angle0.dev_attr.attr, - &iio_dev_attr_angle1.dev_attr.attr, - &iio_dev_attr_angle2.dev_attr.attr, - &iio_dev_attr_avahr.dev_attr.attr, - &iio_dev_attr_bvahr.dev_attr.attr, - &iio_dev_attr_cvahr.dev_attr.attr, - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_const_attr_name.dev_attr.attr, - &iio_dev_attr_vpeak.dev_attr.attr, - &iio_dev_attr_ipeak.dev_attr.attr, - &iio_dev_attr_aphcal.dev_attr.attr, - &iio_dev_attr_bphcal.dev_attr.attr, - &iio_dev_attr_cphcal.dev_attr.attr, - &iio_dev_attr_cf1den.dev_attr.attr, - &iio_dev_attr_cf2den.dev_attr.attr, - &iio_dev_attr_cf3den.dev_attr.attr, - &iio_dev_attr_airms.dev_attr.attr, - &iio_dev_attr_birms.dev_attr.attr, - &iio_dev_attr_cirms.dev_attr.attr, - &iio_dev_attr_nirms.dev_attr.attr, - &iio_dev_attr_avrms.dev_attr.attr, - &iio_dev_attr_bvrms.dev_attr.attr, - &iio_dev_attr_cvrms.dev_attr.attr, - &iio_dev_attr_airmsos.dev_attr.attr, - &iio_dev_attr_birmsos.dev_attr.attr, - &iio_dev_attr_cirmsos.dev_attr.attr, - &iio_dev_attr_avrmsos.dev_attr.attr, - &iio_dev_attr_bvrmsos.dev_attr.attr, - &iio_dev_attr_cvrmsos.dev_attr.attr, - &iio_dev_attr_volt_a.dev_attr.attr, - &iio_dev_attr_volt_b.dev_attr.attr, - &iio_dev_attr_volt_c.dev_attr.attr, - &iio_dev_attr_current_a.dev_attr.attr, - &iio_dev_attr_current_b.dev_attr.attr, - &iio_dev_attr_current_c.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ade7854_attribute_group = { - .attrs = ade7854_attributes, -}; - -static const struct iio_info ade7854_info = { - .attrs = &ade7854_attribute_group, -}; - -int ade7854_probe(struct iio_dev *indio_dev, struct device *dev) -{ - int ret; - struct ade7854_state *st = iio_priv(indio_dev); - /* setup the industrialio driver allocated elements */ - mutex_init(&st->buf_lock); - - indio_dev->dev.parent = dev; - indio_dev->info = &ade7854_info; - indio_dev->modes = INDIO_DIRECT_MODE; - - ret = devm_iio_device_register(dev, indio_dev); - if (ret) - return ret; - - /* Get the device into a sane initial state */ - return ade7854_initial_setup(indio_dev); -} -EXPORT_SYMBOL(ade7854_probe); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Energy Meter"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/meter/ade7854.h b/drivers/staging/iio/meter/ade7854.h deleted file mode 100644 index 7a49f8f1016f..000000000000 --- a/drivers/staging/iio/meter/ade7854.h +++ /dev/null @@ -1,173 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ADE7854_H -#define _ADE7854_H - -#define ADE7854_AIGAIN 0x4380 -#define ADE7854_AVGAIN 0x4381 -#define ADE7854_BIGAIN 0x4382 -#define ADE7854_BVGAIN 0x4383 -#define ADE7854_CIGAIN 0x4384 -#define ADE7854_CVGAIN 0x4385 -#define ADE7854_NIGAIN 0x4386 -#define ADE7854_AIRMSOS 0x4387 -#define ADE7854_AVRMSOS 0x4388 -#define ADE7854_BIRMSOS 0x4389 -#define ADE7854_BVRMSOS 0x438A -#define ADE7854_CIRMSOS 0x438B -#define ADE7854_CVRMSOS 0x438C -#define ADE7854_NIRMSOS 0x438D -#define ADE7854_AVAGAIN 0x438E -#define ADE7854_BVAGAIN 0x438F -#define ADE7854_CVAGAIN 0x4390 -#define ADE7854_AWGAIN 0x4391 -#define ADE7854_AWATTOS 0x4392 -#define ADE7854_BWGAIN 0x4393 -#define ADE7854_BWATTOS 0x4394 -#define ADE7854_CWGAIN 0x4395 -#define ADE7854_CWATTOS 0x4396 -#define ADE7854_AVARGAIN 0x4397 -#define ADE7854_AVAROS 0x4398 -#define ADE7854_BVARGAIN 0x4399 -#define ADE7854_BVAROS 0x439A -#define ADE7854_CVARGAIN 0x439B -#define ADE7854_CVAROS 0x439C -#define ADE7854_AFWGAIN 0x439D -#define ADE7854_AFWATTOS 0x439E -#define ADE7854_BFWGAIN 0x439F -#define ADE7854_BFWATTOS 0x43A0 -#define ADE7854_CFWGAIN 0x43A1 -#define ADE7854_CFWATTOS 0x43A2 -#define ADE7854_AFVARGAIN 0x43A3 -#define ADE7854_AFVAROS 0x43A4 -#define ADE7854_BFVARGAIN 0x43A5 -#define ADE7854_BFVAROS 0x43A6 -#define ADE7854_CFVARGAIN 0x43A7 -#define ADE7854_CFVAROS 0x43A8 -#define ADE7854_VATHR1 0x43A9 -#define ADE7854_VATHR0 0x43AA -#define ADE7854_WTHR1 0x43AB -#define ADE7854_WTHR0 0x43AC -#define ADE7854_VARTHR1 0x43AD -#define ADE7854_VARTHR0 0x43AE -#define ADE7854_RSV 0x43AF -#define ADE7854_VANOLOAD 0x43B0 -#define ADE7854_APNOLOAD 0x43B1 -#define ADE7854_VARNOLOAD 0x43B2 -#define ADE7854_VLEVEL 0x43B3 -#define ADE7854_DICOEFF 0x43B5 -#define ADE7854_HPFDIS 0x43B6 -#define ADE7854_ISUMLVL 0x43B8 -#define ADE7854_ISUM 0x43BF -#define ADE7854_AIRMS 0x43C0 -#define ADE7854_AVRMS 0x43C1 -#define ADE7854_BIRMS 0x43C2 -#define ADE7854_BVRMS 0x43C3 -#define ADE7854_CIRMS 0x43C4 -#define ADE7854_CVRMS 0x43C5 -#define ADE7854_NIRMS 0x43C6 -#define ADE7854_RUN 0xE228 -#define ADE7854_AWATTHR 0xE400 -#define ADE7854_BWATTHR 0xE401 -#define ADE7854_CWATTHR 0xE402 -#define ADE7854_AFWATTHR 0xE403 -#define ADE7854_BFWATTHR 0xE404 -#define ADE7854_CFWATTHR 0xE405 -#define ADE7854_AVARHR 0xE406 -#define ADE7854_BVARHR 0xE407 -#define ADE7854_CVARHR 0xE408 -#define ADE7854_AFVARHR 0xE409 -#define ADE7854_BFVARHR 0xE40A -#define ADE7854_CFVARHR 0xE40B -#define ADE7854_AVAHR 0xE40C -#define ADE7854_BVAHR 0xE40D -#define ADE7854_CVAHR 0xE40E -#define ADE7854_IPEAK 0xE500 -#define ADE7854_VPEAK 0xE501 -#define ADE7854_STATUS0 0xE502 -#define ADE7854_STATUS1 0xE503 -#define ADE7854_OILVL 0xE507 -#define ADE7854_OVLVL 0xE508 -#define ADE7854_SAGLVL 0xE509 -#define ADE7854_MASK0 0xE50A -#define ADE7854_MASK1 0xE50B -#define ADE7854_IAWV 0xE50C -#define ADE7854_IBWV 0xE50D -#define ADE7854_ICWV 0xE50E -#define ADE7854_VAWV 0xE510 -#define ADE7854_VBWV 0xE511 -#define ADE7854_VCWV 0xE512 -#define ADE7854_AWATT 0xE513 -#define ADE7854_BWATT 0xE514 -#define ADE7854_CWATT 0xE515 -#define ADE7854_AVA 0xE519 -#define ADE7854_BVA 0xE51A -#define ADE7854_CVA 0xE51B -#define ADE7854_CHECKSUM 0xE51F -#define ADE7854_VNOM 0xE520 -#define ADE7854_PHSTATUS 0xE600 -#define ADE7854_ANGLE0 0xE601 -#define ADE7854_ANGLE1 0xE602 -#define ADE7854_ANGLE2 0xE603 -#define ADE7854_PERIOD 0xE607 -#define ADE7854_PHNOLOAD 0xE608 -#define ADE7854_LINECYC 0xE60C -#define ADE7854_ZXTOUT 0xE60D -#define ADE7854_COMPMODE 0xE60E -#define ADE7854_GAIN 0xE60F -#define ADE7854_CFMODE 0xE610 -#define ADE7854_CF1DEN 0xE611 -#define ADE7854_CF2DEN 0xE612 -#define ADE7854_CF3DEN 0xE613 -#define ADE7854_APHCAL 0xE614 -#define ADE7854_BPHCAL 0xE615 -#define ADE7854_CPHCAL 0xE616 -#define ADE7854_PHSIGN 0xE617 -#define ADE7854_CONFIG 0xE618 -#define ADE7854_MMODE 0xE700 -#define ADE7854_ACCMODE 0xE701 -#define ADE7854_LCYCMODE 0xE702 -#define ADE7854_PEAKCYC 0xE703 -#define ADE7854_SAGCYC 0xE704 -#define ADE7854_CFCYC 0xE705 -#define ADE7854_HSDC_CFG 0xE706 -#define ADE7854_CONFIG2 0xEC01 - -#define ADE7854_READ_REG 0x1 -#define ADE7854_WRITE_REG 0x0 - -#define ADE7854_MAX_TX 7 -#define ADE7854_MAX_RX 7 -#define ADE7854_STARTUP_DELAY 1000 - -#define ADE7854_SPI_SLOW (u32)(300 * 1000) -#define ADE7854_SPI_BURST (u32)(1000 * 1000) -#define ADE7854_SPI_FAST (u32)(2000 * 1000) - -/** - * struct ade7854_state - device instance specific data - * @spi: actual spi_device - * @read_reg Wrapper function for I2C and SPI read - * @write_reg Wrapper function for I2C and SPI write - * @indio_dev: industrial I/O device structure - * @buf_lock: mutex to protect tx and rx - * @tx: transmit buffer - * @rx: receive buffer - **/ -struct ade7854_state { - struct spi_device *spi; - struct i2c_client *i2c; - int (*read_reg)(struct device *dev, u16 reg_address, u32 *val, - int bits); - int (*write_reg)(struct device *dev, u16 reg_address, u32 val, - int bits); - int irq; - struct mutex buf_lock; - u8 tx[ADE7854_MAX_TX] __aligned(IIO_DMA_MINALIGN); - u8 rx[ADE7854_MAX_RX]; - -}; - -int ade7854_probe(struct iio_dev *indio_dev, struct device *dev); -int ade7854_remove(struct iio_dev *indio_dev); - -#endif diff --git a/drivers/staging/iio/meter/meter.h b/drivers/staging/iio/meter/meter.h deleted file mode 100644 index 5ed59bf30a25..000000000000 --- a/drivers/staging/iio/meter/meter.h +++ /dev/null @@ -1,398 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _METER_H -#define _METER_H - -#include - -/* metering ic types of attribute */ - -#define IIO_DEV_ATTR_CURRENT_A_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(current_a_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CURRENT_B_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(current_b_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CURRENT_C_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(current_c_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VOLT_A_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(volt_a_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VOLT_B_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(volt_b_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VOLT_C_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(volt_c_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(reactive_power_a_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(reactive_power_b_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(reactive_power_c_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_a_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_b_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_c_offset, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CURRENT_A_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(current_a_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CURRENT_B_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(current_b_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CURRENT_C_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(current_c_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(apparent_power_a_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(apparent_power_b_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(apparent_power_c_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_a_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_b_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(active_power_c_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(reactive_power_a_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(reactive_power_b_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(reactive_power_c_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CURRENT_A(_show, _addr) \ - IIO_DEVICE_ATTR(current_a, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CURRENT_B(_show, _addr) \ - IIO_DEVICE_ATTR(current_b, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CURRENT_C(_show, _addr) \ - IIO_DEVICE_ATTR(current_c, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_VOLT_A(_show, _addr) \ - IIO_DEVICE_ATTR(volt_a, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_VOLT_B(_show, _addr) \ - IIO_DEVICE_ATTR(volt_b, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_VOLT_C(_show, _addr) \ - IIO_DEVICE_ATTR(volt_c, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_AENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(aenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_LENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(lenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_RAENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(raenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_LAENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(laenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_VAENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(vaenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_LVAENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(lvaenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_RVAENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(rvaenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_LVARENERGY(_show, _addr) \ - IIO_DEVICE_ATTR(lvarenergy, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CHKSUM(_show, _addr) \ - IIO_DEVICE_ATTR(chksum, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_ANGLE0(_show, _addr) \ - IIO_DEVICE_ATTR(angle0, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_ANGLE1(_show, _addr) \ - IIO_DEVICE_ATTR(angle1, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_ANGLE2(_show, _addr) \ - IIO_DEVICE_ATTR(angle2, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_AWATTHR(_show, _addr) \ - IIO_DEVICE_ATTR(awatthr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_BWATTHR(_show, _addr) \ - IIO_DEVICE_ATTR(bwatthr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CWATTHR(_show, _addr) \ - IIO_DEVICE_ATTR(cwatthr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_AFWATTHR(_show, _addr) \ - IIO_DEVICE_ATTR(afwatthr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_BFWATTHR(_show, _addr) \ - IIO_DEVICE_ATTR(bfwatthr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CFWATTHR(_show, _addr) \ - IIO_DEVICE_ATTR(cfwatthr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_AVARHR(_show, _addr) \ - IIO_DEVICE_ATTR(avarhr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_BVARHR(_show, _addr) \ - IIO_DEVICE_ATTR(bvarhr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CVARHR(_show, _addr) \ - IIO_DEVICE_ATTR(cvarhr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_AVAHR(_show, _addr) \ - IIO_DEVICE_ATTR(avahr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_BVAHR(_show, _addr) \ - IIO_DEVICE_ATTR(bvahr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_CVAHR(_show, _addr) \ - IIO_DEVICE_ATTR(cvahr, 0444, _show, NULL, _addr) - -#define IIO_DEV_ATTR_IOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(ios, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_PHCAL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(phcal, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_APHCAL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(aphcal, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BPHCAL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bphcal, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CPHCAL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cphcal, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_APOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(apos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AAPOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(aapos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BAPOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bapos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CAPOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(capos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AVRMSGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(avrmsgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BVRMSGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bvrmsgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CVRMSGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cvrmsgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AIGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(aigain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BIGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bigain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CIGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cigain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_NIGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(nigain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AVGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(avgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BVGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bvgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CVGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cvgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_WGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(wgain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_WDIV(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(wdiv, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CFNUM(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cfnum, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CFDEN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cfden, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CF1DEN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cf1den, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CF2DEN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cf2den, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CF3DEN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cf3den, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_IRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(irms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vrms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AIRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(airms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BIRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(birms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CIRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cirms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_NIRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(nirms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AVRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(avrms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BVRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bvrms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CVRMS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cvrms, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_IRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(irmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vrmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AIRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(airmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BIRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(birmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CIRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cirmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_AVRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(avrmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_BVRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(bvrmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CVRMSOS(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cvrmsos, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VAGAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vagain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_PGA_GAIN(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(pga_gain, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VADIV(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vadiv, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_LINECYC(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(linecyc, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_SAGCYC(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(sagcyc, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_CFCYC(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(cfcyc, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_PEAKCYC(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(peakcyc, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_SAGLVL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(saglvl, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_IPKLVL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(ipklvl, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VPKLVL(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vpklvl, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_IPEAK(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(ipeak, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_RIPEAK(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(ripeak, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VPEAK(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vpeak, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_RVPEAK(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(rvpeak, _mode, _show, _store, _addr) - -#define IIO_DEV_ATTR_VPERIOD(_mode, _show, _store, _addr) \ - IIO_DEVICE_ATTR(vperiod, _mode, _show, _store, _addr) - -/* active energy register, AENERGY, is more than half full */ -#define IIO_EVENT_ATTR_AENERGY_HALF_FULL(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(aenergy_half_full, _evlist, _show, _store, _mask) - -/* a SAG on the line voltage */ -#define IIO_EVENT_ATTR_LINE_VOLT_SAG(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(line_volt_sag, _evlist, _show, _store, _mask) - -/* - * Indicates the end of energy accumulation over an integer number - * of half line cycles - */ -#define IIO_EVENT_ATTR_CYCEND(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(cycend, _evlist, _show, _store, _mask) - -/* on the rising and falling edge of the voltage waveform */ -#define IIO_EVENT_ATTR_ZERO_CROSS(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(zero_cross, _evlist, _show, _store, _mask) - -/* the active energy register has overflowed */ -#define IIO_EVENT_ATTR_AENERGY_OVERFLOW(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(aenergy_overflow, _evlist, _show, _store, _mask) - -/* the apparent energy register has overflowed */ -#define IIO_EVENT_ATTR_VAENERGY_OVERFLOW(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(vaenergy_overflow, _evlist, _show, _store, _mask) - -/* the active energy register, VAENERGY, is more than half full */ -#define IIO_EVENT_ATTR_VAENERGY_HALF_FULL(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(vaenergy_half_full, _evlist, _show, _store, _mask) - -/* the power has gone from negative to positive */ -#define IIO_EVENT_ATTR_PPOS(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(ppos, _evlist, _show, _store, _mask) - -/* the power has gone from positive to negative */ -#define IIO_EVENT_ATTR_PNEG(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(pneg, _evlist, _show, _store, _mask) - -/* waveform sample from Channel 1 has exceeded the IPKLVL value */ -#define IIO_EVENT_ATTR_IPKLVL_EXC(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(ipklvl_exc, _evlist, _show, _store, _mask) - -/* waveform sample from Channel 2 has exceeded the VPKLVL value */ -#define IIO_EVENT_ATTR_VPKLVL_EXC(_evlist, _show, _store, _mask) \ - IIO_EVENT_ATTR_SH(vpklvl_exc, _evlist, _show, _store, _mask) - -#endif /* _METER_H */ -- cgit v1.2.3 From e522a6f7cbeb0cdb672f3f3ed88dd1ed930a76d2 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 10 Feb 2023 16:24:21 -0800 Subject: iio: proximity: sx_common: Add old register mapping Older firmwares still send sensor configuration using a list of registers with opaque values defined during sensor tuning. sx9234 and sx9360 sensor on ACPI based devices are concerned. More schema to configure the sensors will be needed to support devices designed for windows, like Samsung Galaxy Book2. Support schema is: "<_HID>.". For instance "STH9324,reg_adv_ctrl2" in: Scope (\_SB.PCI0.I2C2) { Device (SX28) { Name (_HID, "STH9324") // _HID: Hardware ID ... Name (_DSD, Package (0x02) // _DSD: Device-Specific Data { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */, Package (0x3F) { ... Package (0x02) { "STH9324,reg_adv_ctrl2", Zero },` Signed-off-by: Gwendal Grignou Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20230211002421.3447060-1-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9324.c | 96 ++++++++++++++++++++------------------- drivers/iio/proximity/sx9360.c | 32 ++++++------- drivers/iio/proximity/sx_common.c | 21 +++++++++ drivers/iio/proximity/sx_common.h | 5 ++ 4 files changed, 92 insertions(+), 62 deletions(-) diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index 977cf17cec52..9a40ca32bb1c 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -783,73 +783,75 @@ static int sx9324_write_raw(struct iio_dev *indio_dev, static const struct sx_common_reg_default sx9324_default_regs[] = { { SX9324_REG_IRQ_MSK, 0x00 }, - { SX9324_REG_IRQ_CFG0, 0x00 }, - { SX9324_REG_IRQ_CFG1, SX9324_REG_IRQ_CFG1_FAILCOND }, - { SX9324_REG_IRQ_CFG2, 0x00 }, - { SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS }, + { SX9324_REG_IRQ_CFG0, 0x00, "irq_cfg0" }, + { SX9324_REG_IRQ_CFG1, SX9324_REG_IRQ_CFG1_FAILCOND, "irq_cfg1" }, + { SX9324_REG_IRQ_CFG2, 0x00, "irq_cfg2" }, + { SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS, "gnrl_ctrl0" }, /* * The lower 4 bits should not be set as it enable sensors measurements. * Turning the detection on before the configuration values are set to * good values can cause the device to return erroneous readings. */ - { SX9324_REG_GNRL_CTRL1, SX9324_REG_GNRL_CTRL1_PAUSECTRL }, + { SX9324_REG_GNRL_CTRL1, SX9324_REG_GNRL_CTRL1_PAUSECTRL, "gnrl_ctrl1" }, - { SX9324_REG_AFE_CTRL0, SX9324_REG_AFE_CTRL0_RINT_LOWEST }, - { SX9324_REG_AFE_CTRL3, 0x00 }, + { SX9324_REG_AFE_CTRL0, SX9324_REG_AFE_CTRL0_RINT_LOWEST, "afe_ctrl0" }, + { SX9324_REG_AFE_CTRL3, 0x00, "afe_ctrl3" }, { SX9324_REG_AFE_CTRL4, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ | - SX9324_REG_AFE_CTRL4_RES_100 }, - { SX9324_REG_AFE_CTRL6, 0x00 }, + SX9324_REG_AFE_CTRL4_RES_100, "afe_ctrl4" }, + { SX9324_REG_AFE_CTRL6, 0x00, "afe_ctrl6" }, { SX9324_REG_AFE_CTRL7, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ | - SX9324_REG_AFE_CTRL4_RES_100 }, + SX9324_REG_AFE_CTRL4_RES_100, "afe_ctrl7" }, /* TODO(gwendal): PHx use chip default or all grounded? */ - { SX9324_REG_AFE_PH0, 0x29 }, - { SX9324_REG_AFE_PH1, 0x26 }, - { SX9324_REG_AFE_PH2, 0x1a }, - { SX9324_REG_AFE_PH3, 0x16 }, + { SX9324_REG_AFE_PH0, 0x29, "afe_ph0" }, + { SX9324_REG_AFE_PH1, 0x26, "afe_ph1" }, + { SX9324_REG_AFE_PH2, 0x1a, "afe_ph2" }, + { SX9324_REG_AFE_PH3, 0x16, "afe_ph3" }, { SX9324_REG_AFE_CTRL8, SX9324_REG_AFE_CTRL8_RESERVED | - SX9324_REG_AFE_CTRL8_RESFILTIN_4KOHM }, - { SX9324_REG_AFE_CTRL9, SX9324_REG_AFE_CTRL9_AGAIN_1 }, + SX9324_REG_AFE_CTRL8_RESFILTIN_4KOHM, "afe_ctrl8" }, + { SX9324_REG_AFE_CTRL9, SX9324_REG_AFE_CTRL9_AGAIN_1, "afe_ctrl9" }, { SX9324_REG_PROX_CTRL0, SX9324_REG_PROX_CTRL0_GAIN_1 << SX9324_REG_PROX_CTRL0_GAIN_SHIFT | - SX9324_REG_PROX_CTRL0_RAWFILT_1P50 }, + SX9324_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0" }, { SX9324_REG_PROX_CTRL1, SX9324_REG_PROX_CTRL0_GAIN_1 << SX9324_REG_PROX_CTRL0_GAIN_SHIFT | - SX9324_REG_PROX_CTRL0_RAWFILT_1P50 }, - { SX9324_REG_PROX_CTRL2, SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K }, + SX9324_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl1" }, + { SX9324_REG_PROX_CTRL2, SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K, "prox_ctrl2" }, { SX9324_REG_PROX_CTRL3, SX9324_REG_PROX_CTRL3_AVGDEB_2SAMPLES | - SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K }, + SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K, "prox_ctrl3" }, { SX9324_REG_PROX_CTRL4, SX9324_REG_PROX_CTRL4_AVGNEG_FILT_2 | - SX9324_REG_PROX_CTRL4_AVGPOS_FILT_256 }, - { SX9324_REG_PROX_CTRL5, 0x00 }, - { SX9324_REG_PROX_CTRL6, SX9324_REG_PROX_CTRL6_PROXTHRESH_32 }, - { SX9324_REG_PROX_CTRL7, SX9324_REG_PROX_CTRL6_PROXTHRESH_32 }, - { SX9324_REG_ADV_CTRL0, 0x00 }, - { SX9324_REG_ADV_CTRL1, 0x00 }, - { SX9324_REG_ADV_CTRL2, 0x00 }, - { SX9324_REG_ADV_CTRL3, 0x00 }, - { SX9324_REG_ADV_CTRL4, 0x00 }, + SX9324_REG_PROX_CTRL4_AVGPOS_FILT_256, "prox_ctrl4" }, + { SX9324_REG_PROX_CTRL5, 0x00, "prox_ctrl5" }, + { SX9324_REG_PROX_CTRL6, SX9324_REG_PROX_CTRL6_PROXTHRESH_32, "prox_ctrl6" }, + { SX9324_REG_PROX_CTRL7, SX9324_REG_PROX_CTRL6_PROXTHRESH_32, "prox_ctrl7" }, + { SX9324_REG_ADV_CTRL0, 0x00, "adv_ctrl0" }, + { SX9324_REG_ADV_CTRL1, 0x00, "adv_ctrl1" }, + { SX9324_REG_ADV_CTRL2, 0x00, "adv_ctrl2" }, + { SX9324_REG_ADV_CTRL3, 0x00, "adv_ctrl3" }, + { SX9324_REG_ADV_CTRL4, 0x00, "adv_ctrl4" }, { SX9324_REG_ADV_CTRL5, SX9324_REG_ADV_CTRL5_STARTUP_SENSOR_1 | - SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1 }, - { SX9324_REG_ADV_CTRL6, 0x00 }, - { SX9324_REG_ADV_CTRL7, 0x00 }, - { SX9324_REG_ADV_CTRL8, 0x00 }, - { SX9324_REG_ADV_CTRL9, 0x00 }, + SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1, "adv_ctrl5" }, + { SX9324_REG_ADV_CTRL6, 0x00, "adv_ctrl6" }, + { SX9324_REG_ADV_CTRL7, 0x00, "adv_ctrl7" }, + { SX9324_REG_ADV_CTRL8, 0x00, "adv_ctrl8" }, + { SX9324_REG_ADV_CTRL9, 0x00, "adv_ctrl9" }, /* Body/Table threshold */ - { SX9324_REG_ADV_CTRL10, 0x00 }, - { SX9324_REG_ADV_CTRL11, 0x00 }, - { SX9324_REG_ADV_CTRL12, 0x00 }, + { SX9324_REG_ADV_CTRL10, 0x00, "adv_ctrl10" }, + { SX9324_REG_ADV_CTRL11, 0x00, "adv_ctrl11" }, + { SX9324_REG_ADV_CTRL12, 0x00, "adv_ctrl12" }, /* TODO(gwendal): SAR currenly disabled */ - { SX9324_REG_ADV_CTRL13, 0x00 }, - { SX9324_REG_ADV_CTRL14, 0x00 }, - { SX9324_REG_ADV_CTRL15, 0x00 }, - { SX9324_REG_ADV_CTRL16, 0x00 }, - { SX9324_REG_ADV_CTRL17, 0x00 }, - { SX9324_REG_ADV_CTRL18, 0x00 }, - { SX9324_REG_ADV_CTRL19, SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION }, - { SX9324_REG_ADV_CTRL20, SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION }, + { SX9324_REG_ADV_CTRL13, 0x00, "adv_ctrl13" }, + { SX9324_REG_ADV_CTRL14, 0x00, "adv_ctrl14" }, + { SX9324_REG_ADV_CTRL15, 0x00, "adv_ctrl15" }, + { SX9324_REG_ADV_CTRL16, 0x00, "adv_ctrl16" }, + { SX9324_REG_ADV_CTRL17, 0x00, "adv_ctrl17" }, + { SX9324_REG_ADV_CTRL18, 0x00, "adv_ctrl18" }, + { SX9324_REG_ADV_CTRL19, + SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION, "adv_ctrl19" }, + { SX9324_REG_ADV_CTRL20, + SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION, "adv_ctrl20" }, }; /* Activate all channels and perform an initial compensation. */ @@ -889,13 +891,15 @@ sx9324_get_default_reg(struct device *dev, int idx, const char *res; memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def)); + + sx_common_get_raw_register_config(dev, reg_def); switch (reg_def->reg) { case SX9324_REG_AFE_PH0: case SX9324_REG_AFE_PH1: case SX9324_REG_AFE_PH2: case SX9324_REG_AFE_PH3: ph = reg_def->reg - SX9324_REG_AFE_PH0; - scnprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph); + snprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph); count = device_property_count_u32(dev, prop); if (count != ARRAY_SIZE(pin_defs)) diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index 6e19d22e6a01..a50d9176411a 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -663,37 +663,37 @@ static int sx9360_write_raw(struct iio_dev *indio_dev, static const struct sx_common_reg_default sx9360_default_regs[] = { { SX9360_REG_IRQ_MSK, 0x00 }, - { SX9360_REG_IRQ_CFG, 0x00 }, + { SX9360_REG_IRQ_CFG, 0x00, "irq_cfg" }, /* * The lower 2 bits should not be set as it enable sensors measurements. * Turning the detection on before the configuration values are set to * good values can cause the device to return erroneous readings. */ - { SX9360_REG_GNRL_CTRL0, 0x00 }, - { SX9360_REG_GNRL_CTRL1, 0x00 }, - { SX9360_REG_GNRL_CTRL2, SX9360_REG_GNRL_CTRL2_PERIOD_102MS }, + { SX9360_REG_GNRL_CTRL0, 0x00, "gnrl_ctrl0" }, + { SX9360_REG_GNRL_CTRL1, 0x00, "gnrl_ctrl1" }, + { SX9360_REG_GNRL_CTRL2, SX9360_REG_GNRL_CTRL2_PERIOD_102MS, "gnrl_ctrl2" }, - { SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_CTRL1_RESFILTIN_0OHMS }, + { SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_CTRL1_RESFILTIN_0OHMS, "afe_ctrl0" }, { SX9360_REG_AFE_PARAM0_PHR, SX9360_REG_AFE_PARAM0_RSVD | - SX9360_REG_AFE_PARAM0_RESOLUTION_128 }, + SX9360_REG_AFE_PARAM0_RESOLUTION_128, "afe_param0_phr" }, { SX9360_REG_AFE_PARAM1_PHR, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | - SX9360_REG_AFE_PARAM1_FREQ_83_33HZ }, + SX9360_REG_AFE_PARAM1_FREQ_83_33HZ, "afe_param1_phr" }, { SX9360_REG_AFE_PARAM0_PHM, SX9360_REG_AFE_PARAM0_RSVD | - SX9360_REG_AFE_PARAM0_RESOLUTION_128 }, + SX9360_REG_AFE_PARAM0_RESOLUTION_128, "afe_param0_phm" }, { SX9360_REG_AFE_PARAM1_PHM, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | - SX9360_REG_AFE_PARAM1_FREQ_83_33HZ }, + SX9360_REG_AFE_PARAM1_FREQ_83_33HZ, "afe_param1_phm" }, { SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL0_GAIN_1 | - SX9360_REG_PROX_CTRL0_RAWFILT_1P50 }, + SX9360_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0_phr" }, { SX9360_REG_PROX_CTRL0_PHM, SX9360_REG_PROX_CTRL0_GAIN_1 | - SX9360_REG_PROX_CTRL0_RAWFILT_1P50 }, - { SX9360_REG_PROX_CTRL1, SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K }, + SX9360_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0_phm" }, + { SX9360_REG_PROX_CTRL1, SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K, "prox_ctrl1" }, { SX9360_REG_PROX_CTRL2, SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES | - SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K }, + SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K, "prox_ctrl2" }, { SX9360_REG_PROX_CTRL3, SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 | - SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256 }, - { SX9360_REG_PROX_CTRL4, 0x00 }, - { SX9360_REG_PROX_CTRL5, SX9360_REG_PROX_CTRL5_PROXTHRESH_32 }, + SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256, "prox_ctrl3" }, + { SX9360_REG_PROX_CTRL4, 0x00, "prox_ctrl4" }, + { SX9360_REG_PROX_CTRL5, SX9360_REG_PROX_CTRL5_PROXTHRESH_32, "prox_ctrl5" }, }; /* Activate all channels and perform an initial compensation. */ diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index eba9256730ec..fe07d1444ac3 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -424,6 +424,27 @@ static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = { .postdisable = sx_common_buffer_postdisable, }; +void sx_common_get_raw_register_config(struct device *dev, + struct sx_common_reg_default *reg_def) +{ +#ifdef CONFIG_ACPI + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 raw = 0, ret; + char prop[80]; + + if (!reg_def->property || !adev) + return; + + snprintf(prop, ARRAY_SIZE(prop), "%s,reg_%s", acpi_device_hid(adev), reg_def->property); + ret = device_property_read_u32(dev, prop, &raw); + if (ret) + return; + + reg_def->def = raw; +#endif +} +EXPORT_SYMBOL_NS_GPL(sx_common_get_raw_register_config, SEMTECH_PROX); + #define SX_COMMON_SOFT_RESET 0xde static int sx_common_init_device(struct device *dev, struct iio_dev *indio_dev) diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index a2bbdef200d1..101b90e52ff2 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -8,6 +8,7 @@ #ifndef IIO_SX_COMMON_H #define IIO_SX_COMMON_H +#include #include #include #include @@ -26,6 +27,7 @@ static_assert(SX_COMMON_MAX_NUM_CHANNELS < BITS_PER_LONG); struct sx_common_reg_default { u8 reg; u8 def; + const char *property; }; /** @@ -148,6 +150,9 @@ int sx_common_probe(struct i2c_client *client, const struct sx_common_chip_info *chip_info, const struct regmap_config *regmap_config); +void sx_common_get_raw_register_config(struct device *dev, + struct sx_common_reg_default *reg_def); + /* 3 is the number of events defined by a single phase. */ extern const struct iio_event_spec sx_common_events[3]; -- cgit v1.2.3 From e2b39db176d9d6420669aed477c949e8895d062b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 13 Feb 2023 16:32:08 +0100 Subject: iio: imu: adis16475: correct copy paste mistake in comment block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The minimal advised lower rate for adis16475 compatible devices is 1900HZ and not 4000HZ. Set that right in the comments so that it does not generate any confusion. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20230213153208.1027602-1-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index aec55f7e1f26..3abffb01ba31 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -326,11 +326,11 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) /* * This is not an hard requirement but it's not advised to run the IMU - * with a sample rate lower than 4000Hz due to possible undersampling + * with a sample rate lower than 1900Hz due to possible undersampling * issues. However, there are users that might really want to take the risk. * Hence, we provide a module parameter for them. If set, we allow sample - * rates lower than 4KHz. By default, we won't allow this and we just roundup - * the rate to the next multiple of the input clock bigger than 4KHz. This + * rates lower than 1.9KHz. By default, we won't allow this and we just roundup + * the rate to the next multiple of the input clock bigger than 1.9KHz. This * is done like this as in some cases (when DEC_RATE is 0) might give * us the closest value to the one desired by the user... */ -- cgit v1.2.3 From 8957e5344353e9cd3f6b1e3004942e35449fa0e8 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Fri, 17 Feb 2023 15:34:03 +0000 Subject: iio: adc: axp20x_adc: Minor code cleanups The code may be clearer if parameters are not re-purposed to hold temporary results like register values, so introduce local variables as necessary to avoid that. Regroup macros based on chip type, and use the FIELD_PREP() macro instead of a hand-rolled version. Suggested-by: Jonathan Cameron Reviewed-by: Chen-Yu Tsai Reviewed-by: Andy Shevchenko Signed-off-by: Aidan MacDonald Link: https://lore.kernel.org/r/20230217153404.32481-1-aidanmacdonald.0x0@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 61 +++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 53bf7d4899d2..ab25e6e1ff65 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -5,6 +5,7 @@ * Quentin Schulz */ +#include #include #include #include @@ -22,20 +23,20 @@ #include #define AXP20X_ADC_EN1_MASK GENMASK(7, 0) - #define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7)) + #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0)) #define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0) #define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1) -#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0)) -#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1) #define AXP20X_ADC_RATE_MASK GENMASK(7, 6) -#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) -#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) #define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK) + #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) + +#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) +#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) #define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) #define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK) #define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x)) @@ -234,7 +235,7 @@ static int axp20x_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); - int size = 12; + int ret, size; /* * N.B.: Unlike the Chinese datasheets tell, the charging current is @@ -246,10 +247,11 @@ static int axp20x_adc_raw(struct iio_dev *indio_dev, else size = 12; - *val = axp20x_read_variable_width(info->regmap, chan->address, size); - if (*val < 0) - return *val; + ret = axp20x_read_variable_width(info->regmap, chan->address, size); + if (ret < 0) + return ret; + *val = ret; return IIO_VAL_INT; } @@ -257,11 +259,13 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; - *val = axp20x_read_variable_width(info->regmap, chan->address, 12); - if (*val < 0) - return *val; + ret = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (ret < 0) + return ret; + *val = ret; return IIO_VAL_INT; } @@ -269,11 +273,13 @@ static int axp813_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; - *val = axp20x_read_variable_width(info->regmap, chan->address, 12); - if (*val < 0) - return *val; + ret = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (ret < 0) + return ret; + *val = ret; return IIO_VAL_INT; } @@ -443,27 +449,27 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel, int *val) { struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int regval; int ret; - ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val); + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®val); if (ret < 0) return ret; switch (channel) { case AXP20X_GPIO0_V: - *val &= AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO0, regval); break; case AXP20X_GPIO1_V: - *val &= AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO1, regval); break; default: return -EINVAL; } - *val = *val ? 700000 : 0; - + *val = regval ? 700000 : 0; return IIO_VAL_INT; } @@ -548,7 +554,7 @@ static int axp20x_write_raw(struct iio_dev *indio_dev, long mask) { struct axp20x_adc_iio *info = iio_priv(indio_dev); - unsigned int reg, regval; + unsigned int regmask, regval; /* * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets @@ -560,25 +566,22 @@ static int axp20x_write_raw(struct iio_dev *indio_dev, if (val != 0 && val != 700000) return -EINVAL; - val = val ? 1 : 0; - switch (chan->channel) { case AXP20X_GPIO0_V: - reg = AXP20X_GPIO10_IN_RANGE_GPIO0; - regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val); + regmask = AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO0, !!val); break; case AXP20X_GPIO1_V: - reg = AXP20X_GPIO10_IN_RANGE_GPIO1; - regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val); + regmask = AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO1, !!val); break; default: return -EINVAL; } - return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg, - regval); + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval); } static const struct iio_info axp20x_adc_iio_info = { -- cgit v1.2.3 From c286b0472ca8d76092c1f4fb782c6745b26e74f6 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Fri, 17 Feb 2023 15:34:04 +0000 Subject: iio: adc: axp20x_adc: Replace adc_en2 flag with adc_en2_mask field The adc_en2 flag is essentially specific to axp20x-compatible devices because it hardcodes register values. Replace it with a mask field so the register value can be specified in device match data. Reviewed-by: Andy Shevchenko Signed-off-by: Aidan MacDonald Link: https://lore.kernel.org/r/20230217153404.32481-2-aidanmacdonald.0x0@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index ab25e6e1ff65..75bda94dbce1 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -623,9 +623,9 @@ struct axp_data { int num_channels; struct iio_chan_spec const *channels; unsigned long adc_en1_mask; + unsigned long adc_en2_mask; int (*adc_rate)(struct axp20x_adc_iio *info, int rate); - bool adc_en2; struct iio_map *maps; }; @@ -634,8 +634,8 @@ static const struct axp_data axp20x_data = { .num_channels = ARRAY_SIZE(axp20x_adc_channels), .channels = axp20x_adc_channels, .adc_en1_mask = AXP20X_ADC_EN1_MASK, + .adc_en2_mask = AXP20X_ADC_EN2_MASK, .adc_rate = axp20x_adc_rate, - .adc_en2 = true, .maps = axp20x_maps, }; @@ -645,7 +645,6 @@ static const struct axp_data axp22x_data = { .channels = axp22x_adc_channels, .adc_en1_mask = AXP22X_ADC_EN1_MASK, .adc_rate = axp22x_adc_rate, - .adc_en2 = false, .maps = axp22x_maps, }; @@ -655,7 +654,6 @@ static const struct axp_data axp813_data = { .channels = axp813_adc_channels, .adc_en1_mask = AXP22X_ADC_EN1_MASK, .adc_rate = axp813_adc_rate, - .adc_en2 = false, .maps = axp22x_maps, }; @@ -713,10 +711,10 @@ static int axp20x_probe(struct platform_device *pdev) /* Enable the ADCs on IP */ regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask); - if (info->data->adc_en2) - /* Enable GPIO0/1 and internal temperature ADCs */ + if (info->data->adc_en2_mask) regmap_update_bits(info->regmap, AXP20X_ADC_EN2, - AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK); + info->data->adc_en2_mask, + info->data->adc_en2_mask); /* Configure ADCs rate */ info->data->adc_rate(info, 100); @@ -741,7 +739,7 @@ fail_register: fail_map: regmap_write(info->regmap, AXP20X_ADC_EN1, 0); - if (info->data->adc_en2) + if (info->data->adc_en2_mask) regmap_write(info->regmap, AXP20X_ADC_EN2, 0); return ret; @@ -757,7 +755,7 @@ static int axp20x_remove(struct platform_device *pdev) regmap_write(info->regmap, AXP20X_ADC_EN1, 0); - if (info->data->adc_en2) + if (info->data->adc_en2_mask) regmap_write(info->regmap, AXP20X_ADC_EN2, 0); return 0; -- cgit v1.2.3 From 4ab8bef1eaa534d3669852f2fe22e0f1c86c1426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Sun, 19 Feb 2023 21:44:39 +0100 Subject: iio: adc: meson_saradc: Better handle BL30 not releasing the hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meson_sar_adc_lock() might return an error if BL30 doesn't release its lock on the hardware. Just returning early from .remove() is wrong however as this keeps the clocks and regulators on which is never cleaned up later. Given the BL30 not giving up its lock is a strong hint for broken behaviour, and there is nothing we can do about that: Just clean up ignoring the fact that we're not holding the lock. Signed-off-by: Uwe Kleine-König Reviewed-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20230219204439.1641640-1-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 85b6826cc10c..18937a262af6 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -957,14 +957,18 @@ err_lock: return ret; } -static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) +static void meson_sar_adc_hw_disable(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); int ret; + /* + * If taking the lock fails we have to assume that BL30 is broken. The + * best we can do then is to release the resources anyhow. + */ ret = meson_sar_adc_lock(indio_dev); if (ret) - return ret; + dev_err(indio_dev->dev.parent, "Failed to lock ADC (%pE)\n", ERR_PTR(ret)); clk_disable_unprepare(priv->adc_clk); @@ -977,9 +981,8 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) regulator_disable(priv->vref); - meson_sar_adc_unlock(indio_dev); - - return 0; + if (!ret) + meson_sar_adc_unlock(indio_dev); } static irqreturn_t meson_sar_adc_irq(int irq, void *data) @@ -1283,14 +1286,18 @@ static int meson_sar_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); - return meson_sar_adc_hw_disable(indio_dev); + meson_sar_adc_hw_disable(indio_dev); + + return 0; } static int meson_sar_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); - return meson_sar_adc_hw_disable(indio_dev); + meson_sar_adc_hw_disable(indio_dev); + + return 0; } static int meson_sar_adc_resume(struct device *dev) -- cgit v1.2.3 From 0b0b772637cd7f35c15830bbb8e8ec66d6c34ddd Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 17:57:59 +0100 Subject: iio: pressure: bmp280: Use chip_info pointers for each chip as driver data Refactor driver I2C and SPI implementations using pointers for each variant's chip_info as the driver data. Adds the regmap configuration to the chip_info struct. Suggested-by: Andy Shevchenko Suggested-by: Jonathan Cameron Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/a48cfa756be48d61dbf656c65daff6e9a1290e6f.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 190 ++++--------------------------------- drivers/iio/pressure/bmp280-i2c.c | 43 ++++----- drivers/iio/pressure/bmp280-spi.c | 45 ++++----- drivers/iio/pressure/bmp280.h | 164 +++++++++++++++++++++++++++++++- 4 files changed, 215 insertions(+), 227 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index c0aff78489b4..2c8773db4b73 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -49,65 +49,6 @@ */ enum { AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC, MD }; -struct bmp180_calib { - s16 AC1; - s16 AC2; - s16 AC3; - u16 AC4; - u16 AC5; - u16 AC6; - s16 B1; - s16 B2; - s16 MB; - s16 MC; - s16 MD; -}; - -/* See datasheet Section 4.2.2. */ -struct bmp280_calib { - u16 T1; - s16 T2; - s16 T3; - u16 P1; - s16 P2; - s16 P3; - s16 P4; - s16 P5; - s16 P6; - s16 P7; - s16 P8; - s16 P9; - u8 H1; - s16 H2; - u8 H3; - s16 H4; - s16 H5; - s8 H6; -}; - -/* See datasheet Section 3.11.1. */ -struct bmp380_calib { - u16 T1; - u16 T2; - s8 T3; - s16 P1; - s16 P2; - s8 P3; - s8 P4; - u16 P5; - u16 P6; - s8 P7; - s8 P8; - s16 P9; - s8 P10; - s8 P11; -}; - -static const char *const bmp280_supply_names[] = { - "vddd", "vdda" -}; - -#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) enum bmp380_odr { BMP380_ODR_200HZ, @@ -130,94 +71,6 @@ enum bmp380_odr { BMP380_ODR_0_0015HZ, }; -struct bmp280_data { - struct device *dev; - struct mutex lock; - struct regmap *regmap; - struct completion done; - bool use_eoc; - const struct bmp280_chip_info *chip_info; - union { - struct bmp180_calib bmp180; - struct bmp280_calib bmp280; - struct bmp380_calib bmp380; - } calib; - struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; - unsigned int start_up_time; /* in microseconds */ - - /* log of base 2 of oversampling rate */ - u8 oversampling_press; - u8 oversampling_temp; - u8 oversampling_humid; - u8 iir_filter_coeff; - - /* - * BMP380 devices introduce sampling frequency configuration. See - * datasheet sections 3.3.3. and 4.3.19 for more details. - * - * BMx280 devices allowed indirect configuration of sampling frequency - * changing the t_standby duration between measurements, as detailed on - * section 3.6.3 of the datasheet. - */ - int sampling_freq; - - /* - * Carryover value from temperature conversion, used in pressure - * calculation. - */ - s32 t_fine; - - /* - * DMA (thus cache coherency maintenance) may require the - * transfer buffers to live in their own cache lines. - */ - union { - /* Sensor data buffer */ - u8 buf[3]; - /* Calibration data buffers */ - __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; - __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; - u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; - /* Miscellaneous, endianess-aware data buffers */ - __le16 le16; - __be16 be16; - } __aligned(IIO_DMA_MINALIGN); -}; - -struct bmp280_chip_info { - unsigned int id_reg; - - const struct iio_chan_spec *channels; - int num_channels; - unsigned int start_up_time; - - const int *oversampling_temp_avail; - int num_oversampling_temp_avail; - int oversampling_temp_default; - - const int *oversampling_press_avail; - int num_oversampling_press_avail; - int oversampling_press_default; - - const int *oversampling_humid_avail; - int num_oversampling_humid_avail; - int oversampling_humid_default; - - const int *iir_filter_coeffs_avail; - int num_iir_filter_coeffs_avail; - int iir_filter_coeff_default; - - const int (*sampling_freq_avail)[2]; - int num_sampling_freq_avail; - int sampling_freq_default; - - int (*chip_config)(struct bmp280_data *); - int (*read_temp)(struct bmp280_data *, int *); - int (*read_press)(struct bmp280_data *, int *, int *); - int (*read_humid)(struct bmp280_data *, int *, int *); - int (*read_calib)(struct bmp280_data *); -}; - /* * These enums are used for indexing into the array of compensation * parameters for BMP280. @@ -905,8 +758,10 @@ static int bmp280_chip_config(struct bmp280_data *data) static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; -static const struct bmp280_chip_info bmp280_chip_info = { +const struct bmp280_chip_info bmp280_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BMP280_CHIP_ID, + .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 2, @@ -934,6 +789,7 @@ static const struct bmp280_chip_info bmp280_chip_info = { .read_press = bmp280_read_press, .read_calib = bmp280_read_calib, }; +EXPORT_SYMBOL_NS(bmp280_chip_info, IIO_BMP280); static int bme280_chip_config(struct bmp280_data *data) { @@ -953,8 +809,10 @@ static int bme280_chip_config(struct bmp280_data *data) return bmp280_chip_config(data); } -static const struct bmp280_chip_info bme280_chip_info = { +const struct bmp280_chip_info bme280_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BME280_CHIP_ID, + .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 3, @@ -977,6 +835,7 @@ static const struct bmp280_chip_info bme280_chip_info = { .read_humid = bmp280_read_humid, .read_calib = bme280_read_calib, }; +EXPORT_SYMBOL_NS(bme280_chip_info, IIO_BMP280); /* * Helper function to send a command to BMP3XX sensors. @@ -1319,8 +1178,10 @@ static int bmp380_chip_config(struct bmp280_data *data) static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; -static const struct bmp280_chip_info bmp380_chip_info = { +const struct bmp280_chip_info bmp380_chip_info = { .id_reg = BMP380_REG_ID, + .chip_id = BMP380_CHIP_ID, + .regmap_config = &bmp380_regmap_config, .start_up_time = 2000, .channels = bmp380_channels, .num_channels = 2, @@ -1346,6 +1207,7 @@ static const struct bmp280_chip_info bmp380_chip_info = { .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, }; +EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { @@ -1579,8 +1441,10 @@ static int bmp180_chip_config(struct bmp280_data *data) static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; -static const struct bmp280_chip_info bmp180_chip_info = { +const struct bmp280_chip_info bmp180_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BMP180_CHIP_ID, + .regmap_config = &bmp180_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 2, @@ -1600,6 +1464,7 @@ static const struct bmp280_chip_info bmp180_chip_info = { .read_press = bmp180_read_press, .read_calib = bmp180_read_calib, }; +EXPORT_SYMBOL_NS(bmp180_chip_info, IIO_BMP280); static irqreturn_t bmp085_eoc_irq(int irq, void *d) { @@ -1661,11 +1526,10 @@ static void bmp280_regulators_disable(void *data) int bmp280_common_probe(struct device *dev, struct regmap *regmap, - unsigned int chip, + const struct bmp280_chip_info *chip_info, const char *name, int irq) { - const struct bmp280_chip_info *chip_info; struct iio_dev *indio_dev; struct bmp280_data *data; struct gpio_desc *gpiod; @@ -1684,22 +1548,6 @@ int bmp280_common_probe(struct device *dev, indio_dev->info = &bmp280_info; indio_dev->modes = INDIO_DIRECT_MODE; - switch (chip) { - case BMP180_CHIP_ID: - chip_info = &bmp180_chip_info; - break; - case BMP280_CHIP_ID: - chip_info = &bmp280_chip_info; - break; - case BME280_CHIP_ID: - chip_info = &bme280_chip_info; - break; - case BMP380_CHIP_ID: - chip_info = &bmp380_chip_info; - break; - default: - return -EINVAL; - } data->chip_info = chip_info; /* Apply initial values from chip info structure */ @@ -1751,9 +1599,9 @@ int bmp280_common_probe(struct device *dev, ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id); if (ret < 0) return ret; - if (chip_id != chip) { + if (chip_id != data->chip_info->chip_id) { dev_err(dev, "bad chip id: expected %x got %x\n", - chip, chip_id); + data->chip_info->chip_id, chip_id); return -EINVAL; } diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 14eab086d24a..5f908e2c9826 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -8,25 +8,14 @@ static int bmp280_i2c_probe(struct i2c_client *client) { struct regmap *regmap; - const struct regmap_config *regmap_config; + const struct bmp280_chip_info *chip_info; const struct i2c_device_id *id = i2c_client_get_device_id(client); - switch (id->driver_data) { - case BMP180_CHIP_ID: - regmap_config = &bmp180_regmap_config; - break; - case BMP280_CHIP_ID: - case BME280_CHIP_ID: - regmap_config = &bmp280_regmap_config; - break; - case BMP380_CHIP_ID: - regmap_config = &bmp380_regmap_config; - break; - default: - return -EINVAL; - } + chip_info = device_get_match_data(&client->dev); + if (!chip_info) + chip_info = (const struct bmp280_chip_info *) id->driver_data; - regmap = devm_regmap_init_i2c(client, regmap_config); + regmap = devm_regmap_init_i2c(client, chip_info->regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "failed to allocate register map\n"); return PTR_ERR(regmap); @@ -34,27 +23,27 @@ static int bmp280_i2c_probe(struct i2c_client *client) return bmp280_common_probe(&client->dev, regmap, - id->driver_data, + chip_info, id->name, client->irq); } static const struct of_device_id bmp280_of_i2c_match[] = { - { .compatible = "bosch,bmp085", .data = (void *)BMP180_CHIP_ID }, - { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, - { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, - { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, - { .compatible = "bosch,bmp380", .data = (void *)BMP380_CHIP_ID }, + { .compatible = "bosch,bmp085", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp180", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, + { .compatible = "bosch,bme280", .data = &bme280_chip_info }, + { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); static const struct i2c_device_id bmp280_i2c_id[] = { - {"bmp085", BMP180_CHIP_ID }, - {"bmp180", BMP180_CHIP_ID }, - {"bmp280", BMP280_CHIP_ID }, - {"bme280", BME280_CHIP_ID }, - {"bmp380", BMP380_CHIP_ID }, + {"bmp085", (kernel_ulong_t)&bmp180_chip_info }, + {"bmp180", (kernel_ulong_t)&bmp180_chip_info }, + {"bmp280", (kernel_ulong_t)&bmp280_chip_info }, + {"bme280", (kernel_ulong_t)&bme280_chip_info }, + {"bmp380", (kernel_ulong_t)&bmp380_chip_info }, { }, }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 011c68e07ebf..d5a224b95185 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -47,8 +47,8 @@ static struct regmap_bus bmp280_regmap_bus = { static int bmp280_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); + const struct bmp280_chip_info *chip_info; struct regmap *regmap; - const struct regmap_config *regmap_config; int ret; spi->bits_per_word = 8; @@ -58,25 +58,14 @@ static int bmp280_spi_probe(struct spi_device *spi) return ret; } - switch (id->driver_data) { - case BMP180_CHIP_ID: - regmap_config = &bmp180_regmap_config; - break; - case BMP280_CHIP_ID: - case BME280_CHIP_ID: - regmap_config = &bmp280_regmap_config; - break; - case BMP380_CHIP_ID: - regmap_config = &bmp380_regmap_config; - break; - default: - return -EINVAL; - } + chip_info = device_get_match_data(&spi->dev); + if (!chip_info) + chip_info = (const struct bmp280_chip_info *) id->driver_data; regmap = devm_regmap_init(&spi->dev, &bmp280_regmap_bus, &spi->dev, - regmap_config); + chip_info->regmap_config); if (IS_ERR(regmap)) { dev_err(&spi->dev, "failed to allocate register map\n"); return PTR_ERR(regmap); @@ -84,28 +73,28 @@ static int bmp280_spi_probe(struct spi_device *spi) return bmp280_common_probe(&spi->dev, regmap, - id->driver_data, + chip_info, id->name, spi->irq); } static const struct of_device_id bmp280_of_spi_match[] = { - { .compatible = "bosch,bmp085", }, - { .compatible = "bosch,bmp180", }, - { .compatible = "bosch,bmp181", }, - { .compatible = "bosch,bmp280", }, - { .compatible = "bosch,bme280", }, - { .compatible = "bosch,bmp380", }, + { .compatible = "bosch,bmp085", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp180", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp181", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, + { .compatible = "bosch,bme280", .data = &bmp280_chip_info }, + { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); static const struct spi_device_id bmp280_spi_id[] = { - { "bmp180", BMP180_CHIP_ID }, - { "bmp181", BMP180_CHIP_ID }, - { "bmp280", BMP280_CHIP_ID }, - { "bme280", BME280_CHIP_ID }, - { "bmp380", BMP380_CHIP_ID }, + { "bmp180", (kernel_ulong_t)&bmp180_chip_info }, + { "bmp181", (kernel_ulong_t)&bmp180_chip_info }, + { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, + { "bme280", (kernel_ulong_t)&bmp280_chip_info }, + { "bmp380", (kernel_ulong_t)&bmp380_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index c791325c7416..387723fd0772 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -1,7 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include #include +#include #include +#include + /* BMP380 specific registers */ #define BMP380_REG_CMD 0x7E @@ -191,6 +194,165 @@ #define BMP280_PRESS_SKIPPED 0x80000 #define BMP280_HUMIDITY_SKIPPED 0x8000 +/* Core exported structs */ + +static const char *const bmp280_supply_names[] = { + "vddd", "vdda" +}; + +#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) + +struct bmp180_calib { + s16 AC1; + s16 AC2; + s16 AC3; + u16 AC4; + u16 AC5; + u16 AC6; + s16 B1; + s16 B2; + s16 MB; + s16 MC; + s16 MD; +}; + +/* See datasheet Section 4.2.2. */ +struct bmp280_calib { + u16 T1; + s16 T2; + s16 T3; + u16 P1; + s16 P2; + s16 P3; + s16 P4; + s16 P5; + s16 P6; + s16 P7; + s16 P8; + s16 P9; + u8 H1; + s16 H2; + u8 H3; + s16 H4; + s16 H5; + s8 H6; +}; + +/* See datasheet Section 3.11.1. */ +struct bmp380_calib { + u16 T1; + u16 T2; + s8 T3; + s16 P1; + s16 P2; + s8 P3; + s8 P4; + u16 P5; + u16 P6; + s8 P7; + s8 P8; + s16 P9; + s8 P10; + s8 P11; +}; + +struct bmp280_data { + struct device *dev; + struct mutex lock; + struct regmap *regmap; + struct completion done; + bool use_eoc; + const struct bmp280_chip_info *chip_info; + union { + struct bmp180_calib bmp180; + struct bmp280_calib bmp280; + struct bmp380_calib bmp380; + } calib; + struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; + unsigned int start_up_time; /* in microseconds */ + + /* log of base 2 of oversampling rate */ + u8 oversampling_press; + u8 oversampling_temp; + u8 oversampling_humid; + u8 iir_filter_coeff; + + /* + * BMP380 devices introduce sampling frequency configuration. See + * datasheet sections 3.3.3. and 4.3.19 for more details. + * + * BMx280 devices allowed indirect configuration of sampling frequency + * changing the t_standby duration between measurements, as detailed on + * section 3.6.3 of the datasheet. + */ + int sampling_freq; + + /* + * Carryover value from temperature conversion, used in pressure + * calculation. + */ + s32 t_fine; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + union { + /* Sensor data buffer */ + u8 buf[3]; + /* Calibration data buffers */ + __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; + __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; + /* Miscellaneous, endianess-aware data buffers */ + __le16 le16; + __be16 be16; + } __aligned(IIO_DMA_MINALIGN); +}; + +struct bmp280_chip_info { + unsigned int id_reg; + const unsigned int chip_id; + + const struct regmap_config *regmap_config; + + const struct iio_chan_spec *channels; + int num_channels; + unsigned int start_up_time; + + const int *oversampling_temp_avail; + int num_oversampling_temp_avail; + int oversampling_temp_default; + + const int *oversampling_press_avail; + int num_oversampling_press_avail; + int oversampling_press_default; + + const int *oversampling_humid_avail; + int num_oversampling_humid_avail; + int oversampling_humid_default; + + const int *iir_filter_coeffs_avail; + int num_iir_filter_coeffs_avail; + int iir_filter_coeff_default; + + const int (*sampling_freq_avail)[2]; + int num_sampling_freq_avail; + int sampling_freq_default; + + int (*chip_config)(struct bmp280_data *); + int (*read_temp)(struct bmp280_data *, int *); + int (*read_press)(struct bmp280_data *, int *, int *); + int (*read_humid)(struct bmp280_data *, int *, int *); + int (*read_calib)(struct bmp280_data *); +}; + +/* Chip infos for each variant */ +extern const struct bmp280_chip_info bmp180_chip_info; +extern const struct bmp280_chip_info bmp280_chip_info; +extern const struct bmp280_chip_info bme280_chip_info; +extern const struct bmp280_chip_info bmp380_chip_info; + /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; @@ -199,7 +361,7 @@ extern const struct regmap_config bmp380_regmap_config; /* Probe called from different transports */ int bmp280_common_probe(struct device *dev, struct regmap *regmap, - unsigned int chip, + const struct bmp280_chip_info *, const char *name, int irq); -- cgit v1.2.3 From c25ea00fefa4e57a63209769fa3e4903791d05af Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 17:58:00 +0100 Subject: iio: pressure: bmp280: Add preinit callback Adds preinit callback to execute operations on probe before applying initial configuration. Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/fa9513ffad37a6a43b6d46df9d8319ccab6f5870.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 17 ++++++++++++----- drivers/iio/pressure/bmp280.h | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 2c8773db4b73..6467034b1d3e 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1076,6 +1076,12 @@ static const int bmp380_odr_table[][2] = { [BMP380_ODR_0_0015HZ] = {0, 1526}, }; +static int bmp380_preinit(struct bmp280_data *data) +{ + /* BMP3xx requires soft-reset as part of initialization */ + return bmp380_cmd(data, BMP380_CMD_SOFT_RESET); +} + static int bmp380_chip_config(struct bmp280_data *data) { bool change = false, aux; @@ -1206,6 +1212,7 @@ const struct bmp280_chip_info bmp380_chip_info = { .read_temp = bmp380_read_temp, .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, + .preinit = bmp380_preinit, }; EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); @@ -1605,11 +1612,11 @@ int bmp280_common_probe(struct device *dev, return -EINVAL; } - /* BMP3xx requires soft-reset as part of initialization */ - if (chip_id == BMP380_CHIP_ID) { - ret = bmp380_cmd(data, BMP380_CMD_SOFT_RESET); - if (ret < 0) - return ret; + if (data->chip_info->preinit) { + ret = data->chip_info->preinit(data); + if (ret) + return dev_err_probe(data->dev, ret, + "error running preinit tasks\n"); } ret = data->chip_info->chip_config(data); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 387723fd0772..be17104ef0a2 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -345,6 +345,7 @@ struct bmp280_chip_info { int (*read_press)(struct bmp280_data *, int *, int *); int (*read_humid)(struct bmp280_data *, int *, int *); int (*read_calib)(struct bmp280_data *); + int (*preinit)(struct bmp280_data *); }; /* Chip infos for each variant */ -- cgit v1.2.3 From 4d545f9649253a7defda89b44a6a2a2b1b377d9d Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 17:58:01 +0100 Subject: iio: pressure: bmp280: Make read calibration callback optional Newer models do not require read the calibration parameters and apply the compensation algorithms in the sensor. Suggested-by: Jonathan Cameron Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/bb1b95ab3f4e71d3c76543370325c5c9aaa07add.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 6467034b1d3e..22addaaa5393 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1631,10 +1631,12 @@ int bmp280_common_probe(struct device *dev, * time once. They will not change. */ - ret = data->chip_info->read_calib(data); - if (ret < 0) - return dev_err_probe(data->dev, ret, - "failed to read calibration coefficients\n"); + if (data->chip_info->read_calib) { + ret = data->chip_info->read_calib(data); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "failed to read calibration coefficients\n"); + } /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this -- cgit v1.2.3 From 42cde8808573b44e395ee8b3e22f05104afa2e74 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 18:03:03 +0100 Subject: iio: pressure: Kconfig: Delete misleading I2C reference on bmp280 title Title entry for BMP280 called the driver an I2C implementation, when the driver supports both I2C and SPI. Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/d26fe292164ca80685f080101122b5beb564d7db.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index c9453389e4f7..cec4b429cd64 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -17,7 +17,7 @@ config ABP060MG will be called abp060mg. config BMP280 - tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor I2C driver" + tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor driver" depends on (I2C || SPI_MASTER) select REGMAP select BMP280_I2C if (I2C) -- cgit v1.2.3 From 597dfb2af052f175cdd5caa7b62095c7de1ba29e Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 18:03:04 +0100 Subject: iio: pressure: bmp280: Add support for new sensor BMP580 Adds compatibility with the new sensor generation, the BMP580. The measurement and initialization codepaths are adapted from the device datasheet and the repository from manufacturer at https://github.com/boschsensortec/BMP5-Sensor-API. Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/f899fceec9b48bc173bd4b7555f0a237fa32d520.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 6 +- drivers/iio/pressure/bmp280-core.c | 344 ++++++++++++++++++++++++++++++++++- drivers/iio/pressure/bmp280-i2c.c | 2 + drivers/iio/pressure/bmp280-regmap.c | 60 ++++++ drivers/iio/pressure/bmp280-spi.c | 2 + drivers/iio/pressure/bmp280.h | 107 ++++++++++- 6 files changed, 509 insertions(+), 12 deletions(-) diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index cec4b429cd64..02b97e89de50 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -17,14 +17,14 @@ config ABP060MG will be called abp060mg. config BMP280 - tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor driver" + tristate "Bosch Sensortec BMP180/BMP280/BMP380/BMP580 pressure sensor driver" depends on (I2C || SPI_MASTER) select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) help - Say yes here to build support for Bosch Sensortec BMP180, BMP280 and - BMP380 pressure and temperature sensors. Also supports the BME280 with + Say yes here to build support for Bosch Sensortec BMP180, BMP280, BMP380 + and BMP580 pressure and temperature sensors. Also supports the BME280 with an additional humidity sensor channel. To compile this driver as a module, choose M here: the core module diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 22addaaa5393..a7d26d81ec08 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -13,6 +13,7 @@ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf * * Notice: * The link to the bmp180 datasheet points to an outdated version missing these changes: @@ -71,6 +72,41 @@ enum bmp380_odr { BMP380_ODR_0_0015HZ, }; +enum bmp580_odr { + BMP580_ODR_240HZ, + BMP580_ODR_218HZ, + BMP580_ODR_199HZ, + BMP580_ODR_179HZ, + BMP580_ODR_160HZ, + BMP580_ODR_149HZ, + BMP580_ODR_140HZ, + BMP580_ODR_129HZ, + BMP580_ODR_120HZ, + BMP580_ODR_110HZ, + BMP580_ODR_100HZ, + BMP580_ODR_89HZ, + BMP580_ODR_80HZ, + BMP580_ODR_70HZ, + BMP580_ODR_60HZ, + BMP580_ODR_50HZ, + BMP580_ODR_45HZ, + BMP580_ODR_40HZ, + BMP580_ODR_35HZ, + BMP580_ODR_30HZ, + BMP580_ODR_25HZ, + BMP580_ODR_20HZ, + BMP580_ODR_15HZ, + BMP580_ODR_10HZ, + BMP580_ODR_5HZ, + BMP580_ODR_4HZ, + BMP580_ODR_3HZ, + BMP580_ODR_2HZ, + BMP580_ODR_1HZ, + BMP580_ODR_0_5HZ, + BMP580_ODR_0_25HZ, + BMP580_ODR_0_125HZ, +}; + /* * These enums are used for indexing into the array of compensation * parameters for BMP280. @@ -326,7 +362,7 @@ static u32 bmp280_compensate_press(struct bmp280_data *data, } static int bmp280_read_temp(struct bmp280_data *data, - int *val) + int *val, int *val2) { s32 adc_temp, comp_temp; int ret; @@ -366,7 +402,7 @@ static int bmp280_read_press(struct bmp280_data *data, int ret; /* Read and compensate temperature so we get a reading of t_fine. */ - ret = bmp280_read_temp(data, NULL); + ret = bmp280_read_temp(data, NULL, NULL); if (ret < 0) return ret; @@ -398,7 +434,7 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) int ret; /* Read and compensate temperature so we get a reading of t_fine. */ - ret = bmp280_read_temp(data, NULL); + ret = bmp280_read_temp(data, NULL, NULL); if (ret < 0) return ret; @@ -442,7 +478,7 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, ret = data->chip_info->read_press(data, val, val2); break; case IIO_TEMP: - ret = data->chip_info->read_temp(data, val); + ret = data->chip_info->read_temp(data, val, val2); break; default: ret = -EINVAL; @@ -954,7 +990,7 @@ static u32 bmp380_compensate_press(struct bmp280_data *data, u32 adc_press) return comp_press; } -static int bmp380_read_temp(struct bmp280_data *data, int *val) +static int bmp380_read_temp(struct bmp280_data *data, int *val, int *val2) { s32 comp_temp; u32 adc_temp; @@ -994,7 +1030,7 @@ static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2) int ret; /* Read and compensate for temperature so we get a reading of t_fine */ - ret = bmp380_read_temp(data, NULL); + ret = bmp380_read_temp(data, NULL, NULL); if (ret) return ret; @@ -1216,6 +1252,298 @@ const struct bmp280_chip_info bmp380_chip_info = { }; EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); +static int bmp580_soft_reset(struct bmp280_data *data) +{ + unsigned int reg; + int ret; + + ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_SOFT_RESET); + if (ret) { + dev_err(data->dev, "failed to send reset command to device\n"); + return ret; + } + usleep_range(2000, 2500); + + /* Dummy read of chip_id */ + ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, ®); + if (ret) { + dev_err(data->dev, "failed to reestablish comms after reset\n"); + return ret; + } + + ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, ®); + if (ret) { + dev_err(data->dev, "error reading interrupt status register\n"); + return ret; + } + if (!(reg & BMP580_INT_STATUS_POR_MASK)) { + dev_err(data->dev, "error resetting sensor\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Contrary to previous sensors families, compensation algorithm is builtin. + * We are only required to read the register raw data and adapt the ranges + * for what is expected on IIO ABI. + */ + +static int bmp580_read_temp(struct bmp280_data *data, int *val, int *val2) +{ + s32 raw_temp; + int ret; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB, data->buf, + sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read temperature\n"); + return ret; + } + + raw_temp = get_unaligned_le24(data->buf); + if (raw_temp == BMP580_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + return -EIO; + } + + /* + * Temperature is returned in Celsius degrees in fractional + * form down 2^16. We reescale by x1000 to return milli Celsius + * to respect IIO ABI. + */ + *val = raw_temp * 1000; + *val2 = 16; + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int bmp580_read_press(struct bmp280_data *data, int *val, int *val2) +{ + u32 raw_press; + int ret; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_PRESS_XLSB, data->buf, + sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read pressure\n"); + return ret; + } + + raw_press = get_unaligned_le24(data->buf); + if (raw_press == BMP580_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + return -EIO; + } + /* + * Pressure is returned in Pascals in fractional form down 2^16. + * We reescale /1000 to convert to kilopascal to respect IIO ABI. + */ + *val = raw_press; + *val2 = 64000; /* 2^6 * 1000 */ + return IIO_VAL_FRACTIONAL; +} + +static const int bmp580_odr_table[][2] = { + [BMP580_ODR_240HZ] = {240, 0}, + [BMP580_ODR_218HZ] = {218, 0}, + [BMP580_ODR_199HZ] = {199, 0}, + [BMP580_ODR_179HZ] = {179, 0}, + [BMP580_ODR_160HZ] = {160, 0}, + [BMP580_ODR_149HZ] = {149, 0}, + [BMP580_ODR_140HZ] = {140, 0}, + [BMP580_ODR_129HZ] = {129, 0}, + [BMP580_ODR_120HZ] = {120, 0}, + [BMP580_ODR_110HZ] = {110, 0}, + [BMP580_ODR_100HZ] = {100, 0}, + [BMP580_ODR_89HZ] = {89, 0}, + [BMP580_ODR_80HZ] = {80, 0}, + [BMP580_ODR_70HZ] = {70, 0}, + [BMP580_ODR_60HZ] = {60, 0}, + [BMP580_ODR_50HZ] = {50, 0}, + [BMP580_ODR_45HZ] = {45, 0}, + [BMP580_ODR_40HZ] = {40, 0}, + [BMP580_ODR_35HZ] = {35, 0}, + [BMP580_ODR_30HZ] = {30, 0}, + [BMP580_ODR_25HZ] = {25, 0}, + [BMP580_ODR_20HZ] = {20, 0}, + [BMP580_ODR_15HZ] = {15, 0}, + [BMP580_ODR_10HZ] = {10, 0}, + [BMP580_ODR_5HZ] = {5, 0}, + [BMP580_ODR_4HZ] = {4, 0}, + [BMP580_ODR_3HZ] = {3, 0}, + [BMP580_ODR_2HZ] = {2, 0}, + [BMP580_ODR_1HZ] = {1, 0}, + [BMP580_ODR_0_5HZ] = {0, 500000}, + [BMP580_ODR_0_25HZ] = {0, 250000}, + [BMP580_ODR_0_125HZ] = {0, 125000}, +}; + +static int bmp580_preinit(struct bmp280_data *data) +{ + unsigned int reg; + int ret; + + /* Issue soft-reset command */ + ret = bmp580_soft_reset(data); + if (ret) + return ret; + + /* Post powerup sequence */ + ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, ®); + if (ret) + return ret; + + /* Print warn message if we don't know the chip id */ + if (reg != BMP580_CHIP_ID && reg != BMP580_CHIP_ID_ALT) + dev_warn(data->dev, "preinit: unexpected chip_id\n"); + + ret = regmap_read(data->regmap, BMP580_REG_STATUS, ®); + if (ret) + return ret; + + /* Check nvm status */ + if (!(reg & BMP580_STATUS_NVM_RDY_MASK) || (reg & BMP580_STATUS_NVM_ERR_MASK)) { + dev_err(data->dev, "preinit: nvm error on powerup sequence\n"); + return -EIO; + } + + return 0; +} + +static int bmp580_chip_config(struct bmp280_data *data) +{ + bool change = false, aux; + unsigned int tmp; + u8 reg_val; + int ret; + + /* Sets sensor in standby mode */ + ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS, + BMP580_ODR_DEEPSLEEP_DIS | + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to change sensor to standby mode\n"); + return ret; + } + /* From datasheet's table 4: electrical characteristics */ + usleep_range(2500, 3000); + + /* Set default DSP mode settings */ + reg_val = FIELD_PREP(BMP580_DSP_COMP_MASK, BMP580_DSP_PRESS_TEMP_COMP_EN) | + BMP580_DSP_SHDW_IIR_TEMP_EN | BMP580_DSP_SHDW_IIR_PRESS_EN; + + ret = regmap_update_bits(data->regmap, BMP580_REG_DSP_CONFIG, + BMP580_DSP_COMP_MASK | + BMP580_DSP_SHDW_IIR_TEMP_EN | + BMP580_DSP_SHDW_IIR_PRESS_EN, reg_val); + + /* Configure oversampling */ + reg_val = FIELD_PREP(BMP580_OSR_TEMP_MASK, data->oversampling_temp) | + FIELD_PREP(BMP580_OSR_PRESS_MASK, data->oversampling_press) | + BMP580_OSR_PRESS_EN; + + ret = regmap_update_bits_check(data->regmap, BMP580_REG_OSR_CONFIG, + BMP580_OSR_TEMP_MASK | BMP580_OSR_PRESS_MASK | + BMP580_OSR_PRESS_EN, + reg_val, &aux); + if (ret) { + dev_err(data->dev, "failed to write oversampling register\n"); + return ret; + } + change = change || aux; + + /* Configure output data rate */ + ret = regmap_update_bits_check(data->regmap, BMP580_REG_ODR_CONFIG, BMP580_ODR_MASK, + FIELD_PREP(BMP580_ODR_MASK, data->sampling_freq), + &aux); + if (ret) { + dev_err(data->dev, "failed to write ODR configuration register\n"); + return ret; + } + change = change || aux; + + /* Set filter data */ + reg_val = FIELD_PREP(BMP580_DSP_IIR_PRESS_MASK, data->iir_filter_coeff) | + FIELD_PREP(BMP580_DSP_IIR_TEMP_MASK, data->iir_filter_coeff); + + ret = regmap_update_bits_check(data->regmap, BMP580_REG_DSP_IIR, + BMP580_DSP_IIR_PRESS_MASK | + BMP580_DSP_IIR_TEMP_MASK, + reg_val, &aux); + if (ret) { + dev_err(data->dev, "failed to write config register\n"); + return ret; + } + change = change || aux; + + /* Restore sensor to normal operation mode */ + ret = regmap_write_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK, + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_NORMAL)); + if (ret) { + dev_err(data->dev, "failed to set normal mode\n"); + return ret; + } + /* From datasheet's table 4: electrical characteristics */ + usleep_range(3000, 3500); + + if (change) { + /* + * Check if ODR and OSR settings are valid or we are + * operating in a degraded mode. + */ + ret = regmap_read(data->regmap, BMP580_REG_EFF_OSR, &tmp); + if (ret) { + dev_err(data->dev, "error reading effective OSR register\n"); + return ret; + } + if (!(tmp & BMP580_EFF_OSR_VALID_ODR)) { + dev_warn(data->dev, "OSR and ODR incompatible settings detected\n"); + /* Set current OSR settings from data on effective OSR */ + data->oversampling_temp = FIELD_GET(BMP580_EFF_OSR_TEMP_MASK, tmp); + data->oversampling_press = FIELD_GET(BMP580_EFF_OSR_PRESS_MASK, tmp); + return -EINVAL; + } + } + + return 0; +} + +static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +const struct bmp280_chip_info bmp580_chip_info = { + .id_reg = BMP580_REG_CHIP_ID, + .chip_id = BMP580_CHIP_ID, + .regmap_config = &bmp580_regmap_config, + .start_up_time = 2000, + .channels = bmp380_channels, + .num_channels = 2, + + .oversampling_temp_avail = bmp580_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp580_oversampling_avail), + .oversampling_temp_default = ilog2(1), + + .oversampling_press_avail = bmp580_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp580_oversampling_avail), + .oversampling_press_default = ilog2(4), + + .sampling_freq_avail = bmp580_odr_table, + .num_sampling_freq_avail = ARRAY_SIZE(bmp580_odr_table) * 2, + .sampling_freq_default = BMP580_ODR_50HZ, + + .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail, + .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail), + .iir_filter_coeff_default = 2, + + .chip_config = bmp580_chip_config, + .read_temp = bmp580_read_temp, + .read_press = bmp580_read_press, + .preinit = bmp580_preinit, +}; +EXPORT_SYMBOL_NS(bmp580_chip_info, IIO_BMP280); + static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; @@ -1336,7 +1664,7 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) return (data->t_fine + 8) >> 4; } -static int bmp180_read_temp(struct bmp280_data *data, int *val) +static int bmp180_read_temp(struct bmp280_data *data, int *val, int *val2) { s32 adc_temp, comp_temp; int ret; @@ -1424,7 +1752,7 @@ static int bmp180_read_press(struct bmp280_data *data, int ret; /* Read and compensate temperature so we get a reading of t_fine. */ - ret = bmp180_read_temp(data, NULL); + ret = bmp180_read_temp(data, NULL, NULL); if (ret) return ret; diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 5f908e2c9826..567b945e6427 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -34,6 +34,7 @@ static const struct of_device_id bmp280_of_i2c_match[] = { { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, { .compatible = "bosch,bme280", .data = &bme280_chip_info }, { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, + { .compatible = "bosch,bmp580", .data = &bmp580_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); @@ -44,6 +45,7 @@ static const struct i2c_device_id bmp280_i2c_id[] = { {"bmp280", (kernel_ulong_t)&bmp280_chip_info }, {"bme280", (kernel_ulong_t)&bme280_chip_info }, {"bmp380", (kernel_ulong_t)&bmp380_chip_info }, + {"bmp580", (kernel_ulong_t)&bmp580_chip_info }, { }, }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c index c98c67970265..3ee56720428c 100644 --- a/drivers/iio/pressure/bmp280-regmap.c +++ b/drivers/iio/pressure/bmp280-regmap.c @@ -115,6 +115,54 @@ static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg) } } +static bool bmp580_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP580_REG_NVM_DATA_MSB: + case BMP580_REG_NVM_DATA_LSB: + case BMP580_REG_NVM_ADDR: + case BMP580_REG_ODR_CONFIG: + case BMP580_REG_OSR_CONFIG: + case BMP580_REG_INT_SOURCE: + case BMP580_REG_INT_CONFIG: + case BMP580_REG_OOR_THR_MSB: + case BMP580_REG_OOR_THR_LSB: + case BMP580_REG_OOR_CONFIG: + case BMP580_REG_OOR_RANGE: + case BMP580_REG_IF_CONFIG: + case BMP580_REG_FIFO_CONFIG: + case BMP580_REG_FIFO_SEL: + case BMP580_REG_DSP_CONFIG: + case BMP580_REG_DSP_IIR: + case BMP580_REG_CMD: + return true; + default: + return false; + } +} + +static bool bmp580_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP580_REG_NVM_DATA_MSB: + case BMP580_REG_NVM_DATA_LSB: + case BMP580_REG_FIFO_COUNT: + case BMP580_REG_INT_STATUS: + case BMP580_REG_PRESS_XLSB: + case BMP580_REG_PRESS_LSB: + case BMP580_REG_PRESS_MSB: + case BMP580_REG_FIFO_DATA: + case BMP580_REG_TEMP_XLSB: + case BMP580_REG_TEMP_LSB: + case BMP580_REG_TEMP_MSB: + case BMP580_REG_EFF_OSR: + case BMP580_REG_STATUS: + return true; + default: + return false; + } +} + const struct regmap_config bmp280_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -138,3 +186,15 @@ const struct regmap_config bmp380_regmap_config = { .volatile_reg = bmp380_is_volatile_reg, }; EXPORT_SYMBOL_NS(bmp380_regmap_config, IIO_BMP280); + +const struct regmap_config bmp580_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BMP580_REG_CMD, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bmp580_is_writeable_reg, + .volatile_reg = bmp580_is_volatile_reg, +}; +EXPORT_SYMBOL_NS(bmp580_regmap_config, IIO_BMP280); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index d5a224b95185..1dff9bb7c4e9 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -85,6 +85,7 @@ static const struct of_device_id bmp280_of_spi_match[] = { { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, { .compatible = "bosch,bme280", .data = &bmp280_chip_info }, { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, + { .compatible = "bosch,bmp580", .data = &bmp580_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); @@ -95,6 +96,7 @@ static const struct spi_device_id bmp280_spi_id[] = { { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, { "bme280", (kernel_ulong_t)&bmp280_chip_info }, { "bmp380", (kernel_ulong_t)&bmp380_chip_info }, + { "bmp580", (kernel_ulong_t)&bmp580_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index be17104ef0a2..cd4c5be36ed7 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -6,6 +6,107 @@ #include +/* BMP580 specific registers */ +#define BMP580_REG_CMD 0x7E +#define BMP580_REG_EFF_OSR 0x38 +#define BMP580_REG_ODR_CONFIG 0x37 +#define BMP580_REG_OSR_CONFIG 0x36 +#define BMP580_REG_IF_CONFIG 0x13 +#define BMP580_REG_REV_ID 0x02 +#define BMP580_REG_CHIP_ID 0x01 +/* OOR allows to configure a pressure alarm */ +#define BMP580_REG_OOR_CONFIG 0x35 +#define BMP580_REG_OOR_RANGE 0x34 +#define BMP580_REG_OOR_THR_MSB 0x33 +#define BMP580_REG_OOR_THR_LSB 0x32 +/* DSP registers (IIR filters) */ +#define BMP580_REG_DSP_IIR 0x31 +#define BMP580_REG_DSP_CONFIG 0x30 +/* NVM access registers */ +#define BMP580_REG_NVM_DATA_MSB 0x2D +#define BMP580_REG_NVM_DATA_LSB 0x2C +#define BMP580_REG_NVM_ADDR 0x2B +/* Status registers */ +#define BMP580_REG_STATUS 0x28 +#define BMP580_REG_INT_STATUS 0x27 +#define BMP580_REG_CHIP_STATUS 0x11 +/* Data registers */ +#define BMP580_REG_FIFO_DATA 0x29 +#define BMP580_REG_PRESS_MSB 0x22 +#define BMP580_REG_PRESS_LSB 0x21 +#define BMP580_REG_PRESS_XLSB 0x20 +#define BMP580_REG_TEMP_MSB 0x1F +#define BMP580_REG_TEMP_LSB 0x1E +#define BMP580_REG_TEMP_XLSB 0x1D +/* FIFO config registers */ +#define BMP580_REG_FIFO_SEL 0x18 +#define BMP580_REG_FIFO_COUNT 0x17 +#define BMP580_REG_FIFO_CONFIG 0x16 +/* Interruptions config registers */ +#define BMP580_REG_INT_SOURCE 0x15 +#define BMP580_REG_INT_CONFIG 0x14 + +#define BMP580_CMD_NOOP 0x00 +#define BMP580_CMD_EXTMODE_SEQ_0 0x73 +#define BMP580_CMD_EXTMODE_SEQ_1 0xB4 +#define BMP580_CMD_EXTMODE_SEQ_2 0x69 +#define BMP580_CMD_NVM_OP_SEQ_0 0x5D +#define BMP580_CMD_NVM_READ_SEQ_1 0xA5 +#define BMP580_CMD_NVM_WRITE_SEQ_1 0xA0 +#define BMP580_CMD_SOFT_RESET 0xB6 + +#define BMP580_INT_STATUS_POR_MASK BIT(4) + +#define BMP580_STATUS_CORE_RDY_MASK BIT(0) +#define BMP580_STATUS_NVM_RDY_MASK BIT(1) +#define BMP580_STATUS_NVM_ERR_MASK BIT(2) +#define BMP580_STATUS_NVM_CMD_ERR_MASK BIT(3) + +#define BMP580_OSR_PRESS_MASK GENMASK(5, 3) +#define BMP580_OSR_TEMP_MASK GENMASK(2, 0) +#define BMP580_OSR_PRESS_EN BIT(6) +#define BMP580_EFF_OSR_PRESS_MASK GENMASK(5, 3) +#define BMP580_EFF_OSR_TEMP_MASK GENMASK(2, 0) +#define BMP580_EFF_OSR_VALID_ODR BIT(7) + +#define BMP580_ODR_MASK GENMASK(6, 2) +#define BMP580_MODE_MASK GENMASK(1, 0) +#define BMP580_MODE_SLEEP 0 +#define BMP580_MODE_NORMAL 1 +#define BMP580_MODE_FORCED 2 +#define BMP580_MODE_CONTINOUS 3 +#define BMP580_ODR_DEEPSLEEP_DIS BIT(7) + +#define BMP580_DSP_COMP_MASK GENMASK(1, 0) +#define BMP580_DSP_COMP_DIS 0 +#define BMP580_DSP_TEMP_COMP_EN 1 +/* + * In section 7.27 of datasheet, modes 2 and 3 are technically the same. + * Pressure compensation means also enabling temperature compensation + */ +#define BMP580_DSP_PRESS_COMP_EN 2 +#define BMP580_DSP_PRESS_TEMP_COMP_EN 3 +#define BMP580_DSP_IIR_FORCED_FLUSH BIT(2) +#define BMP580_DSP_SHDW_IIR_TEMP_EN BIT(3) +#define BMP580_DSP_FIFO_IIR_TEMP_EN BIT(4) +#define BMP580_DSP_SHDW_IIR_PRESS_EN BIT(5) +#define BMP580_DSP_FIFO_IIR_PRESS_EN BIT(6) +#define BMP580_DSP_OOR_IIR_PRESS_EN BIT(7) + +#define BMP580_DSP_IIR_PRESS_MASK GENMASK(5, 3) +#define BMP580_DSP_IIR_TEMP_MASK GENMASK(2, 0) +#define BMP580_FILTER_OFF 0 +#define BMP580_FILTER_1X 1 +#define BMP580_FILTER_3X 2 +#define BMP580_FILTER_7X 3 +#define BMP580_FILTER_15X 4 +#define BMP580_FILTER_31X 5 +#define BMP580_FILTER_63X 6 +#define BMP580_FILTER_127X 7 + +#define BMP580_TEMP_SKIPPED 0x7f7f7f +#define BMP580_PRESS_SKIPPED 0x7f7f7f + /* BMP380 specific registers */ #define BMP380_REG_CMD 0x7E #define BMP380_REG_CONFIG 0x1F @@ -184,6 +285,8 @@ #define BMP280_REG_ID 0xD0 #define BMP380_CHIP_ID 0x50 +#define BMP580_CHIP_ID 0x50 +#define BMP580_CHIP_ID_ALT 0x51 #define BMP180_CHIP_ID 0x55 #define BMP280_CHIP_ID 0x58 #define BME280_CHIP_ID 0x60 @@ -341,7 +444,7 @@ struct bmp280_chip_info { int sampling_freq_default; int (*chip_config)(struct bmp280_data *); - int (*read_temp)(struct bmp280_data *, int *); + int (*read_temp)(struct bmp280_data *, int *, int *); int (*read_press)(struct bmp280_data *, int *, int *); int (*read_humid)(struct bmp280_data *, int *, int *); int (*read_calib)(struct bmp280_data *); @@ -353,11 +456,13 @@ extern const struct bmp280_chip_info bmp180_chip_info; extern const struct bmp280_chip_info bmp280_chip_info; extern const struct bmp280_chip_info bme280_chip_info; extern const struct bmp280_chip_info bmp380_chip_info; +extern const struct bmp280_chip_info bmp580_chip_info; /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; extern const struct regmap_config bmp380_regmap_config; +extern const struct regmap_config bmp580_regmap_config; /* Probe called from different transports */ int bmp280_common_probe(struct device *dev, -- cgit v1.2.3 From 43cce95ca1396c9678c7ef8a5c065005c9fc1f11 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 18:03:05 +0100 Subject: dt-bindings: iio: pressure: bmp085: Add BMP580 compatible string Add bosch,bmp580 to compatible string for the new family of sensors. This family includes the BMP580 and BMP581 sensors. The register map in this family presents significant departures from previous generations. Signed-off-by: Angel Iglesias Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/2510dccfd44e2225c5978bfdf4136f423326c31a.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/pressure/bmp085.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml index 63885af6a74b..6fda887ee9d4 100644 --- a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml @@ -17,6 +17,7 @@ description: | https://www.bosch-sensortec.com/bst/products/all_products/bmp280 https://www.bosch-sensortec.com/bst/products/all_products/bme280 https://www.bosch-sensortec.com/bst/products/all_products/bmp380 + https://www.bosch-sensortec.com/bst/products/all_products/bmp580 properties: compatible: @@ -26,6 +27,7 @@ properties: - bosch,bmp280 - bosch,bme280 - bosch,bmp380 + - bosch,bmp580 reg: maxItems: 1 -- cgit v1.2.3 From accb9d05df394c38db2a1bab416ee25d6078ca5c Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 18:03:06 +0100 Subject: iio: pressure: bmp280: Add nvmem operations for BMP580 The pressure sensor BMP580 contains a non-volatile memory that stores trimming and configuration params. That memory provides an programmable user range of three 2-byte words. Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/f3f453d9b2c0f7820ca9c56e24e2165b6c39bb67.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 210 ++++++++++++++++++++++++++++++++++++- drivers/iio/pressure/bmp280.h | 3 + 2 files changed, 212 insertions(+), 1 deletion(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index a7d26d81ec08..6089f3f9d8f4 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1284,6 +1285,80 @@ static int bmp580_soft_reset(struct bmp280_data *data) return 0; } +/** + * bmp580_nvm_operation() - Helper function to commit NVM memory operations + * @data: sensor data struct + * @is_write: flag to signal write operation + */ +static int bmp580_nvm_operation(struct bmp280_data *data, bool is_write) +{ + unsigned long timeout, poll; + unsigned int reg; + int ret; + + /* Check NVM ready flag */ + ret = regmap_read(data->regmap, BMP580_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to check nvm status\n"); + return ret; + } + if (!(reg & BMP580_STATUS_NVM_RDY_MASK)) { + dev_err(data->dev, "sensor's nvm is not ready\n"); + return -EIO; + } + + /* Start NVM operation sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_NVM_OP_SEQ_0); + if (ret) { + dev_err(data->dev, "failed to send nvm operation's first sequence\n"); + return ret; + } + if (is_write) { + /* Send NVM write sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, + BMP580_CMD_NVM_WRITE_SEQ_1); + if (ret) { + dev_err(data->dev, "failed to send nvm write sequence\n"); + return ret; + } + /* Datasheet says on 4.8.1.2 it takes approximately 10ms */ + poll = 2000; + timeout = 12000; + } else { + /* Send NVM read sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, + BMP580_CMD_NVM_READ_SEQ_1); + if (ret) { + dev_err(data->dev, "failed to send nvm read sequence\n"); + return ret; + } + /* Datasheet says on 4.8.1.1 it takes approximately 200us */ + poll = 50; + timeout = 400; + } + if (ret) { + dev_err(data->dev, "failed to write command sequence\n"); + return -EIO; + } + + /* Wait until NVM is ready again */ + ret = regmap_read_poll_timeout(data->regmap, BMP580_REG_STATUS, reg, + (reg & BMP580_STATUS_NVM_RDY_MASK), + poll, timeout); + if (ret) { + dev_err(data->dev, "error checking nvm operation status\n"); + return ret; + } + + /* Check NVM error flags */ + if ((reg & BMP580_STATUS_NVM_ERR_MASK) || (reg & BMP580_STATUS_NVM_CMD_ERR_MASK)) { + dev_err(data->dev, "error processing nvm operation\n"); + return -EIO; + } + + return 0; +} + /* * Contrary to previous sensors families, compensation algorithm is builtin. * We are only required to read the register raw data and adapt the ranges @@ -1379,8 +1454,140 @@ static const int bmp580_odr_table[][2] = { [BMP580_ODR_0_125HZ] = {0, 125000}, }; +static const int bmp580_nvmem_addrs[] = { 0x20, 0x21, 0x22 }; + +static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct bmp280_data *data = priv; + u16 *dst = val; + int ret, addr; + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + /* Set sensor in standby mode */ + ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS, + BMP580_ODR_DEEPSLEEP_DIS | + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to change sensor to standby mode\n"); + goto exit; + } + /* Wait standby transition time */ + usleep_range(2500, 3000); + + while (bytes >= sizeof(*dst)) { + addr = bmp580_nvmem_addrs[offset / sizeof(*dst)]; + + ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR, + FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr)); + if (ret) { + dev_err(data->dev, "error writing nvm address\n"); + goto exit; + } + + ret = bmp580_nvm_operation(data, false); + if (ret) + goto exit; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_NVM_DATA_LSB, &data->le16, + sizeof(data->le16)); + if (ret) { + dev_err(data->dev, "error reading nvm data regs\n"); + goto exit; + } + + *dst++ = le16_to_cpu(data->le16); + bytes -= sizeof(*dst); + offset += sizeof(*dst); + } +exit: + /* Restore chip config */ + data->chip_info->chip_config(data); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + return ret; +} + +static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct bmp280_data *data = priv; + u16 *buf = val; + int ret, addr; + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + /* Set sensor in standby mode */ + ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS, + BMP580_ODR_DEEPSLEEP_DIS | + FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to change sensor to standby mode\n"); + goto exit; + } + /* Wait standby transition time */ + usleep_range(2500, 3000); + + while (bytes >= sizeof(*buf)) { + addr = bmp580_nvmem_addrs[offset / sizeof(*buf)]; + + ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR, BMP580_NVM_PROG_EN | + FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr)); + if (ret) { + dev_err(data->dev, "error writing nvm address\n"); + goto exit; + } + data->le16 = cpu_to_le16(*buf++); + + ret = regmap_bulk_write(data->regmap, BMP580_REG_NVM_DATA_LSB, &data->le16, + sizeof(data->le16)); + if (ret) { + dev_err(data->dev, "error writing LSB NVM data regs\n"); + goto exit; + } + + ret = bmp580_nvm_operation(data, true); + if (ret) + goto exit; + + /* Disable programming mode bit */ + ret = regmap_update_bits(data->regmap, BMP580_REG_NVM_ADDR, + BMP580_NVM_PROG_EN, 0); + if (ret) { + dev_err(data->dev, "error resetting nvm write\n"); + goto exit; + } + + bytes -= sizeof(*buf); + offset += sizeof(*buf); + } +exit: + /* Restore chip config */ + data->chip_info->chip_config(data); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + return ret; +} + static int bmp580_preinit(struct bmp280_data *data) { + struct nvmem_config config = { + .dev = data->dev, + .priv = data, + .name = "bmp580_nvmem", + .word_size = sizeof(u16), + .stride = sizeof(u16), + .size = 3 * sizeof(u16), + .reg_read = bmp580_nvmem_read, + .reg_write = bmp580_nvmem_write, + }; unsigned int reg; int ret; @@ -1408,7 +1615,8 @@ static int bmp580_preinit(struct bmp280_data *data) return -EIO; } - return 0; + /* Register nvmem device */ + return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config)); } static int bmp580_chip_config(struct bmp280_data *data) diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index cd4c5be36ed7..5c0563ce7572 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -104,6 +104,9 @@ #define BMP580_FILTER_63X 6 #define BMP580_FILTER_127X 7 +#define BMP580_NVM_ROW_ADDR_MASK GENMASK(5, 0) +#define BMP580_NVM_PROG_EN BIT(6) + #define BMP580_TEMP_SKIPPED 0x7f7f7f #define BMP580_PRESS_SKIPPED 0x7f7f7f -- cgit v1.2.3 From db3c490503bee4d0611f9fc17fcd8cfe6fcdbcad Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 27 Feb 2023 12:14:56 +0100 Subject: iio: imu: st_lsm6dsx: discard samples during filters settling time During digital filters settling time the driver is expected to drop samples since they can be corrupted. Introduce the capability to drop a given number of samples according to the configured ODR. Add sample_to_discard for LSM6DSM-like sensors since new generation devices (e.g. LSM6DSO) support DRDY mask where corrupted samples are masked in hw with values greather than 0x7ffd so the driver can easily discard them. I have not added sample_to_discard support for LSM6DS3 or LSM6DS3H since I do not have any sample for testing at the moment. Reported-by: Philippe De Muyter Tested-by: Philippe De Muyter Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/21dcd94935c147ef9b1da4984b3da6264ee9609e.1677496295.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 11 +++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 57 ++++++++++++++++++++++---- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 18 ++++++++ 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 499fcf8875b4..8e119d78730b 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -137,6 +137,13 @@ struct st_lsm6dsx_odr_table_entry { int odr_len; }; +struct st_lsm6dsx_samples_to_discard { + struct { + u32 milli_hz; + u16 samples; + } val[ST_LSM6DSX_ODR_LIST_SIZE]; +}; + struct st_lsm6dsx_fs { u32 gain; u8 val; @@ -291,6 +298,7 @@ struct st_lsm6dsx_ext_dev_settings { * @irq_config: interrupts related registers. * @drdy_mask: register info for data-ready mask (addr + mask). * @odr_table: Hw sensors odr table (Hz + val). + * @samples_to_discard: Number of samples to discard for filters settling time. * @fs_table: Hw sensors gain table (gain + val). * @decimator: List of decimator register info (addr + mask). * @batch: List of FIFO batching register info (addr + mask). @@ -323,6 +331,7 @@ struct st_lsm6dsx_settings { } irq_config; struct st_lsm6dsx_reg drdy_mask; struct st_lsm6dsx_odr_table_entry odr_table[2]; + struct st_lsm6dsx_samples_to_discard samples_to_discard[2]; struct st_lsm6dsx_fs_table_entry fs_table[2]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; @@ -353,6 +362,7 @@ enum st_lsm6dsx_fifo_mode { * @hw: Pointer to instance of struct st_lsm6dsx_hw. * @gain: Configured sensor sensitivity. * @odr: Output data rate of the sensor [Hz]. + * @samples_to_discard: Number of samples to discard for filters settling time. * @watermark: Sensor watermark level. * @decimator: Sensor decimation factor. * @sip: Number of samples in a given pattern. @@ -367,6 +377,7 @@ struct st_lsm6dsx_sensor { u32 gain; u32 odr; + u16 samples_to_discard; u16 watermark; u8 decimator; u8 sip; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 7dd5205aea5b..f6c11d6fb0b0 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -457,17 +457,31 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) } if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_GYRO], - &hw->scan[ST_LSM6DSX_ID_GYRO], - gyro_sensor->ts_ref + ts); + /* + * We need to discards gyro samples during + * filters settling time + */ + if (gyro_sensor->samples_to_discard > 0) + gyro_sensor->samples_to_discard--; + else + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + &hw->scan[ST_LSM6DSX_ID_GYRO], + gyro_sensor->ts_ref + ts); gyro_sip--; } if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_ACC], - &hw->scan[ST_LSM6DSX_ID_ACC], - acc_sensor->ts_ref + ts); + /* + * We need to discards accel samples during + * filters settling time + */ + if (acc_sensor->samples_to_discard > 0) + acc_sensor->samples_to_discard--; + else + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + &hw->scan[ST_LSM6DSX_ID_ACC], + acc_sensor->ts_ref + ts); acc_sip--; } if (ext_sip > 0 && !(sip % ext_sensor->decimator)) { @@ -654,6 +668,30 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) return err; } +static void +st_lsm6dsx_update_samples_to_discard(struct st_lsm6dsx_sensor *sensor) +{ + const struct st_lsm6dsx_samples_to_discard *data; + struct st_lsm6dsx_hw *hw = sensor->hw; + int i; + + if (sensor->id != ST_LSM6DSX_ID_GYRO && + sensor->id != ST_LSM6DSX_ID_ACC) + return; + + /* check if drdy mask is supported in hw */ + if (hw->settings->drdy_mask.addr) + return; + + data = &hw->settings->samples_to_discard[sensor->id]; + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { + if (data->val[i].milli_hz == sensor->odr) { + sensor->samples_to_discard = data->val[i].samples; + return; + } + } +} + int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; @@ -673,6 +711,9 @@ int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) goto out; } + if (enable) + st_lsm6dsx_update_samples_to_discard(sensor); + err = st_lsm6dsx_device_set_enable(sensor, enable); if (err < 0) goto out; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 3f6060c64f32..966df6ffe874 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -634,6 +634,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .samples_to_discard = { + [ST_LSM6DSX_ID_ACC] = { + .val[0] = { 12500, 1 }, + .val[1] = { 26000, 1 }, + .val[2] = { 52000, 1 }, + .val[3] = { 104000, 2 }, + .val[4] = { 208000, 2 }, + .val[5] = { 416000, 2 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .val[0] = { 12500, 2 }, + .val[1] = { 26000, 5 }, + .val[2] = { 52000, 7 }, + .val[3] = { 104000, 12 }, + .val[4] = { 208000, 20 }, + .val[5] = { 416000, 36 }, + }, + }, .irq_config = { .irq1 = { .addr = 0x0d, -- cgit v1.2.3 From 4ad682e04c1694aba95627f60e89f83178c277c2 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Thu, 2 Mar 2023 14:04:35 +0100 Subject: iio: Improve the kernel-doc of iio_trigger_poll Move the kernel-doc of the function to industrialio-trigger.c Add a note on the context where the function is expected to be called. Signed-off-by: Mehdi Djait Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/bd84fc17e9d22eab998bf48720297f9a77689f45.1677761379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-trigger.c | 6 ++++++ include/linux/iio/trigger.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index a2f3cc2f65ef..270ec0a9f641 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -192,6 +192,12 @@ static void iio_trigger_notify_done_atomic(struct iio_trigger *trig) schedule_work(&trig->reenable_work); } +/** + * iio_trigger_poll() - Call the IRQ trigger handler of the consumers + * @trig: trigger which occurred + * + * This function should only be called from a hard IRQ context. + */ void iio_trigger_poll(struct iio_trigger *trig) { int i; diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index f6360d9a492d..42da55dc3aa7 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -151,12 +151,6 @@ void iio_trigger_unregister(struct iio_trigger *trig_info); **/ int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig); -/** - * iio_trigger_poll() - called on a trigger occurring - * @trig: trigger which occurred - * - * Typically called in relevant hardware interrupt handler. - **/ void iio_trigger_poll(struct iio_trigger *trig); void iio_trigger_poll_chained(struct iio_trigger *trig); -- cgit v1.2.3 From f700e55ef6ef9ec723164659ed4385900981c872 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Thu, 2 Mar 2023 14:04:36 +0100 Subject: iio: Rename iio_trigger_poll_chained and add kernel-doc Rename the function to iio_trigger_poll_nested. Add kernel-doc with a note on the context where the function is expected to be called. Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/841b533cba28ca25a8e87280c44e45979166e8e2.1677761379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma400_core.c | 2 +- drivers/iio/accel/kionix-kx022a.c | 2 +- drivers/iio/accel/mma8452.c | 2 +- drivers/iio/accel/msa311.c | 2 +- drivers/iio/adc/ad7606.c | 2 +- drivers/iio/adc/at91-sama5d2_adc.c | 2 +- drivers/iio/adc/max11410.c | 2 +- drivers/iio/common/st_sensors/st_sensors_trigger.c | 4 ++-- drivers/iio/gyro/fxas21002c_core.c | 2 +- drivers/iio/gyro/mpu3050-core.c | 2 +- drivers/iio/humidity/hts221_buffer.c | 2 +- drivers/iio/industrialio-trigger.c | 11 +++++++++-- drivers/iio/light/acpi-als.c | 2 +- drivers/iio/light/rpr0521.c | 2 +- drivers/iio/light/st_uvis25_core.c | 2 +- drivers/iio/light/vcnl4000.c | 2 +- drivers/iio/light/vcnl4035.c | 2 +- drivers/iio/potentiostat/lmp91000.c | 2 +- drivers/iio/pressure/zpa2326.c | 2 +- drivers/iio/proximity/as3935.c | 2 +- drivers/iio/trigger/iio-trig-loop.c | 2 +- include/linux/iio/trigger.h | 2 +- 22 files changed, 31 insertions(+), 24 deletions(-) diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 623f37cbaf50..a68b845f5b4f 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1688,7 +1688,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private) if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) { mutex_unlock(&data->mutex); - iio_trigger_poll_chained(data->trig); + iio_trigger_poll_nested(data->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 9f8c7631f098..8dac0337c249 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -899,7 +899,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) mutex_lock(&data->mutex); if (data->trigger_enabled) { - iio_trigger_poll_chained(data->trig); + iio_trigger_poll_nested(data->trig); ret = IRQ_HANDLED; } diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f97fb68e3a71..ea14e3aaa30a 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1067,7 +1067,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) return IRQ_NONE; if (src & MMA8452_INT_DRDY) { - iio_trigger_poll_chained(indio_dev->trig); + iio_trigger_poll_nested(indio_dev->trig); ret = IRQ_HANDLED; } diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index af94d3adf6d8..6690fa37da8f 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -951,7 +951,7 @@ static irqreturn_t msa311_irq_thread(int irq, void *p) } if (new_data_int_enabled) - iio_trigger_poll_chained(msa311->new_data_trig); + iio_trigger_poll_nested(msa311->new_data_trig); return IRQ_HANDLED; } diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index dd6b603f65ea..1928d9ae5bcf 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -477,7 +477,7 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id) if (iio_buffer_enabled(indio_dev)) { gpiod_set_value(st->gpio_convst, 0); - iio_trigger_poll_chained(st->trig); + iio_trigger_poll_nested(st->trig); } else { complete(&st->completion); } diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 50d02e5fc6fc..b5d0c9ee284c 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1194,7 +1194,7 @@ static void at91_dma_buffer_done(void *data) { struct iio_dev *indio_dev = data; - iio_trigger_poll_chained(indio_dev->trig); + iio_trigger_poll_nested(indio_dev->trig); } static int at91_adc_dma_start(struct iio_dev *indio_dev) diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index b74b689ee7de..237b2ce3f264 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -678,7 +678,7 @@ static irqreturn_t max11410_interrupt(int irq, void *dev_id) struct max11410_state *st = iio_priv(indio_dev); if (iio_buffer_enabled(indio_dev)) - iio_trigger_poll_chained(st->trig); + iio_trigger_poll_nested(st->trig); else complete(&st->completion); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 899b640c0a70..a0df9250a69f 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -85,7 +85,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) */ if (sdata->hw_irq_trigger && st_sensors_new_samples_available(indio_dev, sdata)) { - iio_trigger_poll_chained(p); + iio_trigger_poll_nested(p); } else { dev_dbg(indio_dev->dev.parent, "spurious IRQ\n"); return IRQ_NONE; @@ -110,7 +110,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) dev_dbg(indio_dev->dev.parent, "more samples came in during polling\n"); sdata->hw_timestamp = iio_get_time_ns(indio_dev); - iio_trigger_poll_chained(p); + iio_trigger_poll_nested(p); } return IRQ_HANDLED; diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index 3ea1d4613080..c28d17ca6f5e 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -813,7 +813,7 @@ static irqreturn_t fxas21002c_data_rdy_thread(int irq, void *private) if (!data_ready) return IRQ_NONE; - iio_trigger_poll_chained(data->dready_trig); + iio_trigger_poll_nested(data->dready_trig); return IRQ_HANDLED; } diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 6a6d84a3deda..a791ba3a693a 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -939,7 +939,7 @@ static irqreturn_t mpu3050_irq_thread(int irq, void *p) if (!(val & MPU3050_INT_STATUS_RAW_RDY)) return IRQ_NONE; - iio_trigger_poll_chained(p); + iio_trigger_poll_nested(p); return IRQ_HANDLED; } diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 2a4107a79662..11ef38994a95 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -68,7 +68,7 @@ static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) if (!(status & HTS221_RH_DRDY_MASK)) return IRQ_NONE; - iio_trigger_poll_chained(hw->trig); + iio_trigger_poll_nested(hw->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 270ec0a9f641..784dc1e00310 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -222,7 +222,14 @@ irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private) } EXPORT_SYMBOL(iio_trigger_generic_data_rdy_poll); -void iio_trigger_poll_chained(struct iio_trigger *trig) +/** + * iio_trigger_poll_nested() - Call the threaded trigger handler of the + * consumers + * @trig: trigger which occurred + * + * This function should only be called from a kernel thread context. + */ +void iio_trigger_poll_nested(struct iio_trigger *trig) { int i; @@ -237,7 +244,7 @@ void iio_trigger_poll_chained(struct iio_trigger *trig) } } } -EXPORT_SYMBOL(iio_trigger_poll_chained); +EXPORT_SYMBOL(iio_trigger_poll_nested); void iio_trigger_notify_done(struct iio_trigger *trig) { diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index e1ff6f524f4b..2d91caf24dd0 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -108,7 +108,7 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) { switch (event) { case ACPI_ALS_NOTIFY_ILLUMINANCE: - iio_trigger_poll_chained(als->trig); + iio_trigger_poll_nested(als->trig); break; default: /* Unhandled event */ diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 668e444f6049..9d0218b7426e 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -431,7 +431,7 @@ static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private) struct rpr0521_data *data = iio_priv(indio_dev); if (rpr0521_is_triggered(data)) { - iio_trigger_poll_chained(data->drdy_trigger0); + iio_trigger_poll_nested(data->drdy_trigger0); return IRQ_HANDLED; } diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index c737d3e193ae..50f95c5d2060 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -161,7 +161,7 @@ static irqreturn_t st_uvis25_trigger_handler_thread(int irq, void *private) if (!(status & ST_UVIS25_REG_UV_DA_MASK)) return IRQ_NONE; - iio_trigger_poll_chained(hw->trig); + iio_trigger_poll_nested(hw->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 6bdfce9747f9..b9460c165d37 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1078,7 +1078,7 @@ static irqreturn_t vcnl4010_irq_thread(int irq, void *p) } if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) - iio_trigger_poll_chained(indio_dev->trig); + iio_trigger_poll_nested(indio_dev->trig); end: return IRQ_HANDLED; diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 84148b944000..14e29330e972 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -89,7 +89,7 @@ static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private) IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), iio_get_time_ns(indio_dev)); - iio_trigger_poll_chained(data->drdy_trigger0); + iio_trigger_poll_nested(data->drdy_trigger0); return IRQ_HANDLED; } diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index b82f093f1e6a..0083e858c21e 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -118,7 +118,7 @@ static int lmp91000_read(struct lmp91000_data *data, int channel, int *val) data->chan_select = channel != LMP91000_REG_MODECN_3LEAD; - iio_trigger_poll_chained(data->trig); + iio_trigger_poll_nested(data->trig); ret = wait_for_completion_timeout(&data->completion, HZ); reinit_completion(&data->completion); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 67119a9b95fc..421e059d1f19 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -829,7 +829,7 @@ static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data) } /* New sample available: dispatch internal trigger consumers. */ - iio_trigger_poll_chained(priv->trigger); + iio_trigger_poll_nested(priv->trigger); if (cont) /* diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index ebc95cf8f5f4..96fa97451cbf 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -257,7 +257,7 @@ static void as3935_event_work(struct work_struct *work) switch (val) { case AS3935_EVENT_INT: - iio_trigger_poll_chained(st->trig); + iio_trigger_poll_nested(st->trig); break; case AS3935_DISTURB_INT: case AS3935_NOISE_INT: diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c index 96ec06bbe546..7aaed0611899 100644 --- a/drivers/iio/trigger/iio-trig-loop.c +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -46,7 +46,7 @@ static int iio_loop_thread(void *data) set_freezable(); do { - iio_trigger_poll_chained(trig); + iio_trigger_poll_nested(trig); } while (likely(!kthread_freezable_should_stop(NULL))); return 0; diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 42da55dc3aa7..51f52c5c6092 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -152,7 +152,7 @@ void iio_trigger_unregister(struct iio_trigger *trig_info); int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig); void iio_trigger_poll(struct iio_trigger *trig); -void iio_trigger_poll_chained(struct iio_trigger *trig); +void iio_trigger_poll_nested(struct iio_trigger *trig); irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private); -- cgit v1.2.3 From 70ed9ced1f415b4f68bfdfffc7240a79bca8e045 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 28 Feb 2023 10:05:14 +0100 Subject: dt-bindings: iio: ti,tmp117: fix documentation link Fix the broken link to point to the correct homepage. Fixes: 5e713b25d137 ("dt-bindings: iio: temperature: Add DT bindings for TMP117") Signed-off-by: Marco Felsch Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230228090518.529811-2-m.felsch@pengutronix.de Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml index c4f1c69f9330..b6d082995e55 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -9,7 +9,7 @@ title: TI TMP117 - Digital temperature sensor with integrated NV memory description: | TI TMP117 - Digital temperature sensor with integrated NV memory that supports I2C interface. - https://www.ti.com/lit/gpn/tmp1 + https://www.ti.com/lit/gpn/tmp117 maintainers: - Puranjay Mohan -- cgit v1.2.3 From 07cc68996d8e83f7f1caccdc588757f6df57861a Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 28 Feb 2023 10:05:15 +0100 Subject: iio: temperature: tmp117: improve fallback capabilities Don't error if the device-id found don't match the device-id for the TMP117 sensor since other TMPxxx might be compatible to the TMP117. The fallback mechanism tries to gather the required information from the of_device_id or from the i2c_client information. The commit also prepares the driver for adding new devices more easily by making use of switch-case at the relevant parts. Signed-off-by: Marco Felsch Link: https://lore.kernel.org/r/20230228090518.529811-3-m.felsch@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp117.c | 44 +++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index f9b8f2b570f6..8a3992d9ee93 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -115,23 +116,40 @@ static const struct iio_info tmp117_info = { static int tmp117_identify(struct i2c_client *client) { + const struct i2c_device_id *id; + unsigned long match_data; int dev_id; dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID); if (dev_id < 0) return dev_id; - if (dev_id != TMP117_DEVICE_ID) { - dev_err(&client->dev, "TMP117 not found\n"); - return -ENODEV; + + switch (dev_id) { + case TMP117_DEVICE_ID: + return dev_id; } - return 0; + + dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n", + dev_id); + + match_data = (uintptr_t)device_get_match_data(&client->dev); + if (match_data) + return match_data; + + id = i2c_client_get_device_id(client); + if (id) + return id->driver_data; + + dev_err(&client->dev, "Failed to identify unsupported device\n"); + + return -ENODEV; } static int tmp117_probe(struct i2c_client *client) { struct tmp117_data *data; struct iio_dev *indio_dev; - int ret; + int ret, dev_id; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; @@ -140,6 +158,8 @@ static int tmp117_probe(struct i2c_client *client) if (ret < 0) return ret; + dev_id = ret; + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -148,24 +168,28 @@ static int tmp117_probe(struct i2c_client *client) data->client = client; data->calibbias = 0; - indio_dev->name = "tmp117"; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &tmp117_info; - indio_dev->channels = tmp117_channels; - indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); + switch (dev_id) { + case TMP117_DEVICE_ID: + indio_dev->channels = tmp117_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); + indio_dev->name = "tmp117"; + break; + } return devm_iio_device_register(&client->dev, indio_dev); } static const struct of_device_id tmp117_of_match[] = { - { .compatible = "ti,tmp117", }, + { .compatible = "ti,tmp117", .data = (void *)TMP117_DEVICE_ID }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { - { "tmp117", 0 }, + { "tmp117", TMP117_DEVICE_ID }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); -- cgit v1.2.3 From 9225efe0806d08494e63eeecebad0b61c66d27dc Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 28 Feb 2023 10:05:16 +0100 Subject: dt-bindings: iio: ti,tmp117: add binding for the TMP116 The TMP116 is the predecessor of the TMP117. Signed-off-by: Marco Felsch Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230228090518.529811-4-m.felsch@pengutronix.de Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml index b6d082995e55..8c6d7735e875 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -7,8 +7,9 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: TI TMP117 - Digital temperature sensor with integrated NV memory description: | - TI TMP117 - Digital temperature sensor with integrated NV memory that supports - I2C interface. + TI TMP116/117 - Digital temperature sensor with integrated NV memory that + supports I2C interface. + https://www.ti.com/lit/gpn/tmp116 https://www.ti.com/lit/gpn/tmp117 maintainers: @@ -17,6 +18,7 @@ maintainers: properties: compatible: enum: + - ti,tmp116 - ti,tmp117 reg: -- cgit v1.2.3 From 0cd2889dc1fed57a2975521d951e1bc91088229c Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 28 Feb 2023 10:05:17 +0100 Subject: iio: temperature: tmp117: add TI TMP116 support The TMP116 is the predecessor of the TMP117. The TMP116 don't support custom offset calibration data, instead this register is used as generic EEPROM storage as well. Signed-off-by: Marco Felsch Link: https://lore.kernel.org/r/20230228090518.529811-5-m.felsch@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp117.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 8a3992d9ee93..9bfb818c3bf1 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -32,9 +32,11 @@ #define TMP117_REG_DEVICE_ID 0xF #define TMP117_RESOLUTION_10UC 78125 -#define TMP117_DEVICE_ID 0x0117 #define MICRODEGREE_PER_10MILLIDEGREE 10000 +#define TMP116_DEVICE_ID 0x1116 +#define TMP117_DEVICE_ID 0x0117 + struct tmp117_data { struct i2c_client *client; s16 calibbias; @@ -109,6 +111,14 @@ static const struct iio_chan_spec tmp117_channels[] = { }, }; +static const struct iio_chan_spec tmp116_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + static const struct iio_info tmp117_info = { .read_raw = tmp117_read_raw, .write_raw = tmp117_write_raw, @@ -125,6 +135,7 @@ static int tmp117_identify(struct i2c_client *client) return dev_id; switch (dev_id) { + case TMP116_DEVICE_ID: case TMP117_DEVICE_ID: return dev_id; } @@ -172,6 +183,11 @@ static int tmp117_probe(struct i2c_client *client) indio_dev->info = &tmp117_info; switch (dev_id) { + case TMP116_DEVICE_ID: + indio_dev->channels = tmp116_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp116_channels); + indio_dev->name = "tmp116"; + break; case TMP117_DEVICE_ID: indio_dev->channels = tmp117_channels; indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); @@ -183,12 +199,14 @@ static int tmp117_probe(struct i2c_client *client) } static const struct of_device_id tmp117_of_match[] = { + { .compatible = "ti,tmp116", .data = (void *)TMP116_DEVICE_ID }, { .compatible = "ti,tmp117", .data = (void *)TMP117_DEVICE_ID }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { + { "tmp116", TMP116_DEVICE_ID }, { "tmp117", TMP117_DEVICE_ID }, { } }; -- cgit v1.2.3 From e94230755641d4832f28b7997755dbf0acac7b54 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 28 Feb 2023 10:05:18 +0100 Subject: iio: temperature: tmp117: cosmetic alignment cleanup Align the code correctly if possible and align the channel bit mask to make it easier to read. Signed-off-by: Marco Felsch Link: https://lore.kernel.org/r/20230228090518.529811-6-m.felsch@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp117.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 9bfb818c3bf1..638e3a5bd6b8 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -43,8 +43,8 @@ struct tmp117_data { }; static int tmp117_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *channel, int *val, - int *val2, long mask) + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) { struct tmp117_data *data = iio_priv(indio_dev); s32 ret; @@ -52,7 +52,7 @@ static int tmp117_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: ret = i2c_smbus_read_word_swapped(data->client, - TMP117_REG_TEMP); + TMP117_REG_TEMP); if (ret < 0) return ret; *val = sign_extend32(ret, 15); @@ -60,7 +60,7 @@ static int tmp117_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_CALIBBIAS: ret = i2c_smbus_read_word_swapped(data->client, - TMP117_REG_TEMP_OFFSET); + TMP117_REG_TEMP_OFFSET); if (ret < 0) return ret; *val = sign_extend32(ret, 15); @@ -82,9 +82,8 @@ static int tmp117_read_raw(struct iio_dev *indio_dev, } } -static int tmp117_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *channel, int val, - int val2, long mask) +static int tmp117_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec + const *channel, int val, int val2, long mask) { struct tmp117_data *data = iio_priv(indio_dev); s16 off; @@ -107,7 +106,8 @@ static const struct iio_chan_spec tmp117_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), }, }; -- cgit v1.2.3 From 42d35bf6c37b9445fa39f995466d716add4b70b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 12:14:54 +0100 Subject: iio: adc: rcar-gyroadc: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/iio/adc/rcar-gyroadc.c:286:34: error: ‘rcar_gyroadc_child_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230311111457.251475-1-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rcar-gyroadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 27d9e147b4b7..b8972f673c9d 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -283,7 +283,7 @@ static const struct of_device_id rcar_gyroadc_match[] = { MODULE_DEVICE_TABLE(of, rcar_gyroadc_match); -static const struct of_device_id rcar_gyroadc_child_match[] = { +static const struct of_device_id rcar_gyroadc_child_match[] __maybe_unused = { /* Mode 1 ADCs */ { .compatible = "fujitsu,mb88101a", -- cgit v1.2.3 From 666cac91ae47c86c7a76fec39771123a92825134 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 10 Mar 2023 19:24:34 -0500 Subject: iio: dac: cio-dac: Fix typo in comment listing supported devices This driver supports the CIO-DAC08 device and not "CIO-DAC06". Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20230311002434.8761-1-william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/dac/cio-dac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c index 791dd999cf29..bb67d3340c9f 100644 --- a/drivers/iio/dac/cio-dac.c +++ b/drivers/iio/dac/cio-dac.c @@ -4,7 +4,7 @@ * Copyright (C) 2016 William Breathitt Gray * * This driver supports the following Measurement Computing devices: CIO-DAC16, - * CIO-DAC06, and PC104-DAC06. + * CIO-DAC08, and PC104-DAC06. */ #include #include -- cgit v1.2.3 From f8c64b146c60d60d9d35c950a854c3975034af19 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Tue, 7 Mar 2023 07:55:33 +0100 Subject: dt-bindings: iio: adc: Add TI ADS1100 and ADS1000 The ADS1100 is a 16-bit ADC (at 8 samples per second). The ADS1000 is similar, but has a fixed data rate. Signed-off-by: Mike Looijmans Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230307065535.7927-1-mike.looijmans@topic.nl Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/ti,ads1100.yaml | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml new file mode 100644 index 000000000000..970ccab15e1e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads1100.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI ADS1100/ADS1000 single channel I2C analog to digital converter + +maintainers: + - Mike Looijmans + +description: | + Datasheet at: https://www.ti.com/lit/gpn/ads1100 + +properties: + compatible: + enum: + - ti,ads1100 + - ti,ads1000 + + reg: + maxItems: 1 + + vdd-supply: true + + "#io-channel-cells": + const: 0 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@49 { + compatible = "ti,ads1100"; + reg = <0x49>; + }; + }; +... -- cgit v1.2.3 From 541880542f2baa7c485b40c1ecad1855315418f6 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Tue, 7 Mar 2023 07:55:34 +0100 Subject: iio: adc: Add TI ADS1100 and ADS1000 The ADS1100 is a 16-bit ADC (at 8 samples per second). The ADS1000 is similar, but has a fixed data rate. Signed-off-by: Mike Looijmans Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230307065535.7927-2-mike.looijmans@topic.nl Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-ads1100.c | 445 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 456 insertions(+) create mode 100644 drivers/iio/adc/ti-ads1100.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 45af2302be53..eb2b09ef5d5b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1229,6 +1229,16 @@ config TI_ADS7924 This driver can also be built as a module. If so, the module will be called ti-ads7924. +config TI_ADS1100 + tristate "Texas Instruments ADS1100 and ADS1000 ADC" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS1100 and + ADS1000 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads1100. + config TI_ADS7950 tristate "Texas Instruments ADS7950 ADC driver" depends on SPI && GPIOLIB diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 36c18177322a..e07e4a3e6237 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -108,6 +108,7 @@ obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o +obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c new file mode 100644 index 000000000000..6a478efb108b --- /dev/null +++ b/drivers/iio/adc/ti-ads1100.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADS1100 - Texas Instruments Analog-to-Digital Converter + * + * Copyright (c) 2023, Topic Embedded Products + * + * Datasheet: https://www.ti.com/lit/gpn/ads1100 + * IIO driver for ADS1100 and ADS1000 ADC 16-bit I2C + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* The ADS1100 has a single byte config register */ + +/* Conversion in progress bit */ +#define ADS1100_CFG_ST_BSY BIT(7) +/* Single conversion bit */ +#define ADS1100_CFG_SC BIT(4) +/* Data rate */ +#define ADS1100_DR_MASK GENMASK(3, 2) +/* Gain */ +#define ADS1100_PGA_MASK GENMASK(1, 0) + +#define ADS1100_CONTINUOUS 0 +#define ADS1100_SINGLESHOT ADS1100_CFG_SC + +#define ADS1100_SLEEP_DELAY_MS 2000 + +static const int ads1100_data_rate[] = { 128, 32, 16, 8 }; +static const int ads1100_data_rate_bits[] = { 12, 14, 15, 16 }; + +struct ads1100_data { + struct i2c_client *client; + struct regulator *reg_vdd; + struct mutex lock; + int scale_avail[2 * 4]; /* 4 gain settings */ + u8 config; + bool supports_data_rate; /* Only the ADS1100 can select the rate */ +}; + +static const struct iio_chan_spec ads1100_channel = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + .datasheet_name = "AIN", +}; + +static int ads1100_set_config_bits(struct ads1100_data *data, u8 mask, u8 value) +{ + int ret; + u8 config = (data->config & ~mask) | (value & mask); + + if (data->config == config) + return 0; /* Already done */ + + ret = i2c_master_send(data->client, &config, 1); + if (ret < 0) + return ret; + + data->config = config; + + return 0; +}; + +static int ads1100_data_bits(struct ads1100_data *data) +{ + return ads1100_data_rate_bits[FIELD_GET(ADS1100_DR_MASK, data->config)]; +} + +static int ads1100_get_adc_result(struct ads1100_data *data, int chan, int *val) +{ + int ret; + __be16 buffer; + s16 value; + + if (chan != 0) + return -EINVAL; + + ret = pm_runtime_resume_and_get(&data->client->dev); + if (ret < 0) + return ret; + + ret = i2c_master_recv(data->client, (char *)&buffer, sizeof(buffer)); + + pm_runtime_mark_last_busy(&data->client->dev); + pm_runtime_put_autosuspend(&data->client->dev); + + if (ret < 0) { + dev_err(&data->client->dev, "I2C read fail: %d\n", ret); + return ret; + } + + /* Value is always 16-bit 2's complement */ + value = be16_to_cpu(buffer); + + /* Shift result to compensate for bit resolution vs. sample rate */ + value <<= 16 - ads1100_data_bits(data); + + *val = sign_extend32(value, 15); + + return 0; +} + +static int ads1100_set_scale(struct ads1100_data *data, int val, int val2) +{ + int microvolts; + int gain; + + /* With Vdd between 2.7 and 5V, the scale is always below 1 */ + if (val) + return -EINVAL; + + if (!val2) + return -EINVAL; + + microvolts = regulator_get_voltage(data->reg_vdd); + /* + * val2 is in 'micro' units, n = val2 / 1000000 + * result must be millivolts, d = microvolts / 1000 + * the full-scale value is d/n, corresponds to 2^15, + * hence the gain = (d / n) >> 15, factoring out the 1000 and moving the + * bitshift so everything fits in 32-bits yields this formula. + */ + gain = DIV_ROUND_CLOSEST(microvolts, BIT(15)) * MILLI / val2; + if (gain < BIT(0) || gain > BIT(3)) + return -EINVAL; + + ads1100_set_config_bits(data, ADS1100_PGA_MASK, ffs(gain) - 1); + + return 0; +} + +static int ads1100_set_data_rate(struct ads1100_data *data, int chan, int rate) +{ + unsigned int i; + unsigned int size; + + size = data->supports_data_rate ? ARRAY_SIZE(ads1100_data_rate) : 1; + for (i = 0; i < size; i++) { + if (ads1100_data_rate[i] == rate) + return ads1100_set_config_bits(data, ADS1100_DR_MASK, + FIELD_PREP(ADS1100_DR_MASK, i)); + } + + return -EINVAL; +} + +static int ads1100_get_vdd_millivolts(struct ads1100_data *data) +{ + return regulator_get_voltage(data->reg_vdd) / (MICRO / MILLI); +} + +static void ads1100_calc_scale_avail(struct ads1100_data *data) +{ + int millivolts = ads1100_get_vdd_millivolts(data); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(data->scale_avail) / 2; i++) { + data->scale_avail[i * 2 + 0] = millivolts; + data->scale_avail[i * 2 + 1] = 15 + i; + } +} + +static int ads1100_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ads1100_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = ads1100_data_rate; + if (data->supports_data_rate) + *length = ARRAY_SIZE(ads1100_data_rate); + else + *length = 1; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *vals = data->scale_avail; + *length = ARRAY_SIZE(data->scale_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ads1100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct ads1100_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + break; + + ret = ads1100_get_adc_result(data, chan->address, val); + if (ret >= 0) + ret = IIO_VAL_INT; + iio_device_release_direct_mode(indio_dev); + break; + case IIO_CHAN_INFO_SCALE: + /* full-scale is the supply voltage in millivolts */ + *val = ads1100_get_vdd_millivolts(data); + *val2 = 15 + FIELD_GET(ADS1100_PGA_MASK, data->config); + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = ads1100_data_rate[FIELD_GET(ADS1100_DR_MASK, + data->config)]; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1100_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1100_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = ads1100_set_scale(data, val, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads1100_set_data_rate(data, chan->address, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static const struct iio_info ads1100_info = { + .read_avail = ads1100_read_avail, + .read_raw = ads1100_read_raw, + .write_raw = ads1100_write_raw, +}; + +static int ads1100_setup(struct ads1100_data *data) +{ + int ret; + u8 buffer[3]; + + /* Setup continuous sampling mode at 8sps */ + buffer[0] = ADS1100_DR_MASK | ADS1100_CONTINUOUS; + ret = i2c_master_send(data->client, buffer, 1); + if (ret < 0) + return ret; + + ret = i2c_master_recv(data->client, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + /* Config register returned in third byte, strip away the busy status */ + data->config = buffer[2] & ~ADS1100_CFG_ST_BSY; + + /* Detect the sample rate capability by checking the DR bits */ + data->supports_data_rate = FIELD_GET(ADS1100_DR_MASK, buffer[2]) != 0; + + return 0; +} + +static void ads1100_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static void ads1100_disable_continuous(void *data) +{ + ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_SINGLESHOT); +} + +static int ads1100_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads1100_data *data; + struct device *dev = &client->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, data); + data->client = client; + mutex_init(&data->lock); + + indio_dev->name = "ads1100"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &ads1100_channel; + indio_dev->num_channels = 1; + indio_dev->info = &ads1100_info; + + data->reg_vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) + return dev_err_probe(dev, PTR_ERR(data->reg_vdd), + "Failed to get vdd regulator\n"); + + ret = regulator_enable(data->reg_vdd); + if (ret < 0) + return dev_err_probe(dev, PTR_ERR(data->reg_vdd), + "Failed to enable vdd regulator\n"); + + ret = devm_add_action_or_reset(dev, ads1100_reg_disable, data->reg_vdd); + if (ret) + return ret; + + ret = ads1100_setup(data); + if (ret) + return dev_err_probe(dev, ret, + "Failed to communicate with device\n"); + + ret = devm_add_action_or_reset(dev, ads1100_disable_continuous, data); + if (ret) + return ret; + + ads1100_calc_scale_avail(data); + + pm_runtime_set_autosuspend_delay(dev, ADS1100_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register IIO device\n"); + + return 0; +} + +static int ads1100_runtime_suspend(struct device *dev) +{ + struct ads1100_data *data = dev_get_drvdata(dev); + + ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_SINGLESHOT); + regulator_disable(data->reg_vdd); + + return 0; +} + +static int ads1100_runtime_resume(struct device *dev) +{ + struct ads1100_data *data = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(data->reg_vdd); + if (ret) { + dev_err(&data->client->dev, "Failed to enable Vdd\n"); + return ret; + } + + /* + * We'll always change the mode bit in the config register, so there is + * no need here to "force" a write to the config register. If the device + * has been power-cycled, we'll re-write its config register now. + */ + return ads1100_set_config_bits(data, ADS1100_CFG_SC, + ADS1100_CONTINUOUS); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ads1100_pm_ops, + ads1100_runtime_suspend, + ads1100_runtime_resume, + NULL); + +static const struct i2c_device_id ads1100_id[] = { + { "ads1100" }, + { "ads1000" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ads1100_id); + +static const struct of_device_id ads1100_of_match[] = { + {.compatible = "ti,ads1100" }, + {.compatible = "ti,ads1000" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ads1100_of_match); + +static struct i2c_driver ads1100_driver = { + .driver = { + .name = "ads1100", + .of_match_table = ads1100_of_match, + .pm = pm_ptr(&ads1100_pm_ops), + }, + .probe_new = ads1100_probe, + .id_table = ads1100_id, +}; + +module_i2c_driver(ads1100_driver); + +MODULE_AUTHOR("Mike Looijmans "); +MODULE_DESCRIPTION("Texas Instruments ADS1100 ADC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 954ea91fb68b771dba6d87cfa61b68e09cc2497f Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 6 Mar 2023 10:43:00 +0100 Subject: dt-bindings: iio: ad74413r: allow setting sink current for digital input Depending on the actual hardware wired up to a digital input channel, it may be necessary to configure the ad74413r to sink a small current. For example, in the case of a simple mechanical switch, the charge on the external 68 nF capacitor (cf. the data sheet's Figure 34) will keep the channel as reading high even after the switch is turned off again. Signed-off-by: Rasmus Villemoes Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230306094301.1357543-2-linux@rasmusvillemoes.dk Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml index 9eb3ecc8bbc8..590ea7936ad7 100644 --- a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml @@ -101,6 +101,15 @@ patternProperties: When not configured as a comparator, the GPO will be treated as an output-only GPIO. + drive-strength-microamp: + description: | + For channels configured as digital input, this configures the sink + current. + minimum: 0 + maximum: 1800 + default: 0 + multipleOf: 120 + required: - reg -- cgit v1.2.3 From 504eb485589d146e99a53cd982b52fe1728fc19e Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 6 Mar 2023 10:43:01 +0100 Subject: iio: ad74413r: wire up support for drive-strength-microamp property Use the value specified in the channel configuration node to populate the DIN_SINK field of the DIN_CONFIGx register. Signed-off-by: Rasmus Villemoes Link: https://lore.kernel.org/r/20230306094301.1357543-3-linux@rasmusvillemoes.dk Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index f32c8c2fb26d..4395758dbaa6 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -39,6 +39,7 @@ struct ad74413r_chip_info { struct ad74413r_channel_config { u32 func; + u32 drive_strength; bool gpo_comparator; bool initialized; }; @@ -111,6 +112,7 @@ struct ad74413r_state { #define AD74413R_REG_DIN_CONFIG_X(x) (0x09 + (x)) #define AD74413R_DIN_DEBOUNCE_MASK GENMASK(4, 0) #define AD74413R_DIN_DEBOUNCE_LEN BIT(5) +#define AD74413R_DIN_SINK_MASK GENMASK(9, 6) #define AD74413R_REG_DAC_CODE_X(x) (0x16 + (x)) #define AD74413R_DAC_CODE_MAX GENMASK(12, 0) @@ -261,6 +263,18 @@ static int ad74413r_set_comp_debounce(struct ad74413r_state *st, val); } +static int ad74413r_set_comp_drive_strength(struct ad74413r_state *st, + unsigned int offset, + unsigned int strength) +{ + strength = min(strength, 1800U); + + return regmap_update_bits(st->regmap, AD74413R_REG_DIN_CONFIG_X(offset), + AD74413R_DIN_SINK_MASK, + FIELD_PREP(AD74413R_DIN_SINK_MASK, strength / 120)); +} + + static void ad74413r_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { @@ -1190,6 +1204,9 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, config->gpo_comparator = fwnode_property_read_bool(channel_node, "adi,gpo-comparator"); + fwnode_property_read_u32(channel_node, "drive-strength-microamp", + &config->drive_strength); + if (!config->gpo_comparator) st->num_gpo_gpios++; @@ -1269,6 +1286,7 @@ static int ad74413r_setup_gpios(struct ad74413r_state *st) unsigned int gpo_gpio_i = 0; unsigned int i; u8 gpo_config; + u32 strength; int ret; for (i = 0; i < AD74413R_CHANNEL_MAX; i++) { @@ -1285,6 +1303,11 @@ static int ad74413r_setup_gpios(struct ad74413r_state *st) config->func == CH_FUNC_DIGITAL_INPUT_LOOP_POWER) st->comp_gpio_offsets[comp_gpio_i++] = i; + strength = config->drive_strength; + ret = ad74413r_set_comp_drive_strength(st, i, strength); + if (ret) + return ret; + ret = ad74413r_set_gpo_config(st, i, gpo_config); if (ret) return ret; -- cgit v1.2.3 From 0088dd6869579f7022b226d1266e6dc23789ed41 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 1 Mar 2023 12:55:11 +0100 Subject: iio: addac: ad74413r: fix Current Input, Loop Powered Mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the driver handles CH_FUNC_CURRENT_INPUT_LOOP_POWER and CH_FUNC_CURRENT_INPUT_EXT_POWER completely identically. But that's not correct. In order for CH_FUNC_CURRENT_INPUT_LOOP_POWER to work, two changes must be made: (1) expose access to the DAC_CODE_x register so that the intended output current can be set, i.e. expose the channel as both current output and current input, and (2) per the data sheet When selecting the current input loop powered function, tie the VIOUTN_x pin to ground via the on-chip 200 kΩ resistor by enabling the CH_200K_TO_GND bit in the ADC_CONFIGx registers. Signed-off-by: Rasmus Villemoes Link: https://lore.kernel.org/r/20230301115511.849418-1-linux@rasmusvillemoes.dk Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 4395758dbaa6..07e9f6ae16a8 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -100,6 +100,7 @@ struct ad74413r_state { #define AD74413R_REG_ADC_CONFIG_X(x) (0x05 + (x)) #define AD74413R_ADC_CONFIG_RANGE_MASK GENMASK(7, 5) #define AD74413R_ADC_CONFIG_REJECTION_MASK GENMASK(4, 3) +#define AD74413R_ADC_CONFIG_CH_200K_TO_GND BIT(2) #define AD74413R_ADC_RANGE_10V 0b000 #define AD74413R_ADC_RANGE_2P5V_EXT_POW 0b001 #define AD74413R_ADC_RANGE_2P5V_INT_POW 0b010 @@ -438,9 +439,20 @@ static int ad74413r_set_channel_dac_code(struct ad74413r_state *st, static int ad74413r_set_channel_function(struct ad74413r_state *st, unsigned int channel, u8 func) { - return regmap_update_bits(st->regmap, + int ret; + + ret = regmap_update_bits(st->regmap, AD74413R_REG_CH_FUNC_SETUP_X(channel), AD74413R_CH_FUNC_SETUP_MASK, func); + if (ret) + return ret; + + if (func == CH_FUNC_CURRENT_INPUT_LOOP_POWER) + ret = regmap_set_bits(st->regmap, + AD74413R_REG_ADC_CONFIG_X(channel), + AD74413R_ADC_CONFIG_CH_200K_TO_GND); + + return ret; } static int ad74413r_set_adc_conv_seq(struct ad74413r_state *st, @@ -1126,6 +1138,11 @@ static struct iio_chan_spec ad74413r_current_input_channels[] = { AD74413R_ADC_CURRENT_CHANNEL, }; +static struct iio_chan_spec ad74413r_current_input_loop_channels[] = { + AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_CURRENT_CHANNEL, +}; + static struct iio_chan_spec ad74413r_resistance_input_channels[] = { AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), }; @@ -1149,7 +1166,7 @@ static const struct ad74413r_channels ad74413r_channels_map[] = { [CH_FUNC_CURRENT_OUTPUT] = AD74413R_CHANNELS(current_output), [CH_FUNC_VOLTAGE_INPUT] = AD74413R_CHANNELS(voltage_input), [CH_FUNC_CURRENT_INPUT_EXT_POWER] = AD74413R_CHANNELS(current_input), - [CH_FUNC_CURRENT_INPUT_LOOP_POWER] = AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER] = AD74413R_CHANNELS(current_input_loop), [CH_FUNC_RESISTANCE_INPUT] = AD74413R_CHANNELS(resistance_input), [CH_FUNC_DIGITAL_INPUT_LOGIC] = AD74413R_CHANNELS(digital_input), [CH_FUNC_DIGITAL_INPUT_LOOP_POWER] = AD74413R_CHANNELS(digital_input), -- cgit v1.2.3 From 18462d080171962d5003b12a7360357fa09b6aad Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 6 Mar 2023 12:08:00 +0100 Subject: iio: imu: st_lsm6dsx: add support to ASM330LHB Add support to STM ASM330LHB (acc + gyro) automotive Mems sensor. The ASM330LHB sensor can use ASM330LHH as fallback device since it implements all the ASM330LHB features currently implemented in st_lsm6dsx. Datasheet: https://www.st.com/resource/en/datasheet/asm330lhb.pdf Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/a1d675457da7aa9e979d8cabea410e942e015e71.1678100533.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/Kconfig | 4 ++-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 2 ++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 4 ++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 5 +++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 5 +++++ 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 8c16cdacf2f2..5865a295a4df 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -14,8 +14,8 @@ config IIO_ST_LSM6DSX sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr, lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx, - lsm6dsv, lsm6dsv16x, lsm6dso16is, ism330is, lsm6dst and the - accelerometer/gyroscope of lsm9ds1. + lsm6dsv, lsm6dsv16x, lsm6dso16is, ism330is, asm330lhb, lsm6dst + and the accelerometer/gyroscope of lsm9ds1. To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 8e119d78730b..9dfe4917a386 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -37,6 +37,7 @@ #define ST_LSM6DSV16X_DEV_NAME "lsm6dsv16x" #define ST_LSM6DSO16IS_DEV_NAME "lsm6dso16is" #define ST_ISM330IS_DEV_NAME "ism330is" +#define ST_ASM330LHB_DEV_NAME "asm330lhb" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -61,6 +62,7 @@ enum st_lsm6dsx_hw_id { ST_LSM6DSV16X_ID, ST_LSM6DSO16IS_ID, ST_ISM330IS_ID, + ST_ASM330LHB_ID, ST_LSM6DSX_MAX_ID, }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index f6c11d6fb0b0..066fe561c5e8 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -15,7 +15,7 @@ * value of the decimation factor and ODR set for each FIFO data set. * * LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/ - * LSM6DST/LSM6DSOP/LSM6DSTX/LSM6DSV: + * LSM6DST/LSM6DSOP/LSM6DSTX/LSM6DSV/ASM330LHB: * The FIFO buffer can be configured to store data from gyroscope and * accelerometer. Each sample is queued with a tag (1B) indicating data * source (gyroscope, accelerometer, hw timer). diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 966df6ffe874..455e5edd3a80 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1032,6 +1032,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .hw_id = ST_LSM6DSOP_ID, .name = ST_LSM6DSOP_DEV_NAME, .wai = 0x6c, + }, { + .hw_id = ST_ASM330LHB_ID, + .name = ST_ASM330LHB_DEV_NAME, + .wai = 0x6b, }, }, .channels = { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index df5f60925260..ccdc4ceffd85 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -125,6 +125,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,ism330is", .data = (void *)ST_ISM330IS_ID, }, + { + .compatible = "st,asm330lhb", + .data = (void *)ST_ASM330LHB_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -152,6 +156,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, + { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 974584bda875..f56c170c41a9 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -125,6 +125,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,ism330is", .data = (void *)ST_ISM330IS_ID, }, + { + .compatible = "st,asm330lhb", + .data = (void *)ST_ASM330LHB_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -152,6 +156,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, + { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); -- cgit v1.2.3 From 2e467c4edc7b08c3e4ad37c5d69a372f3bc59fad Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 6 Mar 2023 12:08:01 +0100 Subject: dt-bindings: iio: imu: st_lsm6dsx: add asm330lhb Add device bindings for asm330lhb IMU sensor. Use asm330lhh as fallback device for asm330lhb since it implements all the features currently supported by asm330lhb. Signed-off-by: Lorenzo Bianconi Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/fecf1f20cc8e99fb8654cc733f14bd449ca7f87a.1678100533.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index decf022335d8..b39f5217d8ff 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -46,6 +46,9 @@ properties: - items: - const: st,ism330is - const: st,lsm6dso16is + - items: + - const: st,asm330lhb + - const: st,asm330lhh reg: maxItems: 1 -- cgit v1.2.3 From b29c49026c3c05a11f845dba17cad0b3ba06836d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 16:34:28 +0100 Subject: iio: light: max44009: add missing OF device matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver currently matches only via i2c_device_id, but also has of_device_id table: drivers/iio/light/max44009.c:545:34: error: ‘max44009_of_match’ defined but not used [-Werror=unused-const-variable=] Fixes: 6aef699a7d7e ("iio: light: add driver for MAX44009") Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230312153429.371702-2-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/max44009.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c index 3dadace09fe2..176dcad6e8e8 100644 --- a/drivers/iio/light/max44009.c +++ b/drivers/iio/light/max44009.c @@ -527,6 +527,12 @@ static int max44009_probe(struct i2c_client *client) return devm_iio_device_register(&client->dev, indio_dev); } +static const struct of_device_id max44009_of_match[] = { + { .compatible = "maxim,max44009" }, + { } +}; +MODULE_DEVICE_TABLE(of, max44009_of_match); + static const struct i2c_device_id max44009_id[] = { { "max44009", 0 }, { } @@ -536,18 +542,13 @@ MODULE_DEVICE_TABLE(i2c, max44009_id); static struct i2c_driver max44009_driver = { .driver = { .name = MAX44009_DRV_NAME, + .of_match_table = max44009_of_match, }, .probe_new = max44009_probe, .id_table = max44009_id, }; module_i2c_driver(max44009_driver); -static const struct of_device_id max44009_of_match[] = { - { .compatible = "maxim,max44009" }, - { } -}; -MODULE_DEVICE_TABLE(of, max44009_of_match); - MODULE_AUTHOR("Robert Eshleman "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MAX44009 ambient light sensor driver"); -- cgit v1.2.3 From 9ee6bc3a035a5882888d6a73a2c13e2f4e65793f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 16:34:29 +0100 Subject: iio: proximity: sx9500: Reference ACPI and OF ID data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always reference acpi_device_id and of_device_id tables, as they is little benefit of conditional compiling and OF table could be used also for ACPI matching via PRP0001. This fixes warning: drivers/iio/proximity/sx9500.c:1039:34: error: ‘sx9500_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230312153429.371702-3-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9500.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 8794e75e5bf9..9b2cfcade6a4 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -1051,8 +1051,8 @@ MODULE_DEVICE_TABLE(i2c, sx9500_id); static struct i2c_driver sx9500_driver = { .driver = { .name = SX9500_DRIVER_NAME, - .acpi_match_table = ACPI_PTR(sx9500_acpi_match), - .of_match_table = of_match_ptr(sx9500_of_match), + .acpi_match_table = sx9500_acpi_match, + .of_match_table = sx9500_of_match, .pm = pm_sleep_ptr(&sx9500_pm_ops), }, .probe_new = sx9500_probe, -- cgit v1.2.3 From 225dc61e2fa8def14426e2a34e926a79fae6118b Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 12 Mar 2023 12:17:33 -0400 Subject: iio: adc: stm32-adc: set some stm32-adc.c variables storage-class-specifier to static smatch reports several warnings drivers/iio/adc/stm32-adc.c:2591:20: warning: symbol 'stm32_adc_min_ts_h7' was not declared. Should it be static? drivers/iio/adc/stm32-adc.c:2610:20: warning: symbol 'stm32_adc_min_ts_mp1' was not declared. Should it be static? drivers/iio/adc/stm32-adc.c:2630:20: warning: symbol 'stm32_adc_min_ts_mp13' was not declared. Should it be static? These variables are only used in stm32-adc.c, so they should be static Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20230312161733.470617-1-trix@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 45d4e79f8e55..1aadb2ad2cab 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2588,7 +2588,7 @@ static const struct stm32_adc_cfg stm32f4_adc_cfg = { .irq_clear = stm32f4_adc_irq_clear, }; -const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; +static const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_h7) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32h7_adc_cfg = { @@ -2607,7 +2607,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .ts_int_ch = stm32_adc_min_ts_h7, }; -const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp1) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp1_adc_cfg = { @@ -2627,7 +2627,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .ts_int_ch = stm32_adc_min_ts_mp1, }; -const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp13) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp13_adc_cfg = { -- cgit v1.2.3 From d7d744abc08f008f105dc7fc59805de6beeb8ec8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 10 Mar 2023 08:47:12 -0600 Subject: misc/sram: Use of_property_read_bool() for boolean properties It is preferred to use typed property access functions (i.e. of_property_read_ functions) rather than low-level of_get_property/of_find_property functions for reading properties. Convert reading boolean properties to to of_property_read_bool(). Signed-off-by: Rob Herring Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230310144713.1543544-1-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index dec92580a42c..99413310956b 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -218,14 +218,9 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) block->res = child_res; list_add_tail(&block->list, &reserve_list); - if (of_find_property(child, "export", NULL)) - block->export = true; - - if (of_find_property(child, "pool", NULL)) - block->pool = true; - - if (of_find_property(child, "protect-exec", NULL)) - block->protect_exec = true; + block->export = of_property_read_bool(child, "export"); + block->pool = of_property_read_bool(child, "pool"); + block->protect_exec = of_property_read_bool(child, "protect-exec"); if ((block->export || block->pool || block->protect_exec) && block->size) { -- cgit v1.2.3 From 536e2fce8980c84247ee52e737d24bd1ffdb0d7f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 10 Mar 2023 08:47:13 -0600 Subject: misc: lis3lv02d: Use of_property_read_bool() for boolean properties It is preferred to use typed property access functions (i.e. of_property_read_ functions) rather than low-level of_get_property/of_find_property functions for reading properties. Convert reading boolean properties to to of_property_read_bool(). Signed-off-by: Rob Herring Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230310144713.1543683-1-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 64 +++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 3a7808b796b1..3e11f4d0b454 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -965,19 +965,19 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) if (!pdata) return -ENOMEM; - if (of_get_property(np, "st,click-single-x", NULL)) + if (of_property_read_bool(np, "st,click-single-x")) pdata->click_flags |= LIS3_CLICK_SINGLE_X; - if (of_get_property(np, "st,click-double-x", NULL)) + if (of_property_read_bool(np, "st,click-double-x")) pdata->click_flags |= LIS3_CLICK_DOUBLE_X; - if (of_get_property(np, "st,click-single-y", NULL)) + if (of_property_read_bool(np, "st,click-single-y")) pdata->click_flags |= LIS3_CLICK_SINGLE_Y; - if (of_get_property(np, "st,click-double-y", NULL)) + if (of_property_read_bool(np, "st,click-double-y")) pdata->click_flags |= LIS3_CLICK_DOUBLE_Y; - if (of_get_property(np, "st,click-single-z", NULL)) + if (of_property_read_bool(np, "st,click-single-z")) pdata->click_flags |= LIS3_CLICK_SINGLE_Z; - if (of_get_property(np, "st,click-double-z", NULL)) + if (of_property_read_bool(np, "st,click-double-z")) pdata->click_flags |= LIS3_CLICK_DOUBLE_Z; if (!of_property_read_u32(np, "st,click-threshold-x", &val)) @@ -994,31 +994,31 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) if (!of_property_read_u32(np, "st,click-window", &val)) pdata->click_window = val; - if (of_get_property(np, "st,irq1-disable", NULL)) + if (of_property_read_bool(np, "st,irq1-disable")) pdata->irq_cfg |= LIS3_IRQ1_DISABLE; - if (of_get_property(np, "st,irq1-ff-wu-1", NULL)) + if (of_property_read_bool(np, "st,irq1-ff-wu-1")) pdata->irq_cfg |= LIS3_IRQ1_FF_WU_1; - if (of_get_property(np, "st,irq1-ff-wu-2", NULL)) + if (of_property_read_bool(np, "st,irq1-ff-wu-2")) pdata->irq_cfg |= LIS3_IRQ1_FF_WU_2; - if (of_get_property(np, "st,irq1-data-ready", NULL)) + if (of_property_read_bool(np, "st,irq1-data-ready")) pdata->irq_cfg |= LIS3_IRQ1_DATA_READY; - if (of_get_property(np, "st,irq1-click", NULL)) + if (of_property_read_bool(np, "st,irq1-click")) pdata->irq_cfg |= LIS3_IRQ1_CLICK; - if (of_get_property(np, "st,irq2-disable", NULL)) + if (of_property_read_bool(np, "st,irq2-disable")) pdata->irq_cfg |= LIS3_IRQ2_DISABLE; - if (of_get_property(np, "st,irq2-ff-wu-1", NULL)) + if (of_property_read_bool(np, "st,irq2-ff-wu-1")) pdata->irq_cfg |= LIS3_IRQ2_FF_WU_1; - if (of_get_property(np, "st,irq2-ff-wu-2", NULL)) + if (of_property_read_bool(np, "st,irq2-ff-wu-2")) pdata->irq_cfg |= LIS3_IRQ2_FF_WU_2; - if (of_get_property(np, "st,irq2-data-ready", NULL)) + if (of_property_read_bool(np, "st,irq2-data-ready")) pdata->irq_cfg |= LIS3_IRQ2_DATA_READY; - if (of_get_property(np, "st,irq2-click", NULL)) + if (of_property_read_bool(np, "st,irq2-click")) pdata->irq_cfg |= LIS3_IRQ2_CLICK; - if (of_get_property(np, "st,irq-open-drain", NULL)) + if (of_property_read_bool(np, "st,irq-open-drain")) pdata->irq_cfg |= LIS3_IRQ_OPEN_DRAIN; - if (of_get_property(np, "st,irq-active-low", NULL)) + if (of_property_read_bool(np, "st,irq-active-low")) pdata->irq_cfg |= LIS3_IRQ_ACTIVE_LOW; if (!of_property_read_u32(np, "st,wu-duration-1", &val)) @@ -1026,32 +1026,32 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) if (!of_property_read_u32(np, "st,wu-duration-2", &val)) pdata->duration2 = val; - if (of_get_property(np, "st,wakeup-x-lo", NULL)) + if (of_property_read_bool(np, "st,wakeup-x-lo")) pdata->wakeup_flags |= LIS3_WAKEUP_X_LO; - if (of_get_property(np, "st,wakeup-x-hi", NULL)) + if (of_property_read_bool(np, "st,wakeup-x-hi")) pdata->wakeup_flags |= LIS3_WAKEUP_X_HI; - if (of_get_property(np, "st,wakeup-y-lo", NULL)) + if (of_property_read_bool(np, "st,wakeup-y-lo")) pdata->wakeup_flags |= LIS3_WAKEUP_Y_LO; - if (of_get_property(np, "st,wakeup-y-hi", NULL)) + if (of_property_read_bool(np, "st,wakeup-y-hi")) pdata->wakeup_flags |= LIS3_WAKEUP_Y_HI; - if (of_get_property(np, "st,wakeup-z-lo", NULL)) + if (of_property_read_bool(np, "st,wakeup-z-lo")) pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO; - if (of_get_property(np, "st,wakeup-z-hi", NULL)) + if (of_property_read_bool(np, "st,wakeup-z-hi")) pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI; if (of_get_property(np, "st,wakeup-threshold", &val)) pdata->wakeup_thresh = val; - if (of_get_property(np, "st,wakeup2-x-lo", NULL)) + if (of_property_read_bool(np, "st,wakeup2-x-lo")) pdata->wakeup_flags2 |= LIS3_WAKEUP_X_LO; - if (of_get_property(np, "st,wakeup2-x-hi", NULL)) + if (of_property_read_bool(np, "st,wakeup2-x-hi")) pdata->wakeup_flags2 |= LIS3_WAKEUP_X_HI; - if (of_get_property(np, "st,wakeup2-y-lo", NULL)) + if (of_property_read_bool(np, "st,wakeup2-y-lo")) pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_LO; - if (of_get_property(np, "st,wakeup2-y-hi", NULL)) + if (of_property_read_bool(np, "st,wakeup2-y-hi")) pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_HI; - if (of_get_property(np, "st,wakeup2-z-lo", NULL)) + if (of_property_read_bool(np, "st,wakeup2-z-lo")) pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_LO; - if (of_get_property(np, "st,wakeup2-z-hi", NULL)) + if (of_property_read_bool(np, "st,wakeup2-z-hi")) pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_HI; if (of_get_property(np, "st,wakeup2-threshold", &val)) pdata->wakeup_thresh2 = val; @@ -1073,9 +1073,9 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) } } - if (of_get_property(np, "st,hipass1-disable", NULL)) + if (of_property_read_bool(np, "st,hipass1-disable")) pdata->hipass_ctrl |= LIS3_HIPASS1_DISABLE; - if (of_get_property(np, "st,hipass2-disable", NULL)) + if (of_property_read_bool(np, "st,hipass2-disable")) pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE; if (of_property_read_s32(np, "st,axis-x", &sval) == 0) -- cgit v1.2.3 From 6ca8f8bf706d874090b74f811194e94ebbc560df Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 10 Mar 2023 08:47:13 -0600 Subject: misc: lis3lv02d: Fix reading 'st,default-rate' property The property 'st,default-rate' is tested for presence, but the value is ignored and the 'default_rate' value is updated with a stale 'val' value. Fix this by using of_property_read_u32(). Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230310144713.1543613-1-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 3e11f4d0b454..299d316f1bda 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -1085,7 +1085,7 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) if (of_property_read_s32(np, "st,axis-z", &sval) == 0) pdata->axis_z = sval; - if (of_get_property(np, "st,default-rate", NULL)) + if (of_property_read_u32(np, "st,default-rate", &val) == 0) pdata->default_rate = val; if (of_property_read_s32(np, "st,min-limit-x", &sval) == 0) -- cgit v1.2.3 From 30da26eacea6867353f7aa2c18f37a0a0fa82af4 Mon Sep 17 00:00:00 2001 From: Kasumov Ruslan Date: Wed, 15 Mar 2023 16:51:14 +0300 Subject: iio: adc: qcom-pm8xxx-xoadc: Remove useless condition in pm8xxx_xoadc_parse_channel() The left side of the loop condition never becomes false. hwchan cannot be NULL, because it points to elements of the hw_channels array that takes one of 4 predefined values: pm8018_xoadc_channels, pm8038_xoadc_channels, pm8058_xoadc_channels, pm8921_xoadc_channels. Found by Linux Verification Center (linuxtesting.org) with SVACE. Reviewed-by: Andi Shyti Reviewed-by: Linus Walleij Signed-off-by: Kasumov Ruslan Link: https://lore.kernel.org/r/20230315135114.22684-1-xhxgldhlpfy@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index eb424496ee1d..64a3aeb6261c 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -758,7 +758,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, /* Find the right channel setting */ chid = 0; hwchan = &hw_channels[0]; - while (hwchan && hwchan->datasheet_name) { + while (hwchan->datasheet_name) { if (hwchan->pre_scale_mux == pre_scale_mux && hwchan->amux_channel == amux_channel) break; -- cgit v1.2.3 From fb37fdd02856e45e78209d32522029c9f77fe7b7 Mon Sep 17 00:00:00 2001 From: Cheng Ziqiu Date: Tue, 14 Mar 2023 15:01:30 +0800 Subject: iio: adc: at91-sama5d2_adc: remove dead code in at91_adc_probe() From the comment of platform_get_irq(), it only returns non-zero IRQ number and negative error number, other than zero. Fix this by removing the if condition. Signed-off-by: Cheng Ziqiu Reviewed-by: Dongliang Mu Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230314070130.60581-1-chengziqiu@hust.edu.cn Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index b5d0c9ee284c..30554026a7fe 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2400,12 +2400,8 @@ static int at91_adc_probe(struct platform_device *pdev) st->dma_st.phys_addr = res->start; st->irq = platform_get_irq(pdev, 0); - if (st->irq <= 0) { - if (!st->irq) - st->irq = -ENXIO; - + if (st->irq < 0) return st->irq; - } st->per_clk = devm_clk_get(&pdev->dev, "adc_clk"); if (IS_ERR(st->per_clk)) -- cgit v1.2.3 From 76f2c491a0cae850ad7413e89f62673e6ef90a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 13 Mar 2023 19:53:33 +0100 Subject: iio: frequency: admv1013: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. To retain ordering move the request to a place that is executed later. This way the time of enable keeps the same. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230313185333.2776785-1-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1013.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index ed8167271358..9bf8337806fc 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -490,11 +490,6 @@ static int admv1013_init(struct admv1013_state *st) st->input_mode); } -static void admv1013_clk_disable(void *data) -{ - clk_disable_unprepare(data); -} - static void admv1013_reg_disable(void *data) { regulator_disable(data); @@ -559,11 +554,6 @@ static int admv1013_properties_parse(struct admv1013_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->reg), "failed to get the common-mode voltage\n"); - st->clkin = devm_clk_get(&spi->dev, "lo_in"); - if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), - "failed to get the LO input clock\n"); - return 0; } @@ -601,13 +591,10 @@ static int admv1013_probe(struct spi_device *spi) if (ret) return ret; - ret = clk_prepare_enable(st->clkin); - if (ret) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, admv1013_clk_disable, st->clkin); - if (ret) - return ret; + st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the LO input clock\n"); st->nb.notifier_call = admv1013_freq_change; ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); -- cgit v1.2.3 From 87a1ff66a0e57d9022665d24793f31fc54de182d Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sat, 11 Mar 2023 09:02:18 -0500 Subject: iio: dac: cio-dac: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. Includes trivial header inclusion tidy up. Suggested-by: Andy Shevchenko Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20230311140218.74920-1-william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 1 + drivers/iio/dac/cio-dac.c | 66 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index d3f90cf86143..3acd9c3f388e 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -277,6 +277,7 @@ config CIO_DAC tristate "Measurement Computing CIO-DAC IIO driver" depends on X86 && (ISA_BUS || PC104) select ISA_BUS_API + select REGMAP_MMIO help Say yes here to build support for the Measurement Computing CIO-DAC analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c index bb67d3340c9f..e10c26dd7cce 100644 --- a/drivers/iio/dac/cio-dac.c +++ b/drivers/iio/dac/cio-dac.c @@ -6,16 +6,15 @@ * This driver supports the following Measurement Computing devices: CIO-DAC16, * CIO-DAC08, and PC104-DAC06. */ -#include +#include #include -#include +#include #include #include -#include -#include #include #include #include +#include #include #define CIO_DAC_NUM_CHAN 16 @@ -35,25 +34,51 @@ static unsigned int num_cio_dac; module_param_hw_array(base, uint, ioport, &num_cio_dac, 0); MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses"); +#define CIO_DAC_BASE 0x00 +#define CIO_DAC_CHANNEL_STRIDE 2 + +static bool cio_dac_precious_reg(struct device *dev, unsigned int reg) +{ + /* + * All registers are considered precious; if the XFER jumper is set on + * the device, then no update occurs until a DAC register is read. + */ + return true; +} + +static const struct regmap_config cio_dac_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .io_port = true, + .max_register = 0x1F, + .precious_reg = cio_dac_precious_reg, +}; + /** * struct cio_dac_iio - IIO device private data structure - * @chan_out_states: channels' output states - * @base: base memory address of the DAC device + * @map: Regmap for the device */ struct cio_dac_iio { - int chan_out_states[CIO_DAC_NUM_CHAN]; - u16 __iomem *base; + struct regmap *map; }; static int cio_dac_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct cio_dac_iio *const priv = iio_priv(indio_dev); + const unsigned int offset = chan->channel * CIO_DAC_CHANNEL_STRIDE; + int err; + unsigned int dac_val; if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; - *val = priv->chan_out_states[chan->channel]; + err = regmap_read(priv->map, CIO_DAC_BASE + offset, &dac_val); + if (err) + return err; + + *val = dac_val; return IIO_VAL_INT; } @@ -62,6 +87,7 @@ static int cio_dac_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct cio_dac_iio *const priv = iio_priv(indio_dev); + const unsigned int offset = chan->channel * CIO_DAC_CHANNEL_STRIDE; if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; @@ -70,10 +96,7 @@ static int cio_dac_write_raw(struct iio_dev *indio_dev, if ((unsigned int)val > 65535) return -EINVAL; - priv->chan_out_states[chan->channel] = val; - iowrite16(val, priv->base + chan->channel); - - return 0; + return regmap_write(priv->map, CIO_DAC_BASE + offset, val); } static const struct iio_info cio_dac_info = { @@ -92,7 +115,7 @@ static int cio_dac_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; struct cio_dac_iio *priv; - unsigned int i; + void __iomem *regs; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); if (!indio_dev) @@ -105,21 +128,22 @@ static int cio_dac_probe(struct device *dev, unsigned int id) return -EBUSY; } - priv = iio_priv(indio_dev); - priv->base = devm_ioport_map(dev, base[id], CIO_DAC_EXTENT); - if (!priv->base) + regs = devm_ioport_map(dev, base[id], CIO_DAC_EXTENT); + if (!regs) return -ENOMEM; + priv = iio_priv(indio_dev); + priv->map = devm_regmap_init_mmio(dev, regs, &cio_dac_regmap_config); + if (IS_ERR(priv->map)) + return dev_err_probe(dev, PTR_ERR(priv->map), + "Unable to initialize register map\n"); + indio_dev->info = &cio_dac_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = cio_dac_channels; indio_dev->num_channels = CIO_DAC_NUM_CHAN; indio_dev->name = dev_name(dev); - /* initialize DAC outputs to 0V */ - for (i = 0; i < CIO_DAC_NUM_CHAN; i++) - iowrite16(0, priv->base + i); - return devm_iio_device_register(dev, indio_dev); } -- cgit v1.2.3 From aec463feaca9f726a778499ed57b63ffd9c4c523 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sat, 18 Mar 2023 14:55:03 -0400 Subject: iio: addac: stx104: Replace bitops.h header inclusion with bits.h The header is included in the stx104 driver so that we can use the BIT() macro. This macro is actually defined in the header, so replace the header inclusion with . Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20230318185503.341914-1-william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/addac/stx104.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 48a91a95e597..e45b70aa5bb7 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -3,7 +3,7 @@ * IIO driver for the Apex Embedded Systems STX104 * Copyright (C) 2016 William Breathitt Gray */ -#include +#include #include #include #include -- cgit v1.2.3 From f9dbd94ba8cf19f8ff76a48ba5864fd140f8a4e1 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Fri, 17 Mar 2023 11:33:19 +0100 Subject: dt-bindings: iio: st-sensors: Add IIS328DQ accelerometer The ST IIS328DQ is an accelerometer sensor, which is compatible with the already supported ST H3LIS331DL. So add the new compatible with a fallback to the ST sensor binding. Link: https://lore.kernel.org/linux-iio/2bac9ecf-9d2e-967e-9020-1c950487d781@i2se.com/ Suggested-by: Jonathan Cameron Signed-off-by: Stefan Wahren Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230317103323.7741-5-stefan.wahren@chargebyte.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/st,st-sensors.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index c6201976378f..5eb71b24a7cb 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -48,6 +48,9 @@ properties: - st,lsm330d-accel - st,lsm330dl-accel - st,lsm330dlc-accel + - items: + - const: st,iis328dq + - const: st,h3lis331dl-accel - description: Silan Accelerometers enum: - silan,sc7a20 -- cgit v1.2.3 From 46e33707fe95a21aa9896bded0be97285b779509 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Fri, 17 Mar 2023 11:33:20 +0100 Subject: iio: accel: add support for IIS328DQ variant Since the ST IIS328DQ accelerometer is compatible to the ST LIS331DL, just add the new compatible to the st_accel framework. Link: https://www.st.com/resource/en/datasheet/iis328dq.pdf Signed-off-by: Stefan Wahren Link: https://lore.kernel.org/r/20230317103323.7741-6-stefan.wahren@chargebyte.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel.h | 1 + drivers/iio/accel/st_accel_core.c | 1 + drivers/iio/accel/st_accel_i2c.c | 5 +++++ drivers/iio/accel/st_accel_spi.c | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 56ed0c776d4a..e7525615712b 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -39,6 +39,7 @@ #define LIS302DL_ACCEL_DEV_NAME "lis302dl" #define LSM303C_ACCEL_DEV_NAME "lsm303c_accel" #define SC7A20_ACCEL_DEV_NAME "sc7a20" +#define IIS328DQ_ACCEL_DEV_NAME "iis328dq" #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 6b8562f684d5..5f7d81b44b1d 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -517,6 +517,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = H3LIS331DL_ACCEL_DEV_NAME, + [1] = IIS328DQ_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_12bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 3f02fd5d5946..fb9e2d6f4210 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -119,6 +119,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "silan,sc7a20", .data = SC7A20_ACCEL_DEV_NAME, }, + { + .compatible = "st,iis328dq", + .data = IIS328DQ_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -157,6 +161,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS302DL_ACCEL_DEV_NAME }, { LSM303C_ACCEL_DEV_NAME }, { SC7A20_ACCEL_DEV_NAME }, + { IIS328DQ_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 5740dc1820bd..f72a24f45322 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -100,6 +100,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lsm303c-accel", .data = LSM303C_ACCEL_DEV_NAME, }, + { + .compatible = "st,iis328dq", + .data = IIS328DQ_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -157,6 +161,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS3DE_ACCEL_DEV_NAME }, { LIS302DL_ACCEL_DEV_NAME }, { LSM303C_ACCEL_DEV_NAME }, + { IIS328DQ_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); -- cgit v1.2.3 From 4658842abce4bebb0a0caef53f9c99f0ec4e27e3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 9 Feb 2023 10:13:10 +0100 Subject: interconnect: qcom: Drop obsolete dependency on COMPILE_TEST Since commit 0166dc11be91 ("of: make CONFIG_OF user selectable"), it is possible to test-build any driver which depends on OF on any architecture by explicitly selecting OF. Therefore depending on COMPILE_TEST as an alternative is no longer needed. Signed-off-by: Jean Delvare Cc: Andy Gross Cc: Bjorn Andersson Cc: Konrad Dybcio Cc: Georgi Djakov Link: https://lore.kernel.org/r/20230209101310.11942cd0@endymion.delvare Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 92d65c7bda23..825b647d9169 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -83,7 +83,7 @@ config INTERCONNECT_QCOM_RPMH_POSSIBLE default INTERCONNECT_QCOM depends on QCOM_RPMH || (COMPILE_TEST && !QCOM_RPMH) depends on QCOM_COMMAND_DB || (COMPILE_TEST && !QCOM_COMMAND_DB) - depends on OF || COMPILE_TEST + depends on OF help Compile-testing RPMH drivers is possible on other platforms, but in order to avoid link failures, drivers must not be built-in -- cgit v1.2.3 From 558ea12354882bb8250be8ea09a6658af3f7d912 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Fri, 13 Jan 2023 14:07:59 -0800 Subject: interconnect: Skip call into provider if initial bw is zero Currently framework sets bw even when init bw requirements are zero during provider registration, thus resulting bulk of set bw to hw. Avoid this behaviour by skipping provider set bw calls if init bw is zero. Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/1673647679-15216-1-git-send-email-quic_viveka@quicinc.com Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 7a24c1444ace..446c9d1fb486 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -981,14 +981,17 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider) node->avg_bw = node->init_avg; node->peak_bw = node->init_peak; - if (provider->pre_aggregate) - provider->pre_aggregate(node); - - if (provider->aggregate) - provider->aggregate(node, 0, node->init_avg, node->init_peak, - &node->avg_bw, &node->peak_bw); + if (node->avg_bw || node->peak_bw) { + if (provider->pre_aggregate) + provider->pre_aggregate(node); + + if (provider->aggregate) + provider->aggregate(node, 0, node->init_avg, node->init_peak, + &node->avg_bw, &node->peak_bw); + if (provider->set) + provider->set(node, node); + } - provider->set(node, node); node->avg_bw = 0; node->peak_bw = 0; -- cgit v1.2.3 From 4c4161b4c81b632fc29002c324e996cbb79894e7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 3 Jan 2023 06:57:17 +0200 Subject: interconnect: qcom: drop obsolete OSM_L3/EPSS defines Since Qualcomm platforms have switched to the separate OSM_L3/EPSS driver, old related defines became unused. Drop them now. Suggested-by: Bjorn Andersson Fixes: 4529992c9474 ("interconnect: qcom: osm-l3: Use platform-independent node ids") Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230103045717.1079067-1-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sc7180.h | 2 -- drivers/interconnect/qcom/sc7280.h | 2 -- drivers/interconnect/qcom/sc8180x.h | 2 -- drivers/interconnect/qcom/sdm845.h | 2 -- drivers/interconnect/qcom/sm8150.h | 2 -- drivers/interconnect/qcom/sm8250.h | 2 -- 6 files changed, 12 deletions(-) diff --git a/drivers/interconnect/qcom/sc7180.h b/drivers/interconnect/qcom/sc7180.h index 7a2b3eb00923..2b718922c109 100644 --- a/drivers/interconnect/qcom/sc7180.h +++ b/drivers/interconnect/qcom/sc7180.h @@ -145,7 +145,5 @@ #define SC7180_SLAVE_SERVICE_SNOC 134 #define SC7180_SLAVE_QDSS_STM 135 #define SC7180_SLAVE_TCU 136 -#define SC7180_MASTER_OSM_L3_APPS 137 -#define SC7180_SLAVE_OSM_L3 138 #endif diff --git a/drivers/interconnect/qcom/sc7280.h b/drivers/interconnect/qcom/sc7280.h index 1fb9839b2c14..175e400305c5 100644 --- a/drivers/interconnect/qcom/sc7280.h +++ b/drivers/interconnect/qcom/sc7280.h @@ -150,7 +150,5 @@ #define SC7280_SLAVE_PCIE_1 139 #define SC7280_SLAVE_QDSS_STM 140 #define SC7280_SLAVE_TCU 141 -#define SC7280_MASTER_EPSS_L3_APPS 142 -#define SC7280_SLAVE_EPSS_L3 143 #endif diff --git a/drivers/interconnect/qcom/sc8180x.h b/drivers/interconnect/qcom/sc8180x.h index c138dcd350f1..f8d90598335a 100644 --- a/drivers/interconnect/qcom/sc8180x.h +++ b/drivers/interconnect/qcom/sc8180x.h @@ -168,8 +168,6 @@ #define SC8180X_SLAVE_EBI_CH0_DISPLAY 158 #define SC8180X_SLAVE_MNOC_SF_MEM_NOC_DISPLAY 159 #define SC8180X_SLAVE_MNOC_HF_MEM_NOC_DISPLAY 160 -#define SC8180X_MASTER_OSM_L3_APPS 161 -#define SC8180X_SLAVE_OSM_L3 162 #define SC8180X_MASTER_QUP_CORE_0 163 #define SC8180X_MASTER_QUP_CORE_1 164 diff --git a/drivers/interconnect/qcom/sdm845.h b/drivers/interconnect/qcom/sdm845.h index 776e9c2acb27..bc7e425ce985 100644 --- a/drivers/interconnect/qcom/sdm845.h +++ b/drivers/interconnect/qcom/sdm845.h @@ -136,7 +136,5 @@ #define SDM845_SLAVE_SERVICE_SNOC 128 #define SDM845_SLAVE_QDSS_STM 129 #define SDM845_SLAVE_TCU 130 -#define SDM845_MASTER_OSM_L3_APPS 131 -#define SDM845_SLAVE_OSM_L3 132 #endif /* __DRIVERS_INTERCONNECT_QCOM_SDM845_H__ */ diff --git a/drivers/interconnect/qcom/sm8150.h b/drivers/interconnect/qcom/sm8150.h index 023161681fb8..1d587c94eb06 100644 --- a/drivers/interconnect/qcom/sm8150.h +++ b/drivers/interconnect/qcom/sm8150.h @@ -148,7 +148,5 @@ #define SM8150_SLAVE_VSENSE_CTRL_CFG 137 #define SM8150_SNOC_CNOC_MAS 138 #define SM8150_SNOC_CNOC_SLV 139 -#define SM8150_MASTER_OSM_L3_APPS 140 -#define SM8150_SLAVE_OSM_L3 141 #endif diff --git a/drivers/interconnect/qcom/sm8250.h b/drivers/interconnect/qcom/sm8250.h index e3fc56bc7ca0..209ab195f21f 100644 --- a/drivers/interconnect/qcom/sm8250.h +++ b/drivers/interconnect/qcom/sm8250.h @@ -158,7 +158,5 @@ #define SM8250_SLAVE_VSENSE_CTRL_CFG 147 #define SM8250_SNOC_CNOC_MAS 148 #define SM8250_SNOC_CNOC_SLV 149 -#define SM8250_MASTER_EPSS_L3_APPS 150 -#define SM8250_SLAVE_EPSS_L3 151 #endif -- cgit v1.2.3 From f730038fe6a6de170268fd779b2c029aa70a928b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 3 Jan 2023 05:11:59 +0200 Subject: interconnect: qcom: osm-l3: drop unuserd header inclusion The commit 4529992c9474 ("interconnect: qcom: osm-l3: Use platform-independent node ids") made osm-l3 driver use platform-independent IDs, removing the need to include platform headers. Fixes: 4529992c9474 ("interconnect: qcom: osm-l3: Use platform-independent node ids") Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230103031159.1060075-1-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/osm-l3.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index 1bafb54f1432..a1f4f918b911 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -14,13 +14,6 @@ #include -#include "sc7180.h" -#include "sc7280.h" -#include "sc8180x.h" -#include "sdm845.h" -#include "sm8150.h" -#include "sm8250.h" - #define LUT_MAX_ENTRIES 40U #define LUT_SRC GENMASK(31, 30) #define LUT_L_VAL GENMASK(7, 0) -- cgit v1.2.3 From 213913ff2b89ab5ba76aceba24198b217e8e325c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 10 Mar 2023 08:47:08 -0600 Subject: interconnect: Use of_property_present() for testing DT property presence It is preferred to use typed property access functions (i.e. of_property_read_ functions) rather than low-level of_get_property/of_find_property functions for reading properties. As part of this, convert of_get_property/of_find_property calls to the recently added of_property_present() helper when we just want to test for presence of a property and nothing more. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230310144709.1542841-1-robh@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 446c9d1fb486..4175a42bf748 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -451,7 +451,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx) * When the consumer DT node do not have "interconnects" property * return a NULL path to skip setting constraints. */ - if (!of_find_property(np, "interconnects", NULL)) + if (!of_property_present(np, "interconnects")) return NULL; /* @@ -544,7 +544,7 @@ struct icc_path *of_icc_get(struct device *dev, const char *name) * When the consumer DT node do not have "interconnects" property * return a NULL path to skip setting constraints. */ - if (!of_find_property(np, "interconnects", NULL)) + if (!of_property_present(np, "interconnects")) return NULL; /* -- cgit v1.2.3 From 419405c92299d793b95053aa54d95e2d3f45a1a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 6 Mar 2023 08:56:49 +0100 Subject: interconnect: drop racy registration API Now that all interconnect drivers have been converted to the new provider registration API, the old racy interface can be removed. Reviewed-by: Konrad Dybcio Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20230306075651.2449-22-johan+linaro@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 16 ---------------- include/linux/interconnect-provider.h | 11 ----------- 2 files changed, 27 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 4175a42bf748..da456329c524 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -1084,22 +1084,6 @@ void icc_provider_deregister(struct icc_provider *provider) } EXPORT_SYMBOL_GPL(icc_provider_deregister); -int icc_provider_add(struct icc_provider *provider) -{ - icc_provider_init(provider); - - return icc_provider_register(provider); -} -EXPORT_SYMBOL_GPL(icc_provider_add); - -void icc_provider_del(struct icc_provider *provider) -{ - WARN_ON(!list_empty(&provider->nodes)); - - icc_provider_deregister(provider); -} -EXPORT_SYMBOL_GPL(icc_provider_del); - static const struct of_device_id __maybe_unused ignore_list[] = { { .compatible = "qcom,sc7180-ipa-virt" }, { .compatible = "qcom,sc8180x-ipa-virt" }, diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index d12cd18aab3f..b9af9016a95e 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -125,8 +125,6 @@ int icc_nodes_remove(struct icc_provider *provider); void icc_provider_init(struct icc_provider *provider); int icc_provider_register(struct icc_provider *provider); void icc_provider_deregister(struct icc_provider *provider); -int icc_provider_add(struct icc_provider *provider); -void icc_provider_del(struct icc_provider *provider); struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec); void icc_sync_state(struct device *dev); @@ -179,15 +177,6 @@ static inline int icc_provider_register(struct icc_provider *provider) static inline void icc_provider_deregister(struct icc_provider *provider) { } -static inline int icc_provider_add(struct icc_provider *provider) -{ - return -ENOTSUPP; -} - -static inline void icc_provider_del(struct icc_provider *provider) -{ -} - static inline struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec) { return ERR_PTR(-ENOTSUPP); -- cgit v1.2.3 From 1fb1ea0d9cb8359ae9d6f67f22666c74d5b9f47d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Mar 2023 19:07:47 +0200 Subject: mei: Move uuid.h to the MEI namespace There is only a single user of the UUID uAPI, let's make it part of that user. The way it's done is to prevent compilation time breakage for the user space that does #include In the future MEI user space tools can switch over to use mei_uuid.h. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230310170747.22782-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + drivers/misc/mei/bus-fixup.c | 2 +- drivers/misc/mei/hdcp/mei_hdcp.c | 2 +- drivers/misc/mei/hw.h | 2 +- drivers/misc/mei/main.c | 1 - drivers/misc/mei/pxp/mei_pxp.c | 2 +- include/linux/mod_devicetable.h | 1 + include/linux/uuid.h | 3 --- include/uapi/linux/mei.h | 2 +- include/uapi/linux/mei_uuid.h | 29 +++++++++++++++++++++++++++++ include/uapi/linux/uuid.h | 31 +------------------------------ 11 files changed, 37 insertions(+), 39 deletions(-) create mode 100644 include/uapi/linux/mei_uuid.h diff --git a/MAINTAINERS b/MAINTAINERS index 7ee30a356e89..619c30ec36dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10459,6 +10459,7 @@ F: drivers/watchdog/mei_wdt.c F: include/linux/mei_aux.h F: include/linux/mei_cl_bus.h F: include/uapi/linux/mei.h +F: include/uapi/linux/mei_uuid.h F: include/uapi/linux/uuid.h F: samples/mei/* diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 211536109308..31e3c74ca1f1 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -9,8 +9,8 @@ #include #include #include -#include +#include #include #include "mei_dev.h" diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index e0dcd5c114db..45e3d4d27797 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 319418ddf4fb..e910302fcd1f 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -7,7 +7,7 @@ #ifndef _MEI_HW_TYPES_H_ #define _MEI_HW_TYPES_H_ -#include +#include /* * Timeouts in Seconds diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 632d4ae21e46..c64291741d73 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/mei/pxp/mei_pxp.c b/drivers/misc/mei/pxp/mei_pxp.c index 7ee1fa7b1cb3..3bf560bbdee0 100644 --- a/drivers/misc/mei/pxp/mei_pxp.c +++ b/drivers/misc/mei/pxp/mei_pxp.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 549590e9c644..35203ca3fc37 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -9,6 +9,7 @@ #define LINUX_MOD_DEVICETABLE_H #ifdef __KERNEL__ +#include #include #include typedef unsigned long kernel_ulong_t; diff --git a/include/linux/uuid.h b/include/linux/uuid.h index 6b1a3efa1e0b..43d4a79b273d 100644 --- a/include/linux/uuid.h +++ b/include/linux/uuid.h @@ -107,7 +107,4 @@ extern const u8 uuid_index[16]; int guid_parse(const char *uuid, guid_t *u); int uuid_parse(const char *uuid, uuid_t *u); -/* MEI UUID type, don't use anywhere else */ -#include - #endif diff --git a/include/uapi/linux/mei.h b/include/uapi/linux/mei.h index 4f3638489d01..6e57743628c0 100644 --- a/include/uapi/linux/mei.h +++ b/include/uapi/linux/mei.h @@ -7,7 +7,7 @@ #ifndef _LINUX_MEI_H #define _LINUX_MEI_H -#include +#include /* * This IOCTL is used to associate the current file descriptor with a diff --git a/include/uapi/linux/mei_uuid.h b/include/uapi/linux/mei_uuid.h new file mode 100644 index 000000000000..676ebe12d623 --- /dev/null +++ b/include/uapi/linux/mei_uuid.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * MEI UUID definition + * + * Copyright (C) 2010, Intel Corp. + * Huang Ying + */ + +#ifndef _UAPI_LINUX_MEI_UUID_H_ +#define _UAPI_LINUX_MEI_UUID_H_ + +#include + +typedef struct { + __u8 b[16]; +} uuid_le; + +#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((uuid_le) \ +{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) + +#define NULL_UUID_LE \ + UUID_LE(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00) + +#endif /* _UAPI_LINUX_MEI_UUID_H_ */ diff --git a/include/uapi/linux/uuid.h b/include/uapi/linux/uuid.h index 96ac684a4b2f..8443738f4bb2 100644 --- a/include/uapi/linux/uuid.h +++ b/include/uapi/linux/uuid.h @@ -1,30 +1 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* DO NOT USE in new code! This is solely for MEI due to legacy reasons */ -/* - * MEI UUID definition - * - * Copyright (C) 2010, Intel Corp. - * Huang Ying - */ - -#ifndef _UAPI_LINUX_UUID_H_ -#define _UAPI_LINUX_UUID_H_ - -#include - -typedef struct { - __u8 b[16]; -} uuid_le; - -#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ -((uuid_le) \ -{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ - (b) & 0xff, ((b) >> 8) & 0xff, \ - (c) & 0xff, ((c) >> 8) & 0xff, \ - (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) - -#define NULL_UUID_LE \ - UUID_LE(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00) - -#endif /* _UAPI_LINUX_UUID_H_ */ +#include -- cgit v1.2.3 From 52a7a5e91f9f63c99632687e85a7531d9ac9a706 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 19 Mar 2023 13:04:47 -0400 Subject: misc: hpilo: remove unused is_device_reset function clang with W=1 reports drivers/misc/hpilo.c:395:19: error: unused function 'is_device_reset' [-Werror,-Wunused-function] static inline int is_device_reset(struct ilo_hwinfo *hw) ^ This function is not used, so remove it. Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20230319170447.1707655-1-trix@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 8d00df9243c4..ba4b00b8bea1 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -392,12 +392,6 @@ static inline int is_db_reset(int db_out) return db_out & (1 << DB_RESET); } -static inline int is_device_reset(struct ilo_hwinfo *hw) -{ - /* check for global reset condition */ - return is_db_reset(get_device_outbound(hw)); -} - static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr) { iowrite32(clr, &hw->mmio_vaddr[DB_OUT]); -- cgit v1.2.3 From 71d74aaa15b49d2ea6927706ee2a7debc88fe3c2 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 20 Mar 2023 20:02:03 -0400 Subject: misc: alcor_pci: remove unused alcor functions clang with W=1 reports drivers/misc/cardreader/alcor_pci.c:98:20: error: unused function 'alcor_mask_sd_irqs' [-Werror,-Wunused-function] static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv) ^ drivers/misc/cardreader/alcor_pci.c:103:20: error: unused function 'alcor_unmask_sd_irqs' [-Werror,-Wunused-function] static inline void alcor_unmask_sd_irqs(struct alcor_pci_priv *priv) ^ drivers/misc/cardreader/alcor_pci.c:111:20: error: unused function 'alcor_mask_ms_irqs' [-Werror,-Wunused-function] static inline void alcor_mask_ms_irqs(struct alcor_pci_priv *priv) ^ drivers/misc/cardreader/alcor_pci.c:116:20: error: unused function 'alcor_unmask_ms_irqs' [-Werror,-Wunused-function] static inline void alcor_unmask_ms_irqs(struct alcor_pci_priv *priv) ^ These functions are not used, so remove them. Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20230321000203.1730802-1-trix@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/alcor_pci.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 5b637171c46c..0142c4bf4f42 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -95,29 +95,6 @@ u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr) } EXPORT_SYMBOL_GPL(alcor_read32be); -static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv) -{ - alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); -} - -static inline void alcor_unmask_sd_irqs(struct alcor_pci_priv *priv) -{ - alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK | - AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE | - AU6601_INT_OVER_CURRENT_ERR, - AU6601_REG_INT_ENABLE); -} - -static inline void alcor_mask_ms_irqs(struct alcor_pci_priv *priv) -{ - alcor_write32(priv, 0, AU6601_MS_INT_ENABLE); -} - -static inline void alcor_unmask_ms_irqs(struct alcor_pci_priv *priv) -{ - alcor_write32(priv, 0x3d00fa, AU6601_MS_INT_ENABLE); -} - static int alcor_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { -- cgit v1.2.3 From 2959ab247061e67485d83b6af8feb3761ec08cb9 Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:30 +0530 Subject: cdx: add the cdx bus driver Introduce AMD CDX bus, which provides a mechanism for scanning and probing CDX devices. These devices are memory mapped on system bus for Application Processors(APUs). CDX devices can be changed dynamically in the Fabric and CDX bus interacts with CDX controller to rescan the bus and rediscover the devices. Signed-off-by: Nipun Gupta Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-2-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-cdx | 12 + MAINTAINERS | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/cdx/Kconfig | 17 ++ drivers/cdx/Makefile | 8 + drivers/cdx/cdx.c | 408 ++++++++++++++++++++++++++++++++ drivers/cdx/cdx.h | 62 +++++ include/linux/cdx/cdx_bus.h | 147 ++++++++++++ include/linux/mod_devicetable.h | 15 ++ scripts/mod/devicetable-offsets.c | 4 + scripts/mod/file2alias.c | 12 + 12 files changed, 695 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-cdx create mode 100644 drivers/cdx/Kconfig create mode 100644 drivers/cdx/Makefile create mode 100644 drivers/cdx/cdx.c create mode 100644 drivers/cdx/cdx.h create mode 100644 include/linux/cdx/cdx_bus.h diff --git a/Documentation/ABI/testing/sysfs-bus-cdx b/Documentation/ABI/testing/sysfs-bus-cdx new file mode 100644 index 000000000000..43b4e161f226 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-cdx @@ -0,0 +1,12 @@ +What: /sys/bus/cdx/rescan +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Writing y/1/on to this file will cause rescan of the bus + and devices on the CDX bus. Any new devices are scanned and + added to the list of Linux devices and any devices removed are + also deleted from Linux. + + For example:: + + # echo 1 > /sys/bus/cdx/rescan diff --git a/MAINTAINERS b/MAINTAINERS index 619c30ec36dc..973cfbd3e3e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -964,6 +964,13 @@ Q: https://patchwork.kernel.org/project/linux-rdma/list/ F: drivers/infiniband/hw/efa/ F: include/uapi/rdma/efa-abi.h +AMD CDX BUS DRIVER +M: Nipun Gupta +M: Nikhil Agarwal +S: Maintained +F: drivers/cdx/* +F: include/linux/cdx/* + AMD CRYPTOGRAPHIC COPROCESSOR (CCP) DRIVER M: Tom Lendacky M: John Allen diff --git a/drivers/Kconfig b/drivers/Kconfig index 968bd0a6fd78..514ae6b24cb2 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -241,4 +241,6 @@ source "drivers/peci/Kconfig" source "drivers/hte/Kconfig" +source "drivers/cdx/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 20b118dca999..7241d80a7b29 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -194,3 +194,4 @@ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_PECI) += peci/ obj-$(CONFIG_HTE) += hte/ obj-$(CONFIG_DRM_ACCEL) += accel/ +obj-$(CONFIG_CDX_BUS) += cdx/ diff --git a/drivers/cdx/Kconfig b/drivers/cdx/Kconfig new file mode 100644 index 000000000000..1aaee0ec5bd9 --- /dev/null +++ b/drivers/cdx/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# CDX bus configuration +# +# Copyright (C) 2022-2023, Advanced Micro Devices, Inc. +# + +config CDX_BUS + bool "CDX Bus driver" + depends on OF && ARM64 + help + Driver to enable Composable DMA Transfer(CDX) Bus. CDX bus + exposes Fabric devices which uses composable DMA IP to the + APU. CDX bus provides a mechanism for scanning and probing + of CDX devices. CDX devices are memory mapped on system bus + for embedded CPUs. CDX bus uses CDX controller and firmware + to scan these CDX devices. diff --git a/drivers/cdx/Makefile b/drivers/cdx/Makefile new file mode 100644 index 000000000000..c5feb11fc718 --- /dev/null +++ b/drivers/cdx/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for CDX +# +# Copyright (C) 2022-2023, Advanced Micro Devices, Inc. +# + +obj-$(CONFIG_CDX_BUS) += cdx.o diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c new file mode 100644 index 000000000000..6b162bbb9b25 --- /dev/null +++ b/drivers/cdx/cdx.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CDX bus driver. + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +/* + * Architecture Overview + * ===================== + * CDX is a Hardware Architecture designed for AMD FPGA devices. It + * consists of sophisticated mechanism for interaction between FPGA, + * Firmware and the APUs (Application CPUs). + * + * Firmware resides on RPU (Realtime CPUs) which interacts with + * the FPGA program manager and the APUs. The RPU provides memory-mapped + * interface (RPU if) which is used to communicate with APUs. + * + * The diagram below shows an overview of the CDX architecture: + * + * +--------------------------------------+ + * | Application CPUs (APU) | + * | | + * | CDX device drivers| + * | Linux OS | | + * | CDX bus | + * | | | + * | CDX controller | + * | | | + * +-----------------------------|--------+ + * | (discover, config, + * | reset, rescan) + * | + * +------------------------| RPU if |----+ + * | | | + * | V | + * | Realtime CPUs (RPU) | + * | | + * +--------------------------------------+ + * | + * +---------------------|----------------+ + * | FPGA | | + * | +-----------------------+ | + * | | | | | + * | +-------+ +-------+ +-------+ | + * | | dev 1 | | dev 2 | | dev 3 | | + * | +-------+ +-------+ +-------+ | + * +--------------------------------------+ + * + * The RPU firmware extracts the device information from the loaded FPGA + * image and implements a mechanism that allows the APU drivers to + * enumerate such devices (device personality and resource details) via + * a dedicated communication channel. RPU mediates operations such as + * discover, reset and rescan of the FPGA devices for the APU. This is + * done using memory mapped interface provided by the RPU to APU. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cdx.h" + +/* Default DMA mask for devices on a CDX bus */ +#define CDX_DEFAULT_DMA_MASK (~0ULL) +#define MAX_CDX_CONTROLLERS 16 + +/* CDX controllers registered with the CDX bus */ +static DEFINE_XARRAY_ALLOC(cdx_controllers); + +/** + * cdx_unregister_device - Unregister a CDX device + * @dev: CDX device + * @data: This is always passed as NULL, and is not used in this API, + * but is required here as the bus_for_each_dev() API expects + * the passed function (cdx_unregister_device) to have this + * as an argument. + * + * Return: 0 on success. + */ +static int cdx_unregister_device(struct device *dev, + void *data) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + + kfree(cdx_dev->driver_override); + cdx_dev->driver_override = NULL; + /* + * Do not free cdx_dev here as it would be freed in + * cdx_device_release() called from within put_device(). + */ + device_del(&cdx_dev->dev); + put_device(&cdx_dev->dev); + + return 0; +} + +static void cdx_unregister_devices(struct bus_type *bus) +{ + /* Reset all the devices attached to cdx bus */ + bus_for_each_dev(bus, NULL, NULL, cdx_unregister_device); +} + +/** + * cdx_match_one_device - Tell if a CDX device structure has a matching + * CDX device id structure + * @id: single CDX device id structure to match + * @dev: the CDX device structure to match against + * + * Return: matching cdx_device_id structure or NULL if there is no match. + */ +static inline const struct cdx_device_id * +cdx_match_one_device(const struct cdx_device_id *id, + const struct cdx_device *dev) +{ + /* Use vendor ID and device ID for matching */ + if ((id->vendor == CDX_ANY_ID || id->vendor == dev->vendor) && + (id->device == CDX_ANY_ID || id->device == dev->device)) + return id; + return NULL; +} + +/** + * cdx_match_id - See if a CDX device matches a given cdx_id table + * @ids: array of CDX device ID structures to search in + * @dev: the CDX device structure to match against. + * + * Used by a driver to check whether a CDX device is in its list of + * supported devices. Returns the matching cdx_device_id structure or + * NULL if there is no match. + * + * Return: matching cdx_device_id structure or NULL if there is no match. + */ +static inline const struct cdx_device_id * +cdx_match_id(const struct cdx_device_id *ids, struct cdx_device *dev) +{ + if (ids) { + while (ids->vendor || ids->device) { + if (cdx_match_one_device(ids, dev)) + return ids; + ids++; + } + } + return NULL; +} + +/** + * cdx_bus_match - device to driver matching callback + * @dev: the cdx device to match against + * @drv: the device driver to search for matching cdx device + * structures + * + * Return: true on success, false otherwise. + */ +static int cdx_bus_match(struct device *dev, struct device_driver *drv) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + struct cdx_driver *cdx_drv = to_cdx_driver(drv); + const struct cdx_device_id *found_id = NULL; + const struct cdx_device_id *ids; + + ids = cdx_drv->match_id_table; + + /* When driver_override is set, only bind to the matching driver */ + if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name)) + return false; + + found_id = cdx_match_id(ids, cdx_dev); + if (!found_id) + return false; + + do { + /* + * In case override_only was set, enforce driver_override + * matching. + */ + if (!found_id->override_only) + return true; + if (cdx_dev->driver_override) + return true; + + ids = found_id + 1; + found_id = cdx_match_id(ids, cdx_dev); + } while (found_id); + + return false; +} + +static int cdx_probe(struct device *dev) +{ + struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver); + struct cdx_device *cdx_dev = to_cdx_device(dev); + int error; + + error = cdx_drv->probe(cdx_dev); + if (error) { + dev_err_probe(dev, error, "%s failed\n", __func__); + return error; + } + + return 0; +} + +static void cdx_remove(struct device *dev) +{ + struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver); + struct cdx_device *cdx_dev = to_cdx_device(dev); + + if (cdx_drv && cdx_drv->remove) + cdx_drv->remove(cdx_dev); +} + +static void cdx_shutdown(struct device *dev) +{ + struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver); + struct cdx_device *cdx_dev = to_cdx_device(dev); + + if (cdx_drv && cdx_drv->shutdown) + cdx_drv->shutdown(cdx_dev); +} + +static int cdx_dma_configure(struct device *dev) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + u32 input_id = cdx_dev->req_id; + int ret; + + ret = of_dma_configure_id(dev, dev->parent->of_node, 0, &input_id); + if (ret && ret != -EPROBE_DEFER) { + dev_err(dev, "of_dma_configure_id() failed\n"); + return ret; + } + + return 0; +} + +static ssize_t rescan_store(struct bus_type *bus, + const char *buf, size_t count) +{ + struct cdx_controller *cdx; + unsigned long index; + bool val; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + if (!val) + return -EINVAL; + + /* Unregister all the devices on the bus */ + cdx_unregister_devices(&cdx_bus_type); + + /* Rescan all the devices */ + xa_for_each(&cdx_controllers, index, cdx) { + int ret; + + ret = cdx->ops->scan(cdx); + if (ret) + dev_err(cdx->dev, "cdx bus scanning failed\n"); + } + + return count; +} +static BUS_ATTR_WO(rescan); + +static struct attribute *cdx_bus_attrs[] = { + &bus_attr_rescan.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cdx_bus); + +struct bus_type cdx_bus_type = { + .name = "cdx", + .match = cdx_bus_match, + .probe = cdx_probe, + .remove = cdx_remove, + .shutdown = cdx_shutdown, + .dma_configure = cdx_dma_configure, + .bus_groups = cdx_bus_groups, +}; +EXPORT_SYMBOL_GPL(cdx_bus_type); + +int __cdx_driver_register(struct cdx_driver *cdx_driver, + struct module *owner) +{ + int error; + + cdx_driver->driver.owner = owner; + cdx_driver->driver.bus = &cdx_bus_type; + + error = driver_register(&cdx_driver->driver); + if (error) { + pr_err("driver_register() failed for %s: %d\n", + cdx_driver->driver.name, error); + return error; + } + + return 0; +} +EXPORT_SYMBOL_GPL(__cdx_driver_register); + +void cdx_driver_unregister(struct cdx_driver *cdx_driver) +{ + driver_unregister(&cdx_driver->driver); +} +EXPORT_SYMBOL_GPL(cdx_driver_unregister); + +static void cdx_device_release(struct device *dev) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + + kfree(cdx_dev); +} + +int cdx_device_add(struct cdx_dev_params *dev_params) +{ + struct cdx_controller *cdx = dev_params->cdx; + struct device *parent = cdx->dev; + struct cdx_device *cdx_dev; + int ret; + + cdx_dev = kzalloc(sizeof(*cdx_dev), GFP_KERNEL); + if (!cdx_dev) + return -ENOMEM; + + /* Populate resource */ + memcpy(cdx_dev->res, dev_params->res, sizeof(struct resource) * + dev_params->res_count); + cdx_dev->res_count = dev_params->res_count; + + /* Populate CDX dev params */ + cdx_dev->req_id = dev_params->req_id; + cdx_dev->vendor = dev_params->vendor; + cdx_dev->device = dev_params->device; + cdx_dev->bus_num = dev_params->bus_num; + cdx_dev->dev_num = dev_params->dev_num; + cdx_dev->cdx = dev_params->cdx; + cdx_dev->dma_mask = CDX_DEFAULT_DMA_MASK; + + /* Initialize generic device */ + device_initialize(&cdx_dev->dev); + cdx_dev->dev.parent = parent; + cdx_dev->dev.bus = &cdx_bus_type; + cdx_dev->dev.dma_mask = &cdx_dev->dma_mask; + cdx_dev->dev.release = cdx_device_release; + + /* Set Name */ + dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x", + ((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)), + cdx_dev->dev_num); + + ret = device_add(&cdx_dev->dev); + if (ret) { + dev_err(&cdx_dev->dev, + "cdx device add failed: %d", ret); + goto fail; + } + + return 0; +fail: + /* + * Do not free cdx_dev here as it would be freed in + * cdx_device_release() called from put_device(). + */ + put_device(&cdx_dev->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(cdx_device_add); + +int cdx_register_controller(struct cdx_controller *cdx) +{ + int ret; + + ret = xa_alloc(&cdx_controllers, &cdx->id, cdx, + XA_LIMIT(0, MAX_CDX_CONTROLLERS - 1), GFP_KERNEL); + if (ret) { + dev_err(cdx->dev, + "No free index available. Maximum controllers already registered\n"); + cdx->id = (u8)MAX_CDX_CONTROLLERS; + return ret; + } + + /* Scan all the devices */ + cdx->ops->scan(cdx); + + return 0; +} +EXPORT_SYMBOL_GPL(cdx_register_controller); + +void cdx_unregister_controller(struct cdx_controller *cdx) +{ + if (cdx->id >= MAX_CDX_CONTROLLERS) + return; + + device_for_each_child(cdx->dev, NULL, cdx_unregister_device); + xa_erase(&cdx_controllers, cdx->id); +} +EXPORT_SYMBOL_GPL(cdx_unregister_controller); + +static int __init cdx_bus_init(void) +{ + return bus_register(&cdx_bus_type); +} +postcore_initcall(cdx_bus_init); diff --git a/drivers/cdx/cdx.h b/drivers/cdx/cdx.h new file mode 100644 index 000000000000..c436ac7ac86f --- /dev/null +++ b/drivers/cdx/cdx.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Header file for the CDX Bus + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef _CDX_H_ +#define _CDX_H_ + +#include + +/** + * struct cdx_dev_params - CDX device parameters + * @cdx: CDX controller associated with the device + * @parent: Associated CDX controller + * @vendor: Vendor ID for CDX device + * @device: Device ID for CDX device + * @bus_num: Bus number for this CDX device + * @dev_num: Device number for this device + * @res: array of MMIO region entries + * @res_count: number of valid MMIO regions + * @req_id: Requestor ID associated with CDX device + */ +struct cdx_dev_params { + struct cdx_controller *cdx; + u16 vendor; + u16 device; + u8 bus_num; + u8 dev_num; + struct resource res[MAX_CDX_DEV_RESOURCES]; + u8 res_count; + u32 req_id; +}; + +/** + * cdx_register_controller - Register a CDX controller and its ports + * on the CDX bus. + * @cdx: The CDX controller to register + * + * Return: -errno on failure, 0 on success. + */ +int cdx_register_controller(struct cdx_controller *cdx); + +/** + * cdx_unregister_controller - Unregister a CDX controller + * @cdx: The CDX controller to unregister + */ +void cdx_unregister_controller(struct cdx_controller *cdx); + +/** + * cdx_device_add - Add a CDX device. This function adds a CDX device + * on the CDX bus as per the device parameters provided + * by caller. It also creates and registers an associated + * Linux generic device. + * @dev_params: device parameters associated with the device to be created. + * + * Return: -errno on failure, 0 on success. + */ +int cdx_device_add(struct cdx_dev_params *dev_params); + +#endif /* _CDX_H_ */ diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h new file mode 100644 index 000000000000..d134e0104724 --- /dev/null +++ b/include/linux/cdx/cdx_bus.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * CDX bus public interface + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + * + */ + +#ifndef _CDX_BUS_H_ +#define _CDX_BUS_H_ + +#include +#include +#include + +#define MAX_CDX_DEV_RESOURCES 4 +#define CDX_ANY_ID (0xFFFF) +#define CDX_CONTROLLER_ID_SHIFT 4 +#define CDX_BUS_NUM_MASK 0xF + +/* Forward declaration for CDX controller */ +struct cdx_controller; + +typedef int (*cdx_scan_cb)(struct cdx_controller *cdx); + +/** + * CDX_DEVICE_DRIVER_OVERRIDE - macro used to describe a CDX device with + * override_only flags. + * @vend: the 16 bit CDX Vendor ID + * @dev: the 16 bit CDX Device ID + * @driver_override: the 32 bit CDX Device override_only + * + * This macro is used to create a struct cdx_device_id that matches only a + * driver_override device. + */ +#define CDX_DEVICE_DRIVER_OVERRIDE(vend, dev, driver_override) \ + .vendor = (vend), .device = (dev), .override_only = (driver_override) + +/** + * struct cdx_ops - Callbacks supported by CDX controller. + * @scan: scan the devices on the controller + */ +struct cdx_ops { + cdx_scan_cb scan; +}; + +/** + * struct cdx_controller: CDX controller object + * @dev: Linux device associated with the CDX controller. + * @priv: private data + * @id: Controller ID + * @ops: CDX controller ops + */ +struct cdx_controller { + struct device *dev; + void *priv; + u32 id; + struct cdx_ops *ops; +}; + +/** + * struct cdx_device - CDX device object + * @dev: Linux driver model device object + * @cdx: CDX controller associated with the device + * @vendor: Vendor ID for CDX device + * @device: Device ID for CDX device + * @bus_num: Bus number for this CDX device + * @dev_num: Device number for this device + * @res: array of MMIO region entries + * @res_attr: resource binary attribute + * @res_count: number of valid MMIO regions + * @dma_mask: Default DMA mask + * @flags: CDX device flags + * @req_id: Requestor ID associated with CDX device + * @driver_override: driver name to force a match; do not set directly, + * because core frees it; use driver_set_override() to + * set or clear it. + */ +struct cdx_device { + struct device dev; + struct cdx_controller *cdx; + u16 vendor; + u16 device; + u8 bus_num; + u8 dev_num; + struct resource res[MAX_CDX_DEV_RESOURCES]; + u8 res_count; + u64 dma_mask; + u16 flags; + u32 req_id; + const char *driver_override; +}; + +#define to_cdx_device(_dev) \ + container_of(_dev, struct cdx_device, dev) + +/** + * struct cdx_driver - CDX device driver + * @driver: Generic device driver + * @match_id_table: table of supported device matching Ids + * @probe: Function called when a device is added + * @remove: Function called when a device is removed + * @shutdown: Function called at shutdown time to quiesce the device + * @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA. + * For most device drivers, no need to care about this flag + * as long as all DMAs are handled through the kernel DMA API. + * For some special ones, for example VFIO drivers, they know + * how to manage the DMA themselves and set this flag so that + * the IOMMU layer will allow them to setup and manage their + * own I/O address space. + */ +struct cdx_driver { + struct device_driver driver; + const struct cdx_device_id *match_id_table; + int (*probe)(struct cdx_device *dev); + int (*remove)(struct cdx_device *dev); + void (*shutdown)(struct cdx_device *dev); + bool driver_managed_dma; +}; + +#define to_cdx_driver(_drv) \ + container_of(_drv, struct cdx_driver, driver) + +/* Macro to avoid include chaining to get THIS_MODULE */ +#define cdx_driver_register(drv) \ + __cdx_driver_register(drv, THIS_MODULE) + +/** + * __cdx_driver_register - registers a CDX device driver + * @cdx_driver: CDX driver to register + * @owner: module owner + * + * Return: -errno on failure, 0 on success. + */ +int __must_check __cdx_driver_register(struct cdx_driver *cdx_driver, + struct module *owner); + +/** + * cdx_driver_unregister - unregisters a device driver from the + * CDX bus. + * @cdx_driver: CDX driver to register + */ +void cdx_driver_unregister(struct cdx_driver *cdx_driver); + +extern struct bus_type cdx_bus_type; + +#endif /* _CDX_BUS_H_ */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 35203ca3fc37..ccaaeda792c0 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -912,4 +912,19 @@ struct ishtp_device_id { kernel_ulong_t driver_data; }; +/** + * struct cdx_device_id - CDX device identifier + * @vendor: Vendor ID + * @device: Device ID + * @override_only: Match only when dev->driver_override is this driver. + * + * Type of entries in the "device Id" table for CDX devices supported by + * a CDX device driver. + */ +struct cdx_device_id { + __u16 vendor; + __u16 device; + __u32 override_only; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index c0d3bcb99138..62dc988df84d 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -262,5 +262,9 @@ int main(void) DEVID(ishtp_device_id); DEVID_FIELD(ishtp_device_id, guid); + DEVID(cdx_device_id); + DEVID_FIELD(cdx_device_id, vendor); + DEVID_FIELD(cdx_device_id, device); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 91c2e7ba5e52..28da34ba4359 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1452,6 +1452,17 @@ static int do_dfl_entry(const char *filename, void *symval, char *alias) return 1; } +/* Looks like: cdx:vNdN */ +static int do_cdx_entry(const char *filename, void *symval, + char *alias) +{ + DEF_FIELD(symval, cdx_device_id, vendor); + DEF_FIELD(symval, cdx_device_id, device); + + sprintf(alias, "cdx:v%08Xd%08Xd", vendor, device); + return 1; +} + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { @@ -1531,6 +1542,7 @@ static const struct devtable devtable[] = { {"ssam", SIZE_ssam_device_id, do_ssam_entry}, {"dfl", SIZE_dfl_device_id, do_dfl_entry}, {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry}, + {"cdx", SIZE_cdx_device_id, do_cdx_entry}, }; /* Create MODULE_ALIAS() statements. -- cgit v1.2.3 From 3f47d3e44df0072c340ce417f6509a7cbd51ef7c Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:31 +0530 Subject: iommu: Add iommu probe for CDX bus Add CDX bus to iommu_buses so that IOMMU probe is called for it. Signed-off-by: Nipun Gupta Reviewed-by: Pieter Jansen van Vuuren Reviewed-by: Lu Baolu Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-3-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 10db680acaed..0025a631b8e1 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,9 @@ static struct bus_type * const iommu_buses[] = { #ifdef CONFIG_TEGRA_HOST1X_CONTEXT_BUS &host1x_context_device_bus_type, #endif +#ifdef CONFIG_CDX_BUS + &cdx_bus_type, +#endif }; /* -- cgit v1.2.3 From c47a88e1930fa0f2df8aaa643a5cc86bcf5812bf Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:32 +0530 Subject: dt-bindings: bus: add CDX bus controller for versal net Add CDX bus controller device tree bindings for versal-net devices. Signed-off-by: Nipun Gupta Reviewed-by: Rob Herring Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-4-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/bus/xlnx,versal-net-cdx.yaml | 82 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 83 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/xlnx,versal-net-cdx.yaml diff --git a/Documentation/devicetree/bindings/bus/xlnx,versal-net-cdx.yaml b/Documentation/devicetree/bindings/bus/xlnx,versal-net-cdx.yaml new file mode 100644 index 000000000000..7f62ffbdc245 --- /dev/null +++ b/Documentation/devicetree/bindings/bus/xlnx,versal-net-cdx.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/xlnx,versal-net-cdx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMD CDX bus controller + +description: | + CDX bus controller for AMD devices is implemented to dynamically + detect CDX bus and devices using the firmware. + The CDX bus manages multiple FPGA based hardware devices, which + can support network, crypto or any other specialized type of + devices. These FPGA based devices can be added/modified dynamically + on run-time. + + All devices on the CDX bus will have a unique streamid (for IOMMU) + and a unique device ID (for MSI) corresponding to a requestor ID + (one to one associated with the device). The streamid and deviceid + are used to configure SMMU and GIC-ITS respectively. + + iommu-map property is used to define the set of stream ids + corresponding to each device and the associated IOMMU. + + The MSI writes are accompanied by sideband data (Device ID). + The msi-map property is used to associate the devices with the + device ID as well as the associated ITS controller. + + rproc property (xlnx,rproc) is used to identify the remote processor + with which APU (Application Processor Unit) interacts to find out + the bus and device configuration. + +maintainers: + - Nipun Gupta + - Nikhil Agarwal + +properties: + compatible: + const: xlnx,versal-net-cdx + + iommu-map: true + + msi-map: true + + xlnx,rproc: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to the remoteproc_r5 rproc node using which APU interacts + with remote processor. + + ranges: true + + "#address-cells": + enum: [1, 2] + + "#size-cells": + enum: [1, 2] + +required: + - compatible + - iommu-map + - msi-map + - xlnx,rproc + - ranges + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + cdx { + compatible = "xlnx,versal-net-cdx"; + #address-cells = <1>; + #size-cells = <1>; + /* define map for RIDs 250-259 */ + iommu-map = <250 &smmu 250 10>; + /* define msi map for RIDs 250-259 */ + msi-map = <250 &its 250 10>; + xlnx,rproc = <&remoteproc_r5>; + ranges; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 973cfbd3e3e0..4d4f778ede5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -968,6 +968,7 @@ AMD CDX BUS DRIVER M: Nipun Gupta M: Nikhil Agarwal S: Maintained +F: Documentation/devicetree/bindings/bus/xlnx,versal-net-cdx.yaml F: drivers/cdx/* F: include/linux/cdx/* -- cgit v1.2.3 From eb96b740192b2a09720aaed8a8c132e6a29d5bdb Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:33 +0530 Subject: cdx: add MCDI protocol interface for firmware interaction The MCDI (Management CPU Driver Interface) is used as a protocol to communicate with the RPU firmware. It has pre-defined set of messages for different message exchanges between APU and RPU. Signed-off-by: Puneet Gupta Signed-off-by: Nipun Gupta Signed-off-by: Tarak Reddy Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-5-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/Kconfig | 2 + drivers/cdx/Makefile | 2 +- drivers/cdx/controller/Kconfig | 20 + drivers/cdx/controller/Makefile | 9 + drivers/cdx/controller/bitfield.h | 90 ++++ drivers/cdx/controller/mc_cdx_pcol.h | 590 +++++++++++++++++++++++ drivers/cdx/controller/mcdi.c | 903 +++++++++++++++++++++++++++++++++++ drivers/cdx/controller/mcdi.h | 238 +++++++++ 8 files changed, 1853 insertions(+), 1 deletion(-) create mode 100644 drivers/cdx/controller/Kconfig create mode 100644 drivers/cdx/controller/Makefile create mode 100644 drivers/cdx/controller/bitfield.h create mode 100644 drivers/cdx/controller/mc_cdx_pcol.h create mode 100644 drivers/cdx/controller/mcdi.c create mode 100644 drivers/cdx/controller/mcdi.h diff --git a/drivers/cdx/Kconfig b/drivers/cdx/Kconfig index 1aaee0ec5bd9..a08958485e31 100644 --- a/drivers/cdx/Kconfig +++ b/drivers/cdx/Kconfig @@ -15,3 +15,5 @@ config CDX_BUS of CDX devices. CDX devices are memory mapped on system bus for embedded CPUs. CDX bus uses CDX controller and firmware to scan these CDX devices. + +source "drivers/cdx/controller/Kconfig" diff --git a/drivers/cdx/Makefile b/drivers/cdx/Makefile index c5feb11fc718..0324e4914f6e 100644 --- a/drivers/cdx/Makefile +++ b/drivers/cdx/Makefile @@ -5,4 +5,4 @@ # Copyright (C) 2022-2023, Advanced Micro Devices, Inc. # -obj-$(CONFIG_CDX_BUS) += cdx.o +obj-$(CONFIG_CDX_BUS) += cdx.o controller/ diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig new file mode 100644 index 000000000000..785c71063b2a --- /dev/null +++ b/drivers/cdx/controller/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# CDX controller configuration +# +# Copyright (C) 2022-2023, Advanced Micro Devices, Inc. +# + +if CDX_BUS + +config MCDI_LOGGING + bool "MCDI Logging for the CDX controller" + depends on CDX_CONTROLLER + help + Enable MCDI Logging for + the CDX Controller for debug + purpose. + + If unsure, say N. + +endif diff --git a/drivers/cdx/controller/Makefile b/drivers/cdx/controller/Makefile new file mode 100644 index 000000000000..0ce200678eda --- /dev/null +++ b/drivers/cdx/controller/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for CDX controller drivers +# +# Copyright (C) 2022-2023, Advanced Micro Devices, Inc. +# + +obj-$(CONFIG_CDX_CONTROLLER) += cdx-controller.o +cdx-controller-objs := mcdi.o diff --git a/drivers/cdx/controller/bitfield.h b/drivers/cdx/controller/bitfield.h new file mode 100644 index 000000000000..567f8ec47582 --- /dev/null +++ b/drivers/cdx/controller/bitfield.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef CDX_BITFIELD_H +#define CDX_BITFIELD_H + +#include + +/* Lowest bit numbers and widths */ +#define CDX_DWORD_LBN 0 +#define CDX_DWORD_WIDTH 32 + +/* Specified attribute (e.g. LBN) of the specified field */ +#define CDX_VAL(field, attribute) field ## _ ## attribute +/* Low bit number of the specified field */ +#define CDX_LOW_BIT(field) CDX_VAL(field, LBN) +/* Bit width of the specified field */ +#define CDX_WIDTH(field) CDX_VAL(field, WIDTH) +/* High bit number of the specified field */ +#define CDX_HIGH_BIT(field) (CDX_LOW_BIT(field) + CDX_WIDTH(field) - 1) + +/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */ +struct cdx_dword { + __le32 cdx_u32; +}; + +/* Value expanders for printk */ +#define CDX_DWORD_VAL(dword) \ + ((unsigned int)le32_to_cpu((dword).cdx_u32)) + +/* + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define CDX_DWORD_FIELD(dword, field) \ + (FIELD_GET(GENMASK(CDX_HIGH_BIT(field), CDX_LOW_BIT(field)), \ + le32_to_cpu((dword).cdx_u32))) + +/* + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define CDX_INSERT_FIELD(field, value) \ + (FIELD_PREP(GENMASK(CDX_HIGH_BIT(field), \ + CDX_LOW_BIT(field)), value)) + +/* + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define CDX_INSERT_FIELDS(field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7) \ + (CDX_INSERT_FIELD(field1, (value1)) | \ + CDX_INSERT_FIELD(field2, (value2)) | \ + CDX_INSERT_FIELD(field3, (value3)) | \ + CDX_INSERT_FIELD(field4, (value4)) | \ + CDX_INSERT_FIELD(field5, (value5)) | \ + CDX_INSERT_FIELD(field6, (value6)) | \ + CDX_INSERT_FIELD(field7, (value7))) + +#define CDX_POPULATE_DWORD(dword, ...) \ + (dword).cdx_u32 = cpu_to_le32(CDX_INSERT_FIELDS(__VA_ARGS__)) + +/* Populate a dword field with various numbers of arguments */ +#define CDX_POPULATE_DWORD_7 CDX_POPULATE_DWORD +#define CDX_POPULATE_DWORD_6(dword, ...) \ + CDX_POPULATE_DWORD_7(dword, CDX_DWORD, 0, __VA_ARGS__) +#define CDX_POPULATE_DWORD_5(dword, ...) \ + CDX_POPULATE_DWORD_6(dword, CDX_DWORD, 0, __VA_ARGS__) +#define CDX_POPULATE_DWORD_4(dword, ...) \ + CDX_POPULATE_DWORD_5(dword, CDX_DWORD, 0, __VA_ARGS__) +#define CDX_POPULATE_DWORD_3(dword, ...) \ + CDX_POPULATE_DWORD_4(dword, CDX_DWORD, 0, __VA_ARGS__) +#define CDX_POPULATE_DWORD_2(dword, ...) \ + CDX_POPULATE_DWORD_3(dword, CDX_DWORD, 0, __VA_ARGS__) +#define CDX_POPULATE_DWORD_1(dword, ...) \ + CDX_POPULATE_DWORD_2(dword, CDX_DWORD, 0, __VA_ARGS__) +#define CDX_SET_DWORD(dword) \ + CDX_POPULATE_DWORD_1(dword, CDX_DWORD, 0xffffffff) + +#endif /* CDX_BITFIELD_H */ diff --git a/drivers/cdx/controller/mc_cdx_pcol.h b/drivers/cdx/controller/mc_cdx_pcol.h new file mode 100644 index 000000000000..4ccb7b52951b --- /dev/null +++ b/drivers/cdx/controller/mc_cdx_pcol.h @@ -0,0 +1,590 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Driver for AMD network controllers and boards + * + * Copyright (C) 2021, Xilinx, Inc. + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef MC_CDX_PCOL_H +#define MC_CDX_PCOL_H + +/* The current version of the MCDI protocol. */ +#define MCDI_PCOL_VERSION 2 + +/* + * Each MCDI request starts with an MCDI_HEADER, which is a 32bit + * structure, filled in by the client. + * + * 0 7 8 16 20 22 23 24 31 + * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS | + * | | | + * | | \--- Response + * | \------- Error + * \------------------------------ Resync (always set) + * + * The client writes its request into MC shared memory, and rings the + * doorbell. Each request is completed either by the MC writing + * back into shared memory, or by writing out an event. + * + * All MCDI commands support completion by shared memory response. Each + * request may also contain additional data (accounted for by HEADER.LEN), + * and some responses may also contain additional data (again, accounted + * for by HEADER.LEN). + * + * Some MCDI commands support completion by event, in which any associated + * response data is included in the event. + * + * The protocol requires one response to be delivered for every request; a + * request should not be sent unless the response for the previous request + * has been received (either by polling shared memory, or by receiving + * an event). + */ + +/** Request/Response structure */ +#define MCDI_HEADER_OFST 0 +#define MCDI_HEADER_CODE_LBN 0 +#define MCDI_HEADER_CODE_WIDTH 7 +#define MCDI_HEADER_RESYNC_LBN 7 +#define MCDI_HEADER_RESYNC_WIDTH 1 +#define MCDI_HEADER_DATALEN_LBN 8 +#define MCDI_HEADER_DATALEN_WIDTH 8 +#define MCDI_HEADER_SEQ_LBN 16 +#define MCDI_HEADER_SEQ_WIDTH 4 +#define MCDI_HEADER_RSVD_LBN 20 +#define MCDI_HEADER_RSVD_WIDTH 1 +#define MCDI_HEADER_NOT_EPOCH_LBN 21 +#define MCDI_HEADER_NOT_EPOCH_WIDTH 1 +#define MCDI_HEADER_ERROR_LBN 22 +#define MCDI_HEADER_ERROR_WIDTH 1 +#define MCDI_HEADER_RESPONSE_LBN 23 +#define MCDI_HEADER_RESPONSE_WIDTH 1 +#define MCDI_HEADER_XFLAGS_LBN 24 +#define MCDI_HEADER_XFLAGS_WIDTH 8 +/* Request response using event */ +#define MCDI_HEADER_XFLAGS_EVREQ 0x01 +/* Request (and signal) early doorbell return */ +#define MCDI_HEADER_XFLAGS_DBRET 0x02 + +/* Maximum number of payload bytes */ +#define MCDI_CTL_SDU_LEN_MAX_V2 0x400 + +#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V2 + +/* + * The MC can generate events for two reasons: + * - To advance a shared memory request if XFLAGS_EVREQ was set + * - As a notification (link state, i2c event), controlled + * via MC_CMD_LOG_CTRL + * + * Both events share a common structure: + * + * 0 32 33 36 44 52 60 + * | Data | Cont | Level | Src | Code | Rsvd | + * | + * \ There is another event pending in this notification + * + * If Code==CMDDONE, then the fields are further interpreted as: + * + * - LEVEL==INFO Command succeeded + * - LEVEL==ERR Command failed + * + * 0 8 16 24 32 + * | Seq | Datalen | Errno | Rsvd | + * + * These fields are taken directly out of the standard MCDI header, i.e., + * LEVEL==ERR, Datalen == 0 => Reboot + * + * Events can be squirted out of the UART (using LOG_CTRL) without a + * MCDI header. An event can be distinguished from a MCDI response by + * examining the first byte which is 0xc0. This corresponds to the + * non-existent MCDI command MC_CMD_DEBUG_LOG. + * + * 0 7 8 + * | command | Resync | = 0xc0 + * + * Since the event is written in big-endian byte order, this works + * providing bits 56-63 of the event are 0xc0. + * + * 56 60 63 + * | Rsvd | Code | = 0xc0 + * + * Which means for convenience the event code is 0xc for all MC + * generated events. + */ + +/* + * the errno value may be followed by the (0-based) number of the + * first argument that could not be processed. + */ +#define MC_CMD_ERR_ARG_OFST 4 + +/* MC_CMD_ERR MCDI error codes. */ +/* Operation not permitted. */ +#define MC_CMD_ERR_EPERM 0x1 +/* Non-existent command target */ +#define MC_CMD_ERR_ENOENT 0x2 +/* assert() has killed the MC */ +#define MC_CMD_ERR_EINTR 0x4 +/* I/O failure */ +#define MC_CMD_ERR_EIO 0x5 +/* Already exists */ +#define MC_CMD_ERR_EEXIST 0x6 +/* Try again */ +#define MC_CMD_ERR_EAGAIN 0xb +/* Out of memory */ +#define MC_CMD_ERR_ENOMEM 0xc +/* Caller does not hold required locks */ +#define MC_CMD_ERR_EACCES 0xd +/* Resource is currently unavailable (e.g. lock contention) */ +#define MC_CMD_ERR_EBUSY 0x10 +/* No such device */ +#define MC_CMD_ERR_ENODEV 0x13 +/* Invalid argument to target */ +#define MC_CMD_ERR_EINVAL 0x16 +/* No space */ +#define MC_CMD_ERR_ENOSPC 0x1c +/* Read-only */ +#define MC_CMD_ERR_EROFS 0x1e +/* Broken pipe */ +#define MC_CMD_ERR_EPIPE 0x20 +/* Out of range */ +#define MC_CMD_ERR_ERANGE 0x22 +/* Non-recursive resource is already acquired */ +#define MC_CMD_ERR_EDEADLK 0x23 +/* Operation not implemented */ +#define MC_CMD_ERR_ENOSYS 0x26 +/* Operation timed out */ +#define MC_CMD_ERR_ETIME 0x3e +/* Link has been severed */ +#define MC_CMD_ERR_ENOLINK 0x43 +/* Protocol error */ +#define MC_CMD_ERR_EPROTO 0x47 +/* Bad message */ +#define MC_CMD_ERR_EBADMSG 0x4a +/* Operation not supported */ +#define MC_CMD_ERR_ENOTSUP 0x5f +/* Address not available */ +#define MC_CMD_ERR_EADDRNOTAVAIL 0x63 +/* Not connected */ +#define MC_CMD_ERR_ENOTCONN 0x6b +/* Operation already in progress */ +#define MC_CMD_ERR_EALREADY 0x72 +/* Stale handle. The handle references resource that no longer exists */ +#define MC_CMD_ERR_ESTALE 0x74 +/* Resource allocation failed. */ +#define MC_CMD_ERR_ALLOC_FAIL 0x1000 +/* V-adaptor not found. */ +#define MC_CMD_ERR_NO_VADAPTOR 0x1001 +/* EVB port not found. */ +#define MC_CMD_ERR_NO_EVB_PORT 0x1002 +/* V-switch not found. */ +#define MC_CMD_ERR_NO_VSWITCH 0x1003 +/* Too many VLAN tags. */ +#define MC_CMD_ERR_VLAN_LIMIT 0x1004 +/* Bad PCI function number. */ +#define MC_CMD_ERR_BAD_PCI_FUNC 0x1005 +/* Invalid VLAN mode. */ +#define MC_CMD_ERR_BAD_VLAN_MODE 0x1006 +/* Invalid v-switch type. */ +#define MC_CMD_ERR_BAD_VSWITCH_TYPE 0x1007 +/* Invalid v-port type. */ +#define MC_CMD_ERR_BAD_VPORT_TYPE 0x1008 +/* MAC address exists. */ +#define MC_CMD_ERR_MAC_EXIST 0x1009 +/* Slave core not present */ +#define MC_CMD_ERR_SLAVE_NOT_PRESENT 0x100a +/* The datapath is disabled. */ +#define MC_CMD_ERR_DATAPATH_DISABLED 0x100b +/* The requesting client is not a function */ +#define MC_CMD_ERR_CLIENT_NOT_FN 0x100c +/* + * The requested operation might require the command to be passed between + * MCs, and the transport doesn't support that. Should only ever been seen over + * the UART. + */ +#define MC_CMD_ERR_NO_PRIVILEGE 0x1013 +/* + * Workaround 26807 could not be turned on/off because some functions + * have already installed filters. See the comment at + * MC_CMD_WORKAROUND_BUG26807. May also returned for other operations such as + * sub-variant switching. + */ +#define MC_CMD_ERR_FILTERS_PRESENT 0x1014 +/* The clock whose frequency you've attempted to set doesn't exist */ +#define MC_CMD_ERR_NO_CLOCK 0x1015 +/* + * Returned by MC_CMD_TESTASSERT if the action that should have caused an + * assertion failed to do so. + */ +#define MC_CMD_ERR_UNREACHABLE 0x1016 +/* + * This command needs to be processed in the background but there were no + * resources to do so. Send it again after a command has completed. + */ +#define MC_CMD_ERR_QUEUE_FULL 0x1017 +/* + * The operation could not be completed because the PCIe link has gone + * away. This error code is never expected to be returned over the TLP + * transport. + */ +#define MC_CMD_ERR_NO_PCIE 0x1018 +/* + * The operation could not be completed because the datapath has gone + * away. This is distinct from MC_CMD_ERR_DATAPATH_DISABLED in that the + * datapath absence may be temporary + */ +#define MC_CMD_ERR_NO_DATAPATH 0x1019 +/* The operation could not complete because some VIs are allocated */ +#define MC_CMD_ERR_VIS_PRESENT 0x101a +/* + * The operation could not complete because some PIO buffers are + * allocated + */ +#define MC_CMD_ERR_PIOBUFS_PRESENT 0x101b + +/***********************************/ +/* + * MC_CMD_CDX_BUS_ENUM_BUSES + * CDX bus hosts devices (functions) that are implemented using the Composable + * DMA subsystem and directly mapped into the memory space of the FGPA PSX + * Application Processors (APUs). As such, they only apply to the PSX APU side, + * not the host (PCIe). Unlike PCIe, these devices have no native configuration + * space or enumeration mechanism, so this message set provides a minimal + * interface for discovery and management (bus reset, FLR, BME) of such + * devices. This command returns the number of CDX buses present in the system. + */ +#define MC_CMD_CDX_BUS_ENUM_BUSES 0x1 +#define MC_CMD_CDX_BUS_ENUM_BUSES_MSGSET 0x1 +#undef MC_CMD_0x1_PRIVILEGE_CTG + +#define MC_CMD_0x1_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_BUS_ENUM_BUSES_IN msgrequest */ +#define MC_CMD_CDX_BUS_ENUM_BUSES_IN_LEN 0 + +/* MC_CMD_CDX_BUS_ENUM_BUSES_OUT msgresponse */ +#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN 4 +/* + * Number of CDX buses present in the system. Buses are numbered 0 to + * BUS_COUNT-1 + */ +#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT_OFST 0 +#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT_LEN 4 + +/***********************************/ +/* + * MC_CMD_CDX_BUS_ENUM_DEVICES + * Enumerate CDX bus devices on a given bus + */ +#define MC_CMD_CDX_BUS_ENUM_DEVICES 0x2 +#define MC_CMD_CDX_BUS_ENUM_DEVICES_MSGSET 0x2 +#undef MC_CMD_0x2_PRIVILEGE_CTG + +#define MC_CMD_0x2_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_BUS_ENUM_DEVICES_IN msgrequest */ +#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_LEN 4 +/* + * Bus number to enumerate, in range 0 to BUS_COUNT-1, as returned by + * MC_CMD_CDX_BUS_ENUM_BUSES_OUT + */ +#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_BUS_OFST 0 +#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_BUS_LEN 4 + +/* MC_CMD_CDX_BUS_ENUM_DEVICES_OUT msgresponse */ +#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN 4 +/* + * Number of devices present on the bus. Devices on the bus are numbered 0 to + * DEVICE_COUNT-1. Returns EAGAIN if number of devices unknown or if the target + * devices are not ready (e.g. undergoing a bus reset) + */ +#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT_OFST 0 +#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT_LEN 4 + +/***********************************/ +/* + * MC_CMD_CDX_BUS_GET_DEVICE_CONFIG + * Returns device identification and MMIO/MSI resource data for a CDX device. + * The expected usage is for the caller to first retrieve the number of devices + * on the bus using MC_CMD_BUS_ENUM_DEVICES, then loop through the range (0, + * DEVICE_COUNT - 1), retrieving device resource data. May return EAGAIN if the + * number of exposed devices or device resources change during enumeration (due + * to e.g. a PL reload / bus reset), in which case the caller is expected to + * restart the enumeration loop. MMIO addresses are specified in terms of bus + * addresses (prior to any potential IOMMU translation). For versal-net, these + * are equivalent to APU physical addresses. Implementation note - for this to + * work, the implementation needs to keep state (generation count) per client. + */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG 0x3 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_MSGSET 0x3 +#undef MC_CMD_0x3_PRIVILEGE_CTG + +#define MC_CMD_0x3_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN msgrequest */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN 8 +/* Device bus number, in range 0 to BUS_COUNT-1 */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_BUS_OFST 0 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_BUS_LEN 4 +/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE_OFST 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE_LEN 4 + +/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT msgresponse */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN 88 +/* 16-bit Vendor identifier, compliant with PCI-SIG VendorID assignment. */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID_OFST 0 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID_LEN 2 +/* 16-bit Device ID assigned by the vendor */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID_OFST 2 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID_LEN 2 +/* + * 16-bit Subsystem Vendor ID, , compliant with PCI-SIG VendorID assignment. + * For further device differentiation, as required. 0 if unused. + */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID_OFST 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID_LEN 2 +/* + * 16-bit Subsystem Device ID assigned by the vendor. For further device + * differentiation, as required. 0 if unused. + */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID_OFST 6 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID_LEN 2 +/* 24-bit Device Class code, compliant with PCI-SIG Device Class codes */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS_OFST 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS_LEN 3 +/* 8-bit vendor-assigned revision */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION_OFST 11 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION_LEN 1 +/* Reserved (alignment) */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_RESERVED_OFST 12 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_RESERVED_LEN 4 +/* MMIO region 0 base address (bus address), 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_OFST 16 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_OFST 16 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_LBN 128 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_OFST 20 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_LBN 160 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_WIDTH 32 +/* MMIO region 0 size, 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_OFST 24 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_OFST 24 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_LBN 192 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_OFST 28 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_LBN 224 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_WIDTH 32 +/* MMIO region 1 base address (bus address), 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_OFST 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_OFST 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_LBN 256 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_OFST 36 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_LBN 288 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_WIDTH 32 +/* MMIO region 1 size, 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_OFST 40 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_OFST 40 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_LBN 320 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_OFST 44 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_LBN 352 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_WIDTH 32 +/* MMIO region 2 base address (bus address), 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_OFST 48 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_OFST 48 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_LBN 384 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_OFST 52 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_LBN 416 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_WIDTH 32 +/* MMIO region 2 size, 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_OFST 56 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_OFST 56 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_LBN 448 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_OFST 60 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_LBN 480 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_WIDTH 32 +/* MMIO region 3 base address (bus address), 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_OFST 64 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_OFST 64 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_LBN 512 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_OFST 68 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_LBN 544 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_WIDTH 32 +/* MMIO region 3 size, 0 if unused */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_OFST 72 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LEN 8 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_OFST 72 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_LBN 576 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_WIDTH 32 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_OFST 76 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_LEN 4 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_LBN 608 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_WIDTH 32 +/* MSI vector count */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT_OFST 80 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT_LEN 4 +/* Requester ID used by device (SMMU StreamID, GIC ITS DeviceID) */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST 84 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN 4 + +/***********************************/ +/* + * MC_CMD_CDX_DEVICE_RESET + * After this call completes, device DMA and interrupts are quiesced, devices + * logic is reset in a hardware-specific way and DMA bus mastering is disabled. + */ +#define MC_CMD_CDX_DEVICE_RESET 0x6 +#define MC_CMD_CDX_DEVICE_RESET_MSGSET 0x6 +#undef MC_CMD_0x6_PRIVILEGE_CTG + +#define MC_CMD_0x6_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_DEVICE_RESET_IN msgrequest */ +#define MC_CMD_CDX_DEVICE_RESET_IN_LEN 8 +/* Device bus number, in range 0 to BUS_COUNT-1 */ +#define MC_CMD_CDX_DEVICE_RESET_IN_BUS_OFST 0 +#define MC_CMD_CDX_DEVICE_RESET_IN_BUS_LEN 4 +/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */ +#define MC_CMD_CDX_DEVICE_RESET_IN_DEVICE_OFST 4 +#define MC_CMD_CDX_DEVICE_RESET_IN_DEVICE_LEN 4 + +/* + * MC_CMD_CDX_DEVICE_RESET_OUT msgresponse: The device is quiesced and all + * pending device initiated DMA has completed. + */ +#define MC_CMD_CDX_DEVICE_RESET_OUT_LEN 0 + +/***********************************/ +/* + * MC_CMD_CDX_DEVICE_CONTROL_SET + * If BUS_MASTER is set to disabled, device DMA and interrupts are quiesced. + * Pending DMA requests and MSI interrupts are flushed and no further DMA or + * interrupts are issued after this command returns. If BUS_MASTER is set to + * enabled, device is allowed to initiate DMA. Whether interrupts are enabled + * also depends on the value of MSI_ENABLE bit. Note that, in this case, the + * device may start DMA before the host receives and processes the MCDI + * response. MSI_ENABLE masks or unmasks device interrupts only. Note that for + * interrupts to be delivered to the host, both BUS_MASTER and MSI_ENABLE needs + * to be set. MMIO_REGIONS_ENABLE enables or disables host accesses to device + * MMIO regions. Note that an implementation is allowed to permanently set this + * bit to 1, in which case MC_CMD_CDX_DEVICE_CONTROL_GET will always return 1 + * for this bit, regardless of the value set here. + */ +#define MC_CMD_CDX_DEVICE_CONTROL_SET 0x7 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_MSGSET 0x7 +#undef MC_CMD_0x7_PRIVILEGE_CTG + +#define MC_CMD_0x7_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_DEVICE_CONTROL_SET_IN msgrequest */ +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_LEN 12 +/* Device bus number, in range 0 to BUS_COUNT-1 */ +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_OFST 0 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_LEN 4 +/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */ +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_DEVICE_OFST 4 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_DEVICE_LEN 4 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_FLAGS_OFST 8 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_FLAGS_LEN 4 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_OFST 8 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN 0 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_WIDTH 1 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_OFST 8 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN 1 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_WIDTH 1 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_OFST 8 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_LBN 2 +#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_WIDTH 1 + +/* MC_CMD_CDX_DEVICE_CONTROL_SET_OUT msgresponse */ +#define MC_CMD_CDX_DEVICE_CONTROL_SET_OUT_LEN 0 + +/***********************************/ +/* + * MC_CMD_CDX_DEVICE_CONTROL_GET + * Returns device DMA, interrupt and MMIO region access control bits. See + * MC_CMD_CDX_DEVICE_CONTROL_SET for definition of the available control bits. + */ +#define MC_CMD_CDX_DEVICE_CONTROL_GET 0x8 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_MSGSET 0x8 +#undef MC_CMD_0x8_PRIVILEGE_CTG + +#define MC_CMD_0x8_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_DEVICE_CONTROL_GET_IN msgrequest */ +#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_LEN 8 +/* Device bus number, in range 0 to BUS_COUNT-1 */ +#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_BUS_OFST 0 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_BUS_LEN 4 +/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */ +#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_DEVICE_OFST 4 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_DEVICE_LEN 4 + +/* MC_CMD_CDX_DEVICE_CONTROL_GET_OUT msgresponse */ +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_LEN 4 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_FLAGS_OFST 0 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_FLAGS_LEN 4 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_OFST 0 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_LBN 0 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_WIDTH 1 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_OFST 0 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_LBN 1 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_WIDTH 1 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_OFST 0 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_LBN 2 +#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_WIDTH 1 + +/***********************************/ +/* MC_CMD_V2_EXTN - Encapsulation for a v2 extended command */ +#define MC_CMD_V2_EXTN 0x7f + +/* MC_CMD_V2_EXTN_IN msgrequest */ +#define MC_CMD_V2_EXTN_IN_LEN 4 +/* the extended command number */ +#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_LBN 0 +#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_WIDTH 15 +#define MC_CMD_V2_EXTN_IN_UNUSED_LBN 15 +#define MC_CMD_V2_EXTN_IN_UNUSED_WIDTH 1 +/* the actual length of the encapsulated command */ +#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_LBN 16 +#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_WIDTH 10 +#define MC_CMD_V2_EXTN_IN_UNUSED2_LBN 26 +#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 2 +/* Type of command/response */ +#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_LBN 28 +#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_WIDTH 4 +/* + * enum: MCDI command directed to versal-net. MCDI responses of this type + * are not defined. + */ +#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM 0x2 + +#endif /* MC_CDX_PCOL_H */ diff --git a/drivers/cdx/controller/mcdi.c b/drivers/cdx/controller/mcdi.c new file mode 100644 index 000000000000..a211a2ca762e --- /dev/null +++ b/drivers/cdx/controller/mcdi.c @@ -0,0 +1,903 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Management-Controller-to-Driver Interface + * + * Copyright 2008-2013 Solarflare Communications Inc. + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bitfield.h" +#include "mcdi.h" + +struct cdx_mcdi_copy_buffer { + struct cdx_dword buffer[DIV_ROUND_UP(MCDI_CTL_SDU_LEN_MAX, 4)]; +}; + +#ifdef CONFIG_MCDI_LOGGING +#define LOG_LINE_MAX (1024 - 32) +#endif + +static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd); +static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx); +static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx, + struct cdx_mcdi_cmd *cmd, + unsigned int *handle); +static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi, + bool allow_retry); +static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd); +static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd, + struct cdx_dword *outbuf, + int len, + struct list_head *cleanup_list); +static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd, + struct list_head *cleanup_list); +static void cdx_mcdi_cmd_work(struct work_struct *context); +static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list); +static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd, + size_t inlen, int raw, int arg, int err_no); + +static bool cdx_cmd_cancelled(struct cdx_mcdi_cmd *cmd) +{ + return cmd->state == MCDI_STATE_RUNNING_CANCELLED; +} + +static void cdx_mcdi_cmd_release(struct kref *ref) +{ + kfree(container_of(ref, struct cdx_mcdi_cmd, ref)); +} + +static unsigned int cdx_mcdi_cmd_handle(struct cdx_mcdi_cmd *cmd) +{ + return cmd->handle; +} + +static void _cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd, + struct list_head *cleanup_list) +{ + /* if cancelled, the completers have already been called */ + if (cdx_cmd_cancelled(cmd)) + return; + + if (cmd->completer) { + list_add_tail(&cmd->cleanup_list, cleanup_list); + ++mcdi->outstanding_cleanups; + kref_get(&cmd->ref); + } +} + +static void cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd, + struct list_head *cleanup_list) +{ + list_del(&cmd->list); + _cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list); + cmd->state = MCDI_STATE_FINISHED; + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + if (list_empty(&mcdi->cmd_list)) + wake_up(&mcdi->cmd_complete_wq); +} + +static unsigned long cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd) +{ + if (!cdx->mcdi_ops->mcdi_rpc_timeout) + return MCDI_RPC_TIMEOUT; + else + return cdx->mcdi_ops->mcdi_rpc_timeout(cdx, cmd); +} + +int cdx_mcdi_init(struct cdx_mcdi *cdx) +{ + struct cdx_mcdi_iface *mcdi; + int rc = -ENOMEM; + + cdx->mcdi = kzalloc(sizeof(*cdx->mcdi), GFP_KERNEL); + if (!cdx->mcdi) + goto fail; + + mcdi = cdx_mcdi_if(cdx); + mcdi->cdx = cdx; + +#ifdef CONFIG_MCDI_LOGGING + mcdi->logging_buffer = kmalloc(LOG_LINE_MAX, GFP_KERNEL); + if (!mcdi->logging_buffer) + goto fail2; +#endif + mcdi->workqueue = alloc_ordered_workqueue("mcdi_wq", 0); + if (!mcdi->workqueue) + goto fail3; + mutex_init(&mcdi->iface_lock); + mcdi->mode = MCDI_MODE_EVENTS; + INIT_LIST_HEAD(&mcdi->cmd_list); + init_waitqueue_head(&mcdi->cmd_complete_wq); + + mcdi->new_epoch = true; + + return 0; +fail3: +#ifdef CONFIG_MCDI_LOGGING + kfree(mcdi->logging_buffer); +fail2: +#endif + kfree(cdx->mcdi); + cdx->mcdi = NULL; +fail: + return rc; +} + +void cdx_mcdi_finish(struct cdx_mcdi *cdx) +{ + struct cdx_mcdi_iface *mcdi; + + mcdi = cdx_mcdi_if(cdx); + if (!mcdi) + return; + + cdx_mcdi_wait_for_cleanup(cdx); + +#ifdef CONFIG_MCDI_LOGGING + kfree(mcdi->logging_buffer); +#endif + + destroy_workqueue(mcdi->workqueue); + kfree(cdx->mcdi); + cdx->mcdi = NULL; +} + +static bool cdx_mcdi_flushed(struct cdx_mcdi_iface *mcdi, bool ignore_cleanups) +{ + bool flushed; + + mutex_lock(&mcdi->iface_lock); + flushed = list_empty(&mcdi->cmd_list) && + (ignore_cleanups || !mcdi->outstanding_cleanups); + mutex_unlock(&mcdi->iface_lock); + return flushed; +} + +/* Wait for outstanding MCDI commands to complete. */ +static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + + if (!mcdi) + return; + + wait_event(mcdi->cmd_complete_wq, + cdx_mcdi_flushed(mcdi, false)); +} + +int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx, + unsigned int timeout_jiffies) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + int rc = 0; + + if (!mcdi) + return -EINVAL; + + flush_workqueue(mcdi->workqueue); + + add_wait_queue(&mcdi->cmd_complete_wq, &wait); + + while (!cdx_mcdi_flushed(mcdi, true)) { + rc = wait_woken(&wait, TASK_IDLE, timeout_jiffies); + if (rc) + continue; + break; + } + + remove_wait_queue(&mcdi->cmd_complete_wq, &wait); + + if (rc > 0) + rc = 0; + else if (rc == 0) + rc = -ETIMEDOUT; + + return rc; +} + +static u8 cdx_mcdi_payload_csum(const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len) +{ + u8 *p = (u8 *)hdr; + u8 csum = 0; + int i; + + for (i = 0; i < hdr_len; i++) + csum += p[i]; + + p = (u8 *)sdu; + for (i = 0; i < sdu_len; i++) + csum += p[i]; + + return ~csum & 0xff; +} + +static void cdx_mcdi_send_request(struct cdx_mcdi *cdx, + struct cdx_mcdi_cmd *cmd) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + const struct cdx_dword *inbuf = cmd->inbuf; + size_t inlen = cmd->inlen; + struct cdx_dword hdr[2]; + size_t hdr_len; + bool not_epoch; + u32 xflags; +#ifdef CONFIG_MCDI_LOGGING + char *buf; +#endif + + if (!mcdi) + return; +#ifdef CONFIG_MCDI_LOGGING + buf = mcdi->logging_buffer; /* page-sized */ +#endif + + mcdi->prev_seq = cmd->seq; + mcdi->seq_held_by[cmd->seq] = cmd; + mcdi->db_held_by = cmd; + cmd->started = jiffies; + + not_epoch = !mcdi->new_epoch; + xflags = 0; + + /* MCDI v2 */ + WARN_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2); + CDX_POPULATE_DWORD_7(hdr[0], + MCDI_HEADER_RESPONSE, 0, + MCDI_HEADER_RESYNC, 1, + MCDI_HEADER_CODE, MC_CMD_V2_EXTN, + MCDI_HEADER_DATALEN, 0, + MCDI_HEADER_SEQ, cmd->seq, + MCDI_HEADER_XFLAGS, xflags, + MCDI_HEADER_NOT_EPOCH, not_epoch); + CDX_POPULATE_DWORD_3(hdr[1], + MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd->cmd, + MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen, + MC_CMD_V2_EXTN_IN_MESSAGE_TYPE, + MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM); + hdr_len = 8; + +#ifdef CONFIG_MCDI_LOGGING + if (!WARN_ON_ONCE(!buf)) { + const struct cdx_dword *frags[] = { hdr, inbuf }; + const size_t frag_len[] = { hdr_len, round_up(inlen, 4) }; + int bytes = 0; + int i, j; + + for (j = 0; j < ARRAY_SIZE(frags); j++) { + const struct cdx_dword *frag; + + frag = frags[j]; + for (i = 0; + i < frag_len[j] / 4; + i++) { + /* + * Do not exceed the internal printk limit. + * The string before that is just over 70 bytes. + */ + if ((bytes + 75) > LOG_LINE_MAX) { + pr_info("MCDI RPC REQ:%s \\\n", buf); + bytes = 0; + } + bytes += snprintf(buf + bytes, + LOG_LINE_MAX - bytes, " %08x", + le32_to_cpu(frag[i].cdx_u32)); + } + } + + pr_info("MCDI RPC REQ:%s\n", buf); + } +#endif + hdr[0].cdx_u32 |= (__force __le32)(cdx_mcdi_payload_csum(hdr, hdr_len, inbuf, inlen) << + MCDI_HEADER_XFLAGS_LBN); + cdx->mcdi_ops->mcdi_request(cdx, hdr, hdr_len, inbuf, inlen); + + mcdi->new_epoch = false; +} + +static int cdx_mcdi_errno(struct cdx_mcdi *cdx, unsigned int mcdi_err) +{ + switch (mcdi_err) { + case 0: + case MC_CMD_ERR_QUEUE_FULL: + return mcdi_err; + case MC_CMD_ERR_EPERM: + return -EPERM; + case MC_CMD_ERR_ENOENT: + return -ENOENT; + case MC_CMD_ERR_EINTR: + return -EINTR; + case MC_CMD_ERR_EAGAIN: + return -EAGAIN; + case MC_CMD_ERR_EACCES: + return -EACCES; + case MC_CMD_ERR_EBUSY: + return -EBUSY; + case MC_CMD_ERR_EINVAL: + return -EINVAL; + case MC_CMD_ERR_ERANGE: + return -ERANGE; + case MC_CMD_ERR_EDEADLK: + return -EDEADLK; + case MC_CMD_ERR_ENOSYS: + return -EOPNOTSUPP; + case MC_CMD_ERR_ETIME: + return -ETIME; + case MC_CMD_ERR_EALREADY: + return -EALREADY; + case MC_CMD_ERR_ENOSPC: + return -ENOSPC; + case MC_CMD_ERR_ENOMEM: + return -ENOMEM; + case MC_CMD_ERR_ENOTSUP: + return -EOPNOTSUPP; + case MC_CMD_ERR_ALLOC_FAIL: + return -ENOBUFS; + case MC_CMD_ERR_MAC_EXIST: + return -EADDRINUSE; + case MC_CMD_ERR_NO_EVB_PORT: + return -EAGAIN; + default: + return -EPROTO; + } +} + +static void cdx_mcdi_process_cleanup_list(struct cdx_mcdi *cdx, + struct list_head *cleanup_list) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + unsigned int cleanups = 0; + + if (!mcdi) + return; + + while (!list_empty(cleanup_list)) { + struct cdx_mcdi_cmd *cmd = + list_first_entry(cleanup_list, + struct cdx_mcdi_cmd, cleanup_list); + cmd->completer(cdx, cmd->cookie, cmd->rc, + cmd->outbuf, cmd->outlen); + list_del(&cmd->cleanup_list); + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + ++cleanups; + } + + if (cleanups) { + bool all_done; + + mutex_lock(&mcdi->iface_lock); + CDX_WARN_ON_PARANOID(cleanups > mcdi->outstanding_cleanups); + all_done = (mcdi->outstanding_cleanups -= cleanups) == 0; + mutex_unlock(&mcdi->iface_lock); + if (all_done) + wake_up(&mcdi->cmd_complete_wq); + } +} + +static void _cdx_mcdi_cancel_cmd(struct cdx_mcdi_iface *mcdi, + unsigned int handle, + struct list_head *cleanup_list) +{ + struct cdx_mcdi_cmd *cmd; + + list_for_each_entry(cmd, &mcdi->cmd_list, list) + if (cdx_mcdi_cmd_handle(cmd) == handle) { + switch (cmd->state) { + case MCDI_STATE_QUEUED: + case MCDI_STATE_RETRY: + pr_debug("command %#x inlen %zu cancelled in queue\n", + cmd->cmd, cmd->inlen); + /* if not yet running, properly cancel it */ + cmd->rc = -EPIPE; + cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list); + break; + case MCDI_STATE_RUNNING: + case MCDI_STATE_RUNNING_CANCELLED: + case MCDI_STATE_FINISHED: + default: + /* invalid state? */ + WARN_ON(1); + } + break; + } +} + +static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + LIST_HEAD(cleanup_list); + + if (!mcdi) + return; + + mutex_lock(&mcdi->iface_lock); + cdx_mcdi_timeout_cmd(mcdi, cmd, &cleanup_list); + mutex_unlock(&mcdi->iface_lock); + cdx_mcdi_process_cleanup_list(cdx, &cleanup_list); +} + +struct cdx_mcdi_blocking_data { + struct kref ref; + bool done; + wait_queue_head_t wq; + int rc; + struct cdx_dword *outbuf; + size_t outlen; + size_t outlen_actual; +}; + +static void cdx_mcdi_blocking_data_release(struct kref *ref) +{ + kfree(container_of(ref, struct cdx_mcdi_blocking_data, ref)); +} + +static void cdx_mcdi_rpc_completer(struct cdx_mcdi *cdx, unsigned long cookie, + int rc, struct cdx_dword *outbuf, + size_t outlen_actual) +{ + struct cdx_mcdi_blocking_data *wait_data = + (struct cdx_mcdi_blocking_data *)cookie; + + wait_data->rc = rc; + memcpy(wait_data->outbuf, outbuf, + min(outlen_actual, wait_data->outlen)); + wait_data->outlen_actual = outlen_actual; + /* memory barrier */ + smp_wmb(); + wait_data->done = true; + wake_up(&wait_data->wq); + kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release); +} + +static int cdx_mcdi_rpc_sync(struct cdx_mcdi *cdx, unsigned int cmd, + const struct cdx_dword *inbuf, size_t inlen, + struct cdx_dword *outbuf, size_t outlen, + size_t *outlen_actual, bool quiet) +{ + struct cdx_mcdi_blocking_data *wait_data; + struct cdx_mcdi_cmd *cmd_item; + unsigned int handle; + int rc; + + if (outlen_actual) + *outlen_actual = 0; + + wait_data = kmalloc(sizeof(*wait_data), GFP_KERNEL); + if (!wait_data) + return -ENOMEM; + + cmd_item = kmalloc(sizeof(*cmd_item), GFP_KERNEL); + if (!cmd_item) { + kfree(wait_data); + return -ENOMEM; + } + + kref_init(&wait_data->ref); + wait_data->done = false; + init_waitqueue_head(&wait_data->wq); + wait_data->outbuf = outbuf; + wait_data->outlen = outlen; + + kref_init(&cmd_item->ref); + cmd_item->quiet = quiet; + cmd_item->cookie = (unsigned long)wait_data; + cmd_item->completer = &cdx_mcdi_rpc_completer; + cmd_item->cmd = cmd; + cmd_item->inlen = inlen; + cmd_item->inbuf = inbuf; + + /* Claim an extra reference for the completer to put. */ + kref_get(&wait_data->ref); + rc = cdx_mcdi_rpc_async_internal(cdx, cmd_item, &handle); + if (rc) { + kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release); + goto out; + } + + if (!wait_event_timeout(wait_data->wq, wait_data->done, + cdx_mcdi_rpc_timeout(cdx, cmd)) && + !wait_data->done) { + pr_err("MC command 0x%x inlen %zu timed out (sync)\n", + cmd, inlen); + + cdx_mcdi_cancel_cmd(cdx, cmd_item); + + wait_data->rc = -ETIMEDOUT; + wait_data->outlen_actual = 0; + } + + if (outlen_actual) + *outlen_actual = wait_data->outlen_actual; + rc = wait_data->rc; + +out: + kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release); + + return rc; +} + +static bool cdx_mcdi_get_seq(struct cdx_mcdi_iface *mcdi, unsigned char *seq) +{ + *seq = mcdi->prev_seq; + do { + *seq = (*seq + 1) % ARRAY_SIZE(mcdi->seq_held_by); + } while (mcdi->seq_held_by[*seq] && *seq != mcdi->prev_seq); + return !mcdi->seq_held_by[*seq]; +} + +static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx, + struct cdx_mcdi_cmd *cmd, + unsigned int *handle) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + LIST_HEAD(cleanup_list); + + if (!mcdi) { + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + return -ENETDOWN; + } + + if (mcdi->mode == MCDI_MODE_FAIL) { + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + return -ENETDOWN; + } + + cmd->mcdi = mcdi; + INIT_WORK(&cmd->work, cdx_mcdi_cmd_work); + INIT_LIST_HEAD(&cmd->list); + INIT_LIST_HEAD(&cmd->cleanup_list); + cmd->rc = 0; + cmd->outbuf = NULL; + cmd->outlen = 0; + + queue_work(mcdi->workqueue, &cmd->work); + return 0; +} + +static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd) +{ + struct cdx_mcdi *cdx = mcdi->cdx; + u8 seq; + + if (!mcdi->db_held_by && + cdx_mcdi_get_seq(mcdi, &seq)) { + cmd->seq = seq; + cmd->reboot_seen = false; + cdx_mcdi_send_request(cdx, cmd); + cmd->state = MCDI_STATE_RUNNING; + } else { + cmd->state = MCDI_STATE_QUEUED; + } +} + +/* try to advance other commands */ +static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi, + bool allow_retry) +{ + struct cdx_mcdi_cmd *cmd, *tmp; + + list_for_each_entry_safe(cmd, tmp, &mcdi->cmd_list, list) + if (cmd->state == MCDI_STATE_QUEUED || + (cmd->state == MCDI_STATE_RETRY && allow_retry)) + cdx_mcdi_cmd_start_or_queue(mcdi, cmd); +} + +void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len) +{ + struct cdx_mcdi_iface *mcdi; + struct cdx_mcdi_cmd *cmd; + LIST_HEAD(cleanup_list); + unsigned int respseq; + + if (!len || !outbuf) { + pr_err("Got empty MC response\n"); + return; + } + + mcdi = cdx_mcdi_if(cdx); + if (!mcdi) + return; + + respseq = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_SEQ); + + mutex_lock(&mcdi->iface_lock); + cmd = mcdi->seq_held_by[respseq]; + + if (cmd) { + if (cmd->state == MCDI_STATE_FINISHED) { + mutex_unlock(&mcdi->iface_lock); + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + return; + } + + cdx_mcdi_complete_cmd(mcdi, cmd, outbuf, len, &cleanup_list); + } else { + pr_err("MC response unexpected for seq : %0X\n", respseq); + } + + mutex_unlock(&mcdi->iface_lock); + + cdx_mcdi_process_cleanup_list(mcdi->cdx, &cleanup_list); +} + +static void cdx_mcdi_cmd_work(struct work_struct *context) +{ + struct cdx_mcdi_cmd *cmd = + container_of(context, struct cdx_mcdi_cmd, work); + struct cdx_mcdi_iface *mcdi = cmd->mcdi; + + mutex_lock(&mcdi->iface_lock); + + cmd->handle = mcdi->prev_handle++; + list_add_tail(&cmd->list, &mcdi->cmd_list); + cdx_mcdi_cmd_start_or_queue(mcdi, cmd); + + mutex_unlock(&mcdi->iface_lock); +} + +/* + * Returns true if the MCDI module is finished with the command. + * (examples of false would be if the command was proxied, or it was + * rejected by the MC due to lack of resources and requeued). + */ +static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd, + struct cdx_dword *outbuf, + int len, + struct list_head *cleanup_list) +{ + size_t resp_hdr_len, resp_data_len; + struct cdx_mcdi *cdx = mcdi->cdx; + unsigned int respcmd, error; + bool completed = false; + int rc; + + /* ensure the command can't go away before this function returns */ + kref_get(&cmd->ref); + + respcmd = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_CODE); + error = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_ERROR); + + if (respcmd != MC_CMD_V2_EXTN) { + resp_hdr_len = 4; + resp_data_len = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_DATALEN); + } else { + resp_data_len = 0; + resp_hdr_len = 8; + if (len >= 8) + resp_data_len = + CDX_DWORD_FIELD(outbuf[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN); + } + + if ((resp_hdr_len + resp_data_len) > len) { + pr_warn("Incomplete MCDI response received %d. Expected %zu\n", + len, (resp_hdr_len + resp_data_len)); + resp_data_len = 0; + } + +#ifdef CONFIG_MCDI_LOGGING + if (!WARN_ON_ONCE(!mcdi->logging_buffer)) { + char *log = mcdi->logging_buffer; + int i, bytes = 0; + size_t rlen; + + WARN_ON_ONCE(resp_hdr_len % 4); + + rlen = resp_hdr_len / 4 + DIV_ROUND_UP(resp_data_len, 4); + + for (i = 0; i < rlen; i++) { + if ((bytes + 75) > LOG_LINE_MAX) { + pr_info("MCDI RPC RESP:%s \\\n", log); + bytes = 0; + } + bytes += snprintf(log + bytes, LOG_LINE_MAX - bytes, + " %08x", le32_to_cpu(outbuf[i].cdx_u32)); + } + + pr_info("MCDI RPC RESP:%s\n", log); + } +#endif + + if (error && resp_data_len == 0) { + /* MC rebooted during command */ + rc = -EIO; + } else { + if (WARN_ON_ONCE(error && resp_data_len < 4)) + resp_data_len = 4; + if (error) { + rc = CDX_DWORD_FIELD(outbuf[resp_hdr_len / 4], CDX_DWORD); + if (!cmd->quiet) { + int err_arg = 0; + + if (resp_data_len >= MC_CMD_ERR_ARG_OFST + 4) { + int offset = (resp_hdr_len + MC_CMD_ERR_ARG_OFST) / 4; + + err_arg = CDX_DWORD_VAL(outbuf[offset]); + } + + _cdx_mcdi_display_error(cdx, cmd->cmd, + cmd->inlen, rc, err_arg, + cdx_mcdi_errno(cdx, rc)); + } + rc = cdx_mcdi_errno(cdx, rc); + } else { + rc = 0; + } + } + + /* free doorbell */ + if (mcdi->db_held_by == cmd) + mcdi->db_held_by = NULL; + + if (cdx_cmd_cancelled(cmd)) { + list_del(&cmd->list); + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + completed = true; + } else if (rc == MC_CMD_ERR_QUEUE_FULL) { + cmd->state = MCDI_STATE_RETRY; + } else { + cmd->rc = rc; + cmd->outbuf = outbuf + DIV_ROUND_UP(resp_hdr_len, 4); + cmd->outlen = resp_data_len; + cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list); + completed = true; + } + + /* free sequence number and buffer */ + mcdi->seq_held_by[cmd->seq] = NULL; + + cdx_mcdi_start_or_queue(mcdi, rc != MC_CMD_ERR_QUEUE_FULL); + + /* wake up anyone waiting for flush */ + wake_up(&mcdi->cmd_complete_wq); + + kref_put(&cmd->ref, cdx_mcdi_cmd_release); + + return completed; +} + +static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi, + struct cdx_mcdi_cmd *cmd, + struct list_head *cleanup_list) +{ + struct cdx_mcdi *cdx = mcdi->cdx; + + pr_err("MC command 0x%x inlen %zu state %d timed out after %u ms\n", + cmd->cmd, cmd->inlen, cmd->state, + jiffies_to_msecs(jiffies - cmd->started)); + + cmd->rc = -ETIMEDOUT; + cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list); + + cdx_mcdi_mode_fail(cdx, cleanup_list); +} + +/** + * cdx_mcdi_rpc - Issue an MCDI command and wait for completion + * @cdx: NIC through which to issue the command + * @cmd: Command type number + * @inbuf: Command parameters + * @inlen: Length of command parameters, in bytes. Must be a multiple + * of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1. + * @outbuf: Response buffer. May be %NULL if @outlen is 0. + * @outlen: Length of response buffer, in bytes. If the actual + * response is longer than @outlen & ~3, it will be truncated + * to that length. + * @outlen_actual: Pointer through which to return the actual response + * length. May be %NULL if this is not needed. + * + * This function may sleep and therefore must be called in process + * context. + * + * Return: A negative error code, or zero if successful. The error + * code may come from the MCDI response or may indicate a failure + * to communicate with the MC. In the former case, the response + * will still be copied to @outbuf and *@outlen_actual will be + * set accordingly. In the latter case, *@outlen_actual will be + * set to zero. + */ +int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd, + const struct cdx_dword *inbuf, size_t inlen, + struct cdx_dword *outbuf, size_t outlen, + size_t *outlen_actual) +{ + return cdx_mcdi_rpc_sync(cdx, cmd, inbuf, inlen, outbuf, outlen, + outlen_actual, false); +} + +/** + * cdx_mcdi_rpc_async - Schedule an MCDI command to run asynchronously + * @cdx: NIC through which to issue the command + * @cmd: Command type number + * @inbuf: Command parameters + * @inlen: Length of command parameters, in bytes + * @complete: Function to be called on completion or cancellation. + * @cookie: Arbitrary value to be passed to @complete. + * + * This function does not sleep and therefore may be called in atomic + * context. It will fail if event queues are disabled or if MCDI + * event completions have been disabled due to an error. + * + * If it succeeds, the @complete function will be called exactly once + * in process context, when one of the following occurs: + * (a) the completion event is received (in process context) + * (b) event queues are disabled (in the process that disables them) + */ +int +cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd, + const struct cdx_dword *inbuf, size_t inlen, + cdx_mcdi_async_completer *complete, unsigned long cookie) +{ + struct cdx_mcdi_cmd *cmd_item = + kmalloc(sizeof(struct cdx_mcdi_cmd) + inlen, GFP_ATOMIC); + + if (!cmd_item) + return -ENOMEM; + + kref_init(&cmd_item->ref); + cmd_item->quiet = true; + cmd_item->cookie = cookie; + cmd_item->completer = complete; + cmd_item->cmd = cmd; + cmd_item->inlen = inlen; + /* inbuf is probably not valid after return, so take a copy */ + cmd_item->inbuf = (struct cdx_dword *)(cmd_item + 1); + memcpy(cmd_item + 1, inbuf, inlen); + + return cdx_mcdi_rpc_async_internal(cdx, cmd_item, NULL); +} + +static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd, + size_t inlen, int raw, int arg, int err_no) +{ + pr_err("MC command 0x%x inlen %d failed err_no=%d (raw=%d) arg=%d\n", + cmd, (int)inlen, err_no, raw, arg); +} + +/* + * Set MCDI mode to fail to prevent any new commands, then cancel any + * outstanding commands. + * Caller must hold the mcdi iface_lock. + */ +static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list) +{ + struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx); + + if (!mcdi) + return; + + mcdi->mode = MCDI_MODE_FAIL; + + while (!list_empty(&mcdi->cmd_list)) { + struct cdx_mcdi_cmd *cmd; + + cmd = list_first_entry(&mcdi->cmd_list, struct cdx_mcdi_cmd, + list); + _cdx_mcdi_cancel_cmd(mcdi, cdx_mcdi_cmd_handle(cmd), cleanup_list); + } +} diff --git a/drivers/cdx/controller/mcdi.h b/drivers/cdx/controller/mcdi.h new file mode 100644 index 000000000000..63933ede33ed --- /dev/null +++ b/drivers/cdx/controller/mcdi.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2008-2013 Solarflare Communications Inc. + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef CDX_MCDI_H +#define CDX_MCDI_H + +#include +#include + +#include "bitfield.h" +#include "mc_cdx_pcol.h" + +#ifdef DEBUG +#define CDX_WARN_ON_ONCE_PARANOID(x) WARN_ON_ONCE(x) +#define CDX_WARN_ON_PARANOID(x) WARN_ON(x) +#else +#define CDX_WARN_ON_ONCE_PARANOID(x) do {} while (0) +#define CDX_WARN_ON_PARANOID(x) do {} while (0) +#endif + +/** + * enum cdx_mcdi_mode - MCDI transaction mode + * @MCDI_MODE_EVENTS: wait for an mcdi response callback. + * @MCDI_MODE_FAIL: we think MCDI is dead, so fail-fast all calls + */ +enum cdx_mcdi_mode { + MCDI_MODE_EVENTS, + MCDI_MODE_FAIL, +}; + +#define MCDI_RPC_TIMEOUT (10 * HZ) +#define MCDI_RPC_LONG_TIMEOU (60 * HZ) +#define MCDI_RPC_POST_RST_TIME (10 * HZ) + +#define MCDI_BUF_LEN (8 + MCDI_CTL_SDU_LEN_MAX) + +/** + * enum cdx_mcdi_cmd_state - State for an individual MCDI command + * @MCDI_STATE_QUEUED: Command not started and is waiting to run. + * @MCDI_STATE_RETRY: Command was submitted and MC rejected with no resources, + * as MC have too many outstanding commands. Command will be retried once + * another command returns. + * @MCDI_STATE_RUNNING: Command was accepted and is running. + * @MCDI_STATE_RUNNING_CANCELLED: Command is running but the issuer cancelled + * the command. + * @MCDI_STATE_FINISHED: Processing of this command has completed. + */ + +enum cdx_mcdi_cmd_state { + MCDI_STATE_QUEUED, + MCDI_STATE_RETRY, + MCDI_STATE_RUNNING, + MCDI_STATE_RUNNING_CANCELLED, + MCDI_STATE_FINISHED, +}; + +/** + * struct cdx_mcdi - CDX MCDI Firmware interface, to interact + * with CDX controller. + * @mcdi: MCDI interface + * @mcdi_ops: MCDI operations + */ +struct cdx_mcdi { + /* MCDI interface */ + struct cdx_mcdi_data *mcdi; + const struct cdx_mcdi_ops *mcdi_ops; +}; + +struct cdx_mcdi_ops { + void (*mcdi_request)(struct cdx_mcdi *cdx, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len); + unsigned int (*mcdi_rpc_timeout)(struct cdx_mcdi *cdx, unsigned int cmd); +}; + +typedef void cdx_mcdi_async_completer(struct cdx_mcdi *cdx, + unsigned long cookie, int rc, + struct cdx_dword *outbuf, + size_t outlen_actual); + +/** + * struct cdx_mcdi_cmd - An outstanding MCDI command + * @ref: Reference count. There will be one reference if the command is + * in the mcdi_iface cmd_list, another if it's on a cleanup list, + * and a third if it's queued in the work queue. + * @list: The data for this entry in mcdi->cmd_list + * @cleanup_list: The data for this entry in a cleanup list + * @work: The work item for this command, queued in mcdi->workqueue + * @mcdi: The mcdi_iface for this command + * @state: The state of this command + * @inlen: inbuf length + * @inbuf: Input buffer + * @quiet: Whether to silence errors + * @reboot_seen: Whether a reboot has been seen during this command, + * to prevent duplicates + * @seq: Sequence number + * @started: Jiffies this command was started at + * @cookie: Context for completion function + * @completer: Completion function + * @handle: Command handle + * @cmd: Command number + * @rc: Return code + * @outlen: Length of output buffer + * @outbuf: Output buffer + */ +struct cdx_mcdi_cmd { + struct kref ref; + struct list_head list; + struct list_head cleanup_list; + struct work_struct work; + struct cdx_mcdi_iface *mcdi; + enum cdx_mcdi_cmd_state state; + size_t inlen; + const struct cdx_dword *inbuf; + bool quiet; + bool reboot_seen; + u8 seq; + unsigned long started; + unsigned long cookie; + cdx_mcdi_async_completer *completer; + unsigned int handle; + unsigned int cmd; + int rc; + size_t outlen; + struct cdx_dword *outbuf; + /* followed by inbuf data if necessary */ +}; + +/** + * struct cdx_mcdi_iface - MCDI protocol context + * @cdx: The associated NIC + * @iface_lock: Serialise access to this structure + * @outstanding_cleanups: Count of cleanups + * @cmd_list: List of outstanding and running commands + * @workqueue: Workqueue used for delayed processing + * @cmd_complete_wq: Waitqueue for command completion + * @db_held_by: Command the MC doorbell is in use by + * @seq_held_by: Command each sequence number is in use by + * @prev_handle: The last used command handle + * @mode: Poll for mcdi completion, or wait for an mcdi_event + * @prev_seq: The last used sequence number + * @new_epoch: Indicates start of day or start of MC reboot recovery + * @logging_buffer: Buffer that may be used to build MCDI tracing messages + * @logging_enabled: Whether to trace MCDI + */ +struct cdx_mcdi_iface { + struct cdx_mcdi *cdx; + /* Serialise access */ + struct mutex iface_lock; + unsigned int outstanding_cleanups; + struct list_head cmd_list; + struct workqueue_struct *workqueue; + wait_queue_head_t cmd_complete_wq; + struct cdx_mcdi_cmd *db_held_by; + struct cdx_mcdi_cmd *seq_held_by[16]; + unsigned int prev_handle; + enum cdx_mcdi_mode mode; + u8 prev_seq; + bool new_epoch; +#ifdef CONFIG_MCDI_LOGGING + bool logging_enabled; + char *logging_buffer; +#endif +}; + +/** + * struct cdx_mcdi_data - extra state for NICs that implement MCDI + * @iface: Interface/protocol state + * @fn_flags: Flags for this function, as returned by %MC_CMD_DRV_ATTACH. + */ +struct cdx_mcdi_data { + struct cdx_mcdi_iface iface; + u32 fn_flags; +}; + +static inline struct cdx_mcdi_iface *cdx_mcdi_if(struct cdx_mcdi *cdx) +{ + return cdx->mcdi ? &cdx->mcdi->iface : NULL; +} + +int cdx_mcdi_init(struct cdx_mcdi *cdx); +void cdx_mcdi_finish(struct cdx_mcdi *cdx); + +void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len); +int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd, + const struct cdx_dword *inbuf, size_t inlen, + struct cdx_dword *outbuf, size_t outlen, size_t *outlen_actual); +int cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd, + const struct cdx_dword *inbuf, size_t inlen, + cdx_mcdi_async_completer *complete, + unsigned long cookie); +int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx, + unsigned int timeout_jiffies); + +/* + * We expect that 16- and 32-bit fields in MCDI requests and responses + * are appropriately aligned, but 64-bit fields are only + * 32-bit-aligned. + */ +#define MCDI_DECLARE_BUF(_name, _len) struct cdx_dword _name[DIV_ROUND_UP(_len, 4)] = {{0}} +#define _MCDI_PTR(_buf, _offset) \ + ((u8 *)(_buf) + (_offset)) +#define MCDI_PTR(_buf, _field) \ + _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST) +#define _MCDI_CHECK_ALIGN(_ofst, _align) \ + ((void)BUILD_BUG_ON_ZERO((_ofst) & ((_align) - 1)), \ + (_ofst)) +#define _MCDI_DWORD(_buf, _field) \ + ((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2)) + +#define MCDI_BYTE(_buf, _field) \ + ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \ + *MCDI_PTR(_buf, _field)) +#define MCDI_WORD(_buf, _field) \ + ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2), \ + le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) +#define MCDI_SET_DWORD(_buf, _field, _value) \ + CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), CDX_DWORD, _value) +#define MCDI_DWORD(_buf, _field) \ + CDX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), CDX_DWORD) +#define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1) \ + CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1) +#define MCDI_SET_QWORD(_buf, _field, _value) \ + do { \ + CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[0], \ + CDX_DWORD, (u32)(_value)); \ + CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[1], \ + CDX_DWORD, (u64)(_value) >> 32); \ + } while (0) +#define MCDI_QWORD(_buf, _field) \ + (CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[0], CDX_DWORD) | \ + (u64)CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[1], CDX_DWORD) << 32) + +#endif /* CDX_MCDI_H */ -- cgit v1.2.3 From 8a7923df35d3a0fa44985148cee5b7fde4a370ba Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:34 +0530 Subject: cdx: add cdx controller CDX controller uses MCDI interface as a protocol to communicate with the RPU firmware and registers the detected CDX devices on the CDX bus. It also uses RPMsg as the communication channel with the Firmware. Signed-off-by: Nipun Gupta Signed-off-by: Puneet Gupta Signed-off-by: Abhijit Gangurde Signed-off-by: Nikhil Agarwal Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-6-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/controller/Kconfig | 9 ++ drivers/cdx/controller/Makefile | 2 +- drivers/cdx/controller/cdx_controller.c | 188 ++++++++++++++++++++++++++++++++ drivers/cdx/controller/mcdi_functions.c | 125 +++++++++++++++++++++ drivers/cdx/controller/mcdi_functions.h | 50 +++++++++ 5 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 drivers/cdx/controller/cdx_controller.c create mode 100644 drivers/cdx/controller/mcdi_functions.c create mode 100644 drivers/cdx/controller/mcdi_functions.h diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig index 785c71063b2a..17f9c6be2fe1 100644 --- a/drivers/cdx/controller/Kconfig +++ b/drivers/cdx/controller/Kconfig @@ -7,6 +7,15 @@ if CDX_BUS +config CDX_CONTROLLER + tristate "CDX bus controller" + help + CDX controller drives the CDX bus. It interacts with + firmware to get the hardware devices and registers with + the CDX bus. Say Y to enable the CDX hardware driver. + + If unsure, say N. + config MCDI_LOGGING bool "MCDI Logging for the CDX controller" depends on CDX_CONTROLLER diff --git a/drivers/cdx/controller/Makefile b/drivers/cdx/controller/Makefile index 0ce200678eda..f7437c882cc9 100644 --- a/drivers/cdx/controller/Makefile +++ b/drivers/cdx/controller/Makefile @@ -6,4 +6,4 @@ # obj-$(CONFIG_CDX_CONTROLLER) += cdx-controller.o -cdx-controller-objs := mcdi.o +cdx-controller-objs := cdx_controller.o mcdi.o mcdi_functions.o diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c new file mode 100644 index 000000000000..fbebc8cdcbf8 --- /dev/null +++ b/drivers/cdx/controller/cdx_controller.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CDX host controller driver for AMD versal-net platform. + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "../cdx.h" +#include "mcdi_functions.h" +#include "mcdi.h" + +static unsigned int cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd) +{ + return MCDI_RPC_TIMEOUT; +} + +static void cdx_mcdi_request(struct cdx_mcdi *cdx, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len) +{ + /* + * This will get updated by rpmsg APIs, with RPMSG introduction + * in CDX controller as a transport layer. + */ +} + +static const struct cdx_mcdi_ops mcdi_ops = { + .mcdi_rpc_timeout = cdx_mcdi_rpc_timeout, + .mcdi_request = cdx_mcdi_request, +}; + +static int cdx_scan_devices(struct cdx_controller *cdx) +{ + struct cdx_mcdi *cdx_mcdi = cdx->priv; + u8 bus_num, dev_num, num_cdx_bus; + int ret; + + /* MCDI FW Read: Fetch the number of CDX buses on this controller */ + ret = cdx_mcdi_get_num_buses(cdx_mcdi); + if (ret < 0) { + dev_err(cdx->dev, + "Get number of CDX buses failed: %d\n", ret); + return ret; + } + num_cdx_bus = (u8)ret; + + for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) { + u8 num_cdx_dev; + + /* MCDI FW Read: Fetch the number of devices present */ + ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num); + if (ret < 0) { + dev_err(cdx->dev, + "Get devices on CDX bus %d failed: %d\n", bus_num, ret); + continue; + } + num_cdx_dev = (u8)ret; + + for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) { + struct cdx_dev_params dev_params; + + /* MCDI FW: Get the device config */ + ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num, + dev_num, &dev_params); + if (ret) { + dev_err(cdx->dev, + "CDX device config get failed for %d(bus):%d(dev), %d\n", + bus_num, dev_num, ret); + continue; + } + dev_params.cdx = cdx; + + /* Add the device to the cdx bus */ + ret = cdx_device_add(&dev_params); + if (ret) { + dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n", + dev_num, ret); + continue; + } + + dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n", + dev_num, bus_num); + } + } + + return 0; +} + +static struct cdx_ops cdx_ops = { + .scan = cdx_scan_devices, +}; + +static int xlnx_cdx_probe(struct platform_device *pdev) +{ + struct cdx_controller *cdx; + struct cdx_mcdi *cdx_mcdi; + int ret; + + cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL); + if (!cdx_mcdi) + return -ENOMEM; + + /* Store the MCDI ops */ + cdx_mcdi->mcdi_ops = &mcdi_ops; + /* MCDI FW: Initialize the FW path */ + ret = cdx_mcdi_init(cdx_mcdi); + if (ret) { + dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n"); + goto mcdi_init_fail; + } + + cdx = kzalloc(sizeof(*cdx), GFP_KERNEL); + if (!cdx) { + ret = -ENOMEM; + goto cdx_alloc_fail; + } + platform_set_drvdata(pdev, cdx); + + cdx->dev = &pdev->dev; + cdx->priv = cdx_mcdi; + cdx->ops = &cdx_ops; + + return 0; + +cdx_alloc_fail: + cdx_mcdi_finish(cdx_mcdi); +mcdi_init_fail: + kfree(cdx_mcdi); + + return ret; +} + +static int xlnx_cdx_remove(struct platform_device *pdev) +{ + struct cdx_controller *cdx = platform_get_drvdata(pdev); + struct cdx_mcdi *cdx_mcdi = cdx->priv; + + kfree(cdx); + + cdx_mcdi_finish(cdx_mcdi); + kfree(cdx_mcdi); + + return 0; +} + +static const struct of_device_id cdx_match_table[] = { + {.compatible = "xlnx,versal-net-cdx",}, + { }, +}; + +MODULE_DEVICE_TABLE(of, cdx_match_table); + +static struct platform_driver cdx_pdriver = { + .driver = { + .name = "cdx-controller", + .pm = NULL, + .of_match_table = cdx_match_table, + }, + .probe = xlnx_cdx_probe, + .remove = xlnx_cdx_remove, +}; + +static int __init cdx_controller_init(void) +{ + int ret; + + ret = platform_driver_register(&cdx_pdriver); + if (ret) + pr_err("platform_driver_register() failed: %d\n", ret); + + return ret; +} + +static void __exit cdx_controller_exit(void) +{ + platform_driver_unregister(&cdx_pdriver); +} + +module_init(cdx_controller_init); +module_exit(cdx_controller_exit); + +MODULE_AUTHOR("AMD Inc."); +MODULE_DESCRIPTION("CDX controller for AMD devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cdx/controller/mcdi_functions.c b/drivers/cdx/controller/mcdi_functions.c new file mode 100644 index 000000000000..012b52881dd5 --- /dev/null +++ b/drivers/cdx/controller/mcdi_functions.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#include + +#include "mcdi.h" +#include "mcdi_functions.h" + +int cdx_mcdi_get_num_buses(struct cdx_mcdi *cdx) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN); + size_t outlen; + int ret; + + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_ENUM_BUSES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (ret) + return ret; + + if (outlen != MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN) + return -EIO; + + return MCDI_DWORD(outbuf, CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT); +} + +int cdx_mcdi_get_num_devs(struct cdx_mcdi *cdx, int bus_num) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN); + MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_ENUM_DEVICES_IN_LEN); + size_t outlen; + int ret; + + MCDI_SET_DWORD(inbuf, CDX_BUS_ENUM_DEVICES_IN_BUS, bus_num); + + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_ENUM_DEVICES, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (ret) + return ret; + + if (outlen != MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN) + return -EIO; + + return MCDI_DWORD(outbuf, CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT); +} + +int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, + u8 bus_num, u8 dev_num, + struct cdx_dev_params *dev_params) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN); + MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN); + struct resource *res = &dev_params->res[0]; + size_t outlen; + u32 req_id; + int ret; + + MCDI_SET_DWORD(inbuf, CDX_BUS_GET_DEVICE_CONFIG_IN_BUS, bus_num); + MCDI_SET_DWORD(inbuf, CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE, dev_num); + + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (ret) + return ret; + + if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN) + return -EIO; + + dev_params->bus_num = bus_num; + dev_params->dev_num = dev_num; + + req_id = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID); + dev_params->req_id = req_id; + + dev_params->res_count = 0; + if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) != 0) { + res[dev_params->res_count].start = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE); + res[dev_params->res_count].end = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE) + + MCDI_QWORD(outbuf, + CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) - 1; + res[dev_params->res_count].flags = IORESOURCE_MEM; + dev_params->res_count++; + } + + if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE) != 0) { + res[dev_params->res_count].start = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE); + res[dev_params->res_count].end = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE) + + MCDI_QWORD(outbuf, + CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE) - 1; + res[dev_params->res_count].flags = IORESOURCE_MEM; + dev_params->res_count++; + } + + if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE) != 0) { + res[dev_params->res_count].start = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE); + res[dev_params->res_count].end = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE) + + MCDI_QWORD(outbuf, + CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE) - 1; + res[dev_params->res_count].flags = IORESOURCE_MEM; + dev_params->res_count++; + } + + if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE) != 0) { + res[dev_params->res_count].start = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE); + res[dev_params->res_count].end = + MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE) + + MCDI_QWORD(outbuf, + CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE) - 1; + res[dev_params->res_count].flags = IORESOURCE_MEM; + dev_params->res_count++; + } + + dev_params->vendor = MCDI_WORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID); + dev_params->device = MCDI_WORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID); + + return 0; +} diff --git a/drivers/cdx/controller/mcdi_functions.h b/drivers/cdx/controller/mcdi_functions.h new file mode 100644 index 000000000000..6bf5a4a0778f --- /dev/null +++ b/drivers/cdx/controller/mcdi_functions.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Header file for MCDI FW interaction for CDX bus. + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef CDX_MCDI_FUNCTIONS_H +#define CDX_MCDI_FUNCTIONS_H + +#include "mcdi.h" +#include "../cdx.h" + +/** + * cdx_mcdi_get_num_buses - Get the total number of buses on + * the controller. + * @cdx: pointer to MCDI interface. + * + * Return: total number of buses available on the controller, + * <0 on failure + */ +int cdx_mcdi_get_num_buses(struct cdx_mcdi *cdx); + +/** + * cdx_mcdi_get_num_devs - Get the total number of devices on + * a particular bus of the controller. + * @cdx: pointer to MCDI interface. + * @bus_num: Bus number. + * + * Return: total number of devices available on the bus, <0 on failure + */ +int cdx_mcdi_get_num_devs(struct cdx_mcdi *cdx, int bus_num); + +/** + * cdx_mcdi_get_dev_config - Get configuration for a particular + * bus_num:dev_num + * @cdx: pointer to MCDI interface. + * @bus_num: Bus number. + * @dev_num: Device number. + * @dev_params: Pointer to cdx_dev_params, this is populated by this + * device with the configuration corresponding to the provided + * bus_num:dev_num. + * + * Return: 0 total number of devices available on the bus, <0 on failure + */ +int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, + u8 bus_num, u8 dev_num, + struct cdx_dev_params *dev_params); + +#endif /* CDX_MCDI_FUNCTIONS_H */ -- cgit v1.2.3 From 2a226927d9b836ddec674aebbcea311727d04240 Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:35 +0530 Subject: cdx: add rpmsg communication channel for CDX RPMsg is used as a transport communication channel. This change introduces RPMsg driver and integrates it with the CDX controller. Signed-off-by: Abhijit Gangurde Signed-off-by: Nipun Gupta Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-7-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/controller/Kconfig | 2 + drivers/cdx/controller/Makefile | 2 +- drivers/cdx/controller/cdx_controller.c | 32 ++++- drivers/cdx/controller/cdx_controller.h | 30 +++++ drivers/cdx/controller/cdx_rpmsg.c | 202 ++++++++++++++++++++++++++++++++ drivers/cdx/controller/mcdi.h | 10 ++ 6 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 drivers/cdx/controller/cdx_controller.h create mode 100644 drivers/cdx/controller/cdx_rpmsg.c diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig index 17f9c6be2fe1..c3e3b9ff8dfe 100644 --- a/drivers/cdx/controller/Kconfig +++ b/drivers/cdx/controller/Kconfig @@ -9,6 +9,8 @@ if CDX_BUS config CDX_CONTROLLER tristate "CDX bus controller" + select REMOTEPROC + select RPMSG help CDX controller drives the CDX bus. It interacts with firmware to get the hardware devices and registers with diff --git a/drivers/cdx/controller/Makefile b/drivers/cdx/controller/Makefile index f7437c882cc9..f071be411d96 100644 --- a/drivers/cdx/controller/Makefile +++ b/drivers/cdx/controller/Makefile @@ -6,4 +6,4 @@ # obj-$(CONFIG_CDX_CONTROLLER) += cdx-controller.o -cdx-controller-objs := cdx_controller.o mcdi.o mcdi_functions.o +cdx-controller-objs := cdx_controller.o cdx_rpmsg.o mcdi.o mcdi_functions.o diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index fbebc8cdcbf8..0d1826980935 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -9,6 +9,7 @@ #include #include +#include "cdx_controller.h" #include "../cdx.h" #include "mcdi_functions.h" #include "mcdi.h" @@ -22,10 +23,8 @@ static void cdx_mcdi_request(struct cdx_mcdi *cdx, const struct cdx_dword *hdr, size_t hdr_len, const struct cdx_dword *sdu, size_t sdu_len) { - /* - * This will get updated by rpmsg APIs, with RPMSG introduction - * in CDX controller as a transport layer. - */ + if (cdx_rpmsg_send(cdx, hdr, hdr_len, sdu, sdu_len)) + dev_err(&cdx->rpdev->dev, "Failed to send rpmsg data\n"); } static const struct cdx_mcdi_ops mcdi_ops = { @@ -33,6 +32,19 @@ static const struct cdx_mcdi_ops mcdi_ops = { .mcdi_request = cdx_mcdi_request, }; +void cdx_rpmsg_post_probe(struct cdx_controller *cdx) +{ + /* Register CDX controller with CDX bus driver */ + if (cdx_register_controller(cdx)) + dev_err(cdx->dev, "Failed to register CDX controller\n"); +} + +void cdx_rpmsg_pre_remove(struct cdx_controller *cdx) +{ + cdx_unregister_controller(cdx); + cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT); +} + static int cdx_scan_devices(struct cdx_controller *cdx) { struct cdx_mcdi *cdx_mcdi = cdx->priv; @@ -124,8 +136,18 @@ static int xlnx_cdx_probe(struct platform_device *pdev) cdx->priv = cdx_mcdi; cdx->ops = &cdx_ops; + ret = cdx_setup_rpmsg(pdev); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n"); + goto cdx_rpmsg_fail; + } + + dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n"); return 0; +cdx_rpmsg_fail: + kfree(cdx); cdx_alloc_fail: cdx_mcdi_finish(cdx_mcdi); mcdi_init_fail: @@ -139,6 +161,8 @@ static int xlnx_cdx_remove(struct platform_device *pdev) struct cdx_controller *cdx = platform_get_drvdata(pdev); struct cdx_mcdi *cdx_mcdi = cdx->priv; + cdx_destroy_rpmsg(pdev); + kfree(cdx); cdx_mcdi_finish(cdx_mcdi); diff --git a/drivers/cdx/controller/cdx_controller.h b/drivers/cdx/controller/cdx_controller.h new file mode 100644 index 000000000000..43b7c742df87 --- /dev/null +++ b/drivers/cdx/controller/cdx_controller.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Header file for the CDX Controller + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#ifndef _CDX_CONTROLLER_H_ +#define _CDX_CONTROLLER_H_ + +#include +#include "mcdi_functions.h" + +void cdx_rpmsg_post_probe(struct cdx_controller *cdx); + +void cdx_rpmsg_pre_remove(struct cdx_controller *cdx); + +int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len); + +void cdx_rpmsg_read_resp(struct cdx_mcdi *cdx_mcdi, + struct cdx_dword *outbuf, size_t offset, + size_t outlen); + +int cdx_setup_rpmsg(struct platform_device *pdev); + +void cdx_destroy_rpmsg(struct platform_device *pdev); + +#endif /* _CDX_CONT_PRIV_H_ */ diff --git a/drivers/cdx/controller/cdx_rpmsg.c b/drivers/cdx/controller/cdx_rpmsg.c new file mode 100644 index 000000000000..f37e639d6ce3 --- /dev/null +++ b/drivers/cdx/controller/cdx_rpmsg.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Platform driver for CDX bus. + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include + +#include "../cdx.h" +#include "cdx_controller.h" +#include "mcdi_functions.h" +#include "mcdi.h" + +static struct rpmsg_device_id cdx_rpmsg_id_table[] = { + { .name = "mcdi_ipc" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, cdx_rpmsg_id_table); + +int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi, + const struct cdx_dword *hdr, size_t hdr_len, + const struct cdx_dword *sdu, size_t sdu_len) +{ + unsigned char *send_buf; + int ret; + + send_buf = kzalloc(hdr_len + sdu_len, GFP_KERNEL); + if (!send_buf) + return -ENOMEM; + + memcpy(send_buf, hdr, hdr_len); + memcpy(send_buf + hdr_len, sdu, sdu_len); + + ret = rpmsg_send(cdx_mcdi->ept, send_buf, hdr_len + sdu_len); + kfree(send_buf); + + return ret; +} + +static int cdx_attach_to_rproc(struct platform_device *pdev) +{ + struct device_node *r5_core_node; + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + struct device *dev; + struct rproc *rp; + int ret; + + dev = &pdev->dev; + cdx_c = platform_get_drvdata(pdev); + cdx_mcdi = cdx_c->priv; + + r5_core_node = of_parse_phandle(dev->of_node, "xlnx,rproc", 0); + if (!r5_core_node) { + dev_err(&pdev->dev, "xlnx,rproc: invalid phandle\n"); + return -EINVAL; + } + + rp = rproc_get_by_phandle(r5_core_node->phandle); + if (!rp) { + ret = -EPROBE_DEFER; + goto pdev_err; + } + + /* Attach to remote processor */ + ret = rproc_boot(rp); + if (ret) { + dev_err(&pdev->dev, "Failed to attach to remote processor\n"); + rproc_put(rp); + goto pdev_err; + } + + cdx_mcdi->r5_rproc = rp; +pdev_err: + of_node_put(r5_core_node); + return ret; +} + +static void cdx_detach_to_r5(struct platform_device *pdev) +{ + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + + cdx_c = platform_get_drvdata(pdev); + cdx_mcdi = cdx_c->priv; + + rproc_detach(cdx_mcdi->r5_rproc); + rproc_put(cdx_mcdi->r5_rproc); +} + +static int cdx_rpmsg_cb(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 src) +{ + struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev); + struct cdx_mcdi *cdx_mcdi = cdx_c->priv; + + if (len > MCDI_BUF_LEN) + return -EINVAL; + + cdx_mcdi_process_cmd(cdx_mcdi, (struct cdx_dword *)data, len); + + return 0; +} + +static void cdx_rpmsg_post_probe_work(struct work_struct *work) +{ + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + + cdx_mcdi = container_of(work, struct cdx_mcdi, work); + cdx_c = dev_get_drvdata(&cdx_mcdi->rpdev->dev); + cdx_rpmsg_post_probe(cdx_c); +} + +static int cdx_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_channel_info chinfo = {0}; + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + + cdx_c = (struct cdx_controller *)cdx_rpmsg_id_table[0].driver_data; + cdx_mcdi = cdx_c->priv; + + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = rpdev->dst; + strscpy(chinfo.name, cdx_rpmsg_id_table[0].name, + strlen(cdx_rpmsg_id_table[0].name)); + + cdx_mcdi->ept = rpmsg_create_ept(rpdev, cdx_rpmsg_cb, NULL, chinfo); + if (!cdx_mcdi->ept) { + dev_err_probe(&rpdev->dev, -ENXIO, + "Failed to create ept for channel %s\n", + chinfo.name); + return -EINVAL; + } + + cdx_mcdi->rpdev = rpdev; + dev_set_drvdata(&rpdev->dev, cdx_c); + + schedule_work(&cdx_mcdi->work); + return 0; +} + +static void cdx_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev); + struct cdx_mcdi *cdx_mcdi = cdx_c->priv; + + flush_work(&cdx_mcdi->work); + cdx_rpmsg_pre_remove(cdx_c); + + rpmsg_destroy_ept(cdx_mcdi->ept); + dev_set_drvdata(&rpdev->dev, NULL); +} + +static struct rpmsg_driver cdx_rpmsg_driver = { + .drv.name = KBUILD_MODNAME, + .id_table = cdx_rpmsg_id_table, + .probe = cdx_rpmsg_probe, + .remove = cdx_rpmsg_remove, + .callback = cdx_rpmsg_cb, +}; + +int cdx_setup_rpmsg(struct platform_device *pdev) +{ + struct cdx_controller *cdx_c; + struct cdx_mcdi *cdx_mcdi; + int ret; + + /* Attach to remote processor */ + ret = cdx_attach_to_rproc(pdev); + if (ret) + return ret; + + cdx_c = platform_get_drvdata(pdev); + cdx_mcdi = cdx_c->priv; + + /* Register RPMsg driver */ + cdx_rpmsg_id_table[0].driver_data = (kernel_ulong_t)cdx_c; + + INIT_WORK(&cdx_mcdi->work, cdx_rpmsg_post_probe_work); + ret = register_rpmsg_driver(&cdx_rpmsg_driver); + if (ret) { + dev_err(&pdev->dev, + "Failed to register cdx RPMsg driver: %d\n", ret); + cdx_detach_to_r5(pdev); + } + + return ret; +} + +void cdx_destroy_rpmsg(struct platform_device *pdev) +{ + unregister_rpmsg_driver(&cdx_rpmsg_driver); + + cdx_detach_to_r5(pdev); +} diff --git a/drivers/cdx/controller/mcdi.h b/drivers/cdx/controller/mcdi.h index 63933ede33ed..0bfbeab04e43 100644 --- a/drivers/cdx/controller/mcdi.h +++ b/drivers/cdx/controller/mcdi.h @@ -9,6 +9,7 @@ #include #include +#include #include "bitfield.h" #include "mc_cdx_pcol.h" @@ -62,11 +63,20 @@ enum cdx_mcdi_cmd_state { * with CDX controller. * @mcdi: MCDI interface * @mcdi_ops: MCDI operations + * @r5_rproc : R5 Remoteproc device handle + * @rpdev: RPMsg device + * @ept: RPMsg endpoint + * @work: Post probe work */ struct cdx_mcdi { /* MCDI interface */ struct cdx_mcdi_data *mcdi; const struct cdx_mcdi_ops *mcdi_ops; + + struct rproc *r5_rproc; + struct rpmsg_device *rpdev; + struct rpmsg_endpoint *ept; + struct work_struct work; }; struct cdx_mcdi_ops { -- cgit v1.2.3 From 48a6c7bced2a781c911497f073347bec5ab4d7a5 Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 13 Mar 2023 18:56:36 +0530 Subject: cdx: add device attributes Create sysfs entry for CDX devices. Sysfs entries provided in each of the CDX device detected by the CDX controller - vendor id - device id - remove - reset of the device. - driver override Signed-off-by: Puneet Gupta Signed-off-by: Nipun Gupta Signed-off-by: Tarak Reddy Reviewed-by: Pieter Jansen van Vuuren Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20230313132636.31850-8-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-cdx | 44 +++++++++++ drivers/cdx/cdx.c | 127 ++++++++++++++++++++++++++++++++ drivers/cdx/controller/cdx_controller.c | 18 +++++ drivers/cdx/controller/mcdi_functions.c | 14 ++++ drivers/cdx/controller/mcdi_functions.h | 11 +++ include/linux/cdx/cdx_bus.h | 27 +++++++ 6 files changed, 241 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-cdx b/Documentation/ABI/testing/sysfs-bus-cdx index 43b4e161f226..7af477f49998 100644 --- a/Documentation/ABI/testing/sysfs-bus-cdx +++ b/Documentation/ABI/testing/sysfs-bus-cdx @@ -10,3 +10,47 @@ Description: For example:: # echo 1 > /sys/bus/cdx/rescan + +What: /sys/bus/cdx/devices/.../vendor +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Vendor ID for this CDX device, in hexadecimal. Vendor ID is + 16 bit identifier which is specific to the device manufacturer. + Combination of Vendor ID and Device ID identifies a device. + +What: /sys/bus/cdx/devices/.../device +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Device ID for this CDX device, in hexadecimal. Device ID is + 16 bit identifier to identify a device type within the range + of a device manufacturer. + Combination of Vendor ID and Device ID identifies a device. + +What: /sys/bus/cdx/devices/.../reset +Date: March 2023 +Contact: nipun.gupta@amd.com +Description: + Writing y/1/on to this file resets the CDX device. + On resetting the device, the corresponding driver is notified + twice, once before the device is being reset, and again after + the reset has been complete. + + For example:: + + # echo 1 > /sys/bus/cdx/.../reset + +What: /sys/bus/cdx/devices/.../remove +Date: March 2023 +Contact: tarak.reddy@amd.com +Description: + Writing y/1/on to this file removes the corresponding + device from the CDX bus. If the device is to be reconfigured + reconfigured in the Hardware, the device can be removed, so + that the device driver does not access the device while it is + being reconfigured. + + For example:: + + # echo 1 > /sys/bus/cdx/devices/.../remove diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 6b162bbb9b25..67c32cb2c006 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -71,6 +71,39 @@ /* CDX controllers registered with the CDX bus */ static DEFINE_XARRAY_ALLOC(cdx_controllers); +/** + * cdx_dev_reset - Reset a CDX device + * @dev: CDX device + * + * Return: -errno on failure, 0 on success. + */ +int cdx_dev_reset(struct device *dev) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + struct cdx_controller *cdx = cdx_dev->cdx; + struct cdx_device_config dev_config = {0}; + struct cdx_driver *cdx_drv; + int ret; + + cdx_drv = to_cdx_driver(dev->driver); + /* Notify driver that device is being reset */ + if (cdx_drv && cdx_drv->reset_prepare) + cdx_drv->reset_prepare(cdx_dev); + + dev_config.type = CDX_DEV_RESET_CONF; + ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num, + cdx_dev->dev_num, &dev_config); + if (ret) + dev_err(dev, "cdx device reset failed\n"); + + /* Notify driver that device reset is complete */ + if (cdx_drv && cdx_drv->reset_done) + cdx_drv->reset_done(cdx_dev); + + return ret; +} +EXPORT_SYMBOL_GPL(cdx_dev_reset); + /** * cdx_unregister_device - Unregister a CDX device * @dev: CDX device @@ -237,6 +270,99 @@ static int cdx_dma_configure(struct device *dev) return 0; } +/* show configuration fields */ +#define cdx_config_attr(field, format_string) \ +static ssize_t \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct cdx_device *cdx_dev = to_cdx_device(dev); \ + return sysfs_emit(buf, format_string, cdx_dev->field); \ +} \ +static DEVICE_ATTR_RO(field) + +cdx_config_attr(vendor, "0x%04x\n"); +cdx_config_attr(device, "0x%04x\n"); + +static ssize_t remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool val; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + if (!val) + return -EINVAL; + + if (device_remove_file_self(dev, attr)) { + int ret; + + ret = cdx_unregister_device(dev, NULL); + if (ret) + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(remove); + +static ssize_t reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + bool val; + int ret; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + if (!val) + return -EINVAL; + + ret = cdx_dev_reset(dev); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + int ret; + + if (WARN_ON(dev->bus != &cdx_bus_type)) + return -EINVAL; + + ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count); + if (ret) + return ret; + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + + return sysfs_emit(buf, "%s\n", cdx_dev->driver_override); +} +static DEVICE_ATTR_RW(driver_override); + +static struct attribute *cdx_dev_attrs[] = { + &dev_attr_remove.attr, + &dev_attr_reset.attr, + &dev_attr_vendor.attr, + &dev_attr_device.attr, + &dev_attr_driver_override.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cdx_dev); + static ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count) { @@ -280,6 +406,7 @@ struct bus_type cdx_bus_type = { .shutdown = cdx_shutdown, .dma_configure = cdx_dma_configure, .bus_groups = cdx_bus_groups, + .dev_groups = cdx_dev_groups, }; EXPORT_SYMBOL_GPL(cdx_bus_type); diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index 0d1826980935..dc52f95f8978 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -45,6 +45,23 @@ void cdx_rpmsg_pre_remove(struct cdx_controller *cdx) cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT); } +static int cdx_configure_device(struct cdx_controller *cdx, + u8 bus_num, u8 dev_num, + struct cdx_device_config *dev_config) +{ + int ret = 0; + + switch (dev_config->type) { + case CDX_DEV_RESET_CONF: + ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num); + break; + default: + ret = -EINVAL; + } + + return ret; +} + static int cdx_scan_devices(struct cdx_controller *cdx) { struct cdx_mcdi *cdx_mcdi = cdx->priv; @@ -104,6 +121,7 @@ static int cdx_scan_devices(struct cdx_controller *cdx) static struct cdx_ops cdx_ops = { .scan = cdx_scan_devices, + .dev_configure = cdx_configure_device, }; static int xlnx_cdx_probe(struct platform_device *pdev) diff --git a/drivers/cdx/controller/mcdi_functions.c b/drivers/cdx/controller/mcdi_functions.c index 012b52881dd5..0158f26533dd 100644 --- a/drivers/cdx/controller/mcdi_functions.c +++ b/drivers/cdx/controller/mcdi_functions.c @@ -123,3 +123,17 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, return 0; } + +int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN); + int ret; + + MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_BUS, bus_num); + MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_DEVICE, dev_num); + + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_RESET, inbuf, sizeof(inbuf), + NULL, 0, NULL); + + return ret; +} diff --git a/drivers/cdx/controller/mcdi_functions.h b/drivers/cdx/controller/mcdi_functions.h index 6bf5a4a0778f..7440ace5539a 100644 --- a/drivers/cdx/controller/mcdi_functions.h +++ b/drivers/cdx/controller/mcdi_functions.h @@ -47,4 +47,15 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, struct cdx_dev_params *dev_params); +/** + * cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num + * @cdx: pointer to MCDI interface. + * @bus_num: Bus number. + * @dev_num: Device number. + * + * Return: 0 on success, <0 on failure + */ +int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, + u8 bus_num, u8 dev_num); + #endif /* CDX_MCDI_FUNCTIONS_H */ diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h index d134e0104724..35ef41d8a61a 100644 --- a/include/linux/cdx/cdx_bus.h +++ b/include/linux/cdx/cdx_bus.h @@ -21,8 +21,20 @@ /* Forward declaration for CDX controller */ struct cdx_controller; +enum { + CDX_DEV_RESET_CONF, +}; + +struct cdx_device_config { + u8 type; +}; + typedef int (*cdx_scan_cb)(struct cdx_controller *cdx); +typedef int (*cdx_dev_configure_cb)(struct cdx_controller *cdx, + u8 bus_num, u8 dev_num, + struct cdx_device_config *dev_config); + /** * CDX_DEVICE_DRIVER_OVERRIDE - macro used to describe a CDX device with * override_only flags. @@ -39,9 +51,12 @@ typedef int (*cdx_scan_cb)(struct cdx_controller *cdx); /** * struct cdx_ops - Callbacks supported by CDX controller. * @scan: scan the devices on the controller + * @dev_configure: configuration like reset, master_enable, + * msi_config etc for a CDX device */ struct cdx_ops { cdx_scan_cb scan; + cdx_dev_configure_cb dev_configure; }; /** @@ -101,6 +116,8 @@ struct cdx_device { * @probe: Function called when a device is added * @remove: Function called when a device is removed * @shutdown: Function called at shutdown time to quiesce the device + * @reset_prepare: Function called before is reset to notify driver + * @reset_done: Function called after reset is complete to notify driver * @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA. * For most device drivers, no need to care about this flag * as long as all DMAs are handled through the kernel DMA API. @@ -115,6 +132,8 @@ struct cdx_driver { int (*probe)(struct cdx_device *dev); int (*remove)(struct cdx_device *dev); void (*shutdown)(struct cdx_device *dev); + void (*reset_prepare)(struct cdx_device *dev); + void (*reset_done)(struct cdx_device *dev); bool driver_managed_dma; }; @@ -144,4 +163,12 @@ void cdx_driver_unregister(struct cdx_driver *cdx_driver); extern struct bus_type cdx_bus_type; +/** + * cdx_dev_reset - Reset CDX device + * @dev: device pointer + * + * Return: 0 for success, -errno on failure + */ +int cdx_dev_reset(struct device *dev); + #endif /* _CDX_BUS_H_ */ -- cgit v1.2.3 From 6a0c637bfee69a74c104468544d9f2a6579626d0 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 24 Mar 2023 10:13:04 -0600 Subject: bus: mhi: host: Range check CHDBOFF and ERDBOFF If the value read from the CHDBOFF and ERDBOFF registers is outside the range of the MHI register space then an invalid address might be computed which later causes a kernel panic. Range check the read value to prevent a crash due to bad data from the device. Fixes: 6cd330ae76ff ("bus: mhi: core: Add support for ringing channel/event ring doorbells") Cc: stable@vger.kernel.org Signed-off-by: Jeffrey Hugo Reviewed-by: Pranjal Ramajor Asha Kanojiya Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1679674384-27209-1-git-send-email-quic_jhugo@quicinc.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/init.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 3d779ee6396d..b46a0821adf9 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -516,6 +516,12 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) return -EIO; } + if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) { + dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n", + val, mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)); + return -ERANGE; + } + /* Setup wake db */ mhi_cntrl->wake_db = base + val + (8 * MHI_DEV_WAKE_DB); mhi_cntrl->wake_set = false; @@ -532,6 +538,12 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) return -EIO; } + if (val >= mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings)) { + dev_err(dev, "ERDB offset: 0x%x is out of range: 0x%zx\n", + val, mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings)); + return -ERANGE; + } + /* Setup event db address for each ev_ring */ mhi_event = mhi_cntrl->mhi_event; for (i = 0; i < mhi_cntrl->total_ev_rings; i++, val += 8, mhi_event++) { -- cgit v1.2.3 From 446271e5d7bcb5f1c70bc35b11abbd92e6bb4378 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 24 Mar 2023 10:21:00 -0600 Subject: bus: mhi: host: Use ERANGE for BHIOFF/BHIEOFF range check If the BHIOFF or BHIEOFF range checks fail, they return EINVAL. ERANGE is a better error code since it implies an out of range condition. Suggested-by: Manivannan Sadhasivam Signed-off-by: Jeffrey Hugo Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1679674860-28229-1-git-send-email-quic_jhugo@quicinc.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index b46a0821adf9..f72fcb66f408 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -1112,7 +1112,7 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) if (bhi_off >= mhi_cntrl->reg_len) { dev_err(dev, "BHI offset: 0x%x is out of range: 0x%zx\n", bhi_off, mhi_cntrl->reg_len); - ret = -EINVAL; + ret = -ERANGE; goto error_reg_offset; } mhi_cntrl->bhi = mhi_cntrl->regs + bhi_off; @@ -1129,7 +1129,7 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) dev_err(dev, "BHIe offset: 0x%x is out of range: 0x%zx\n", bhie_off, mhi_cntrl->reg_len); - ret = -EINVAL; + ret = -ERANGE; goto error_reg_offset; } mhi_cntrl->bhie = mhi_cntrl->regs + bhie_off; -- cgit v1.2.3 From c2dbd34f6a9558b7e99849d4f73eb9b95a45a83c Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Wed, 29 Mar 2023 15:22:39 +0800 Subject: bus: mhi: pci_generic: Add Foxconn T99W510 The Foxconn T99W510 device is designed based on Qualcomm SDX24. Add 3 variants for different potential customer. Signed-off-by: Slark Xiao Link: https://lore.kernel.org/r/20230329072239.93632-1-slark_xiao@163.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/pci_generic.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 91d02f84ba21..db0a0b062d8e 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -363,6 +363,15 @@ static const struct mhi_controller_config modem_foxconn_sdx55_config = { .event_cfg = mhi_foxconn_sdx55_events, }; +static const struct mhi_pci_dev_info mhi_foxconn_sdx24_info = { + .name = "foxconn-sdx24", + .config = &modem_foxconn_sdx55_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = false, +}; + static const struct mhi_pci_dev_info mhi_foxconn_sdx55_info = { .name = "foxconn-sdx55", .fw = "qcom/sdx55m/sbl1.mbn", @@ -587,6 +596,15 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* T99W373 (sdx62) */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0d9), .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info }, + /* T99W510 (sdx24), variant 1 */ + { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f0), + .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info }, + /* T99W510 (sdx24), variant 2 */ + { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f1), + .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info }, + /* T99W510 (sdx24), variant 3 */ + { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f2), + .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info }, /* MV31-W (Cinterion) */ { PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00b3), .driver_data = (kernel_ulong_t) &mhi_mv31_info }, -- cgit v1.2.3 From b2d110cd5d28ded813f060b735cd68bfe2e5010f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 6 Mar 2023 08:56:51 +0100 Subject: interconnect: drop unused icc_link_destroy() interface Now that the link array is deallocated when destroying nodes and the explicit link removal has been dropped from the exynos driver there are no further users of and no need for the icc_link_destroy() interface. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20230306075651.2449-24-johan+linaro@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 46 ----------------------------------- include/linux/interconnect-provider.h | 6 ----- 2 files changed, 52 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index da456329c524..0edf85ba055e 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -910,52 +910,6 @@ out: } EXPORT_SYMBOL_GPL(icc_link_create); -/** - * icc_link_destroy() - destroy a link between two nodes - * @src: pointer to source node - * @dst: pointer to destination node - * - * Return: 0 on success, or an error code otherwise - */ -int icc_link_destroy(struct icc_node *src, struct icc_node *dst) -{ - struct icc_node **new; - size_t slot; - int ret = 0; - - if (IS_ERR_OR_NULL(src)) - return -EINVAL; - - if (IS_ERR_OR_NULL(dst)) - return -EINVAL; - - mutex_lock(&icc_lock); - - for (slot = 0; slot < src->num_links; slot++) - if (src->links[slot] == dst) - break; - - if (WARN_ON(slot == src->num_links)) { - ret = -ENXIO; - goto out; - } - - src->links[slot] = src->links[--src->num_links]; - - new = krealloc(src->links, src->num_links * sizeof(*src->links), - GFP_KERNEL); - if (new) - src->links = new; - else - ret = -ENOMEM; - -out: - mutex_unlock(&icc_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(icc_link_destroy); - /** * icc_node_add() - add interconnect node to interconnect provider * @node: pointer to the interconnect node diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index b9af9016a95e..e6d8aca6886d 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -118,7 +118,6 @@ int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, struct icc_node *icc_node_create(int id); void icc_node_destroy(int id); int icc_link_create(struct icc_node *node, const int dst_id); -int icc_link_destroy(struct icc_node *src, struct icc_node *dst); void icc_node_add(struct icc_node *node, struct icc_provider *provider); void icc_node_del(struct icc_node *node); int icc_nodes_remove(struct icc_provider *provider); @@ -150,11 +149,6 @@ static inline int icc_link_create(struct icc_node *node, const int dst_id) return -ENOTSUPP; } -static inline int icc_link_destroy(struct icc_node *src, struct icc_node *dst) -{ - return -ENOTSUPP; -} - static inline void icc_node_add(struct icc_node *node, struct icc_provider *provider) { } -- cgit v1.2.3 From 72b2720c18ecde92e6a36c4ac897dd5848e3f379 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 13 Mar 2023 09:49:53 +0100 Subject: interconnect: qcom: rpm: drop bogus pm domain attach Any power domain would already have been attached by the platform bus code so drop the bogus power domain attach which always succeeds from probe. This effectively reverts commit 7de109c0abe9 ("interconnect: icc-rpm: Add support for bus power domain"). Fixes: 7de109c0abe9 ("interconnect: icc-rpm: Add support for bus power domain") Cc: Yassine Oudjana Signed-off-by: Johan Hovold Tested-by: Konrad Dybcio # MSM8996 Sony Kagura Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230313084953.24088-3-johan+linaro@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 7 ------- drivers/interconnect/qcom/icc-rpm.h | 1 - drivers/interconnect/qcom/msm8996.c | 1 - 3 files changed, 9 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 4180a06681b2..c80819557923 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -496,12 +495,6 @@ regmap_done: if (ret) return ret; - if (desc->has_bus_pd) { - ret = dev_pm_domain_attach(dev, true); - if (ret) - return ret; - } - provider = &qp->provider; provider->dev = dev; provider->set = qcom_icc_set; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index a49af844ab13..02257b0d3d5c 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -91,7 +91,6 @@ struct qcom_icc_desc { size_t num_nodes; const char * const *clocks; size_t num_clocks; - bool has_bus_pd; enum qcom_icc_type type; const struct regmap_config *regmap_cfg; unsigned int qos_offset; diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index 25a1a32bc611..14efd2761b7a 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -1823,7 +1823,6 @@ static const struct qcom_icc_desc msm8996_a0noc = { .num_nodes = ARRAY_SIZE(a0noc_nodes), .clocks = bus_a0noc_clocks, .num_clocks = ARRAY_SIZE(bus_a0noc_clocks), - .has_bus_pd = true, .regmap_cfg = &msm8996_a0noc_regmap_config }; -- cgit v1.2.3 From 1d779317eb6529d89bfe6d7900a2cf9e03149aeb Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 8 Mar 2023 22:40:07 +0100 Subject: interconnect: qcom: rpm: make QoS INVALID default Currently NOC_QOS_MODE_FIXED is defined as 0x0 which makes it the default option (partial struct initialization). The default option however should be NOC_QOS_MODE_INVALID. That results in bogus QoS configurations being sent for port 0 (which is used for the DRAM endpoint on BIMC, for example) coming from all nodes with .qos.ap_owned = true and uninitialized .qos.qos_mode. It's also an issue for newer SoCs where all nodes are treated as if they were ap_owned, but not all of them have QoS configuration. The NOC_QOS_MODEs are defined as preprocessor constants and are not used anywhere outside qcom_icc_set_noc_qos(), which is easily worked around. Separate the desc->type values from the values sent to msmbus in the aforementioned function. Make the former an enum for better mainainability. Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230228-topic-qos-v7-1-815606092fff@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 24 +++++++++++++----------- drivers/interconnect/qcom/icc-rpm.h | 10 ++++++---- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index c80819557923..56f29e0f0174 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -47,6 +47,9 @@ #define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) #define NOC_QOS_MODEn_MASK 0x3 +#define NOC_QOS_MODE_FIXED_VAL 0x0 +#define NOC_QOS_MODE_BYPASS_VAL 0x2 + static int qcom_icc_set_qnoc_qos(struct icc_node *src, u64 max_bw) { struct icc_provider *provider = src->provider; @@ -152,7 +155,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) struct qcom_icc_provider *qp; struct qcom_icc_node *qn; struct icc_provider *provider; - u32 mode = NOC_QOS_MODE_BYPASS; + u32 mode = NOC_QOS_MODE_BYPASS_VAL; int rc = 0; qn = src->data; @@ -166,18 +169,17 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) return 0; } - if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) - mode = qn->qos.qos_mode; - - if (mode == NOC_QOS_MODE_FIXED) { - dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", - qn->name); + if (qn->qos.qos_mode == NOC_QOS_MODE_FIXED) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", qn->name); + mode = NOC_QOS_MODE_FIXED_VAL; rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos); if (rc) return rc; - } else if (mode == NOC_QOS_MODE_BYPASS) { - dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", - qn->name); + } else if (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", qn->name); + mode = NOC_QOS_MODE_BYPASS_VAL; + } else { + /* How did we get here? */ } return regmap_update_bits(qp->regmap, @@ -243,7 +245,7 @@ static int __qcom_icc_set(struct icc_node *n, struct qcom_icc_node *qn, ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); if (ret) return ret; - } else if (qn->qos.qos_mode != -1) { + } else if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) { /* set bandwidth directly from the AP */ ret = qcom_icc_qos_set(n, sum_bw); if (ret) diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 02257b0d3d5c..d54f76d8b3fd 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -96,10 +96,12 @@ struct qcom_icc_desc { unsigned int qos_offset; }; -/* Valid for both NoC and BIMC */ -#define NOC_QOS_MODE_INVALID -1 -#define NOC_QOS_MODE_FIXED 0x0 -#define NOC_QOS_MODE_BYPASS 0x2 +/* Valid for all bus types */ +enum qos_mode { + NOC_QOS_MODE_INVALID = 0, + NOC_QOS_MODE_FIXED, + NOC_QOS_MODE_BYPASS, +}; int qnoc_probe(struct platform_device *pdev); int qnoc_remove(struct platform_device *pdev); -- cgit v1.2.3 From 02819953b33a4410e6aa4595a043c052408c90d3 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 8 Mar 2023 22:40:08 +0100 Subject: interconnect: qcom: rpm: Add support for specifying channel num Some nodes, like EBI0 (DDR) or L3/LLCC, may be connected over more than one channel. This should be taken into account in bandwidth calcualtion, as we're supposed to feed msmbus with the per-channel bandwidth. Add support for specifying that and use it during bandwidth aggregation. Reviewed-by: Bryan O'Donoghue Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230228-topic-qos-v7-2-815606092fff@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 7 ++++++- drivers/interconnect/qcom/icc-rpm.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 56f29e0f0174..5341fa169dbf 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -316,6 +316,7 @@ static void qcom_icc_bus_aggregate(struct icc_provider *provider, { struct icc_node *node; struct qcom_icc_node *qn; + u64 sum_avg[QCOM_ICC_NUM_BUCKETS]; int i; /* Initialise aggregate values */ @@ -333,7 +334,11 @@ static void qcom_icc_bus_aggregate(struct icc_provider *provider, list_for_each_entry(node, &provider->nodes, node_list) { qn = node->data; for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { - agg_avg[i] += qn->sum_avg[i]; + if (qn->channels) + sum_avg[i] = div_u64(qn->sum_avg[i], qn->channels); + else + sum_avg[i] = qn->sum_avg[i]; + agg_avg[i] += sum_avg[i]; agg_peak[i] = max_t(u64, agg_peak[i], qn->max_peak[i]); } } diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index d54f76d8b3fd..12566014c179 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -66,6 +66,7 @@ struct qcom_icc_qos { * @id: a unique node identifier * @links: an array of nodes where we can go next while traversing * @num_links: the total number of @links + * @channels: number of channels at this node (e.g. DDR channels) * @buswidth: width of the interconnect between a node and the bus (bytes) * @sum_avg: current sum aggregate value of all avg bw requests * @max_peak: current max aggregate value of all peak bw requests @@ -78,6 +79,7 @@ struct qcom_icc_node { u16 id; const u16 *links; u16 num_links; + u16 channels; u16 buswidth; u64 sum_avg[QCOM_ICC_NUM_BUCKETS]; u64 max_peak[QCOM_ICC_NUM_BUCKETS]; -- cgit v1.2.3 From 82a4b285abcaa3631446e7e4103ef61d0787362d Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 8 Mar 2023 22:40:09 +0100 Subject: interconnect: qcom: Sort kerneldoc entries Sort the kerneldoc entries the same way the struct members are sorted. Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230228-topic-qos-v7-3-815606092fff@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 12566014c179..22bdb1e4bb12 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -23,12 +23,12 @@ enum qcom_icc_type { /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider - * @bus_clks: the clk_bulk_data table of bus clocks * @num_clks: the total number of clk_bulk_data entries * @type: the ICC provider type - * @qos_offset: offset to QoS registers * @regmap: regmap for QoS registers read/write access + * @qos_offset: offset to QoS registers * @bus_clk_rate: bus clock rate in Hz + * @bus_clks: the clk_bulk_data table of bus clocks */ struct qcom_icc_provider { struct icc_provider provider; -- cgit v1.2.3 From e0c35141f9ca5869a51d7c135688350c06ebdded Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 16 Mar 2023 15:12:51 +0100 Subject: dt-bindings: interconnect: OSM L3: Add SM6375 CPUCP compatible SM6375 includes a CPUCP block responsible for managing different APSS- related tasks, such as scaling the voltage and frequency of the components within the ARM DSU cluster. Add a compatible for the L3 cache DVFS scaler within. Acked-by: Krzysztof Kozlowski Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230303-topic-sm6375_features0_dts-v2-2-708b8191f7eb@linaro.org Signed-off-by: Georgi Djakov --- Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index 576992a6dc5a..9d0a98d77ae9 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -29,6 +29,7 @@ properties: - enum: - qcom,sc7280-epss-l3 - qcom,sc8280xp-epss-l3 + - qcom,sm6375-cpucp-l3 - qcom,sm8250-epss-l3 - qcom,sm8350-epss-l3 - const: qcom,epss-l3 -- cgit v1.2.3 From b7590f3841f47d5c6732db9a6d6474162109f487 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 15 Mar 2023 15:11:19 +0100 Subject: dt-bindings: interconnect: qcom,msm8998-bwmon: Resolve MSM8998 support BWMONv4 has two sets of registers: one for handling the monitor itself and one called "global" which hosts some sort of a headswitch and an interrupt control register. We did not handle that one before, as on SoCs starting with SDM845 they have been merged into a single contiguous range. To make the qcom,msm8998-bwmon less confusing and in preparation for actual MSM8998 support, describe the global register space and introduce new "qcom,sdm845-cpu-bwmon" compatible while keeping the "qcom,sdm845-bwmon" as a fallback for SoCs with this merged register space scheme. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230304-topic-ddr_bwmon-v3-1-77a050c2fbda@linaro.org Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,msm8998-bwmon.yaml | 41 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 12a0d3ecbabb..5d17bdcfdf70 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -22,14 +22,14 @@ description: | properties: compatible: oneOf: + - const: qcom,msm8998-bwmon # BWMON v4 - items: - enum: - qcom,sc7280-cpu-bwmon - qcom,sc8280xp-cpu-bwmon - - qcom,sdm845-bwmon + - qcom,sdm845-cpu-bwmon - qcom,sm8550-cpu-bwmon - - const: qcom,msm8998-bwmon - - const: qcom,msm8998-bwmon # BWMON v4 + - const: qcom,sdm845-bwmon # BWMON v4, unified register space - items: - enum: - qcom,sc8280xp-llcc-bwmon @@ -49,9 +49,13 @@ properties: type: object reg: - # BWMON v4 (currently described) and BWMON v5 use one register address - # space. BWMON v2 uses two register spaces - not yet described. - maxItems: 1 + # BWMON v5 uses one register address space, v1-v4 use one or two. + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + maxItems: 2 required: - compatible @@ -63,13 +67,36 @@ required: additionalProperties: false +allOf: + - if: + properties: + compatible: + const: qcom,msm8998-bwmon + then: + properties: + reg: + minItems: 2 + + reg-names: + items: + - const: monitor + - const: global + + else: + properties: + reg: + maxItems: 1 + + reg-names: + maxItems: 1 + examples: - | #include #include pmu@1436400 { - compatible = "qcom,sdm845-bwmon", "qcom,msm8998-bwmon"; + compatible = "qcom,sdm845-cpu-bwmon", "qcom,sdm845-bwmon"; reg = <0x01436400 0x600>; interrupts = ; interconnects = <&gladiator_noc MASTER_APPSS_PROC 3 &mem_noc SLAVE_LLCC 3>; -- cgit v1.2.3 From ae13381da5ff0e8e084c0323c3cc0a945e43e9c7 Mon Sep 17 00:00:00 2001 From: "Dae R. Jeong" Date: Mon, 27 Mar 2023 21:01:53 +0900 Subject: vmci_host: fix a race condition in vmci_host_poll() causing GPF During fuzzing, a general protection fault is observed in vmci_host_poll(). general protection fault, probably for non-canonical address 0xdffffc0000000019: 0000 [#1] PREEMPT SMP KASAN KASAN: null-ptr-deref in range [0x00000000000000c8-0x00000000000000cf] RIP: 0010:__lock_acquire+0xf3/0x5e00 kernel/locking/lockdep.c:4926 <- omitting registers -> Call Trace: lock_acquire+0x1a4/0x4a0 kernel/locking/lockdep.c:5672 __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:110 [inline] _raw_spin_lock_irqsave+0xb3/0x100 kernel/locking/spinlock.c:162 add_wait_queue+0x3d/0x260 kernel/sched/wait.c:22 poll_wait include/linux/poll.h:49 [inline] vmci_host_poll+0xf8/0x2b0 drivers/misc/vmw_vmci/vmci_host.c:174 vfs_poll include/linux/poll.h:88 [inline] do_pollfd fs/select.c:873 [inline] do_poll fs/select.c:921 [inline] do_sys_poll+0xc7c/0x1aa0 fs/select.c:1015 __do_sys_ppoll fs/select.c:1121 [inline] __se_sys_ppoll+0x2cc/0x330 fs/select.c:1101 do_syscall_x64 arch/x86/entry/common.c:51 [inline] do_syscall_64+0x4e/0xa0 arch/x86/entry/common.c:82 entry_SYSCALL_64_after_hwframe+0x46/0xb0 Example thread interleaving that causes the general protection fault is as follows: CPU1 (vmci_host_poll) CPU2 (vmci_host_do_init_context) ----- ----- // Read uninitialized context context = vmci_host_dev->context; // Initialize context vmci_host_dev->context = vmci_ctx_create(); vmci_host_dev->ct_type = VMCIOBJ_CONTEXT; if (vmci_host_dev->ct_type == VMCIOBJ_CONTEXT) { // Dereferencing the wrong pointer poll_wait(..., &context->host_context); } In this scenario, vmci_host_poll() reads vmci_host_dev->context first, and then reads vmci_host_dev->ct_type to check that vmci_host_dev->context is initialized. However, since these two reads are not atomically executed, there is a chance of a race condition as described above. To fix this race condition, read vmci_host_dev->context after checking the value of vmci_host_dev->ct_type so that vmci_host_poll() always reads an initialized context. Reported-by: Dae R. Jeong Fixes: 8bf503991f87 ("VMCI: host side driver implementation.") Signed-off-by: Dae R. Jeong Link: https://lore.kernel.org/r/ZCGFsdBAU4cYww5l@dragonet Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_host.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index 857b9851402a..abe79f6fd2a7 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -165,10 +165,16 @@ static int vmci_host_close(struct inode *inode, struct file *filp) static __poll_t vmci_host_poll(struct file *filp, poll_table *wait) { struct vmci_host_dev *vmci_host_dev = filp->private_data; - struct vmci_ctx *context = vmci_host_dev->context; + struct vmci_ctx *context; __poll_t mask = 0; if (vmci_host_dev->ct_type == VMCIOBJ_CONTEXT) { + /* + * Read context only if ct_type == VMCIOBJ_CONTEXT to make + * sure that context is initialized + */ + context = vmci_host_dev->context; + /* Check for VMCI calls to this VM context. */ if (wait) poll_wait(filp, &context->host_context.wait_queue, -- cgit v1.2.3 From b19a4266c52de78496fe40f0b37580a3b762e67d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:09 +0100 Subject: of: Fix modalias string generation The helper generating an OF based modalias (of_device_get_modalias()) works fine, but due to the use of snprintf() internally it needs a buffer one byte longer than what should be needed just for the entire string (excluding the '\0'). Most users of this helper are sysfs hooks providing the modalias string to users. They all provide a PAGE_SIZE buffer which is way above the number of bytes required to fit the modalias string and hence do not suffer from this issue. There is another user though, of_device_request_module(), which is only called by drivers/usb/common/ulpi.c. This request module function is faulty, but maybe because in most cases there is an alternative, ULPI driver users have not noticed it. In this function, of_device_get_modalias() is called twice. The first time without buffer just to get the number of bytes required by the modalias string (excluding the null byte), and a second time, after buffer allocation, to fill the buffer. The allocation asks for an additional byte, in order to store the trailing '\0'. However, the buffer *length* provided to of_device_get_modalias() excludes this extra byte. The internal use of snprintf() with a length that is exactly the number of bytes to be written has the effect of using the last available byte to store a '\0', which then smashes the last character of the modalias string. Provide the actual size of the buffer to of_device_get_modalias() to fix this issue. Note: the "str[size - 1] = '\0';" line is not really needed as snprintf will anyway end the string with a null byte, but there is a possibility that this function might be called on a struct device_node without compatible, in this case snprintf() would not be executed. So we keep it just to avoid possible unbounded strings. Cc: Stephen Boyd Cc: Peter Chen Fixes: 9c829c097f2f ("of: device: Support loading a module with OF based modalias") Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/of/device.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/of/device.c b/drivers/of/device.c index 955bfb3d1a83..c91bb5899256 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -297,12 +297,15 @@ int of_device_request_module(struct device *dev) if (size < 0) return size; - str = kmalloc(size + 1, GFP_KERNEL); + /* Reserve an additional byte for the trailing '\0' */ + size++; + + str = kmalloc(size, GFP_KERNEL); if (!str) return -ENOMEM; of_device_get_modalias(dev, str, size); - str[size] = '\0'; + str[size - 1] = '\0'; ret = request_module(str); kfree(str); -- cgit v1.2.3 From 1ca28dc907b3725940fe88bfd91bc3a53081e2d3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 4 Apr 2023 18:21:10 +0100 Subject: dt-bindings: nvmem: qcom,spmi-sdam: fix example 'reg' property The SPMI PMIC register region width is fixed and should not be encoded in the devicetree. Amend the example with a parent pmic node with the expected '#address-cells' and '#size-cells' and fix up the 'reg' property. Fixes: 9664a6b54c57 ("dt-bindings: nvmem: add binding for QTI SPMI SDAM") Signed-off-by: Johan Hovold Reviewed-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/qcom,spmi-sdam.yaml | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml b/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml index e08504ef3b6e..4702f10ecca2 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml @@ -42,17 +42,22 @@ unevaluatedProperties: false examples: - | - sdam_1: nvram@b000 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "qcom,spmi-sdam"; - reg = <0xb000 0x100>; - ranges = <0 0xb000 0x100>; - - /* Data cells */ - restart_reason: restart@50 { - reg = <0x50 0x1>; - bits = <6 2>; - }; - }; + pmic { + #address-cells = <1>; + #size-cells = <0>; + + sdam_1: nvram@b000 { + compatible = "qcom,spmi-sdam"; + reg = <0xb000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0xb000 0x100>; + + /* Data cells */ + restart_reason: restart@50 { + reg = <0x50 0x1>; + bits = <6 2>; + }; + }; + }; ... -- cgit v1.2.3 From bcd1fe07def0f070eb5f31594620aaee6f81d31a Mon Sep 17 00:00:00 2001 From: Nick Alcock Date: Tue, 4 Apr 2023 18:21:11 +0100 Subject: nvmem: xilinx: zynqmp: make modular This driver has a MODULE_LICENSE but is not tristate so cannot be built as a module, unlike all its peers: make it modular to match. Signed-off-by: Nick Alcock Suggested-by: Michal Simek Cc: Luis Chamberlain Cc: linux-modules@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: Hitomi Hasegawa Cc: Srinivas Kandagatla Cc: Michal Simek Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 6dec38805041..3b3832f4dfad 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -368,7 +368,7 @@ config NVMEM_VF610_OCOTP be called nvmem-vf610-ocotp. config NVMEM_ZYNQMP - bool "Xilinx ZYNQMP SoC nvmem firmware support" + tristate "Xilinx ZYNQMP SoC nvmem firmware support" depends on ARCH_ZYNQMP help This is a driver to access hardware related data like -- cgit v1.2.3 From 9f4615338a35dd0f882cc385a8f960191ce0cc82 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 4 Apr 2023 18:21:12 +0100 Subject: dt-bindings: nvmem: convert amlogic-meson-mx-efuse.txt to dt-schema Convert the Amlogic Meson6 eFuse bindings to dt-schema. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Reviewed-by: Martin Blumenstingl Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/amlogic,meson6-efuse.yaml | 57 ++++++++++++++++++++++ .../bindings/nvmem/amlogic-meson-mx-efuse.txt | 22 --------- 2 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml delete mode 100644 Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt diff --git a/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml b/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml new file mode 100644 index 000000000000..84b3dfd21e09 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/amlogic,meson6-efuse.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/amlogic,meson6-efuse.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Meson6 eFuse + +maintainers: + - Neil Armstrong + - Martin Blumenstingl + +allOf: + - $ref: nvmem.yaml# + +properties: + compatible: + enum: + - amlogic,meson6-efuse + - amlogic,meson8-efuse + - amlogic,meson8b-efuse + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: core + +required: + - compatible + - reg + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + efuse: efuse@0 { + compatible = "amlogic,meson6-efuse"; + reg = <0x0 0x2000>; + clocks = <&clk_efuse>; + clock-names = "core"; + #address-cells = <1>; + #size-cells = <1>; + + ethernet_mac_address: mac@1b4 { + reg = <0x1b4 0x6>; + }; + + temperature_calib: calib@1f4 { + reg = <0x1f4 0x4>; + }; + }; diff --git a/Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt b/Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt deleted file mode 100644 index a3c63954a1a4..000000000000 --- a/Documentation/devicetree/bindings/nvmem/amlogic-meson-mx-efuse.txt +++ /dev/null @@ -1,22 +0,0 @@ -Amlogic Meson6/Meson8/Meson8b efuse - -Required Properties: -- compatible: depending on the SoC this should be one of: - - "amlogic,meson6-efuse" - - "amlogic,meson8-efuse" - - "amlogic,meson8b-efuse" -- reg: base address and size of the efuse registers -- clocks: a reference to the efuse core gate clock -- clock-names: must be "core" - -All properties and sub-nodes as well as the consumer bindings -defined in nvmem.txt in this directory are also supported. - - -Example: - efuse: nvmem@0 { - compatible = "amlogic,meson8-efuse"; - reg = <0x0 0x2000>; - clocks = <&clkc CLKID_EFUSE>; - clock-names = "core"; - }; -- cgit v1.2.3 From ce030eb191b7bf4af32fd76f7456d2291e292e2e Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 4 Apr 2023 18:21:13 +0100 Subject: dt-bindings: nvmem: convert amlogic-efuse.txt to dt-schema Convert the Amlogic Meson GX eFuse bindings to dt-schema. Take in account the used variant with amlogic,meson-gx-efuse. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-6-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/amlogic,meson-gxbb-efuse.yaml | 57 ++++++++++++++++++++++ .../devicetree/bindings/nvmem/amlogic-efuse.txt | 48 ------------------ 2 files changed, 57 insertions(+), 48 deletions(-) create mode 100644 Documentation/devicetree/bindings/nvmem/amlogic,meson-gxbb-efuse.yaml delete mode 100644 Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt diff --git a/Documentation/devicetree/bindings/nvmem/amlogic,meson-gxbb-efuse.yaml b/Documentation/devicetree/bindings/nvmem/amlogic,meson-gxbb-efuse.yaml new file mode 100644 index 000000000000..e49c2754ff55 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/amlogic,meson-gxbb-efuse.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/amlogic,meson-gxbb-efuse.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Meson GX eFuse + +maintainers: + - Neil Armstrong + +allOf: + - $ref: nvmem.yaml# + +properties: + compatible: + oneOf: + - const: amlogic,meson-gxbb-efuse + - items: + - const: amlogic,meson-gx-efuse + - const: amlogic,meson-gxbb-efuse + + clocks: + maxItems: 1 + + secure-monitor: + description: phandle to the secure-monitor node + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - clocks + - secure-monitor + +unevaluatedProperties: false + +examples: + - | + efuse: efuse { + compatible = "amlogic,meson-gxbb-efuse"; + clocks = <&clk_efuse>; + #address-cells = <1>; + #size-cells = <1>; + secure-monitor = <&sm>; + + sn: sn@14 { + reg = <0x14 0x10>; + }; + + eth_mac: mac@34 { + reg = <0x34 0x10>; + }; + + bid: bid@46 { + reg = <0x46 0x30>; + }; + }; diff --git a/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt b/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt deleted file mode 100644 index f7b3ed74db54..000000000000 --- a/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt +++ /dev/null @@ -1,48 +0,0 @@ -= Amlogic Meson GX eFuse device tree bindings = - -Required properties: -- compatible: should be "amlogic,meson-gxbb-efuse" -- clocks: phandle to the efuse peripheral clock provided by the - clock controller. -- secure-monitor: phandle to the secure-monitor node - -= Data cells = -Are child nodes of eFuse, bindings of which as described in -bindings/nvmem/nvmem.txt - -Example: - - efuse: efuse { - compatible = "amlogic,meson-gxbb-efuse"; - clocks = <&clkc CLKID_EFUSE>; - #address-cells = <1>; - #size-cells = <1>; - secure-monitor = <&sm>; - - sn: sn@14 { - reg = <0x14 0x10>; - }; - - eth_mac: eth_mac@34 { - reg = <0x34 0x10>; - }; - - bid: bid@46 { - reg = <0x46 0x30>; - }; - }; - - sm: secure-monitor { - compatible = "amlogic,meson-gxbb-sm"; - }; - -= Data consumers = -Are device nodes which consume nvmem data cells. - -For example: - - eth_mac { - ... - nvmem-cells = <ð_mac>; - nvmem-cell-names = "eth_mac"; - }; -- cgit v1.2.3 From 2187af84e5d0062090f5e0e89281a00d0b8afa40 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 4 Apr 2023 18:21:14 +0100 Subject: dt-bindings: nvmem: Fix spelling mistake "platforn" -> "platform" There is a spelling mistake in platforn-name. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Miquel Raynal Acked-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-7-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml b/Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml index 5a0e7671aa3f..714a6538cc7c 100644 --- a/Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml @@ -61,7 +61,7 @@ properties: type: object additionalProperties: false - platforn-name: + platform-name: type: object additionalProperties: false -- cgit v1.2.3 From 5c3d15e127ebfc0754cd18def7124633b6d46672 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:15 +0100 Subject: of: Update of_device_get_modalias() This function only needs a "struct device_node" to work, but for convenience the author (and only user) of this helper did use a "struct device" and put it in device.c. Let's convert this helper to take a "struct device node" instead. This change asks for two additional changes: renaming it "of_modalias()" to fit the current naming, and moving it outside of device.c which will be done in a follow-up commit. Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-8-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/of/device.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/of/device.c b/drivers/of/device.c index c91bb5899256..351c505ecb50 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -248,7 +248,7 @@ const void *of_device_get_match_data(const struct device *dev) } EXPORT_SYMBOL(of_device_get_match_data); -static ssize_t of_device_get_modalias(const struct device *dev, char *str, ssize_t len) +static ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len) { const char *compat; char *c; @@ -256,19 +256,16 @@ static ssize_t of_device_get_modalias(const struct device *dev, char *str, ssize ssize_t csize; ssize_t tsize; - if ((!dev) || (!dev->of_node) || dev->of_node_reused) - return -ENODEV; - /* Name & Type */ /* %p eats all alphanum characters, so %c must be used here */ - csize = snprintf(str, len, "of:N%pOFn%c%s", dev->of_node, 'T', - of_node_get_device_type(dev->of_node)); + csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T', + of_node_get_device_type(np)); tsize = csize; len -= csize; if (str) str += csize; - of_property_for_each_string(dev->of_node, "compatible", p, compat) { + of_property_for_each_string(np, "compatible", p, compat) { csize = strlen(compat) + 1; tsize += csize; if (csize > len) @@ -293,7 +290,10 @@ int of_device_request_module(struct device *dev) ssize_t size; int ret; - size = of_device_get_modalias(dev, NULL, 0); + if (!dev || !dev->of_node) + return -ENODEV; + + size = of_modalias(dev->of_node, NULL, 0); if (size < 0) return size; @@ -304,7 +304,7 @@ int of_device_request_module(struct device *dev) if (!str) return -ENOMEM; - of_device_get_modalias(dev, str, size); + of_modalias(dev->of_node, str, size); str[size - 1] = '\0'; ret = request_module(str); kfree(str); @@ -321,7 +321,12 @@ EXPORT_SYMBOL_GPL(of_device_request_module); */ ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len) { - ssize_t sl = of_device_get_modalias(dev, str, len - 2); + ssize_t sl; + + if (!dev || !dev->of_node || dev->of_node_reused) + return -ENODEV; + + sl = of_modalias(dev->of_node, str, len - 2); if (sl < 0) return sl; if (sl > len - 2) @@ -386,8 +391,8 @@ int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env * if (add_uevent_var(env, "MODALIAS=")) return -ENOMEM; - sl = of_device_get_modalias(dev, &env->buf[env->buflen-1], - sizeof(env->buf) - env->buflen); + sl = of_modalias(dev->of_node, &env->buf[env->buflen-1], + sizeof(env->buf) - env->buflen); if (sl < 0) return sl; if (sl >= (sizeof(env->buf) - env->buflen)) -- cgit v1.2.3 From 673aa1ed1c9b6710bf24e3f0957d85e2f46c77db Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:16 +0100 Subject: of: Rename of_modalias_node() This helper does not produce a real modalias, but tries to get the "product" compatible part of the "vendor,product" compatibles only. It is far from creating a purely useful modalias string and does not seem to be used like that directly anyway, so let's try to give this helper a more meaningful name before moving there a real modalias helper (already existing under of/device.c). Also update the various documentations to refer to the strings as "aliases" rather than "modaliases" which has a real meaning in the Linux kernel. There is no functional change. Cc: Rafael J. Wysocki Cc: Len Brown Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: Sebastian Reichel Cc: Wolfram Sang Cc: Mark Brown Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Acked-by: Mark Brown Signed-off-by: Srinivas Kandagatla Acked-by: Sebastian Reichel Link: https://lore.kernel.org/r/20230404172148.82422-9-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/bus.c | 7 ++++--- drivers/gpu/drm/drm_mipi_dsi.c | 2 +- drivers/hsi/hsi_core.c | 2 +- drivers/i2c/busses/i2c-powermac.c | 2 +- drivers/i2c/i2c-core-of.c | 2 +- drivers/of/base.c | 18 +++++++++++------- drivers/spi/spi.c | 4 ++-- include/linux/of.h | 3 ++- 8 files changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9531dd0fef50..fc74c786a867 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -817,9 +817,10 @@ static bool acpi_of_modalias(struct acpi_device *adev, * @modalias: Pointer to buffer that modalias value will be copied into * @len: Length of modalias buffer * - * This is a counterpart of of_modalias_node() for struct acpi_device objects. - * If there is a compatible string for @adev, it will be copied to @modalias - * with the vendor prefix stripped; otherwise, @default_id will be used. + * This is a counterpart of of_alias_from_compatible() for struct acpi_device + * objects. If there is a compatible string for @adev, it will be copied to + * @modalias with the vendor prefix stripped; otherwise, @default_id will be + * used. */ void acpi_set_modalias(struct acpi_device *adev, const char *default_id, char *modalias, size_t len) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index b41aaf2bb9f1..b62f5e4425f4 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -160,7 +160,7 @@ of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node) int ret; u32 reg; - if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { + if (of_alias_from_compatible(node, info.type, sizeof(info.type)) < 0) { drm_err(host, "modalias failure on %pOF\n", node); return ERR_PTR(-EINVAL); } diff --git a/drivers/hsi/hsi_core.c b/drivers/hsi/hsi_core.c index 8fda8f1d064d..acbf82f755a8 100644 --- a/drivers/hsi/hsi_core.c +++ b/drivers/hsi/hsi_core.c @@ -207,7 +207,7 @@ static void hsi_add_client_from_dt(struct hsi_port *port, if (!cl) return; - err = of_modalias_node(client, name, sizeof(name)); + err = of_alias_from_compatible(client, name, sizeof(name)); if (err) goto err; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 2e74747eec9c..ec706a3aba26 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -284,7 +284,7 @@ static bool i2c_powermac_get_type(struct i2c_adapter *adap, */ /* First try proper modalias */ - if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) { + if (of_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) { snprintf(type, type_size, "MAC,%s", tmp); return true; } diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index bce6b796e04c..8941a30574e3 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -27,7 +27,7 @@ int of_i2c_get_board_info(struct device *dev, struct device_node *node, memset(info, 0, sizeof(*info)); - if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) { + if (of_alias_from_compatible(node, info->type, sizeof(info->type)) < 0) { dev_err(dev, "of_i2c: modalias failure on %pOF\n", node); return -EINVAL; } diff --git a/drivers/of/base.c b/drivers/of/base.c index ac6fde53342f..161fe3192c46 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1208,19 +1208,23 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from, EXPORT_SYMBOL(of_find_matching_node_and_match); /** - * of_modalias_node - Lookup appropriate modalias for a device node + * of_alias_from_compatible - Lookup appropriate alias for a device node + * depending on compatible * @node: pointer to a device tree node - * @modalias: Pointer to buffer that modalias value will be copied into - * @len: Length of modalias value + * @alias: Pointer to buffer that alias value will be copied into + * @len: Length of alias value * * Based on the value of the compatible property, this routine will attempt - * to choose an appropriate modalias value for a particular device tree node. + * to choose an appropriate alias value for a particular device tree node. * It does this by stripping the manufacturer prefix (as delimited by a ',') * from the first entry in the compatible list property. * + * Note: The matching on just the "product" side of the compatible is a relic + * from I2C and SPI. Please do not add any new user. + * * Return: This routine returns 0 on success, <0 on failure. */ -int of_modalias_node(struct device_node *node, char *modalias, int len) +int of_alias_from_compatible(const struct device_node *node, char *alias, int len) { const char *compatible, *p; int cplen; @@ -1229,10 +1233,10 @@ int of_modalias_node(struct device_node *node, char *modalias, int len) if (!compatible || strlen(compatible) > cplen) return -ENODEV; p = strchr(compatible, ','); - strscpy(modalias, p ? p + 1 : compatible, len); + strscpy(alias, p ? p + 1 : compatible, len); return 0; } -EXPORT_SYMBOL_GPL(of_modalias_node); +EXPORT_SYMBOL_GPL(of_alias_from_compatible); /** * of_find_node_by_phandle - Find a node given a phandle diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 44b85a8d47f1..3bbdc5fe3b99 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2354,8 +2354,8 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc) } /* Select device driver */ - rc = of_modalias_node(nc, spi->modalias, - sizeof(spi->modalias)); + rc = of_alias_from_compatible(nc, spi->modalias, + sizeof(spi->modalias)); if (rc < 0) { dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc); goto err_out; diff --git a/include/linux/of.h b/include/linux/of.h index 0af611307db2..b1eea8569043 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -373,7 +373,8 @@ extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( const struct of_device_id *matches, const struct device_node *node); -extern int of_modalias_node(struct device_node *node, char *modalias, int len); +extern int of_alias_from_compatible(const struct device_node *node, char *alias, + int len); extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args); extern int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int cell_count, -- cgit v1.2.3 From bd7a7ed774afd1a4174df34227626c95573be517 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:17 +0100 Subject: of: Move of_modalias() to module.c Create a specific .c file for OF related module handling. Move of_modalias() inside as a first step. The helper is exposed through of.h even though it is only used by core files because the users from device.c will soon be split into an OF-only helper in module.c as well as a device-oriented inline helper in of_device.h. Putting this helper in of_private.h would require to include of_private.h from of_device.h, which is not acceptable. Suggested-by: Rob Herring Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-10-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/of/Makefile | 2 +- drivers/of/device.c | 37 ------------------------------------- drivers/of/module.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 9 +++++++++ 4 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 drivers/of/module.c diff --git a/drivers/of/Makefile b/drivers/of/Makefile index e0360a44306e..ae9923fd2940 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y = base.o device.o platform.o property.o +obj-y = base.o device.o module.o platform.o property.o obj-$(CONFIG_OF_KOBJ) += kobj.o obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o diff --git a/drivers/of/device.c b/drivers/of/device.c index 351c505ecb50..7183cfd754db 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include #include #include @@ -248,42 +247,6 @@ const void *of_device_get_match_data(const struct device *dev) } EXPORT_SYMBOL(of_device_get_match_data); -static ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len) -{ - const char *compat; - char *c; - struct property *p; - ssize_t csize; - ssize_t tsize; - - /* Name & Type */ - /* %p eats all alphanum characters, so %c must be used here */ - csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T', - of_node_get_device_type(np)); - tsize = csize; - len -= csize; - if (str) - str += csize; - - of_property_for_each_string(np, "compatible", p, compat) { - csize = strlen(compat) + 1; - tsize += csize; - if (csize > len) - continue; - - csize = snprintf(str, len, "C%s", compat); - for (c = str; c; ) { - c = strchr(c, ' '); - if (c) - *c++ = '_'; - } - len -= csize; - str += csize; - } - - return tsize; -} - int of_device_request_module(struct device *dev) { char *str; diff --git a/drivers/of/module.c b/drivers/of/module.c new file mode 100644 index 000000000000..4c59752bc8d6 --- /dev/null +++ b/drivers/of/module.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Linux kernel module helpers. + */ + +#include +#include +#include + +ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len) +{ + const char *compat; + char *c; + struct property *p; + ssize_t csize; + ssize_t tsize; + + /* Name & Type */ + /* %p eats all alphanum characters, so %c must be used here */ + csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T', + of_node_get_device_type(np)); + tsize = csize; + len -= csize; + if (str) + str += csize; + + of_property_for_each_string(np, "compatible", p, compat) { + csize = strlen(compat) + 1; + tsize += csize; + if (csize > len) + continue; + + csize = snprintf(str, len, "C%s", compat); + for (c = str; c; ) { + c = strchr(c, ' '); + if (c) + *c++ = '_'; + } + len -= csize; + str += csize; + } + + return tsize; +} diff --git a/include/linux/of.h b/include/linux/of.h index b1eea8569043..be26c7e8ef9e 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -385,6 +385,9 @@ extern int of_parse_phandle_with_args_map(const struct device_node *np, extern int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name); +/* module functions */ +extern ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len); + /* phandle iterator functions */ extern int of_phandle_iterator_init(struct of_phandle_iterator *it, const struct device_node *np, @@ -742,6 +745,12 @@ static inline int of_count_phandle_with_args(const struct device_node *np, return -ENOSYS; } +static inline ssize_t of_modalias(const struct device_node *np, char *str, + ssize_t len) +{ + return -ENODEV; +} + static inline int of_phandle_iterator_init(struct of_phandle_iterator *it, const struct device_node *np, const char *list_name, -- cgit v1.2.3 From e6506f06d5e82765666902ccf9e9162f3e31d518 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:18 +0100 Subject: of: Move the request module helper logic to module.c Depending on device.c for pure OF handling is considered backwards. Let's extract the content of of_device_request_module() to have the real logic under module.c. The next step will be to convert users of of_device_request_module() to use the new helper. Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-11-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/of/device.c | 25 ++----------------------- drivers/of/module.c | 30 ++++++++++++++++++++++++++++++ include/linux/of.h | 6 ++++++ 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/drivers/of/device.c b/drivers/of/device.c index 7183cfd754db..874a2e1f6308 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -8,7 +8,6 @@ #include /* for bus_dma_region */ #include #include -#include #include #include #include @@ -249,30 +248,10 @@ EXPORT_SYMBOL(of_device_get_match_data); int of_device_request_module(struct device *dev) { - char *str; - ssize_t size; - int ret; - - if (!dev || !dev->of_node) + if (!dev) return -ENODEV; - size = of_modalias(dev->of_node, NULL, 0); - if (size < 0) - return size; - - /* Reserve an additional byte for the trailing '\0' */ - size++; - - str = kmalloc(size, GFP_KERNEL); - if (!str) - return -ENOMEM; - - of_modalias(dev->of_node, str, size); - str[size - 1] = '\0'; - ret = request_module(str); - kfree(str); - - return ret; + return of_request_module(dev->of_node); } EXPORT_SYMBOL_GPL(of_device_request_module); diff --git a/drivers/of/module.c b/drivers/of/module.c index 4c59752bc8d6..0e8aa974f0f2 100644 --- a/drivers/of/module.c +++ b/drivers/of/module.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -42,3 +43,32 @@ ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len) return tsize; } + +int of_request_module(const struct device_node *np) +{ + char *str; + ssize_t size; + int ret; + + if (!np) + return -ENODEV; + + size = of_modalias(np, NULL, 0); + if (size < 0) + return size; + + /* Reserve an additional byte for the trailing '\0' */ + size++; + + str = kmalloc(size, GFP_KERNEL); + if (!str) + return -ENOMEM; + + of_modalias(np, str, size); + str[size - 1] = '\0'; + ret = request_module(str); + kfree(str); + + return ret; +} +EXPORT_SYMBOL_GPL(of_request_module); diff --git a/include/linux/of.h b/include/linux/of.h index be26c7e8ef9e..9b7a99499ef3 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -387,6 +387,7 @@ extern int of_count_phandle_with_args(const struct device_node *np, /* module functions */ extern ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len); +extern int of_request_module(const struct device_node *np); /* phandle iterator functions */ extern int of_phandle_iterator_init(struct of_phandle_iterator *it, @@ -751,6 +752,11 @@ static inline ssize_t of_modalias(const struct device_node *np, char *str, return -ENODEV; } +static inline int of_request_module(const struct device_node *np) +{ + return -ENODEV; +} + static inline int of_phandle_iterator_init(struct of_phandle_iterator *it, const struct device_node *np, const char *list_name, -- cgit v1.2.3 From 19697537708643091d64c8ec9a809883166dcb3f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:19 +0100 Subject: usb: ulpi: Use of_request_module() There is a new helper supposed to replace of_device_request_module(), called of_request_module(). They are both strictly equivalent, besides the fact the latter receives a "struct device_node" directly. Use it. Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Signed-off-by: Miquel Raynal Acked-by: Rob Herring Acked-by: Heikki Krogerus Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-12-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/ulpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index a98b2108376a..6977cf380838 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -229,7 +229,7 @@ static int ulpi_read_id(struct ulpi *ulpi) request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); return 0; err: - of_device_request_module(&ulpi->dev); + of_request_module(ulpi->dev.of_node); return 0; } -- cgit v1.2.3 From 2f555f58f5ce33b201235e104823662d5573c4a5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:20 +0100 Subject: of: device: Kill of_device_request_module() A new helper has been introduced, of_request_module(). Users have been converted, this helper can now be deleted. Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-13-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/of/device.c | 9 --------- include/linux/of_device.h | 6 ------ 2 files changed, 15 deletions(-) diff --git a/drivers/of/device.c b/drivers/of/device.c index 874a2e1f6308..0f00f1b80708 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -246,15 +246,6 @@ const void *of_device_get_match_data(const struct device *dev) } EXPORT_SYMBOL(of_device_get_match_data); -int of_device_request_module(struct device *dev) -{ - if (!dev) - return -ENODEV; - - return of_request_module(dev->of_node); -} -EXPORT_SYMBOL_GPL(of_device_request_module); - /** * of_device_modalias - Fill buffer with newline terminated modalias string * @dev: Calling device diff --git a/include/linux/of_device.h b/include/linux/of_device.h index f4b57614979d..ce20d8b00b3e 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -33,7 +33,6 @@ extern void of_device_unregister(struct platform_device *ofdev); extern const void *of_device_get_match_data(const struct device *dev); extern ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len); -extern int of_device_request_module(struct device *dev); extern void of_device_uevent(const struct device *dev, struct kobj_uevent_env *env); extern int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env); @@ -78,11 +77,6 @@ static inline int of_device_modalias(struct device *dev, return -ENODEV; } -static inline int of_device_request_module(struct device *dev) -{ - return -ENODEV; -} - static inline int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env) { -- cgit v1.2.3 From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:21 +0100 Subject: nvmem: core: introduce NVMEM layouts NVMEM layouts are used to generate NVMEM cells during runtime. Think of an EEPROM with a well-defined conent. For now, the content can be described by a device tree or a board file. But this only works if the offsets and lengths are static and don't change. One could also argue that putting the layout of the EEPROM in the device tree is the wrong place. Instead, the device tree should just have a specific compatible string. Right now there are two use cases: (1) The NVMEM cell needs special processing. E.g. if it only specifies a base MAC address offset and you need to add an offset, or it needs to parse a MAC from ASCII format or some proprietary format. (Post processing of cells is added in a later commit). (2) u-boot environment parsing. The cells don't have a particular offset but it needs parsing the content to determine the offsets and length. Co-developed-by: Miquel Raynal Signed-off-by: Miquel Raynal Signed-off-by: Michael Walle Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/nvmem.rst | 15 +++++ drivers/nvmem/Kconfig | 4 ++ drivers/nvmem/Makefile | 1 + drivers/nvmem/core.c | 120 +++++++++++++++++++++++++++++++++++++ drivers/nvmem/layouts/Kconfig | 5 ++ drivers/nvmem/layouts/Makefile | 4 ++ include/linux/nvmem-consumer.h | 7 +++ include/linux/nvmem-provider.h | 51 ++++++++++++++++ 8 files changed, 207 insertions(+) create mode 100644 drivers/nvmem/layouts/Kconfig create mode 100644 drivers/nvmem/layouts/Makefile diff --git a/Documentation/driver-api/nvmem.rst b/Documentation/driver-api/nvmem.rst index e3366322d46c..de221e91c8e3 100644 --- a/Documentation/driver-api/nvmem.rst +++ b/Documentation/driver-api/nvmem.rst @@ -185,3 +185,18 @@ ex:: ===================== See Documentation/devicetree/bindings/nvmem/nvmem.txt + +8. NVMEM layouts +================ + +NVMEM layouts are yet another mechanism to create cells. With the device +tree binding it is possible to specify simple cells by using an offset +and a length. Sometimes, the cells doesn't have a static offset, but +the content is still well defined, e.g. tag-length-values. In this case, +the NVMEM device content has to be first parsed and the cells need to +be added accordingly. Layouts let you read the content of the NVMEM device +and let you add cells dynamically. + +Another use case for layouts is the post processing of cells. With layouts, +it is possible to associate a custom post processing hook to a cell. It +even possible to add this hook to cells not created by the layout itself. diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 3b3832f4dfad..a2afba11c890 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -21,6 +21,10 @@ config NVMEM_SYSFS This interface is mostly used by userspace applications to read/write directly into nvmem. +# Layouts + +source "drivers/nvmem/layouts/Kconfig" + # Devices config NVMEM_APPLE_EFUSES diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 6a1efffa88f0..f82431ec8aef 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_NVMEM) += nvmem_core.o nvmem_core-y := core.o +obj-y += layouts/ # Devices obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 22024b830788..b9be1faeb7be 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -40,6 +40,7 @@ struct nvmem_device { nvmem_reg_write_t reg_write; nvmem_cell_post_process_t cell_post_process; struct gpio_desc *wp_gpio; + struct nvmem_layout *layout; void *priv; }; @@ -74,6 +75,9 @@ static LIST_HEAD(nvmem_lookup_list); static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); +static DEFINE_SPINLOCK(nvmem_layout_lock); +static LIST_HEAD(nvmem_layouts); + static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { @@ -728,6 +732,101 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) return 0; } +int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner) +{ + layout->owner = owner; + + spin_lock(&nvmem_layout_lock); + list_add(&layout->node, &nvmem_layouts); + spin_unlock(&nvmem_layout_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(__nvmem_layout_register); + +void nvmem_layout_unregister(struct nvmem_layout *layout) +{ + spin_lock(&nvmem_layout_lock); + list_del(&layout->node); + spin_unlock(&nvmem_layout_lock); +} +EXPORT_SYMBOL_GPL(nvmem_layout_unregister); + +static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) +{ + struct device_node *layout_np, *np = nvmem->dev.of_node; + struct nvmem_layout *l, *layout = NULL; + + layout_np = of_get_child_by_name(np, "nvmem-layout"); + if (!layout_np) + return NULL; + + spin_lock(&nvmem_layout_lock); + + list_for_each_entry(l, &nvmem_layouts, node) { + if (of_match_node(l->of_match_table, layout_np)) { + if (try_module_get(l->owner)) + layout = l; + + break; + } + } + + spin_unlock(&nvmem_layout_lock); + of_node_put(layout_np); + + return layout; +} + +static void nvmem_layout_put(struct nvmem_layout *layout) +{ + if (layout) + module_put(layout->owner); +} + +static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem) +{ + struct nvmem_layout *layout = nvmem->layout; + int ret; + + if (layout && layout->add_cells) { + ret = layout->add_cells(&nvmem->dev, nvmem, layout); + if (ret) + return ret; + } + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +/** + * of_nvmem_layout_get_container() - Get OF node to layout container. + * + * @nvmem: nvmem device. + * + * Return: a node pointer with refcount incremented or NULL if no + * container exists. Use of_node_put() on it when done. + */ +struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) +{ + return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); +} +EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); +#endif + +const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout) +{ + struct device_node __maybe_unused *layout_np; + const struct of_device_id *match; + + layout_np = of_nvmem_layout_get_container(nvmem); + match = of_match_node(layout->of_match_table, layout_np); + + return match ? match->data : NULL; +} +EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data); + /** * nvmem_register() - Register a nvmem device for given nvmem_config. * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem @@ -834,6 +933,12 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; } + /* + * If the driver supplied a layout by config->layout, the module + * pointer will be NULL and nvmem_layout_put() will be a noop. + */ + nvmem->layout = config->layout ?: nvmem_layout_get(nvmem); + if (config->cells) { rval = nvmem_add_cells(nvmem, config->cells, config->ncells); if (rval) @@ -854,12 +959,17 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_cells; + rval = nvmem_add_cells_from_layout(nvmem); + if (rval) + goto err_remove_cells; + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); return nvmem; err_remove_cells: nvmem_device_remove_all_cells(nvmem); + nvmem_layout_put(nvmem->layout); if (config->compat) nvmem_sysfs_remove_compat(nvmem, config); err_put_device: @@ -881,6 +991,7 @@ static void nvmem_device_release(struct kref *kref) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); nvmem_device_remove_all_cells(nvmem); + nvmem_layout_put(nvmem->layout); device_unregister(&nvmem->dev); } @@ -1246,6 +1357,15 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) return ERR_PTR(-EINVAL); } + /* nvmem layouts produce cells within the nvmem-layout container */ + if (of_node_name_eq(nvmem_np, "nvmem-layout")) { + nvmem_np = of_get_next_parent(nvmem_np); + if (!nvmem_np) { + of_node_put(cell_np); + return ERR_PTR(-EINVAL); + } + } + nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); of_node_put(nvmem_np); if (IS_ERR(nvmem)) { diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig new file mode 100644 index 000000000000..9ad3911d1605 --- /dev/null +++ b/drivers/nvmem/layouts/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +menu "Layout Types" + +endmenu diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile new file mode 100644 index 000000000000..6fdb3c60a4fa --- /dev/null +++ b/drivers/nvmem/layouts/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for nvmem layouts. +# diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 1f62f7ba71ca..fa030d93b768 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -239,6 +239,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id); struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *name); +struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); #else static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) @@ -251,6 +252,12 @@ static inline struct nvmem_device *of_nvmem_device_get(struct device_node *np, { return ERR_PTR(-EOPNOTSUPP); } + +static inline struct device_node * +of_nvmem_layout_get_container(struct nvmem_device *nvmem) +{ + return ERR_PTR(-EOPNOTSUPP); +} #endif /* CONFIG_NVMEM && CONFIG_OF */ #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 0262b86194eb..535c5f9f3309 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -88,6 +88,7 @@ struct nvmem_cell_info { * @stride: Minimum read/write access stride. * @priv: User context passed to read/write callbacks. * @ignore_wp: Write Protect pin is managed by the provider. + * @layout: Fixed layout associated with this nvmem device. * * Note: A default "nvmem" name will be assigned to the device if * no name is specified in its configuration. In such case "" is @@ -109,6 +110,7 @@ struct nvmem_config { bool read_only; bool root_only; bool ignore_wp; + struct nvmem_layout *layout; struct device_node *of_node; bool no_of_node; nvmem_reg_read_t reg_read; @@ -142,6 +144,33 @@ struct nvmem_cell_table { struct list_head node; }; +/** + * struct nvmem_layout - NVMEM layout definitions + * + * @name: Layout name. + * @of_match_table: Open firmware match table. + * @add_cells: Will be called if a nvmem device is found which + * has this layout. The function will add layout + * specific cells with nvmem_add_one_cell(). + * @owner: Pointer to struct module. + * @node: List node. + * + * A nvmem device can hold a well defined structure which can just be + * evaluated during runtime. For example a TLV list, or a list of "name=val" + * pairs. A nvmem layout can parse the nvmem device and add appropriate + * cells. + */ +struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem, + struct nvmem_layout *layout); + + /* private */ + struct module *owner; + struct list_head node; +}; + #if IS_ENABLED(CONFIG_NVMEM) struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); @@ -156,6 +185,14 @@ void nvmem_del_cell_table(struct nvmem_cell_table *table); int nvmem_add_one_cell(struct nvmem_device *nvmem, const struct nvmem_cell_info *info); +int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner); +#define nvmem_layout_register(layout) \ + __nvmem_layout_register(layout, THIS_MODULE) +void nvmem_layout_unregister(struct nvmem_layout *layout); + +const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout); + #else static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) @@ -179,5 +216,19 @@ static inline int nvmem_add_one_cell(struct nvmem_device *nvmem, return -EOPNOTSUPP; } +static inline int nvmem_layout_register(struct nvmem_layout *layout) +{ + return -EOPNOTSUPP; +} + +static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {} + +static inline const void * +nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout) +{ + return NULL; +} + #endif /* CONFIG_NVMEM */ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ -- cgit v1.2.3 From 6468a6f45148fb5e95c86b4efebf63f9abcd2137 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:22 +0100 Subject: nvmem: core: handle the absence of expected layouts Make nvmem_layout_get() return -EPROBE_DEFER while the expected layout is not available. This condition cannot be triggered today as nvmem layout drivers are initialed as part of an early init call, but soon these drivers will be converted into modules and be initialized with a standard priority, so the unavailability of the drivers might become a reality that must be taken care of. Let's anticipate this by telling the caller the layout might not yet be available. A probe deferral is requested in this case. Please note this does not affect any nvmem device not using layouts, because an early check against the "nvmem-layout" container presence will return NULL in this case. Signed-off-by: Miquel Raynal Tested-by: Michael Walle Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-15-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index b9be1faeb7be..51fd792b8d70 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -755,7 +755,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister); static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) { struct device_node *layout_np, *np = nvmem->dev.of_node; - struct nvmem_layout *l, *layout = NULL; + struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER); layout_np = of_get_child_by_name(np, "nvmem-layout"); if (!layout_np) @@ -938,6 +938,13 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) * pointer will be NULL and nvmem_layout_put() will be a noop. */ nvmem->layout = config->layout ?: nvmem_layout_get(nvmem); + if (IS_ERR(nvmem->layout)) { + rval = PTR_ERR(nvmem->layout); + nvmem->layout = NULL; + + if (rval == -EPROBE_DEFER) + goto err_teardown_compat; + } if (config->cells) { rval = nvmem_add_cells(nvmem, config->cells, config->ncells); @@ -970,6 +977,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) err_remove_cells: nvmem_device_remove_all_cells(nvmem); nvmem_layout_put(nvmem->layout); +err_teardown_compat: if (config->compat) nvmem_sysfs_remove_compat(nvmem, config); err_put_device: -- cgit v1.2.3 From b1c37bec1ccfe5ccab72bc0ddc0dfa45c43e2de2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:23 +0100 Subject: nvmem: core: request layout modules loading When a storage device like an eeprom or an mtd device probes, it registers an nvmem device if the nvmem subsystem has been enabled (bool symbol). During nvmem registration, if the device is using layouts to expose dynamic nvmem cells, the core will first try to get a reference over the layout driver callbacks. In practice there is not relationship that can be described between the storage driver and the nvmem layout. So there is no way we can enforce both drivers will be built-in or both will be modules. If the storage device driver is built-in but the layout is built as a module, instead of badly failing with an endless probe deferral loop, lets just make a modprobe call in case the driver was made available in an initramfs with of_device_node_request_module(), and offer a fully functional system to the user. Signed-off-by: Miquel Raynal Tested-by: Michael Walle Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-16-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 51fd792b8d70..49b4bbaf59e8 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include struct nvmem_device { @@ -761,6 +762,13 @@ static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) if (!layout_np) return NULL; + /* + * In case the nvmem device was built-in while the layout was built as a + * module, we shall manually request the layout driver loading otherwise + * we'll never have any match. + */ + of_request_module(layout_np); + spin_lock(&nvmem_layout_lock); list_for_each_entry(l, &nvmem_layouts, node) { -- cgit v1.2.3 From 345ec382cd4b736c36e01f155d08c913b225b736 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:24 +0100 Subject: nvmem: core: add per-cell post processing Instead of relying on the name the consumer is using for the cell, like it is done for the nvmem .cell_post_process configuration parameter, provide a per-cell post processing hook. This can then be populated by the NVMEM provider (or the NVMEM layout) when adding the cell. Signed-off-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-17-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 17 +++++++++++++++++ include/linux/nvmem-provider.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 49b4bbaf59e8..0708f9f27898 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -54,6 +54,7 @@ struct nvmem_cell_entry { int bytes; int bit_offset; int nbits; + nvmem_cell_post_process_t read_post_process; struct device_node *np; struct nvmem_device *nvmem; struct list_head node; @@ -470,6 +471,7 @@ static int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem, cell->offset = info->offset; cell->bytes = info->bytes; cell->name = info->name; + cell->read_post_process = info->read_post_process; cell->bit_offset = info->bit_offset; cell->nbits = info->nbits; @@ -1563,6 +1565,13 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, if (cell->bit_offset || cell->nbits) nvmem_shift_read_buffer_in_place(cell, buf); + if (cell->read_post_process) { + rc = cell->read_post_process(nvmem->priv, id, index, + cell->offset, buf, cell->bytes); + if (rc) + return rc; + } + if (nvmem->cell_post_process) { rc = nvmem->cell_post_process(nvmem->priv, id, index, cell->offset, buf, cell->bytes); @@ -1671,6 +1680,14 @@ static int __nvmem_cell_entry_write(struct nvmem_cell_entry *cell, void *buf, si (cell->bit_offset == 0 && len != cell->bytes)) return -EINVAL; + /* + * Any cells which have a read_post_process hook are read-only because + * we cannot reverse the operation and it might affect other cells, + * too. + */ + if (cell->read_post_process) + return -EINVAL; + if (cell->bit_offset || cell->nbits) { buf = nvmem_cell_prepare_write_buffer(cell, buf, len); if (IS_ERR(buf)) diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 535c5f9f3309..3bfc23553a9e 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -54,6 +54,8 @@ struct nvmem_keepout { * @bit_offset: Bit offset if cell is smaller than a byte. * @nbits: Number of bits. * @np: Optional device_node pointer. + * @read_post_process: Callback for optional post processing of cell data + * on reads. */ struct nvmem_cell_info { const char *name; @@ -62,6 +64,7 @@ struct nvmem_cell_info { unsigned int bit_offset; unsigned int nbits; struct device_node *np; + nvmem_cell_post_process_t read_post_process; }; /** -- cgit v1.2.3 From de12c9691501ccba41a154c223869f82be4c12fd Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:25 +0100 Subject: nvmem: core: allow to modify a cell before adding it Provide a way to modify a cell before it will get added. This is useful to attach a custom post processing hook via a layout. Signed-off-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-18-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 ++++ include/linux/nvmem-provider.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 0708f9f27898..f43025ad315b 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -695,6 +695,7 @@ static int nvmem_validate_keepouts(struct nvmem_device *nvmem) static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) { + struct nvmem_layout *layout = nvmem->layout; struct device *dev = &nvmem->dev; struct device_node *child; const __be32 *addr; @@ -724,6 +725,9 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) info.np = of_node_get(child); + if (layout && layout->fixup_cell_info) + layout->fixup_cell_info(nvmem, layout, &info); + ret = nvmem_add_one_cell(nvmem, &info); kfree(info.name); if (ret) { diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 3bfc23553a9e..be81cc88eabc 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -155,6 +155,8 @@ struct nvmem_cell_table { * @add_cells: Will be called if a nvmem device is found which * has this layout. The function will add layout * specific cells with nvmem_add_one_cell(). + * @fixup_cell_info: Will be called before a cell is added. Can be + * used to modify the nvmem_cell_info. * @owner: Pointer to struct module. * @node: List node. * @@ -168,6 +170,9 @@ struct nvmem_layout { const struct of_device_id *of_match_table; int (*add_cells)(struct device *dev, struct nvmem_device *nvmem, struct nvmem_layout *layout); + void (*fixup_cell_info)(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell); /* private */ struct module *owner; -- cgit v1.2.3 From 6c56a82d7895a213a43182a5d01a21a906a79847 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:26 +0100 Subject: nvmem: imx-ocotp: replace global post processing with layouts In preparation of retiring the global post processing hook change this driver to use layouts. The layout will be supplied during registration and will be used to add the post processing hook to all added cells. Signed-off-by: Michael Walle Tested-by: Michael Walle # on kontron-pitx-imx8m Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-19-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index e9b52ecb3f72..ac0edb6398f1 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -225,18 +225,13 @@ read_end: static int imx_ocotp_cell_pp(void *context, const char *id, int index, unsigned int offset, void *data, size_t bytes) { - struct ocotp_priv *priv = context; + u8 *buf = data; + int i; /* Deal with some post processing of nvmem cell data */ - if (id && !strcmp(id, "mac-address")) { - if (priv->params->reverse_mac_address) { - u8 *buf = data; - int i; - - for (i = 0; i < bytes/2; i++) - swap(buf[i], buf[bytes - i - 1]); - } - } + if (id && !strcmp(id, "mac-address")) + for (i = 0; i < bytes / 2; i++) + swap(buf[i], buf[bytes - i - 1]); return 0; } @@ -488,7 +483,6 @@ static struct nvmem_config imx_ocotp_nvmem_config = { .stride = 1, .reg_read = imx_ocotp_read, .reg_write = imx_ocotp_write, - .cell_post_process = imx_ocotp_cell_pp, }; static const struct ocotp_params imx6q_params = { @@ -595,6 +589,17 @@ static const struct of_device_id imx_ocotp_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); +static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell) +{ + cell->read_post_process = imx_ocotp_cell_pp; +} + +struct nvmem_layout imx_ocotp_layout = { + .fixup_cell_info = imx_ocotp_fixup_cell_info, +}; + static int imx_ocotp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -619,6 +624,9 @@ static int imx_ocotp_probe(struct platform_device *pdev) imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; imx_ocotp_nvmem_config.dev = dev; imx_ocotp_nvmem_config.priv = priv; + if (priv->params->reverse_mac_address) + imx_ocotp_nvmem_config.layout = &imx_ocotp_layout; + priv->config = &imx_ocotp_nvmem_config; clk_prepare_enable(priv->clk); -- cgit v1.2.3 From 011e40a166fdaa65fb9946b7cd91efec85b70dbb Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:27 +0100 Subject: nvmem: cell: drop global cell_post_process There are no users anymore for the global cell_post_process callback anymore. New users should use proper nvmem layouts. Signed-off-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-20-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 9 --------- include/linux/nvmem-provider.h | 2 -- 2 files changed, 11 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index f43025ad315b..fccb2728193a 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -39,7 +39,6 @@ struct nvmem_device { unsigned int nkeepout; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; - nvmem_cell_post_process_t cell_post_process; struct gpio_desc *wp_gpio; struct nvmem_layout *layout; void *priv; @@ -903,7 +902,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->type = config->type; nvmem->reg_read = config->reg_read; nvmem->reg_write = config->reg_write; - nvmem->cell_post_process = config->cell_post_process; nvmem->keepout = config->keepout; nvmem->nkeepout = config->nkeepout; if (config->of_node) @@ -1576,13 +1574,6 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, return rc; } - if (nvmem->cell_post_process) { - rc = nvmem->cell_post_process(nvmem->priv, id, index, - cell->offset, buf, cell->bytes); - if (rc) - return rc; - } - if (len) *len = cell->bytes; diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index be81cc88eabc..d3d7af86a283 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -85,7 +85,6 @@ struct nvmem_cell_info { * @no_of_node: Device should not use the parent's of_node even if it's !NULL. * @reg_read: Callback to read data. * @reg_write: Callback to write data. - * @cell_post_process: Callback for vendor specific post processing of cell data * @size: Device size. * @word_size: Minimum read/write access granularity. * @stride: Minimum read/write access stride. @@ -118,7 +117,6 @@ struct nvmem_config { bool no_of_node; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; - nvmem_cell_post_process_t cell_post_process; int size; int word_size; int stride; -- cgit v1.2.3 From 8a134fd9f9323f4c39ec27055b3d3723cfb5c1e9 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:28 +0100 Subject: nvmem: core: provide own priv pointer in post process callback It doesn't make any more sense to have a opaque pointer set up by the nvmem device. Usually, the layout isn't associated with a particular nvmem device. Instead, let the caller who set the post process callback provide the priv pointer. Signed-off-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-21-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 +++- include/linux/nvmem-provider.h | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index fccb2728193a..212c5ba5789f 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -54,6 +54,7 @@ struct nvmem_cell_entry { int bit_offset; int nbits; nvmem_cell_post_process_t read_post_process; + void *priv; struct device_node *np; struct nvmem_device *nvmem; struct list_head node; @@ -471,6 +472,7 @@ static int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem, cell->bytes = info->bytes; cell->name = info->name; cell->read_post_process = info->read_post_process; + cell->priv = info->priv; cell->bit_offset = info->bit_offset; cell->nbits = info->nbits; @@ -1568,7 +1570,7 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, nvmem_shift_read_buffer_in_place(cell, buf); if (cell->read_post_process) { - rc = cell->read_post_process(nvmem->priv, id, index, + rc = cell->read_post_process(cell->priv, id, index, cell->offset, buf, cell->bytes); if (rc) return rc; diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index d3d7af86a283..0cf9f9490514 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -20,7 +20,8 @@ typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset, void *val, size_t bytes); /* used for vendor specific post processing of cell data */ typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index, - unsigned int offset, void *buf, size_t bytes); + unsigned int offset, void *buf, + size_t bytes); enum nvmem_type { NVMEM_TYPE_UNKNOWN = 0, @@ -56,6 +57,7 @@ struct nvmem_keepout { * @np: Optional device_node pointer. * @read_post_process: Callback for optional post processing of cell data * on reads. + * @priv: Opaque data passed to the read_post_process hook. */ struct nvmem_cell_info { const char *name; @@ -65,6 +67,7 @@ struct nvmem_cell_info { unsigned int nbits; struct device_node *np; nvmem_cell_post_process_t read_post_process; + void *priv; }; /** -- cgit v1.2.3 From d9fae023fe86069750092fc1c2f3a73e2fb18512 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:29 +0100 Subject: nvmem: layouts: sl28vpd: Add new layout driver This layout applies to the VPD of the Kontron sl28 boards. The VPD only contains a base MAC address. Therefore, we have to add an individual offset to it. This is done by taking the second argument of the nvmem phandle into account. Also this let us checking the VPD version and the checksum. Signed-off-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-22-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/Kconfig | 9 +++ drivers/nvmem/layouts/Makefile | 2 + drivers/nvmem/layouts/sl28vpd.c | 165 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 drivers/nvmem/layouts/sl28vpd.c diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig index 9ad3911d1605..fd161347c129 100644 --- a/drivers/nvmem/layouts/Kconfig +++ b/drivers/nvmem/layouts/Kconfig @@ -2,4 +2,13 @@ menu "Layout Types" +config NVMEM_LAYOUT_SL28_VPD + tristate "Kontron sl28 VPD layout support" + select CRC8 + help + Say Y here if you want to support the VPD layout of the Kontron + SMARC-sAL28 boards. + + If unsure, say N. + endmenu diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile index 6fdb3c60a4fa..fc617b9e87d0 100644 --- a/drivers/nvmem/layouts/Makefile +++ b/drivers/nvmem/layouts/Makefile @@ -2,3 +2,5 @@ # # Makefile for nvmem layouts. # + +obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o diff --git a/drivers/nvmem/layouts/sl28vpd.c b/drivers/nvmem/layouts/sl28vpd.c new file mode 100644 index 000000000000..9370e41bad73 --- /dev/null +++ b/drivers/nvmem/layouts/sl28vpd.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +#define SL28VPD_MAGIC 'V' + +struct sl28vpd_header { + u8 magic; + u8 version; +} __packed; + +struct sl28vpd_v1 { + struct sl28vpd_header header; + char serial_number[15]; + u8 base_mac_address[ETH_ALEN]; + u8 crc8; +} __packed; + +static int sl28vpd_mac_address_pp(void *priv, const char *id, int index, + unsigned int offset, void *buf, + size_t bytes) +{ + if (bytes != ETH_ALEN) + return -EINVAL; + + if (index < 0) + return -EINVAL; + + if (!is_valid_ether_addr(buf)) + return -EINVAL; + + eth_addr_add(buf, index); + + return 0; +} + +static const struct nvmem_cell_info sl28vpd_v1_entries[] = { + { + .name = "serial-number", + .offset = offsetof(struct sl28vpd_v1, serial_number), + .bytes = sizeof_field(struct sl28vpd_v1, serial_number), + }, + { + .name = "base-mac-address", + .offset = offsetof(struct sl28vpd_v1, base_mac_address), + .bytes = sizeof_field(struct sl28vpd_v1, base_mac_address), + .read_post_process = sl28vpd_mac_address_pp, + }, +}; + +static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem) +{ + struct sl28vpd_v1 data_v1; + u8 table[CRC8_TABLE_SIZE]; + int ret; + u8 crc; + + crc8_populate_msb(table, 0x07); + + ret = nvmem_device_read(nvmem, 0, sizeof(data_v1), &data_v1); + if (ret < 0) + return ret; + else if (ret != sizeof(data_v1)) + return -EIO; + + crc = crc8(table, (void *)&data_v1, sizeof(data_v1) - 1, 0); + + if (crc != data_v1.crc8) { + dev_err(dev, + "Checksum is invalid (got %02x, expected %02x).\n", + crc, data_v1.crc8); + return -EINVAL; + } + + return 0; +} + +static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, + struct nvmem_layout *layout) +{ + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; + struct device_node *layout_np; + struct sl28vpd_header hdr; + int ret, i; + + /* check header */ + ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr); + if (ret < 0) + return ret; + else if (ret != sizeof(hdr)) + return -EIO; + + if (hdr.magic != SL28VPD_MAGIC) { + dev_err(dev, "Invalid magic value (%02x)\n", hdr.magic); + return -EINVAL; + } + + if (hdr.version != 1) { + dev_err(dev, "Version %d is unsupported.\n", hdr.version); + return -EINVAL; + } + + ret = sl28vpd_v1_check_crc(dev, nvmem); + if (ret) + return ret; + + layout_np = of_nvmem_layout_get_container(nvmem); + if (!layout_np) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(sl28vpd_v1_entries); i++) { + pinfo = &sl28vpd_v1_entries[i]; + + info.name = pinfo->name; + info.offset = pinfo->offset; + info.bytes = pinfo->bytes; + info.read_post_process = pinfo->read_post_process; + info.np = of_get_child_by_name(layout_np, pinfo->name); + + ret = nvmem_add_one_cell(nvmem, &info); + if (ret) { + of_node_put(layout_np); + return ret; + } + } + + of_node_put(layout_np); + + return 0; +} + +static const struct of_device_id sl28vpd_of_match_table[] = { + { .compatible = "kontron,sl28-vpd" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table); + +struct nvmem_layout sl28vpd_layout = { + .name = "sl28-vpd", + .of_match_table = sl28vpd_of_match_table, + .add_cells = sl28vpd_add_cells, +}; + +static int __init sl28vpd_init(void) +{ + return nvmem_layout_register(&sl28vpd_layout); +} + +static void __exit sl28vpd_exit(void) +{ + nvmem_layout_unregister(&sl28vpd_layout); +} + +module_init(sl28vpd_init); +module_exit(sl28vpd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Walle "); +MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards"); -- cgit v1.2.3 From fe191489d66f2bcd7be3cd78645bcefea5a3f68d Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 4 Apr 2023 18:21:30 +0100 Subject: MAINTAINERS: add myself as sl28vpd nvmem layout driver Add myself as a maintainer for the new sl28vpd nvmem layout driver. Signed-off-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-23-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4d4f778ede5d..a7763063ffa3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19175,6 +19175,12 @@ F: drivers/irqchip/irq-sl28cpld.c F: drivers/pwm/pwm-sl28cpld.c F: drivers/watchdog/sl28cpld_wdt.c +SL28 VPD NVMEM LAYOUT DRIVER +M: Michael Walle +S: Maintained +F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml +F: drivers/nvmem/layouts/sl28vpd.c + SLAB ALLOCATOR M: Christoph Lameter M: Pekka Enberg -- cgit v1.2.3 From d3c0d12f6474216bf386101e2449cc73e5c5b61d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:31 +0100 Subject: nvmem: layouts: onie-tlv: Add new layout driver This layout applies on top of any non volatile storage device containing an ONIE table factory flashed. This table follows the tlv (type-length-value) organization described in the link below. We cannot afford using regular parsers because the content of these tables is manufacturer specific and must be dynamically discovered. Link: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-24-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/Kconfig | 9 ++ drivers/nvmem/layouts/Makefile | 1 + drivers/nvmem/layouts/onie-tlv.c | 257 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 drivers/nvmem/layouts/onie-tlv.c diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig index fd161347c129..7ff1ee1c1f05 100644 --- a/drivers/nvmem/layouts/Kconfig +++ b/drivers/nvmem/layouts/Kconfig @@ -11,4 +11,13 @@ config NVMEM_LAYOUT_SL28_VPD If unsure, say N. +config NVMEM_LAYOUT_ONIE_TLV + tristate "ONIE tlv support" + select CRC32 + help + Say Y here if you want to support the Open Compute Project ONIE + Type-Length-Value standard table. + + If unsure, say N. + endmenu diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile index fc617b9e87d0..2974bd7d33ed 100644 --- a/drivers/nvmem/layouts/Makefile +++ b/drivers/nvmem/layouts/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o +obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o diff --git a/drivers/nvmem/layouts/onie-tlv.c b/drivers/nvmem/layouts/onie-tlv.c new file mode 100644 index 000000000000..d45b7301a69d --- /dev/null +++ b/drivers/nvmem/layouts/onie-tlv.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ONIE tlv NVMEM cells provider + * + * Copyright (C) 2022 Open Compute Group ONIE + * Author: Miquel Raynal + * Based on the nvmem driver written by: Vadym Kochan + * Inspired by the first layout written by: Rafał Miłecki + */ + +#include +#include +#include +#include +#include + +#define ONIE_TLV_MAX_LEN 2048 +#define ONIE_TLV_CRC_FIELD_SZ 6 +#define ONIE_TLV_CRC_SZ 4 +#define ONIE_TLV_HDR_ID "TlvInfo" + +struct onie_tlv_hdr { + u8 id[8]; + u8 version; + __be16 data_len; +} __packed; + +struct onie_tlv { + u8 type; + u8 len; +} __packed; + +static const char *onie_tlv_cell_name(u8 type) +{ + switch (type) { + case 0x21: + return "product-name"; + case 0x22: + return "part-number"; + case 0x23: + return "serial-number"; + case 0x24: + return "mac-address"; + case 0x25: + return "manufacture-date"; + case 0x26: + return "device-version"; + case 0x27: + return "label-revision"; + case 0x28: + return "platform-name"; + case 0x29: + return "onie-version"; + case 0x2A: + return "num-macs"; + case 0x2B: + return "manufacturer"; + case 0x2C: + return "country-code"; + case 0x2D: + return "vendor"; + case 0x2E: + return "diag-version"; + case 0x2F: + return "service-tag"; + case 0xFD: + return "vendor-extension"; + case 0xFE: + return "crc32"; + default: + break; + } + + return NULL; +} + +static int onie_tlv_mac_read_cb(void *priv, const char *id, int index, + unsigned int offset, void *buf, + size_t bytes) +{ + eth_addr_add(buf, index); + + return 0; +} + +static nvmem_cell_post_process_t onie_tlv_read_cb(u8 type, u8 *buf) +{ + switch (type) { + case 0x24: + return &onie_tlv_mac_read_cb; + default: + break; + } + + return NULL; +} + +static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem, + size_t data_len, u8 *data) +{ + struct nvmem_cell_info cell = {}; + struct device_node *layout; + struct onie_tlv tlv; + unsigned int hdr_len = sizeof(struct onie_tlv_hdr); + unsigned int offset = 0; + int ret; + + layout = of_nvmem_layout_get_container(nvmem); + if (!layout) + return -ENOENT; + + while (offset < data_len) { + memcpy(&tlv, data + offset, sizeof(tlv)); + if (offset + tlv.len >= data_len) { + dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n", + tlv.len, hdr_len + offset); + break; + } + + cell.name = onie_tlv_cell_name(tlv.type); + if (!cell.name) + continue; + + cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len); + cell.bytes = tlv.len; + cell.np = of_get_child_by_name(layout, cell.name); + cell.read_post_process = onie_tlv_read_cb(tlv.type, data + offset + sizeof(tlv)); + + ret = nvmem_add_one_cell(nvmem, &cell); + if (ret) { + of_node_put(layout); + return ret; + } + + offset += sizeof(tlv) + tlv.len; + } + + of_node_put(layout); + + return 0; +} + +static bool onie_tlv_hdr_is_valid(struct device *dev, struct onie_tlv_hdr *hdr) +{ + if (memcmp(hdr->id, ONIE_TLV_HDR_ID, sizeof(hdr->id))) { + dev_err(dev, "Invalid header\n"); + return false; + } + + if (hdr->version != 0x1) { + dev_err(dev, "Invalid version number\n"); + return false; + } + + return true; +} + +static bool onie_tlv_crc_is_valid(struct device *dev, size_t table_len, u8 *table) +{ + struct onie_tlv crc_hdr; + u32 read_crc, calc_crc; + __be32 crc_be; + + memcpy(&crc_hdr, table + table_len - ONIE_TLV_CRC_FIELD_SZ, sizeof(crc_hdr)); + if (crc_hdr.type != 0xfe || crc_hdr.len != ONIE_TLV_CRC_SZ) { + dev_err(dev, "Invalid CRC field\n"); + return false; + } + + /* The table contains a JAMCRC, which is XOR'ed compared to the original + * CRC32 implementation as known in the Ethernet world. + */ + memcpy(&crc_be, table + table_len - ONIE_TLV_CRC_SZ, ONIE_TLV_CRC_SZ); + read_crc = be32_to_cpu(crc_be); + calc_crc = crc32(~0, table, table_len - ONIE_TLV_CRC_SZ) ^ 0xFFFFFFFF; + if (read_crc != calc_crc) { + dev_err(dev, "Invalid CRC read: 0x%08x, expected: 0x%08x\n", + read_crc, calc_crc); + return false; + } + + return true; +} + +static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, + struct nvmem_layout *layout) +{ + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; + u8 *table, *data; + int ret; + + ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr); + if (ret < 0) + return ret; + + if (!onie_tlv_hdr_is_valid(dev, &hdr)) { + dev_err(dev, "Invalid ONIE TLV header\n"); + return -EINVAL; + } + + hdr_len = sizeof(hdr.id) + sizeof(hdr.version) + sizeof(hdr.data_len); + data_len = be16_to_cpu(hdr.data_len); + table_len = hdr_len + data_len; + if (table_len > ONIE_TLV_MAX_LEN) { + dev_err(dev, "Invalid ONIE TLV data length\n"); + return -EINVAL; + } + + table = devm_kmalloc(dev, table_len, GFP_KERNEL); + if (!table) + return -ENOMEM; + + ret = nvmem_device_read(nvmem, 0, table_len, table); + if (ret != table_len) + return ret; + + if (!onie_tlv_crc_is_valid(dev, table_len, table)) + return -EINVAL; + + data = table + hdr_len; + ret = onie_tlv_add_cells(dev, nvmem, data_len, data); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id onie_tlv_of_match_table[] = { + { .compatible = "onie,tlv-layout", }, + {}, +}; +MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table); + +static struct nvmem_layout onie_tlv_layout = { + .name = "ONIE tlv layout", + .of_match_table = onie_tlv_of_match_table, + .add_cells = onie_tlv_parse_table, +}; + +static int __init onie_tlv_init(void) +{ + return nvmem_layout_register(&onie_tlv_layout); +} + +static void __exit onie_tlv_exit(void) +{ + nvmem_layout_unregister(&onie_tlv_layout); +} + +module_init(onie_tlv_init); +module_exit(onie_tlv_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Miquel Raynal "); +MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing"); +MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing"); -- cgit v1.2.3 From f126612181fbd706c7a43d6c7d0758898815ad38 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:32 +0100 Subject: MAINTAINERS: Add myself as ONIE tlv NVMEM layout maintainer Following the introduction of the bindings for this NVMEM parser and the layout driver, add myself as maintainer. Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-25-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a7763063ffa3..de05ac9b58ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15547,6 +15547,12 @@ L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/oxp-sensors.c +ONIE TLV NVMEM LAYOUT DRIVER +M: Miquel Raynal +S: Maintained +F: Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml +F: drivers/nvmem/layouts/onie-tlv.c + ONION OMEGA2+ BOARD M: Harvey Hunt L: linux-mips@vger.kernel.org -- cgit v1.2.3 From d14d9f0e737e0602c17f416c26e56e57df4985b1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 4 Apr 2023 18:21:33 +0100 Subject: dt-bindings: nvmem: Drop unneeded quotes Cleanup bindings dropping unneeded quotes. Once all these are fixed, checking for this can be enabled in yamllint. Signed-off-by: Rob Herring Acked-by: Jernej Skrabec Reviewed-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-26-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml | 2 +- Documentation/devicetree/bindings/nvmem/apple,efuses.yaml | 2 +- Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml | 2 +- Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml | 2 +- Documentation/devicetree/bindings/nvmem/imx-iim.yaml | 2 +- Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml | 2 +- Documentation/devicetree/bindings/nvmem/ingenic,jz4780-efuse.yaml | 2 +- Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml | 2 +- Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml | 2 +- Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml | 2 +- Documentation/devicetree/bindings/nvmem/nintendo-otp.yaml | 2 +- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 2 +- Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml | 2 +- Documentation/devicetree/bindings/nvmem/rmem.yaml | 2 +- Documentation/devicetree/bindings/nvmem/rockchip-efuse.yaml | 2 +- Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml | 2 +- Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml | 2 +- Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml index 14c170c6a86e..296001e7f498 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml @@ -11,7 +11,7 @@ maintainers: - Maxime Ripard allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/apple,efuses.yaml b/Documentation/devicetree/bindings/nvmem/apple,efuses.yaml index 5ec8f2bdb3a5..e0860b6b85f3 100644 --- a/Documentation/devicetree/bindings/nvmem/apple,efuses.yaml +++ b/Documentation/devicetree/bindings/nvmem/apple,efuses.yaml @@ -15,7 +15,7 @@ maintainers: - Sven Peter allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml b/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml index 25033de3ef6b..36def7128fca 100644 --- a/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml +++ b/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml @@ -20,7 +20,7 @@ maintainers: - Rafał Miłecki allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml b/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml index 3b4e6e94cb81..70fb2ad25103 100644 --- a/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml +++ b/Documentation/devicetree/bindings/nvmem/fsl,layerscape-sfp.yaml @@ -14,7 +14,7 @@ description: | unique identifier per part. allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/imx-iim.yaml b/Documentation/devicetree/bindings/nvmem/imx-iim.yaml index 7aac1995cfaf..e9d9d8df4811 100644 --- a/Documentation/devicetree/bindings/nvmem/imx-iim.yaml +++ b/Documentation/devicetree/bindings/nvmem/imx-iim.yaml @@ -14,7 +14,7 @@ description: | i.MX25, i.MX27, i.MX31, i.MX35, i.MX51 and i.MX53 SoCs. allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml index d0a239d7e199..9876243ff1e8 100644 --- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml +++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml @@ -15,7 +15,7 @@ description: | i.MX7D/S, i.MX7ULP, i.MX8MQ, i.MX8MM, i.MX8MN and i.MX8MP SoCs. allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/ingenic,jz4780-efuse.yaml b/Documentation/devicetree/bindings/nvmem/ingenic,jz4780-efuse.yaml index fe2cd7f1afba..e89fd879c968 100644 --- a/Documentation/devicetree/bindings/nvmem/ingenic,jz4780-efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/ingenic,jz4780-efuse.yaml @@ -10,7 +10,7 @@ maintainers: - PrasannaKumar Muralidharan allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml index 75e0a516e59a..d16d42fb98b6 100644 --- a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml @@ -15,7 +15,7 @@ maintainers: - Lala Lin allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: $nodename: diff --git a/Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml b/Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml index c3c96fd0baac..a296d348adb4 100644 --- a/Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml +++ b/Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml @@ -15,7 +15,7 @@ description: | settings, chip identifiers) or user specific data could be stored. allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml index ff317fd7c15b..8938eec22b52 100644 --- a/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml +++ b/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml @@ -10,7 +10,7 @@ maintainers: - Anson Huang allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/nintendo-otp.yaml b/Documentation/devicetree/bindings/nvmem/nintendo-otp.yaml index f93bc50c40d7..6c26800f8b79 100644 --- a/Documentation/devicetree/bindings/nvmem/nintendo-otp.yaml +++ b/Documentation/devicetree/bindings/nvmem/nintendo-otp.yaml @@ -17,7 +17,7 @@ maintainers: - Emmanuel Gil Peyrot allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index 2173fe82317d..e952907ad1d5 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -10,7 +10,7 @@ maintainers: - Srinivas Kandagatla allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml b/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml index 4702f10ecca2..dce0c7d84ce7 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml @@ -15,7 +15,7 @@ description: | to/from the PBUS. allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/rmem.yaml b/Documentation/devicetree/bindings/nvmem/rmem.yaml index a4a755dcfc43..38a39c9b8c1c 100644 --- a/Documentation/devicetree/bindings/nvmem/rmem.yaml +++ b/Documentation/devicetree/bindings/nvmem/rmem.yaml @@ -10,7 +10,7 @@ maintainers: - Nicolas Saenz Julienne allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/rockchip-efuse.yaml b/Documentation/devicetree/bindings/nvmem/rockchip-efuse.yaml index febee8129aa9..c5403e149080 100644 --- a/Documentation/devicetree/bindings/nvmem/rockchip-efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/rockchip-efuse.yaml @@ -10,7 +10,7 @@ maintainers: - Heiko Stuebner allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml b/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml index dc790d2cd9f0..b8bca0599c45 100644 --- a/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml @@ -11,7 +11,7 @@ maintainers: - Kunihiko Hayashi allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: "#address-cells": true diff --git a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml index 172597cc5c63..a69de3e92282 100644 --- a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml +++ b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml @@ -16,7 +16,7 @@ maintainers: - Fabrice Gasnier allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml index a7644ebbc2ca..8877c2283e9e 100644 --- a/Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml +++ b/Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml @@ -11,7 +11,7 @@ maintainers: - Vincent Shih allOf: - - $ref: "nvmem.yaml#" + - $ref: nvmem.yaml# properties: compatible: -- cgit v1.2.3 From a4fb434ef96ace5af758ca2c52c3a3f8f3abc87c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 4 Apr 2023 18:21:34 +0100 Subject: nvmem: stm32-romem: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/nvmem/stm32-romem.c:271:34: error: ‘stm32_romem_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-27-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/stm32-romem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvmem/stm32-romem.c b/drivers/nvmem/stm32-romem.c index ba779e26937a..38d0bf557129 100644 --- a/drivers/nvmem/stm32-romem.c +++ b/drivers/nvmem/stm32-romem.c @@ -268,7 +268,7 @@ static const struct stm32_romem_cfg stm32mp13_bsec_cfg = { .ta = true, }; -static const struct of_device_id stm32_romem_of_match[] = { +static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { { .compatible = "st,stm32f4-otp", }, { .compatible = "st,stm32mp15-bsec", .data = (void *)&stm32mp15_bsec_cfg, -- cgit v1.2.3 From de6e05097f7db066afb0ad4c88b730949f7b7749 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 4 Apr 2023 18:21:35 +0100 Subject: nvmem: mtk-efuse: Support postprocessing for GPU speed binning data On some MediaTek SoCs GPU speed binning data is available for read in the SoC's eFuse array but it has a format that is incompatible with what the OPP API expects, as we read a number from 0 to 7 but opp-supported-hw is expecting a bitmask to enable an OPP entry: being what we read limited to 0-7, it's straightforward to simply convert the value to BIT(value) as a post-processing action. So, introduce post-processing support and enable it by evaluating the newly introduced platform data's `uses_post_processing` member, currently enabled only for MT8186. Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-28-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/mtk-efuse.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index a08e0aedd21c..b36cd0dcc8c7 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -10,6 +10,11 @@ #include #include #include +#include + +struct mtk_efuse_pdata { + bool uses_post_processing; +}; struct mtk_efuse_priv { void __iomem *base; @@ -29,6 +34,37 @@ static int mtk_reg_read(void *context, return 0; } +static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index, + unsigned int offset, void *data, size_t bytes) +{ + u8 *val = data; + + if (val[0] < 8) + val[0] = BIT(val[0]); + + return 0; +} + +static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell) +{ + size_t sz = strlen(cell->name); + + /* + * On some SoCs, the GPU speedbin is not read as bitmask but as + * a number with range [0-7] (max 3 bits): post process to use + * it in OPP tables to describe supported-hw. + */ + if (cell->nbits <= 3 && + strncmp(cell->name, "gpu-speedbin", min(sz, strlen("gpu-speedbin"))) == 0) + cell->read_post_process = mtk_efuse_gpu_speedbin_pp; +} + +static struct nvmem_layout mtk_efuse_layout = { + .fixup_cell_info = mtk_efuse_fixup_cell_info, +}; + static int mtk_efuse_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -36,6 +72,7 @@ static int mtk_efuse_probe(struct platform_device *pdev) struct nvmem_device *nvmem; struct nvmem_config econfig = {}; struct mtk_efuse_priv *priv; + const struct mtk_efuse_pdata *pdata; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -45,20 +82,32 @@ static int mtk_efuse_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + pdata = device_get_match_data(dev); econfig.stride = 1; econfig.word_size = 1; econfig.reg_read = mtk_reg_read; econfig.size = resource_size(res); econfig.priv = priv; econfig.dev = dev; + if (pdata->uses_post_processing) + econfig.layout = &mtk_efuse_layout; nvmem = devm_nvmem_register(dev, &econfig); return PTR_ERR_OR_ZERO(nvmem); } +static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = { + .uses_post_processing = true, +}; + +static const struct mtk_efuse_pdata mtk_efuse_pdata = { + .uses_post_processing = false, +}; + static const struct of_device_id mtk_efuse_of_match[] = { - { .compatible = "mediatek,mt8173-efuse",}, - { .compatible = "mediatek,efuse",}, + { .compatible = "mediatek,mt8173-efuse", .data = &mtk_efuse_pdata }, + { .compatible = "mediatek,mt8186-efuse", .data = &mtk_mt8186_efuse_pdata }, + { .compatible = "mediatek,efuse", .data = &mtk_efuse_pdata }, {/* sentinel */}, }; MODULE_DEVICE_TABLE(of, mtk_efuse_of_match); -- cgit v1.2.3 From 796d160c4ed032c66415120d67aea8ecd502879f Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 4 Apr 2023 18:21:36 +0100 Subject: dt-bindings: nvmem: Add compatible for SM6350 Docuemnt the QFPROM on SM6350. Signed-off-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-29-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index e952907ad1d5..c20bd9bdcea4 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -32,6 +32,7 @@ properties: - qcom,sdm670-qfprom - qcom,sdm845-qfprom - qcom,sm6115-qfprom + - qcom,sm6350-qfprom - qcom,sm8150-qfprom - qcom,sm8250-qfprom - const: qcom,qfprom -- cgit v1.2.3 From cc25eba0b88200730ce03b066f7569ca6c29605d Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 4 Apr 2023 18:21:37 +0100 Subject: dt-bindings: nvmem: Add compatible for SM6375 Docuemnt the QFPROM on SM6375. Signed-off-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-30-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index c20bd9bdcea4..8d8503dd934b 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -33,6 +33,7 @@ properties: - qcom,sdm845-qfprom - qcom,sm6115-qfprom - qcom,sm6350-qfprom + - qcom,sm6375-qfprom - qcom,sm8150-qfprom - qcom,sm8250-qfprom - const: qcom,qfprom -- cgit v1.2.3 From 1dc552fa33cf98af3e784dbc0500da93cae3b24a Mon Sep 17 00:00:00 2001 From: Yang Li Date: Tue, 4 Apr 2023 18:21:38 +0100 Subject: nvmem: bcm-ocotp: Use devm_platform_ioremap_resource() According to commit 7945f929f1a7 ("drivers: provide devm_platform_ioremap_resource()"), convert platform_get_resource(), devm_ioremap_resource() to a single call to use devm_platform_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yang Li Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-31-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/bcm-ocotp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c index a128c7f5e351..0c1fa0c4feb2 100644 --- a/drivers/nvmem/bcm-ocotp.c +++ b/drivers/nvmem/bcm-ocotp.c @@ -244,7 +244,6 @@ MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_ids); static int bcm_otpc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct otpc_priv *priv; struct nvmem_device *nvmem; int err; @@ -259,8 +258,7 @@ static int bcm_otpc_probe(struct platform_device *pdev) return -ENODEV; /* Get OTP base address register. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { dev_err(dev, "unable to map I/O memory\n"); return PTR_ERR(priv->base); -- cgit v1.2.3 From 649409990d2e93fac657be7c6960c28a2c601d65 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Tue, 4 Apr 2023 18:21:39 +0100 Subject: nvmem: nintendo-otp: Use devm_platform_ioremap_resource() According to commit 7945f929f1a7 ("drivers: provide devm_platform_ioremap_resource()"), convert platform_get_resource(), devm_ioremap_resource() to a single call to use devm_platform_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yang Li Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-32-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/nintendo-otp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/nvmem/nintendo-otp.c b/drivers/nvmem/nintendo-otp.c index 33961b17f9f1..355e7f1fc6d5 100644 --- a/drivers/nvmem/nintendo-otp.c +++ b/drivers/nvmem/nintendo-otp.c @@ -76,7 +76,6 @@ static int nintendo_otp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct of_device_id *of_id = of_match_device(nintendo_otp_of_table, dev); - struct resource *res; struct nvmem_device *nvmem; struct nintendo_otp_priv *priv; @@ -92,8 +91,7 @@ static int nintendo_otp_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->regs = devm_ioremap_resource(dev, res); + priv->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->regs)) return PTR_ERR(priv->regs); -- cgit v1.2.3 From c2367aa60d5e34d48582362c6de34b4131d92be7 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Tue, 4 Apr 2023 18:21:40 +0100 Subject: nvmem: vf610-ocotp: Use devm_platform_get_and_ioremap_resource() According to commit 890cc39a8799 ("drivers: provide devm_platform_get_and_ioremap_resource()"), convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yang Li Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-33-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/vf610-ocotp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c index 5b6cad16892f..ee9c61ae727d 100644 --- a/drivers/nvmem/vf610-ocotp.c +++ b/drivers/nvmem/vf610-ocotp.c @@ -219,8 +219,7 @@ static int vf610_ocotp_probe(struct platform_device *pdev) if (!ocotp_dev) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ocotp_dev->base = devm_ioremap_resource(dev, res); + ocotp_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(ocotp_dev->base)) return PTR_ERR(ocotp_dev->base); -- cgit v1.2.3 From 7e2805c203a6c8dc85c1cfda205161ed39ae82d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 4 Apr 2023 18:21:41 +0100 Subject: dt-bindings: nvmem: u-boot,env: add MAC's #nvmem-cell-cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit U-Boot's "ethaddr" environment variable is very often used to store *base* MAC address. It's used as a base for calculating addresses for multiple interfaces. It's done by adding proper values. Actual offsets are picked by manufacturers and vary across devices. Signed-off-by: Rafał Miłecki Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-34-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/u-boot,env.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/nvmem/u-boot,env.yaml b/Documentation/devicetree/bindings/nvmem/u-boot,env.yaml index cbc5c69fd405..36d97fb87865 100644 --- a/Documentation/devicetree/bindings/nvmem/u-boot,env.yaml +++ b/Documentation/devicetree/bindings/nvmem/u-boot,env.yaml @@ -50,7 +50,11 @@ properties: ethaddr: type: object - description: Ethernet interface's MAC address + description: Ethernet interfaces base MAC address. + properties: + "#nvmem-cell-cells": + description: The first argument is a MAC address offset. + const: 1 additionalProperties: false @@ -72,6 +76,7 @@ examples: reg = <0x40000 0x10000>; mac: ethaddr { + #nvmem-cell-cells = <1>; }; }; }; -- cgit v1.2.3 From 55d4980ce55b6bb4be66877de4dbec513911b988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 4 Apr 2023 18:21:42 +0100 Subject: nvmem: core: support specifying both: cell raw data & post read lengths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Callback .read_post_process() is designed to modify raw cell content before providing it to the consumer. So far we were dealing with modifications that didn't affect cell size (length). In some cases however cell content needs to be reformatted and resized. It's required e.g. to provide properly formatted MAC address in case it's stored in a non-binary format (e.g. using ASCII). There were few discussions how to optimally handle that. Following possible solutions were considered: 1. Allow .read_post_process() to realloc (resize) content buffer 2. Allow .read_post_process() to adjust (decrease) just buffer length 3. Register NVMEM cells using post-read sizes The preferred solution was the last one. The problem is that simply adjusting "bytes" in NVMEM providers would result in core code NOT passing whole raw data to .read_post_process() callbacks. It means callback functions couldn't do their job without somehow manually reading original cell content on their own. This patch deals with that by registering NVMEM cells with both lengths: raw content one and post read one. It allows: 1. Core code to read whole raw cell content 2. Callbacks to return content they want Signed-off-by: Rafał Miłecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-35-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 11 +++++++---- include/linux/nvmem-provider.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 212c5ba5789f..a62973d010ff 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -50,6 +50,7 @@ struct nvmem_device { struct nvmem_cell_entry { const char *name; int offset; + size_t raw_len; int bytes; int bit_offset; int nbits; @@ -469,6 +470,7 @@ static int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem, { cell->nvmem = nvmem; cell->offset = info->offset; + cell->raw_len = info->raw_len ?: info->bytes; cell->bytes = info->bytes; cell->name = info->name; cell->read_post_process = info->read_post_process; @@ -1560,7 +1562,7 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, { int rc; - rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes); + rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->raw_len); if (rc) return rc; @@ -1571,7 +1573,7 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, if (cell->read_post_process) { rc = cell->read_post_process(cell->priv, id, index, - cell->offset, buf, cell->bytes); + cell->offset, buf, cell->raw_len); if (rc) return rc; } @@ -1594,14 +1596,15 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, */ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) { - struct nvmem_device *nvmem = cell->entry->nvmem; + struct nvmem_cell_entry *entry = cell->entry; + struct nvmem_device *nvmem = entry->nvmem; u8 *buf; int rc; if (!nvmem) return ERR_PTR(-EINVAL); - buf = kzalloc(cell->entry->bytes, GFP_KERNEL); + buf = kzalloc(max_t(size_t, entry->raw_len, entry->bytes), GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 0cf9f9490514..8ffb42ba0f62 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -51,6 +51,7 @@ struct nvmem_keepout { * struct nvmem_cell_info - NVMEM cell description * @name: Name. * @offset: Offset within the NVMEM device. + * @raw_len: Length of raw data (without post processing). * @bytes: Length of the cell. * @bit_offset: Bit offset if cell is smaller than a byte. * @nbits: Number of bits. @@ -62,6 +63,7 @@ struct nvmem_keepout { struct nvmem_cell_info { const char *name; unsigned int offset; + size_t raw_len; unsigned int bytes; unsigned int bit_offset; unsigned int nbits; -- cgit v1.2.3 From c49f1a8af6bcf6d18576bca898f8083ca4b129e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 4 Apr 2023 18:21:43 +0100 Subject: nvmem: u-boot-env: post-process "ethaddr" env variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit U-Boot environment variables are stored in ASCII format so "ethaddr" requires parsing into binary to make it work with Ethernet interfaces. This includes support for indexes to support #nvmem-cell-cells = <1>. Signed-off-by: Rafał Miłecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-36-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 1 + drivers/nvmem/u-boot-env.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index a2afba11c890..b291b27048c7 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -340,6 +340,7 @@ config NVMEM_U_BOOT_ENV tristate "U-Boot environment variables support" depends on OF && MTD select CRC32 + select GENERIC_NET_UTILS help U-Boot stores its setup as environment variables. This driver adds support for verifying & exporting such data. It also exposes variables diff --git a/drivers/nvmem/u-boot-env.c b/drivers/nvmem/u-boot-env.c index 29b1d87a3c51..ee9fd9989b6e 100644 --- a/drivers/nvmem/u-boot-env.c +++ b/drivers/nvmem/u-boot-env.c @@ -4,6 +4,8 @@ */ #include +#include +#include #include #include #include @@ -70,6 +72,25 @@ static int u_boot_env_read(void *context, unsigned int offset, void *val, return 0; } +static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index, + unsigned int offset, void *buf, size_t bytes) +{ + u8 mac[ETH_ALEN]; + + if (bytes != 3 * ETH_ALEN - 1) + return -EINVAL; + + if (!mac_pton(buf, mac)) + return -EINVAL; + + if (index) + eth_addr_add(mac, index); + + ether_addr_copy(buf, mac); + + return 0; +} + static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, size_t data_offset, size_t data_len) { @@ -101,6 +122,11 @@ static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, priv->cells[idx].offset = data_offset + value - data; priv->cells[idx].bytes = strlen(value); priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); + if (!strcmp(var, "ethaddr")) { + priv->cells[idx].raw_len = strlen(value); + priv->cells[idx].bytes = ETH_ALEN; + priv->cells[idx].read_post_process = u_boot_env_read_post_process_ethaddr; + } } if (WARN_ON(idx != priv->ncells)) -- cgit v1.2.3 From 814c978f02db17f16e6aa2efa2a929372f06da09 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:44 +0100 Subject: nvmem: Add macro to register nvmem layout drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide a module_nvmem_layout_driver() macro at the end of the nvmem-provider.h header to reduce the boilerplate when registering nvmem layout drivers. Suggested-by: Srinivas Kandagatla Signed-off-by: Miquel Raynal Acked-by: Rafał Miłecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-37-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- include/linux/nvmem-provider.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 8ffb42ba0f62..dae26295e6be 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -9,6 +9,7 @@ #ifndef _LINUX_NVMEM_PROVIDER_H #define _LINUX_NVMEM_PROVIDER_H +#include #include #include #include @@ -242,4 +243,9 @@ nvmem_layout_get_match_data(struct nvmem_device *nvmem, } #endif /* CONFIG_NVMEM */ + +#define module_nvmem_layout_driver(__layout_driver) \ + module_driver(__layout_driver, nvmem_layout_register, \ + nvmem_layout_unregister) + #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ -- cgit v1.2.3 From 0abdf99fe0c86252ba274703425f8d543d7e7f0d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:45 +0100 Subject: nvmem: layouts: sl28vpd: Use module_nvmem_layout_driver() Stop open-coding the module init/exit functions. Use the module_nvmem_layout_driver() instead. Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-38-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/sl28vpd.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/nvmem/layouts/sl28vpd.c b/drivers/nvmem/layouts/sl28vpd.c index 9370e41bad73..9cc1715c2fd5 100644 --- a/drivers/nvmem/layouts/sl28vpd.c +++ b/drivers/nvmem/layouts/sl28vpd.c @@ -146,19 +146,7 @@ struct nvmem_layout sl28vpd_layout = { .of_match_table = sl28vpd_of_match_table, .add_cells = sl28vpd_add_cells, }; - -static int __init sl28vpd_init(void) -{ - return nvmem_layout_register(&sl28vpd_layout); -} - -static void __exit sl28vpd_exit(void) -{ - nvmem_layout_unregister(&sl28vpd_layout); -} - -module_init(sl28vpd_init); -module_exit(sl28vpd_exit); +module_nvmem_layout_driver(sl28vpd_layout); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Walle "); -- cgit v1.2.3 From d119eb38faab61125aaa4f63c74eef61585cf34c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:46 +0100 Subject: nvmem: layouts: onie-tlv: Use module_nvmem_layout_driver() Stop open-coding the module init/exit functions. Use the module_nvmem_layout_driver() instead. Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-39-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/onie-tlv.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/nvmem/layouts/onie-tlv.c b/drivers/nvmem/layouts/onie-tlv.c index d45b7301a69d..661093de33b4 100644 --- a/drivers/nvmem/layouts/onie-tlv.c +++ b/drivers/nvmem/layouts/onie-tlv.c @@ -237,19 +237,7 @@ static struct nvmem_layout onie_tlv_layout = { .of_match_table = onie_tlv_of_match_table, .add_cells = onie_tlv_parse_table, }; - -static int __init onie_tlv_init(void) -{ - return nvmem_layout_register(&onie_tlv_layout); -} - -static void __exit onie_tlv_exit(void) -{ - nvmem_layout_unregister(&onie_tlv_layout); -} - -module_init(onie_tlv_init); -module_exit(onie_tlv_exit); +module_nvmem_layout_driver(onie_tlv_layout); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Miquel Raynal "); -- cgit v1.2.3 From 6b13e4b6a9a45028ac730e550380077df1845912 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 4 Apr 2023 18:21:47 +0100 Subject: nvmem: layouts: onie-tlv: Drop wrong module alias The MODULE_ALIAS macro is misused here as it carries the description. There is currently no relevant alias to provide so let's just drop it. Signed-off-by: Miquel Raynal Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-40-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/onie-tlv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nvmem/layouts/onie-tlv.c b/drivers/nvmem/layouts/onie-tlv.c index 661093de33b4..59fc87ccfcff 100644 --- a/drivers/nvmem/layouts/onie-tlv.c +++ b/drivers/nvmem/layouts/onie-tlv.c @@ -242,4 +242,3 @@ module_nvmem_layout_driver(onie_tlv_layout); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Miquel Raynal "); MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing"); -MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing"); -- cgit v1.2.3 From a8642cd11635a35a5f1dc31857887900d6610778 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 4 Apr 2023 18:21:48 +0100 Subject: nvmem: layouts: sl28vpd: set varaiable sl28vpd_layout storage-class-specifier to static smatch reports drivers/nvmem/layouts/sl28vpd.c:144:21: warning: symbol 'sl28vpd_layout' was not declared. Should it be static? This variable is only used in one file so it should be static. Signed-off-by: Tom Rix Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230404172148.82422-41-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/sl28vpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvmem/layouts/sl28vpd.c b/drivers/nvmem/layouts/sl28vpd.c index 9cc1715c2fd5..05671371f631 100644 --- a/drivers/nvmem/layouts/sl28vpd.c +++ b/drivers/nvmem/layouts/sl28vpd.c @@ -141,7 +141,7 @@ static const struct of_device_id sl28vpd_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table); -struct nvmem_layout sl28vpd_layout = { +static struct nvmem_layout sl28vpd_layout = { .name = "sl28-vpd", .of_match_table = sl28vpd_of_match_table, .add_cells = sl28vpd_add_cells, -- cgit v1.2.3 From c4351b646123536048ea71cbcabe1cbd7f4e40c8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 23 Mar 2023 18:23:56 +0300 Subject: iio: adc: ti-ads1100: fix error code in probe() This code has a copy and paste bug so it accidentally returns "PTR_ERR(data->reg_vdd)" which is a valid pointer cast to int. It should return "ret" instead. Fixes: 541880542f2b ("iio: adc: Add TI ADS1100 and ADS1000") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/36fa2aeb-f392-4793-8b38-ae15514033c8@kili.mountain Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c index 6a478efb108b..6b5aebb82455 100644 --- a/drivers/iio/adc/ti-ads1100.c +++ b/drivers/iio/adc/ti-ads1100.c @@ -344,7 +344,7 @@ static int ads1100_probe(struct i2c_client *client) ret = regulator_enable(data->reg_vdd); if (ret < 0) - return dev_err_probe(dev, PTR_ERR(data->reg_vdd), + return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n"); ret = devm_add_action_or_reset(dev, ads1100_reg_disable, data->reg_vdd); -- cgit v1.2.3 From 87441b31232577050a55503362bba4aad439db48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 27 Mar 2023 16:54:14 +0200 Subject: staging: iio: resolver: ads1210: fix config mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As stated in the device datasheet [1], bits a0 and a1 have to be set to 1 for the configuration mode. [1]: https://www.analog.com/media/en/technical-documentation/data-sheets/ad2s1210.pdf Fixes: b19e9ad5e2cb9 ("staging:iio:resolver:ad2s1210 general driver cleanup") Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20230327145414.1505537-1-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index e4cf42438487..636c45b12843 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -101,7 +101,7 @@ struct ad2s1210_state { static const int ad2s1210_mode_vals[4][2] = { [MOD_POS] = { 0, 0 }, [MOD_VEL] = { 0, 1 }, - [MOD_CONFIG] = { 1, 0 }, + [MOD_CONFIG] = { 1, 1 }, }; static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, -- cgit v1.2.3 From 86fb8b3aebd7f2236c11521ee0c460cceb0e4d6f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Mar 2023 09:46:14 +0200 Subject: dt-bindings: iio: st-sensors: Fix repeated text The description oddly contains a copy of the initial paragraph. Let's not repeat ourselves. Suggested-by: Diederik de Haas Signed-off-by: Linus Walleij Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230329074614.1037625-1-linus.walleij@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/st,st-sensors.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index 5eb71b24a7cb..1ff3afca9149 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -11,9 +11,6 @@ description: The STMicroelectronics sensor devices are pretty straight-forward what type of sensor it is. Note that whilst this covers many STMicro MEMs sensors, some more complex IMUs need their own bindings. - The STMicroelectronics sensor devices are pretty straight-forward I2C or - SPI devices, all sharing the same device tree descriptions no matter what - type of sensor it is. maintainers: - Denis Ciocca -- cgit v1.2.3 From 9740827468cea80c42db29e7171a50e99acf7328 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 6 Apr 2023 10:40:10 -0400 Subject: iio: addac: stx104: Fix race condition for stx104_write_raw() The priv->chan_out_states array and actual DAC value can become mismatched if stx104_write_raw() is called concurrently. Prevent such a race condition by utilizing a mutex. Fixes: 97a445dad37a ("iio: Add IIO support for the DAC on the Apex Embedded Systems STX104") Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/c95c9a77fcef36b2a052282146950f23bbc1ebdc.1680790580.git.william.gray@linaro.org Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/addac/stx104.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index e45b70aa5bb7..4239aafe42fc 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -69,10 +70,12 @@ struct stx104_reg { /** * struct stx104_iio - IIO device private data structure + * @lock: synchronization lock to prevent I/O race conditions * @chan_out_states: channels' output states * @reg: I/O address offset for the device registers */ struct stx104_iio { + struct mutex lock; unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; struct stx104_reg __iomem *reg; }; @@ -178,9 +181,12 @@ static int stx104_write_raw(struct iio_dev *indio_dev, if ((unsigned int)val > 65535) return -EINVAL; + mutex_lock(&priv->lock); + priv->chan_out_states[chan->channel] = val; iowrite16(val, &priv->reg->dac[chan->channel]); + mutex_unlock(&priv->lock); return 0; } return -EINVAL; @@ -351,6 +357,8 @@ static int stx104_probe(struct device *dev, unsigned int id) indio_dev->name = dev_name(dev); + mutex_init(&priv->lock); + /* configure device for software trigger operation */ iowrite8(0, &priv->reg->acr); -- cgit v1.2.3 From 4f9b80aefb9e2f542a49d9ec087cf5919730e1dd Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 6 Apr 2023 10:40:11 -0400 Subject: iio: addac: stx104: Fix race condition when converting analog-to-digital The ADC conversion procedure requires several device I/O operations performed in a particular sequence. If stx104_read_raw() is called concurrently, the ADC conversion procedure could be clobbered. Prevent such a race condition by utilizing a mutex. Fixes: 4075a283ae83 ("iio: stx104: Add IIO support for the ADC channels") Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/2ae5e40eed5006ca735e4c12181a9ff5ced65547.1680790580.git.william.gray@linaro.org Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/addac/stx104.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 4239aafe42fc..8730b79e921c 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -117,6 +117,8 @@ static int stx104_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } + mutex_lock(&priv->lock); + /* select ADC channel */ iowrite8(chan->channel | (chan->channel << 4), ®->achan); @@ -127,6 +129,8 @@ static int stx104_read_raw(struct iio_dev *indio_dev, while (ioread8(®->cir_asr) & BIT(7)); *val = ioread16(®->ssr_ad); + + mutex_unlock(&priv->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: /* get ADC bipolar/unipolar configuration */ -- cgit v1.2.3 From 46a4cac7f841a2a2cb397bcb2fd24324004c853e Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 6 Apr 2023 10:40:12 -0400 Subject: iio: addac: stx104: Use define rather than hardcoded limit for write val The DAC register is 16 bits wide, so the value passed by write_raw() should be checked against that limit. Rather than hardcoding the 16-bit maximum value limit, use a define to improve readability and make the intention of the code clearer. The explicit cast is also avoided by instead explicitly checking for negative values. Suggested-by: Andy Shevchenko Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/4c9f4f1b4a270d133be70c82a091351b531b5e3e.1680790580.git.william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/addac/stx104.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 8730b79e921c..0ed5f71b18cb 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -181,8 +182,7 @@ static int stx104_write_raw(struct iio_dev *indio_dev, return 0; case IIO_CHAN_INFO_RAW: if (chan->output) { - /* DAC can only accept up to a 16-bit value */ - if ((unsigned int)val > 65535) + if (val < 0 || val > U16_MAX) return -EINVAL; mutex_lock(&priv->lock); -- cgit v1.2.3 From a94abc74c5248d3c09bb18aa8437e85a6a31eadd Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 6 Apr 2023 10:40:13 -0400 Subject: iio: addac: stx104: Improve indentation in stx104_write_raw() By bailing out early if chan->output is false for the IIO_CHAN_INFO_RAW, indentation can be decreased by a tab and code readability improved. Suggested-by: Andy Shevchenko Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/487d17da9e2612f3e6b2bd1c3def2fa1b955db9b.1680790580.git.william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/addac/stx104.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 0ed5f71b18cb..81020b710195 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -181,19 +181,19 @@ static int stx104_write_raw(struct iio_dev *indio_dev, return 0; case IIO_CHAN_INFO_RAW: - if (chan->output) { - if (val < 0 || val > U16_MAX) - return -EINVAL; + if (!chan->output) + return -EINVAL; - mutex_lock(&priv->lock); + if (val < 0 || val > U16_MAX) + return -EINVAL; - priv->chan_out_states[chan->channel] = val; - iowrite16(val, &priv->reg->dac[chan->channel]); + mutex_lock(&priv->lock); - mutex_unlock(&priv->lock); - return 0; - } - return -EINVAL; + priv->chan_out_states[chan->channel] = val; + iowrite16(val, &priv->reg->dac[chan->channel]); + + mutex_unlock(&priv->lock); + return 0; } return -EINVAL; -- cgit v1.2.3 From c7301b848191f18719c5a6247300998269059c0b Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 6 Apr 2023 10:40:14 -0400 Subject: iio: addac: stx104: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. In addition, to improve code organization in stx104_probe(), the devm_iio_device_register() call is moved above GPIO configuration in order to keep relevant code closer together. Suggested-by: Andy Shevchenko Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/0bcdfc4738cc019fb2ff83f61eb46a3488bc166d.1680790580.git.william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/addac/Kconfig | 2 + drivers/iio/addac/stx104.c | 438 +++++++++++++++++++++++++-------------------- 2 files changed, 245 insertions(+), 195 deletions(-) diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 2843fcb70e24..877f9124803c 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -35,7 +35,9 @@ config STX104 tristate "Apex Embedded Systems STX104 driver" depends on PC104 && X86 select ISA_BUS_API + select REGMAP_MMIO select GPIOLIB + select GPIO_REGMAP help Say yes here to build support for the Apex Embedded Systems STX104 integrated analog PC/104 card. diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 81020b710195..798f98a8872e 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -3,21 +3,20 @@ * IIO driver for the Apex Embedded Systems STX104 * Copyright (C) 2016 William Breathitt Gray */ +#include #include #include -#include -#include +#include +#include #include #include -#include -#include #include #include #include #include #include #include -#include +#include #include #define STX104_OUT_CHAN(chan) { \ @@ -47,107 +46,207 @@ static unsigned int num_stx104; module_param_hw_array(base, uint, ioport, &num_stx104, 0); MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); -/** - * struct stx104_reg - device register structure - * @ssr_ad: Software Strobe Register and ADC Data - * @achan: ADC Channel - * @dio: Digital I/O - * @dac: DAC Channels - * @cir_asr: Clear Interrupts and ADC Status - * @acr: ADC Control - * @pccr_fsh: Pacer Clock Control and FIFO Status MSB - * @acfg: ADC Configuration - */ -struct stx104_reg { - u16 ssr_ad; - u8 achan; - u8 dio; - u16 dac[2]; - u8 cir_asr; - u8 acr; - u8 pccr_fsh; - u8 acfg; -}; +#define STX104_AIO_BASE 0x0 +#define STX104_SOFTWARE_STROBE STX104_AIO_BASE +#define STX104_ADC_DATA STX104_AIO_BASE +#define STX104_ADC_CHANNEL (STX104_AIO_BASE + 0x2) +#define STX104_DIO_REG (STX104_AIO_BASE + 0x3) +#define STX104_DAC_BASE (STX104_AIO_BASE + 0x4) +#define STX104_ADC_STATUS (STX104_AIO_BASE + 0x8) +#define STX104_ADC_CONTROL (STX104_AIO_BASE + 0x9) +#define STX104_ADC_CONFIGURATION (STX104_AIO_BASE + 0x11) + +#define STX104_AIO_DATA_STRIDE 2 +#define STX104_DAC_OFFSET(_channel) (STX104_DAC_BASE + STX104_AIO_DATA_STRIDE * (_channel)) + +/* ADC Channel */ +#define STX104_FC GENMASK(3, 0) +#define STX104_LC GENMASK(7, 4) +#define STX104_SINGLE_CHANNEL(_channel) \ + (u8_encode_bits(_channel, STX104_FC) | u8_encode_bits(_channel, STX104_LC)) + +/* ADC Status */ +#define STX104_SD BIT(5) +#define STX104_CNV BIT(7) +#define STX104_DIFFERENTIAL 1 + +/* ADC Control */ +#define STX104_ALSS GENMASK(1, 0) +#define STX104_SOFTWARE_TRIGGER u8_encode_bits(0x0, STX104_ALSS) + +/* ADC Configuration */ +#define STX104_GAIN GENMASK(1, 0) +#define STX104_ADBU BIT(2) +#define STX104_BIPOLAR 0 +#define STX104_GAIN_X1 0 +#define STX104_GAIN_X2 1 +#define STX104_GAIN_X4 2 +#define STX104_GAIN_X8 3 /** * struct stx104_iio - IIO device private data structure * @lock: synchronization lock to prevent I/O race conditions - * @chan_out_states: channels' output states - * @reg: I/O address offset for the device registers + * @aio_data_map: Regmap for analog I/O data + * @aio_ctl_map: Regmap for analog I/O control */ struct stx104_iio { struct mutex lock; - unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; - struct stx104_reg __iomem *reg; + struct regmap *aio_data_map; + struct regmap *aio_ctl_map; }; -/** - * struct stx104_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device - * @out_state: output bits state - */ -struct stx104_gpio { - struct gpio_chip chip; - spinlock_t lock; - u8 __iomem *base; - unsigned int out_state; +static const struct regmap_range aio_ctl_wr_ranges[] = { + regmap_reg_range(0x0, 0x0), regmap_reg_range(0x2, 0x2), regmap_reg_range(0x9, 0x9), + regmap_reg_range(0x11, 0x11), +}; +static const struct regmap_range aio_ctl_rd_ranges[] = { + regmap_reg_range(0x2, 0x2), regmap_reg_range(0x8, 0x9), regmap_reg_range(0x11, 0x11), +}; +static const struct regmap_range aio_ctl_volatile_ranges[] = { + regmap_reg_range(0x8, 0x8), +}; +static const struct regmap_access_table aio_ctl_wr_table = { + .yes_ranges = aio_ctl_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_ctl_wr_ranges), +}; +static const struct regmap_access_table aio_ctl_rd_table = { + .yes_ranges = aio_ctl_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_ctl_rd_ranges), +}; +static const struct regmap_access_table aio_ctl_volatile_table = { + .yes_ranges = aio_ctl_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_ctl_volatile_ranges), +}; + +static const struct regmap_config aio_ctl_regmap_config = { + .name = "aio_ctl", + .reg_bits = 8, + .reg_stride = 1, + .reg_base = STX104_AIO_BASE, + .val_bits = 8, + .io_port = true, + .wr_table = &aio_ctl_wr_table, + .rd_table = &aio_ctl_rd_table, + .volatile_table = &aio_ctl_volatile_table, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_range aio_data_wr_ranges[] = { + regmap_reg_range(0x4, 0x6), +}; +static const struct regmap_range aio_data_rd_ranges[] = { + regmap_reg_range(0x0, 0x0), +}; +static const struct regmap_access_table aio_data_wr_table = { + .yes_ranges = aio_data_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_data_wr_ranges), +}; +static const struct regmap_access_table aio_data_rd_table = { + .yes_ranges = aio_data_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(aio_data_rd_ranges), +}; + +static const struct regmap_config aio_data_regmap_config = { + .name = "aio_data", + .reg_bits = 16, + .reg_stride = STX104_AIO_DATA_STRIDE, + .reg_base = STX104_AIO_BASE, + .val_bits = 16, + .io_port = true, + .wr_table = &aio_data_wr_table, + .rd_table = &aio_data_rd_table, + .volatile_table = &aio_data_rd_table, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config dio_regmap_config = { + .name = "dio", + .reg_bits = 8, + .reg_stride = 1, + .reg_base = STX104_DIO_REG, + .val_bits = 8, + .io_port = true, }; static int stx104_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct stx104_iio *const priv = iio_priv(indio_dev); - struct stx104_reg __iomem *const reg = priv->reg; + int err; unsigned int adc_config; - int adbu; - int gain; + unsigned int value; + unsigned int adc_status; switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: - /* get gain configuration */ - adc_config = ioread8(®->acfg); - gain = adc_config & 0x3; + err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config); + if (err) + return err; - *val = 1 << gain; + *val = BIT(u8_get_bits(adc_config, STX104_GAIN)); return IIO_VAL_INT; case IIO_CHAN_INFO_RAW: if (chan->output) { - *val = priv->chan_out_states[chan->channel]; + err = regmap_read(priv->aio_data_map, STX104_DAC_OFFSET(chan->channel), + &value); + if (err) + return err; + *val = value; return IIO_VAL_INT; } mutex_lock(&priv->lock); /* select ADC channel */ - iowrite8(chan->channel | (chan->channel << 4), ®->achan); + err = regmap_write(priv->aio_ctl_map, STX104_ADC_CHANNEL, + STX104_SINGLE_CHANNEL(chan->channel)); + if (err) { + mutex_unlock(&priv->lock); + return err; + } /* trigger ADC sample capture by writing to the 8-bit * Software Strobe Register and wait for completion */ - iowrite8(0, ®->ssr_ad); - while (ioread8(®->cir_asr) & BIT(7)); - - *val = ioread16(®->ssr_ad); + err = regmap_write(priv->aio_ctl_map, STX104_SOFTWARE_STROBE, 0); + if (err) { + mutex_unlock(&priv->lock); + return err; + } + do { + err = regmap_read(priv->aio_ctl_map, STX104_ADC_STATUS, &adc_status); + if (err) { + mutex_unlock(&priv->lock); + return err; + } + } while (u8_get_bits(adc_status, STX104_CNV)); + + err = regmap_read(priv->aio_data_map, STX104_ADC_DATA, &value); + if (err) { + mutex_unlock(&priv->lock); + return err; + } + *val = value; mutex_unlock(&priv->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: /* get ADC bipolar/unipolar configuration */ - adc_config = ioread8(®->acfg); - adbu = !(adc_config & BIT(2)); + err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config); + if (err) + return err; - *val = -32768 * adbu; + *val = (u8_get_bits(adc_config, STX104_ADBU) == STX104_BIPOLAR) ? -32768 : 0; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* get ADC bipolar/unipolar and gain configuration */ - adc_config = ioread8(®->acfg); - adbu = !(adc_config & BIT(2)); - gain = adc_config & 0x3; + err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config); + if (err) + return err; *val = 5; - *val2 = 15 - adbu + gain; + *val2 = (u8_get_bits(adc_config, STX104_ADBU) == STX104_BIPOLAR) ? 14 : 15; + *val2 += u8_get_bits(adc_config, STX104_GAIN); return IIO_VAL_FRACTIONAL_LOG2; } @@ -158,28 +257,29 @@ static int stx104_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct stx104_iio *const priv = iio_priv(indio_dev); + u8 gain; switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: /* Only four gain states (x1, x2, x4, x8) */ switch (val) { case 1: - iowrite8(0, &priv->reg->acfg); + gain = STX104_GAIN_X1; break; case 2: - iowrite8(1, &priv->reg->acfg); + gain = STX104_GAIN_X2; break; case 4: - iowrite8(2, &priv->reg->acfg); + gain = STX104_GAIN_X4; break; case 8: - iowrite8(3, &priv->reg->acfg); + gain = STX104_GAIN_X8; break; default: return -EINVAL; } - return 0; + return regmap_write(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, gain); case IIO_CHAN_INFO_RAW: if (!chan->output) return -EINVAL; @@ -187,13 +287,7 @@ static int stx104_write_raw(struct iio_dev *indio_dev, if (val < 0 || val > U16_MAX) return -EINVAL; - mutex_lock(&priv->lock); - - priv->chan_out_states[chan->channel] = val; - iowrite16(val, &priv->reg->dac[chan->channel]); - - mutex_unlock(&priv->lock); - return 0; + return regmap_write(priv->aio_data_map, STX104_DAC_OFFSET(chan->channel), val); } return -EINVAL; @@ -222,119 +316,66 @@ static const struct iio_chan_spec stx104_channels_diff[] = { STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1) }; -static int stx104_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - /* GPIO 0-3 are input only, while the rest are output only */ - if (offset < 4) - return 1; - - return 0; -} - -static int stx104_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - if (offset >= 4) - return -EINVAL; - - return 0; -} - -static int stx104_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) +static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base, + unsigned int offset, unsigned int *const reg, + unsigned int *const mask) { - if (offset < 4) - return -EINVAL; - - chip->set(chip, offset, value); - return 0; -} - -static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - + /* Output lines are located at same register bit offsets as input lines */ if (offset >= 4) - return -EINVAL; + offset -= 4; - return !!(ioread8(stx104gpio->base) & BIT(offset)); -} - -static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - - *bits = ioread8(stx104gpio->base); + *reg = base; + *mask = BIT(offset); return 0; } -static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - const unsigned int mask = BIT(offset) >> 4; - unsigned long flags; - - if (offset < 4) - return; - - spin_lock_irqsave(&stx104gpio->lock, flags); - - if (value) - stx104gpio->out_state |= mask; - else - stx104gpio->out_state &= ~mask; - - iowrite8(stx104gpio->out_state, stx104gpio->base); - - spin_unlock_irqrestore(&stx104gpio->lock, flags); -} - #define STX104_NGPIO 8 static const char *stx104_names[STX104_NGPIO] = { "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" }; -static void stx104_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) +static int stx104_init_hw(struct stx104_iio *const priv) { - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - unsigned long flags; - - /* verify masked GPIO are output */ - if (!(*mask & 0xF0)) - return; + int err; - *mask >>= 4; - *bits >>= 4; + /* configure device for software trigger operation */ + err = regmap_write(priv->aio_ctl_map, STX104_ADC_CONTROL, STX104_SOFTWARE_TRIGGER); + if (err) + return err; - spin_lock_irqsave(&stx104gpio->lock, flags); + /* initialize gain setting to x1 */ + err = regmap_write(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, STX104_GAIN_X1); + if (err) + return err; - stx104gpio->out_state &= ~*mask; - stx104gpio->out_state |= *mask & *bits; - iowrite8(stx104gpio->out_state, stx104gpio->base); + /* initialize DAC outputs to 0V */ + err = regmap_write(priv->aio_data_map, STX104_DAC_BASE, 0); + if (err) + return err; + err = regmap_write(priv->aio_data_map, STX104_DAC_BASE + STX104_AIO_DATA_STRIDE, 0); + if (err) + return err; - spin_unlock_irqrestore(&stx104gpio->lock, flags); + return 0; } static int stx104_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; struct stx104_iio *priv; - struct stx104_gpio *stx104gpio; + struct gpio_regmap_config gpio_config; + void __iomem *stx104_base; + struct regmap *aio_ctl_map; + struct regmap *aio_data_map; + struct regmap *dio_map; int err; + unsigned int adc_status; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); if (!indio_dev) return -ENOMEM; - stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); - if (!stx104gpio) - return -ENOMEM; - if (!devm_request_region(dev, base[id], STX104_EXTENT, dev_name(dev))) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -342,16 +383,37 @@ static int stx104_probe(struct device *dev, unsigned int id) return -EBUSY; } - priv = iio_priv(indio_dev); - priv->reg = devm_ioport_map(dev, base[id], STX104_EXTENT); - if (!priv->reg) + stx104_base = devm_ioport_map(dev, base[id], STX104_EXTENT); + if (!stx104_base) return -ENOMEM; + aio_ctl_map = devm_regmap_init_mmio(dev, stx104_base, &aio_ctl_regmap_config); + if (IS_ERR(aio_ctl_map)) + return dev_err_probe(dev, PTR_ERR(aio_ctl_map), + "Unable to initialize aio_ctl register map\n"); + + aio_data_map = devm_regmap_init_mmio(dev, stx104_base, &aio_data_regmap_config); + if (IS_ERR(aio_data_map)) + return dev_err_probe(dev, PTR_ERR(aio_data_map), + "Unable to initialize aio_data register map\n"); + + dio_map = devm_regmap_init_mmio(dev, stx104_base, &dio_regmap_config); + if (IS_ERR(dio_map)) + return dev_err_probe(dev, PTR_ERR(dio_map), + "Unable to initialize dio register map\n"); + + priv = iio_priv(indio_dev); + priv->aio_ctl_map = aio_ctl_map; + priv->aio_data_map = aio_data_map; + indio_dev->info = &stx104_info; indio_dev->modes = INDIO_DIRECT_MODE; - /* determine if differential inputs */ - if (ioread8(&priv->reg->cir_asr) & BIT(5)) { + err = regmap_read(aio_ctl_map, STX104_ADC_STATUS, &adc_status); + if (err) + return err; + + if (u8_get_bits(adc_status, STX104_SD) == STX104_DIFFERENTIAL) { indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff); indio_dev->channels = stx104_channels_diff; } else { @@ -363,41 +425,27 @@ static int stx104_probe(struct device *dev, unsigned int id) mutex_init(&priv->lock); - /* configure device for software trigger operation */ - iowrite8(0, &priv->reg->acr); + err = stx104_init_hw(priv); + if (err) + return err; - /* initialize gain setting to x1 */ - iowrite8(0, &priv->reg->acfg); - - /* initialize DAC output to 0V */ - iowrite16(0, &priv->reg->dac[0]); - iowrite16(0, &priv->reg->dac[1]); - - stx104gpio->chip.label = dev_name(dev); - stx104gpio->chip.parent = dev; - stx104gpio->chip.owner = THIS_MODULE; - stx104gpio->chip.base = -1; - stx104gpio->chip.ngpio = STX104_NGPIO; - stx104gpio->chip.names = stx104_names; - stx104gpio->chip.get_direction = stx104_gpio_get_direction; - stx104gpio->chip.direction_input = stx104_gpio_direction_input; - stx104gpio->chip.direction_output = stx104_gpio_direction_output; - stx104gpio->chip.get = stx104_gpio_get; - stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; - stx104gpio->chip.set = stx104_gpio_set; - stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; - stx104gpio->base = &priv->reg->dio; - stx104gpio->out_state = 0x0; - - spin_lock_init(&stx104gpio->lock); - - err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); + err = devm_iio_device_register(dev, indio_dev); + if (err) return err; - } - return devm_iio_device_register(dev, indio_dev); + gpio_config = (struct gpio_regmap_config) { + .parent = dev, + .regmap = dio_map, + .ngpio = STX104_NGPIO, + .names = stx104_names, + .reg_dat_base = GPIO_REGMAP_ADDR(STX104_DIO_REG), + .reg_set_base = GPIO_REGMAP_ADDR(STX104_DIO_REG), + .ngpio_per_reg = STX104_NGPIO, + .reg_mask_xlate = stx104_reg_mask_xlate, + .drvdata = dio_map, + }; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } static struct isa_driver stx104_driver = { -- cgit v1.2.3 From 7c95a3f51a54db694d3eeef60e77e3a8558ef25e Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 6 Apr 2023 10:40:15 -0400 Subject: iio: addac: stx104: Use regmap_read_poll_timeout() for conversion poll ADC sample captures take a certain amount of time to complete after initiated; this conversion time range can be anywhere from 5 microseconds to 53.68 seconds depending on the configuration of the Analog Input Frame Timer register. When the conversion is in progress, the ADC Status register CNV bit is high. Call regmap_read_poll_timeout() to poll until the ADC conversion is completed (or timeout if more than 53.68 seconds passes). Suggested-by: Jonathan Cameron Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/9ef433f107afd1d4dcd2d97ef0e932d7045c2bbd.1680790580.git.william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/addac/stx104.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index 798f98a8872e..d1f7ce033b46 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -205,21 +205,25 @@ static int stx104_read_raw(struct iio_dev *indio_dev, return err; } - /* trigger ADC sample capture by writing to the 8-bit - * Software Strobe Register and wait for completion + /* + * Trigger ADC sample capture by writing to the 8-bit Software Strobe Register and + * wait for completion; the conversion time range is 5 microseconds to 53.68 seconds + * in steps of 25 nanoseconds. The actual Analog Input Frame Timer time interval is + * calculated as: + * ai_time_frame_ns = ( AIFT + 1 ) * ( 25 nanoseconds ). + * Where 0 <= AIFT <= 2147483648. */ err = regmap_write(priv->aio_ctl_map, STX104_SOFTWARE_STROBE, 0); if (err) { mutex_unlock(&priv->lock); return err; } - do { - err = regmap_read(priv->aio_ctl_map, STX104_ADC_STATUS, &adc_status); - if (err) { - mutex_unlock(&priv->lock); - return err; - } - } while (u8_get_bits(adc_status, STX104_CNV)); + err = regmap_read_poll_timeout(priv->aio_ctl_map, STX104_ADC_STATUS, adc_status, + !u8_get_bits(adc_status, STX104_CNV), 0, 53687092); + if (err) { + mutex_unlock(&priv->lock); + return err; + } err = regmap_read(priv->aio_data_map, STX104_ADC_DATA, &value); if (err) { -- cgit v1.2.3 From 543c8f2f3cc4706ab0dd2e70dc87bdcbcf8126b3 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 4 Apr 2023 15:50:13 -0500 Subject: dt-bindings: iio: temperature: ltc2983: Fix child node unevaluated properties The child node schemas are missing 'unevaluatedProperties' constraints, so any unknown properties are allowed. The current structure with multiple patternProperties schemas doesn't work for unevaluatedProperties as each sub-schema is evaluated independently. To fix this, move the sub-schema for all child nodes to a $defs entry and reference it from each named child node. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230404205014.644336-1-robh@kernel.org Signed-off-by: Jonathan Cameron --- .../bindings/iio/temperature/adi,ltc2983.yaml | 65 +++++++++++++--------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml index f44fc32ce87e..e04f961ab92c 100644 --- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml @@ -18,6 +18,28 @@ description: | https://www.analog.com/media/en/technical-documentation/data-sheets/29861fa.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/ltm2985.pdf +$defs: + sensor-node: + type: object + description: Sensor node common constraints + + properties: + reg: + description: + Channel number. Connects the sensor to the channel with this number + of the device. + minimum: 1 + maximum: 20 + + adi,sensor-type: + description: Type of sensor connected to the device. + $ref: /schemas/types.yaml#/definitions/uint32 + + required: + - reg + - adi,sensor-type + + properties: compatible: oneOf: @@ -64,28 +86,10 @@ properties: const: 0 patternProperties: - "@([0-9a-f]+)$": - type: object - description: Sensor. - - properties: - reg: - description: - Channel number. Connects the sensor to the channel with this number - of the device. - minimum: 1 - maximum: 20 - - adi,sensor-type: - description: Type of sensor connected to the device. - $ref: /schemas/types.yaml#/definitions/uint32 - - required: - - reg - - adi,sensor-type - "^thermocouple@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false + description: Thermocouple sensor. properties: @@ -141,7 +145,9 @@ patternProperties: - adi,custom-thermocouple "^diode@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false + description: Diode sensor. properties: @@ -184,7 +190,8 @@ patternProperties: default: 0 "^rtd@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false description: RTD sensor. properties: @@ -282,7 +289,8 @@ patternProperties: - adi,custom-rtd "^thermistor@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false description: Thermistor sensor. properties: @@ -383,7 +391,8 @@ patternProperties: - adi,custom-thermistor "^adc@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false description: Direct ADC sensor. properties: @@ -397,7 +406,8 @@ patternProperties: type: boolean "^temp@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false description: Active analog temperature sensor. properties: @@ -426,7 +436,8 @@ patternProperties: - adi,custom-temp "^rsense@": - type: object + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false description: Sense resistor sensor. properties: -- cgit v1.2.3 From ac2babe70a9b8ab11a408481cad69b71a8b5fa91 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 4 Apr 2023 15:50:14 -0500 Subject: dt-bindings: iio: temperature: ltc2983: Make 'adi,custom-thermocouple' signed The 'adi,custom-thermocouple' property is signed based on the example and driver, so it's type should be int64-matrix rather than uint64-matrix. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230404205014.644336-2-robh@kernel.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml index e04f961ab92c..dbb85135fd66 100644 --- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml @@ -127,7 +127,7 @@ patternProperties: description: Used for digitizing custom thermocouples. See Page 59 of the datasheet. - $ref: /schemas/types.yaml#/definitions/uint64-matrix + $ref: /schemas/types.yaml#/definitions/int64-matrix minItems: 3 maxItems: 64 items: -- cgit v1.2.3 From f1caa90085ef31078076990c2ac64d05dd035278 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 3 Apr 2023 21:38:28 -0400 Subject: iio: dac: set variable max5522_channels storage-class-specifier to static smatch reports drivers/iio/dac/max5522.c:55:28: warning: symbol 'max5522_channels' was not declared. Should it be static? This variable is only used in one file so it should be static. Signed-off-by: Tom Rix Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230404013828.1914523-1-trix@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5522.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 00ba4e98fb9c..05034a306597 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -52,7 +52,7 @@ struct max5522_state { } \ } -const struct iio_chan_spec max5522_channels[] = { +static const struct iio_chan_spec max5522_channels[] = { MAX5522_CHANNEL(0), MAX5522_CHANNEL(1), }; -- cgit v1.2.3 From b27f0b40e49f4b109353c62e8eae74f96f842745 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Sat, 25 Feb 2023 15:56:16 +0200 Subject: doc: Make sysfs-bus-iio doc more exact A few IIC channel descriptions explained used units as: data is in foo "that can be processed into an" [unit] value. The "can be processed into" is quite broad statement as it does not really explain what this processing means. This makes units pretty much useless. After discussion with Jonathan, it seems the units for these channels should also be well-defined as for all other channels. The processing means the standard scale and offset application that is used throughout the IIO. Let's make it more obvious by stating that the units are [unit] after scale ane offset are applied. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/41eafb0caa510cddf650cf5ff940639a184f3005.1677331779.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 6ba34c0d9789..7140e8e7313f 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1807,8 +1807,8 @@ What: /sys/bus/iio/devices/iio:deviceX/out_resistanceX_raw KernelVersion: 4.3 Contact: linux-iio@vger.kernel.org Description: - Raw (unscaled no offset etc.) resistance reading that can be processed - into an ohm value. + Raw (unscaled no offset etc.) resistance reading. + Units after application of scale and offset are ohms. What: /sys/bus/iio/devices/iio:deviceX/heater_enable KernelVersion: 4.1.0 @@ -1894,8 +1894,9 @@ What: /sys/bus/iio/devices/iio:deviceX/in_electricalconductivity_raw KernelVersion: 4.8 Contact: linux-iio@vger.kernel.org Description: - Raw (unscaled no offset etc.) electric conductivity reading that - can be processed to siemens per meter. + Raw (unscaled no offset etc.) electric conductivity reading. + Units after application of scale and offset are siemens per + meter. What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw KernelVersion: 4.10 @@ -1951,8 +1952,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw KernelVersion: 4.18 Contact: linux-iio@vger.kernel.org Description: - Raw (unscaled) phase difference reading from channel Y - that can be processed to radians. + Raw (unscaled) phase difference reading from channel Y. + Units after application of scale and offset are radians. What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input -- cgit v1.2.3 From 38416c28e16890b52fdd5eb73479299ec3f062f3 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 31 Mar 2023 15:40:28 +0300 Subject: iio: light: Add gain-time-scale helpers Some light sensors can adjust both the HW-gain and integration time. There are cases where adjusting the integration time has similar impact to the scale of the reported values as gain setting has. IIO users do typically expect to handle scale by a single writable 'scale' entry. Driver should then adjust the gain/time accordingly. It however is difficult for a driver to know whether it should change gain or integration time to meet the requested scale. Usually it is preferred to have longer integration time which usually improves accuracy, but there may be use-cases where long measurement times can be an issue. Thus it can be preferable to allow also changing the integration time - but mitigate the scale impact by also changing the gain underneath. Eg, if integration time change doubles the measured values, the driver can reduce the HW-gain to half. The theory of the computations of gain-time-scale is simple. However, some people (undersigned) got that implemented wrong for more than once. Add some gain-time-scale helpers in order to not dublicate errors in all drivers needing these computations. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/268d418e7cffcdaa2ece6738478bbc57692c213e.1680263956.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 3 + drivers/iio/Makefile | 1 + drivers/iio/industrialio-gts-helper.c | 1077 +++++++++++++++++++++++++++++++++ include/linux/iio/iio-gts-helper.h | 206 +++++++ 4 files changed, 1287 insertions(+) create mode 100644 drivers/iio/industrialio-gts-helper.c create mode 100644 include/linux/iio/iio-gts-helper.h diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b190846c3dc2..52eb46ef84c1 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -30,6 +30,9 @@ config IIO_CONFIGFS (e.g. software triggers). For more info see Documentation/iio/iio_configfs.rst. +config IIO_GTS_HELPER + tristate + config IIO_TRIGGER bool "Enable triggered sampling support" help diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 3be08cdadd7e..9622347a1c1b 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o +obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c new file mode 100644 index 000000000000..8bb68975b259 --- /dev/null +++ b/drivers/iio/industrialio-gts-helper.c @@ -0,0 +1,1077 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* gain-time-scale conversion helpers for IIO light sensors + * + * Copyright (c) 2023 Matti Vaittinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * iio_gts_get_gain - Convert scale to total gain + * + * Internal helper for converting scale to total gain. + * + * @max: Maximum linearized scale. As an example, when scale is created + * in magnitude of NANOs and max scale is 64.1 - The linearized + * scale is 64 100 000 000. + * @scale: Linearized scale to compute the gain for. + * + * Return: (floored) gain corresponding to the scale. -EINVAL if scale + * is invalid. + */ +static int iio_gts_get_gain(const u64 max, const u64 scale) +{ + u64 full = max; + int tmp = 1; + + if (scale > full || !scale) + return -EINVAL; + + if (U64_MAX - full < scale) { + /* Risk of overflow */ + if (full - scale < scale) + return 1; + + full -= scale; + tmp++; + } + + while (full > scale * (u64)tmp) + tmp++; + + return tmp; +} + +/** + * gain_get_scale_fraction - get the gain or time based on scale and known one + * + * @max: Maximum linearized scale. As an example, when scale is created + * in magnitude of NANOs and max scale is 64.1 - The linearized + * scale is 64 100 000 000. + * @scale: Linearized scale to compute the gain/time for. + * @known: Either integration time or gain depending on which one is known + * @unknown: Pointer to variable where the computed gain/time is stored + * + * Internal helper for computing unknown fraction of total gain. + * Compute either gain or time based on scale and either the gain or time + * depending on which one is known. + * + * Return: 0 on success. + */ +static int gain_get_scale_fraction(const u64 max, u64 scale, int known, + int *unknown) +{ + int tot_gain; + + tot_gain = iio_gts_get_gain(max, scale); + if (tot_gain < 0) + return tot_gain; + + *unknown = tot_gain / known; + + /* We require total gain to be exact multiple of known * unknown */ + if (!*unknown || *unknown * known != tot_gain) + return -EINVAL; + + return 0; +} + +static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler, + int *scale_whole, int *scale_nano) +{ + int frac; + + if (scaler > NANO) + return -EOVERFLOW; + + if (!scaler) + return -EINVAL; + + frac = do_div(lin_scale, scaler); + + *scale_whole = lin_scale; + *scale_nano = frac * (NANO / scaler); + + return 0; +} + +static int iio_gts_linearize(int scale_whole, int scale_nano, + unsigned long scaler, u64 *lin_scale) +{ + /* + * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of + * multiplication followed by division to avoid overflow. + */ + if (scaler > NANO || !scaler) + return -EINVAL; + + *lin_scale = (u64)scale_whole * (u64)scaler + + (u64)(scale_nano / (NANO / scaler)); + + return 0; +} + +/** + * iio_gts_total_gain_to_scale - convert gain to scale + * @gts: Gain time scale descriptor + * @total_gain: the gain to be converted + * @scale_int: Pointer to integral part of the scale (typically val1) + * @scale_nano: Pointer to fractional part of the scale (nano or ppb) + * + * Convert the total gain value to scale. NOTE: This does not separate gain + * generated by HW-gain or integration time. It is up to caller to decide what + * part of the total gain is due to integration time and what due to HW-gain. + * + * Return: 0 on success. Negative errno on failure. + */ +int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, + int *scale_int, int *scale_nano) +{ + u64 tmp; + + tmp = gts->max_scale; + + do_div(tmp, total_gain); + + return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano); +} +EXPORT_SYMBOL_NS_GPL(iio_gts_total_gain_to_scale, IIO_GTS_HELPER); + +/** + * iio_gts_purge_avail_scale_table - free-up the available scale tables + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_scale_table(). + */ +static void iio_gts_purge_avail_scale_table(struct iio_gts *gts) +{ + int i; + + if (gts->per_time_avail_scale_tables) { + for (i = 0; i < gts->num_itime; i++) + kfree(gts->per_time_avail_scale_tables[i]); + + kfree(gts->per_time_avail_scale_tables); + gts->per_time_avail_scale_tables = NULL; + } + + kfree(gts->avail_all_scales_table); + gts->avail_all_scales_table = NULL; + + gts->num_avail_all_scales = 0; +} + +static int iio_gts_gain_cmp(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) +{ + int ret, i, j, new_idx, time_idx; + int *all_gains; + size_t gain_bytes; + + for (i = 0; i < gts->num_itime; i++) { + /* + * Sort the tables for nice output and for easier finding of + * unique values. + */ + sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp, + NULL); + + /* Convert gains to scales */ + for (j = 0; j < gts->num_hwgain; j++) { + ret = iio_gts_total_gain_to_scale(gts, gains[i][j], + &scales[i][2 * j], + &scales[i][2 * j + 1]); + if (ret) + return ret; + } + } + + gain_bytes = array_size(gts->num_hwgain, sizeof(int)); + all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL); + if (!all_gains) + return -ENOMEM; + + /* + * We assume all the gains for same integration time were unique. + * It is likely the first time table had greatest time multiplier as + * the times are in the order of preference and greater times are + * usually preferred. Hence we start from the last table which is likely + * to have the smallest total gains. + */ + time_idx = gts->num_itime - 1; + memcpy(all_gains, gains[time_idx], gain_bytes); + new_idx = gts->num_hwgain; + + while (time_idx--) { + for (j = 0; j < gts->num_hwgain; j++) { + int candidate = gains[time_idx][j]; + int chk; + + if (candidate > all_gains[new_idx - 1]) { + all_gains[new_idx] = candidate; + new_idx++; + + continue; + } + for (chk = 0; chk < new_idx; chk++) + if (candidate <= all_gains[chk]) + break; + + if (candidate == all_gains[chk]) + continue; + + memmove(&all_gains[chk + 1], &all_gains[chk], + (new_idx - chk) * sizeof(int)); + all_gains[chk] = candidate; + new_idx++; + } + } + + gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int), + GFP_KERNEL); + if (!gts->avail_all_scales_table) { + ret = -ENOMEM; + goto free_out; + } + gts->num_avail_all_scales = new_idx; + + for (i = 0; i < gts->num_avail_all_scales; i++) { + ret = iio_gts_total_gain_to_scale(gts, all_gains[i], + >s->avail_all_scales_table[i * 2], + >s->avail_all_scales_table[i * 2 + 1]); + + if (ret) { + kfree(gts->avail_all_scales_table); + gts->num_avail_all_scales = 0; + goto free_out; + } + } + +free_out: + kfree(all_gains); + + return ret; +} + +/** + * iio_gts_build_avail_scale_table - create tables of available scales + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales based on the + * originally given gain and time tables. When both time and gain tables are + * given this results: + * 1. A set of tables representing available scales for each supported + * integration time. + * 2. A single table listing all the unique scales that any combination of + * supported gains and times can provide. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_scale_table() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_scale_table(struct iio_gts *gts) +{ + int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM; + + per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL); + if (!per_time_gains) + return ret; + + per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL); + if (!per_time_scales) + goto free_gains; + + for (i = 0; i < gts->num_itime; i++) { + per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int), + GFP_KERNEL); + if (!per_time_scales[i]) + goto err_free_out; + + per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int), + GFP_KERNEL); + if (!per_time_gains[i]) { + kfree(per_time_scales[i]); + goto err_free_out; + } + + for (j = 0; j < gts->num_hwgain; j++) + per_time_gains[i][j] = gts->hwgain_table[j].gain * + gts->itime_table[i].mul; + } + + ret = gain_to_scaletables(gts, per_time_gains, per_time_scales); + if (ret) + goto err_free_out; + + kfree(per_time_gains); + gts->per_time_avail_scale_tables = per_time_scales; + + return 0; + +err_free_out: + for (i--; i; i--) { + kfree(per_time_scales[i]); + kfree(per_time_gains[i]); + } + kfree(per_time_scales); +free_gains: + kfree(per_time_gains); + + return ret; +} + +/** + * iio_gts_build_avail_time_table - build table of available integration times + * @gts: Gain time scale descriptor + * + * Build the table which can represent the available times to be returned + * to users using the read_avail-callback. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_time_table() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_time_table(struct iio_gts *gts) +{ + int *times, i, j, idx = 0; + + if (!gts->num_itime) + return 0; + + times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL); + if (!times) + return -ENOMEM; + + /* Sort times from all tables to one and remove duplicates */ + for (i = gts->num_itime - 1; i >= 0; i--) { + int new = gts->itime_table[i].time_us; + + if (times[idx] < new) { + times[idx++] = new; + continue; + } + + for (j = 0; j <= idx; j++) { + if (times[j] > new) { + memmove(×[j + 1], ×[j], + (idx - j) * sizeof(int)); + times[j] = new; + idx++; + } + } + } + gts->avail_time_tables = times; + /* + * This is just to survive a unlikely corner-case where times in the + * given time table were not unique. Else we could just trust the + * gts->num_itime. + */ + gts->num_avail_time_tables = idx; + + return 0; +} + +/** + * iio_gts_purge_avail_time_table - free-up the available integration time table + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_time_table(). + */ +static void iio_gts_purge_avail_time_table(struct iio_gts *gts) +{ + if (gts->num_avail_time_tables) { + kfree(gts->avail_time_tables); + gts->avail_time_tables = NULL; + gts->num_avail_time_tables = 0; + } +} + +/** + * iio_gts_build_avail_tables - create tables of available scales and int times + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales and available + * integration times. Availability tables are built based on the originally + * given gain and given time tables. + * + * When both time and gain tables are + * given this results: + * 1. A set of sorted tables representing available scales for each supported + * integration time. + * 2. A single sorted table listing all the unique scales that any combination + * of supported gains and times can provide. + * 3. A sorted table of supported integration times + * + * After these tables are built one can use the iio_gts_all_avail_scales(), + * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to + * implement the read_avail operations. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_tables() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_tables(struct iio_gts *gts) +{ + int ret; + + ret = iio_gts_build_avail_scale_table(gts); + if (ret) + return ret; + + ret = iio_gts_build_avail_time_table(gts); + if (ret) + iio_gts_purge_avail_scale_table(gts); + + return ret; +} + +/** + * iio_gts_purge_avail_tables - free-up the availability tables + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_tables(). Frees both the + * integration time and scale tables. + */ +static void iio_gts_purge_avail_tables(struct iio_gts *gts) +{ + iio_gts_purge_avail_time_table(gts); + iio_gts_purge_avail_scale_table(gts); +} + +static void devm_iio_gts_avail_all_drop(void *res) +{ + iio_gts_purge_avail_tables(res); +} + +/** + * devm_iio_gts_build_avail_tables - manged add availability tables + * @dev: Pointer to the device whose lifetime tables are bound + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales and available + * integration times. Availability tables are built based on the originally + * given gain and given time tables. + * + * When both time and gain tables are given this results: + * 1. A set of sorted tables representing available scales for each supported + * integration time. + * 2. A single sorted table listing all the unique scales that any combination + * of supported gains and times can provide. + * 3. A sorted table of supported integration times + * + * After these tables are built one can use the iio_gts_all_avail_scales(), + * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to + * implement the read_avail operations. + * + * The tables are automatically released upon device detach. + * + * Return: 0 on success. + */ +static int devm_iio_gts_build_avail_tables(struct device *dev, + struct iio_gts *gts) +{ + int ret; + + ret = iio_gts_build_avail_tables(gts); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts); +} + +static int sanity_check_time(const struct iio_itime_sel_mul *t) +{ + if (t->sel < 0 || t->time_us < 0 || t->mul <= 0) + return -EINVAL; + + return 0; +} + +static int sanity_check_gain(const struct iio_gain_sel_pair *g) +{ + if (g->sel < 0 || g->gain <= 0) + return -EINVAL; + + return 0; +} + +static int iio_gts_sanity_check(struct iio_gts *gts) +{ + int g, t, ret; + + if (!gts->num_hwgain && !gts->num_itime) + return -EINVAL; + + for (t = 0; t < gts->num_itime; t++) { + ret = sanity_check_time(>s->itime_table[t]); + if (ret) + return ret; + } + + for (g = 0; g < gts->num_hwgain; g++) { + ret = sanity_check_gain(>s->hwgain_table[g]); + if (ret) + return ret; + } + + for (g = 0; g < gts->num_hwgain; g++) { + for (t = 0; t < gts->num_itime; t++) { + int gain, mul, res; + + gain = gts->hwgain_table[g].gain; + mul = gts->itime_table[t].mul; + + if (check_mul_overflow(gain, mul, &res)) + return -EOVERFLOW; + } + } + + return 0; +} + +static int iio_init_iio_gts(int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts) +{ + int ret; + + memset(gts, 0, sizeof(*gts)); + + ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO, + >s->max_scale); + if (ret) + return ret; + + gts->hwgain_table = gain_tbl; + gts->num_hwgain = num_gain; + gts->itime_table = tim_tbl; + gts->num_itime = num_times; + + return iio_gts_sanity_check(gts); +} + +/** + * devm_iio_init_iio_gts - Initialize the gain-time-scale helper + * @dev: Pointer to the device whose lifetime gts resources are + * bound + * @max_scale_int: integer part of the maximum scale value + * @max_scale_nano: fraction part of the maximum scale value + * @gain_tbl: table describing supported gains + * @num_gain: number of gains in the gain table + * @tim_tbl: table describing supported integration times. Provide + * the integration time table sorted so that the preferred + * integration time is in the first array index. The search + * functions like the + * iio_gts_find_time_and_gain_sel_for_scale() start search + * from first provided time. + * @num_times: number of times in the time table + * @gts: pointer to the helper struct + * + * Initialize the gain-time-scale helper for use. Note, gains, times, selectors + * and multipliers must be positive. Negative values are reserved for error + * checking. The total gain (maximum gain * maximum time multiplier) must not + * overflow int. The allocated resources will be released upon device detach. + * + * Return: 0 on success. + */ +int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts) +{ + int ret; + + ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gain_tbl, + num_gain, tim_tbl, num_times, gts); + if (ret) + return ret; + + return devm_iio_gts_build_avail_tables(dev, gts); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_init_iio_gts, IIO_GTS_HELPER); + +/** + * iio_gts_all_avail_scales - helper for listing all available scales + * @gts: Gain time scale descriptor + * @vals: Returned array of supported scales + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, + int *length) +{ + if (!gts->num_avail_all_scales) + return -EINVAL; + + *vals = gts->avail_all_scales_table; + *type = IIO_VAL_INT_PLUS_NANO; + *length = gts->num_avail_all_scales * 2; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_all_avail_scales, IIO_GTS_HELPER); + +/** + * iio_gts_avail_scales_for_time - list scales for integration time + * @gts: Gain time scale descriptor + * @time: Integration time for which the scales are listed + * @vals: Returned array of supported scales + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Drivers which do not allow scale setting to change integration time can + * use this helper to list only the scales which are valid for given integration + * time. + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, + const int **vals, int *type, int *length) +{ + int i; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].time_us == time) + break; + + if (i == gts->num_itime) + return -EINVAL; + + *vals = gts->per_time_avail_scale_tables[i]; + *type = IIO_VAL_INT_PLUS_NANO; + *length = gts->num_hwgain * 2; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_avail_scales_for_time, IIO_GTS_HELPER); + +/** + * iio_gts_avail_times - helper for listing available integration times + * @gts: Gain time scale descriptor + * @vals: Returned array of supported times + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, + int *length) +{ + if (!gts->num_avail_time_tables) + return -EINVAL; + + *vals = gts->avail_time_tables; + *type = IIO_VAL_INT; + *length = gts->num_avail_time_tables; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_avail_times, IIO_GTS_HELPER); + +/** + * iio_gts_find_sel_by_gain - find selector corresponding to a HW-gain + * @gts: Gain time scale descriptor + * @gain: HW-gain for which matching selector is searched for + * + * Return: a selector matching given HW-gain or -EINVAL if selector was + * not found. + */ +int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain) +{ + int i; + + for (i = 0; i < gts->num_hwgain; i++) + if (gts->hwgain_table[i].gain == gain) + return gts->hwgain_table[i].sel; + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_sel_by_gain, IIO_GTS_HELPER); + +/** + * iio_gts_find_gain_by_sel - find HW-gain corresponding to a selector + * @gts: Gain time scale descriptor + * @sel: selector for which matching HW-gain is searched for + * + * Return: a HW-gain matching given selector or -EINVAL if HW-gain was not + * found. + */ +int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel) +{ + int i; + + for (i = 0; i < gts->num_hwgain; i++) + if (gts->hwgain_table[i].sel == sel) + return gts->hwgain_table[i].gain; + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_by_sel, IIO_GTS_HELPER); + +/** + * iio_gts_get_min_gain - find smallest valid HW-gain + * @gts: Gain time scale descriptor + * + * Return: The smallest HW-gain -EINVAL if no HW-gains were in the tables. + */ +int iio_gts_get_min_gain(struct iio_gts *gts) +{ + int i, min = -EINVAL; + + for (i = 0; i < gts->num_hwgain; i++) { + int gain = gts->hwgain_table[i].gain; + + if (min == -EINVAL) + min = gain; + else + min = min(min, gain); + } + + return min; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_get_min_gain, IIO_GTS_HELPER); + +/** + * iio_find_closest_gain_low - Find the closest lower matching gain + * @gts: Gain time scale descriptor + * @gain: HW-gain for which the closest match is searched + * @in_range: indicate if the @gain was actually in the range of + * supported gains. + * + * Search for closest supported gain that is lower than or equal to the + * gain given as a parameter. This is usable for drivers which do not require + * user to request exact matching gain but rather for rounding to a supported + * gain value which is equal or lower (setting lower gain is typical for + * avoiding saturation) + * + * Return: The closest matching supported gain or -EINVAL if @gain + * was smaller than the smallest supported gain. + */ +int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range) +{ + int i, diff = 0; + int best = -1; + + *in_range = false; + + for (i = 0; i < gts->num_hwgain; i++) { + if (gain == gts->hwgain_table[i].gain) { + *in_range = true; + return gain; + } + + if (gain > gts->hwgain_table[i].gain) { + if (!diff) { + diff = gain - gts->hwgain_table[i].gain; + best = i; + } else { + int tmp = gain - gts->hwgain_table[i].gain; + + if (tmp < diff) { + diff = tmp; + best = i; + } + } + } else { + /* + * We found valid HW-gain which is greater than + * reference. So, unless we return a failure below we + * will have found an in-range gain + */ + *in_range = true; + } + } + /* The requested gain was smaller than anything we support */ + if (!diff) { + *in_range = false; + + return -EINVAL; + } + + return gts->hwgain_table[best].gain; +} +EXPORT_SYMBOL_NS_GPL(iio_find_closest_gain_low, IIO_GTS_HELPER); + +static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts, + int sel) +{ + const struct iio_itime_sel_mul *time; + + time = iio_gts_find_itime_by_sel(gts, sel); + if (!time) + return -EINVAL; + + return time->mul; +} + +/** + * iio_gts_find_gain_for_scale_using_time - Find gain by time and scale + * @gts: Gain time scale descriptor + * @time_sel: Integration time selector corresponding to the time gain is + * searched for + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain: Pointer to value where gain is stored. + * + * In some cases the light sensors may want to find a gain setting which + * corresponds given scale and integration time. Sensors which fill the + * gain and time tables may use this helper to retrieve the gain. + * + * Return: 0 on success. -EINVAL if gain matching the parameters is not + * found. + */ +static int iio_gts_find_gain_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain) +{ + u64 scale_linear; + int ret, mul; + + ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear); + if (ret) + return ret; + + ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel); + if (ret < 0) + return ret; + + mul = ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *gain)) + return -EINVAL; + + return 0; +} + +/** + * iio_gts_find_gain_sel_for_scale_using_time - Fetch gain selector. + * @gts: Gain time scale descriptor + * @time_sel: Integration time selector corresponding to the time gain is + * searched for + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain_sel: Pointer to value where gain selector is stored. + * + * See iio_gts_find_gain_for_scale_using_time() for more information + */ +int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain_sel) +{ + int gain, ret; + + ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int, + scale_nano, &gain); + if (ret) + return ret; + + ret = iio_gts_find_sel_by_gain(gts, gain); + if (ret < 0) + return ret; + + *gain_sel = ret; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, IIO_GTS_HELPER); + +static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) +{ + const struct iio_itime_sel_mul *itime; + + if (!iio_gts_valid_gain(gts, gain)) + return -EINVAL; + + if (!gts->num_itime) + return gain; + + itime = iio_gts_find_itime_by_time(gts, time); + if (!itime) + return -EINVAL; + + return gain * itime->mul; +} + +static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time, + u64 *scale) +{ + int total_gain; + u64 tmp; + + total_gain = iio_gts_get_total_gain(gts, gain, time); + if (total_gain < 0) + return total_gain; + + tmp = gts->max_scale; + + do_div(tmp, total_gain); + + *scale = tmp; + + return 0; +} + +/** + * iio_gts_get_scale - get scale based on integration time and HW-gain + * @gts: Gain time scale descriptor + * @gain: HW-gain for which the scale is computed + * @time: Integration time for which the scale is computed + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * + * Compute scale matching the integration time and HW-gain given as parameter. + * + * Return: 0 on success. + */ +int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, + int *scale_nano) +{ + u64 lin_scale; + int ret; + + ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale); + if (ret) + return ret; + + return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano); +} +EXPORT_SYMBOL_NS_GPL(iio_gts_get_scale, IIO_GTS_HELPER); + +/** + * iio_gts_find_new_gain_sel_by_old_gain_time - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time_sel: Selector corresponding previously set time + * @new_time_sel: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * + * We may want to mitigate the scale change caused by setting a new integration + * time (for a light sensor) by also updating the (HW)gain. This helper computes + * new gain value to maintain the scale with new integration time. + * + * Return: 0 if an exactly matching supported new gain was found. When a + * non-zero value is returned, the @new_gain will be set to a negative or + * positive value. The negative value means that no gain could be computed. + * Positive value will be the "best possible new gain there could be". There + * can be two reasons why finding the "best possible" new gain is not deemed + * successful. 1) This new value cannot be supported by the hardware. 2) The new + * gain required to maintain the scale would not be an integer. In this case, + * the "best possible" new gain will be a floored optimal gain, which may or + * may not be supported by the hardware. + */ +int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, + int old_gain, int old_time_sel, + int new_time_sel, int *new_gain) +{ + const struct iio_itime_sel_mul *itime_old, *itime_new; + u64 scale; + int ret; + + *new_gain = -1; + + itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel); + if (!itime_old) + return -EINVAL; + + itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel); + if (!itime_new) + return -EINVAL; + + ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us, + &scale); + if (ret) + return ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, + new_gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *new_gain)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_sel_by_old_gain_time, IIO_GTS_HELPER); + +/** + * iio_gts_find_new_gain_by_old_gain_time - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time: Selector corresponding previously set time + * @new_time: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * + * We may want to mitigate the scale change caused by setting a new integration + * time (for a light sensor) by also updating the (HW)gain. This helper computes + * new gain value to maintain the scale with new integration time. + * + * Return: 0 if an exactly matching supported new gain was found. When a + * non-zero value is returned, the @new_gain will be set to a negative or + * positive value. The negative value means that no gain could be computed. + * Positive value will be the "best possible new gain there could be". There + * can be two reasons why finding the "best possible" new gain is not deemed + * successful. 1) This new value cannot be supported by the hardware. 2) The new + * gain required to maintain the scale would not be an integer. In this case, + * the "best possible" new gain will be a floored optimal gain, which may or + * may not be supported by the hardware. + */ +int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain) +{ + const struct iio_itime_sel_mul *itime_new; + u64 scale; + int ret; + + *new_gain = -1; + + itime_new = iio_gts_find_itime_by_time(gts, new_time); + if (!itime_new) + return -EINVAL; + + ret = iio_gts_get_scale_linear(gts, old_gain, old_time, &scale); + if (ret) + return ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, + new_gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *new_gain)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, IIO_GTS_HELPER); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); diff --git a/include/linux/iio/iio-gts-helper.h b/include/linux/iio/iio-gts-helper.h new file mode 100644 index 000000000000..dd64e544a3da --- /dev/null +++ b/include/linux/iio/iio-gts-helper.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* gain-time-scale conversion helpers for IIO light sensors + * + * Copyright (c) 2023 Matti Vaittinen + */ + +#ifndef __IIO_GTS_HELPER__ +#define __IIO_GTS_HELPER__ + +#include + +struct device; + +/** + * struct iio_gain_sel_pair - gain - selector values + * + * In many cases devices like light sensors allow setting signal amplification + * (gain) using a register interface. This structure describes amplification + * and corresponding selector (register value) + * + * @gain: Gain (multiplication) value. Gain must be positive, negative + * values are reserved for error handling. + * @sel: Selector (usually register value) used to indicate this gain. + * NOTE: Only selectors >= 0 supported. + */ +struct iio_gain_sel_pair { + int gain; + int sel; +}; + +/** + * struct iio_itime_sel_mul - integration time description + * + * In many cases devices like light sensors allow setting the duration of + * collecting data. Typically this duration has also an impact to the magnitude + * of measured values (gain). This structure describes the relation of + * integration time and amplification as well as corresponding selector + * (register value). + * + * An example could be a sensor allowing 50, 100, 200 and 400 mS times. The + * respective multiplication values could be 50 mS => 1, 100 mS => 2, + * 200 mS => 4 and 400 mS => 8 assuming the impact of integration time would be + * linear in a way that when collecting data for 50 mS caused value X, doubling + * the data collection time caused value 2X etc. + * + * @time_us: Integration time in microseconds. Time values must be positive, + * negative values are reserved for error handling. + * @sel: Selector (usually register value) used to indicate this time + * NOTE: Only selectors >= 0 supported. + * @mul: Multiplication to the values caused by this time. + * NOTE: Only multipliers > 0 supported. + */ +struct iio_itime_sel_mul { + int time_us; + int sel; + int mul; +}; + +struct iio_gts { + u64 max_scale; + const struct iio_gain_sel_pair *hwgain_table; + int num_hwgain; + const struct iio_itime_sel_mul *itime_table; + int num_itime; + int **per_time_avail_scale_tables; + int *avail_all_scales_table; + int num_avail_all_scales; + int *avail_time_tables; + int num_avail_time_tables; +}; + +#define GAIN_SCALE_GAIN(_gain, _sel) \ +{ \ + .gain = (_gain), \ + .sel = (_sel), \ +} + +#define GAIN_SCALE_ITIME_US(_itime, _sel, _mul) \ +{ \ + .time_us = (_itime), \ + .sel = (_sel), \ + .mul = (_mul), \ +} + +static inline const struct iio_itime_sel_mul * +iio_gts_find_itime_by_time(struct iio_gts *gts, int time) +{ + int i; + + if (!gts->num_itime) + return NULL; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].time_us == time) + return >s->itime_table[i]; + + return NULL; +} + +static inline const struct iio_itime_sel_mul * +iio_gts_find_itime_by_sel(struct iio_gts *gts, int sel) +{ + int i; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].sel == sel) + return >s->itime_table[i]; + + return NULL; +} + +int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts); +/** + * iio_gts_find_int_time_by_sel - find integration time matching a selector + * @gts: Gain time scale descriptor + * @sel: selector for which matching integration time is searched for + * + * Return: integration time matching given selector or -EINVAL if + * integration time was not found. + */ +static inline int iio_gts_find_int_time_by_sel(struct iio_gts *gts, int sel) +{ + const struct iio_itime_sel_mul *itime; + + itime = iio_gts_find_itime_by_sel(gts, sel); + if (!itime) + return -EINVAL; + + return itime->time_us; +} + +/** + * iio_gts_find_sel_by_int_time - find selector matching integration time + * @gts: Gain time scale descriptor + * @gain: HW-gain for which matching selector is searched for + * + * Return: a selector matching given integration time or -EINVAL if + * selector was not found. + */ +static inline int iio_gts_find_sel_by_int_time(struct iio_gts *gts, int time) +{ + const struct iio_itime_sel_mul *itime; + + itime = iio_gts_find_itime_by_time(gts, time); + if (!itime) + return -EINVAL; + + return itime->sel; +} + +/** + * iio_gts_valid_time - check if given integration time is valid + * @gts: Gain time scale descriptor + * @time_us: Integration time to check + * + * Return: True if given time is supported by device. False if not. + */ +static inline bool iio_gts_valid_time(struct iio_gts *gts, int time_us) +{ + return iio_gts_find_itime_by_time(gts, time_us) != NULL; +} + +int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain); + +/** + * iio_gts_valid_gain - check if given HW-gain is valid + * @gts: Gain time scale descriptor + * @gain: HW-gain to check + * + * Return: True if given time is supported by device. False if not. + */ +static inline bool iio_gts_valid_gain(struct iio_gts *gts, int gain) +{ + return iio_gts_find_sel_by_gain(gts, gain) >= 0; +} + +int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range); +int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel); +int iio_gts_get_min_gain(struct iio_gts *gts); +int iio_gts_find_int_time_by_sel(struct iio_gts *gts, int sel); +int iio_gts_find_sel_by_int_time(struct iio_gts *gts, int time); + +int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, + int *scale_int, int *scale_nano); +int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain_sel); +int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, + int *scale_nano); +int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, + int old_gain, int old_time_sel, + int new_time_sel, int *new_gain); +int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain); +int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, + int *length); +int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, + int *length); +int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, + const int **vals, int *type, int *length); + +#endif -- cgit v1.2.3 From ca11e4a3515430a083fd59822bce0d418f6b6541 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 31 Mar 2023 15:41:06 +0300 Subject: MAINTAINERS: Add IIO gain-time-scale helpers Add myself as a maintainer for IIO light sensor helpers (helpers for maintaining the scale while adjusting intergration time or gain) and related Kunit tests. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/d46414eabe8dd4cd3edb15f859f3b93cd406d9aa.1680263956.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8d5bc223f305..169d1dd45c1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9938,6 +9938,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector F: Documentation/devicetree/bindings/iio/adc/envelope-detector.yaml F: drivers/iio/adc/envelope-detector.c +IIO LIGHT SENSOR GAIN-TIME-SCALE HELPERS +M: Matti Vaittinen +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/light/gain-time-scale-helper.c +F: drivers/iio/light/gain-time-scale-helper.h + IIO MULTIPLEXER M: Peter Rosin L: linux-iio@vger.kernel.org -- cgit v1.2.3 From 0dca5c9730dcfcf9ba05362e5e36b1ea0c85066d Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 31 Mar 2023 15:41:33 +0300 Subject: dt-bindings: iio: light: Support ROHM BU27034 ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. Add dt-bindings. Signed-off-by: Matti Vaittinen Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/66a222574176ee2adbfccf6d9a591c04571a18d9.1680263956.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/rohm,bu27034.yaml | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml diff --git a/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml b/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml new file mode 100644 index 000000000000..30a109a1bf3b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/rohm,bu27034.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BU27034 ambient light sensor + +maintainers: + - Matti Vaittinen + +description: | + ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes + capable of detecting a very wide range of illuminance. Typical application + is adjusting LCD and backlight power of TVs and mobile phones. + https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf + +properties: + compatible: + const: rohm,bu27034 + + reg: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@38 { + compatible = "rohm,bu27034"; + reg = <0x38>; + vdd-supply = <&vdd>; + }; + }; + +... -- cgit v1.2.3 From e52afbd61039e2c5a4e611e23b4aa963d34a4aef Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 31 Mar 2023 15:41:58 +0300 Subject: iio: light: ROHM BU27034 Ambient Light Sensor ROHM BU27034 is an ambient light sensor with 3 channels and 3 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. Add initial support for the ROHM BU27034 ambient light sensor. NOTE: - Driver exposes 4 channels. One IIO_LIGHT channel providing the calculated lux values based on measured data from diodes #0 and #1. In addition, 3 IIO_INTENSITY channels are emitting the raw register data from all diodes for more intense user-space computations. - Sensor has GAIN values that can be adjusted from 1x to 4096x. - Sensor has adjustible measurement times of 5, 55, 100, 200 and 400 mS. Driver does not support 5 mS which has special limitations. - Driver exposes standard 'scale' adjustment which is implemented by: 1) Trying to adjust only the GAIN 2) If GAIN adjustment alone can't provide requested scale, adjusting both the time and the gain is attempted. - Driver exposes writable INT_TIME property that can be used for adjusting the measurement time. Time adjustment will also cause the driver to try to adjust the GAIN so that the overall scale is kept as close to the original as possible. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/2a7efb6f335da5526fbe34b95137c5e45db5c5d3.1680263956.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 14 + drivers/iio/light/Makefile | 1 + drivers/iio/light/rohm-bu27034.c | 1497 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1512 insertions(+) create mode 100644 drivers/iio/light/rohm-bu27034.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 0d4447df7200..6fa31fcd71a1 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -289,6 +289,20 @@ config JSA1212 To compile this driver as a module, choose M here: the module will be called jsa1212. +config ROHM_BU27034 + tristate "ROHM BU27034 ambient light sensor" + depends on I2C + select REGMAP_I2C + select IIO_GTS_HELPER + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Enable support for the ROHM BU27034 ambient light sensor. ROHM BU27034 + is an ambient light sesnor with 3 channels and 3 photo diodes capable + of detecting a very wide range of illuminance. + Typical application is adjusting LCD and backlight power of TVs and + mobile phones. + config RPR0521 tristate "ROHM RPR0521 ALS and proximity sensor driver" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index d74d2b5ff14c..985f6feaccd4 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o +obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SI1133) += si1133.o obj-$(CONFIG_SI1145) += si1145.o diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c new file mode 100644 index 000000000000..e486dcf35eba --- /dev/null +++ b/drivers/iio/light/rohm-bu27034.c @@ -0,0 +1,1497 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BU27034 ROHM Ambient Light Sensor + * + * Copyright (c) 2023, ROHM Semiconductor. + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BU27034_REG_SYSTEM_CONTROL 0x40 +#define BU27034_MASK_SW_RESET BIT(7) +#define BU27034_MASK_PART_ID GENMASK(5, 0) +#define BU27034_ID 0x19 +#define BU27034_REG_MODE_CONTROL1 0x41 +#define BU27034_MASK_MEAS_MODE GENMASK(2, 0) + +#define BU27034_REG_MODE_CONTROL2 0x42 +#define BU27034_MASK_D01_GAIN GENMASK(7, 3) +#define BU27034_MASK_D2_GAIN_HI GENMASK(7, 6) +#define BU27034_MASK_D2_GAIN_LO GENMASK(2, 0) + +#define BU27034_REG_MODE_CONTROL3 0x43 +#define BU27034_REG_MODE_CONTROL4 0x44 +#define BU27034_MASK_MEAS_EN BIT(0) +#define BU27034_MASK_VALID BIT(7) +#define BU27034_REG_DATA0_LO 0x50 +#define BU27034_REG_DATA1_LO 0x52 +#define BU27034_REG_DATA2_LO 0x54 +#define BU27034_REG_DATA2_HI 0x55 +#define BU27034_REG_MANUFACTURER_ID 0x92 +#define BU27034_REG_MAX BU27034_REG_MANUFACTURER_ID + +/* + * The BU27034 does not have interrupt to trigger the data read when a + * measurement has finished. Hence we poll the VALID bit in a thread. We will + * try to wake the thread BU27034_MEAS_WAIT_PREMATURE_MS milliseconds before + * the expected sampling time to prevent the drifting. + * + * If we constantly wake up a bit too late we would eventually skip a sample. + * And because the sleep can't wake up _exactly_ at given time this would be + * inevitable even if the sensor clock would be perfectly phase-locked to CPU + * clock - which we can't say is the case. + * + * This is still fragile. No matter how big advance do we have, we will still + * risk of losing a sample because things can in a rainy-day scenario be + * delayed a lot. Yet, more we reserve the time for polling, more we also lose + * the performance by spending cycles polling the register. So, selecting this + * value is a balancing dance between severity of wasting CPU time and severity + * of losing samples. + * + * In most cases losing the samples is not _that_ crucial because light levels + * tend to change slowly. + * + * Other option that was pointed to me would be always sleeping 1/2 of the + * measurement time, checking the VALID bit and just sleeping again if the bit + * was not set. That should be pretty tolerant against missing samples due to + * the scheduling delays while also not wasting much of cycles for polling. + * Downside is that the time-stamps would be very inaccurate as the wake-up + * would not really be tied to the sensor toggling the valid bit. This would also + * result 'jumps' in the time-stamps when the delay drifted so that wake-up was + * performed during the consecutive wake-ups (Or, when sensor and CPU clocks + * were very different and scheduling the wake-ups was very close to given + * timeout - and when the time-outs were very close to the actual sensor + * sampling, Eg. once in a blue moon, two consecutive time-outs would occur + * without having a sample ready). + */ +#define BU27034_MEAS_WAIT_PREMATURE_MS 5 +#define BU27034_DATA_WAIT_TIME_US 1000 +#define BU27034_TOTAL_DATA_WAIT_TIME_US (BU27034_MEAS_WAIT_PREMATURE_MS * 1000) + +#define BU27034_RETRY_LIMIT 18 + +enum { + BU27034_CHAN_ALS, + BU27034_CHAN_DATA0, + BU27034_CHAN_DATA1, + BU27034_CHAN_DATA2, + BU27034_NUM_CHANS +}; + +static const unsigned long bu27034_scan_masks[] = { + GENMASK(BU27034_CHAN_DATA2, BU27034_CHAN_ALS), 0 +}; + +/* + * Available scales with gain 1x - 4096x, timings 55, 100, 200, 400 mS + * Time impacts to gain: 1x, 2x, 4x, 8x. + * + * => Max total gain is HWGAIN * gain by integration time (8 * 4096) = 32768 + * + * Using NANO precision for scale we must use scale 64x corresponding gain 1x + * to avoid precision loss. (32x would result scale 976 562.5(nanos). + */ +#define BU27034_SCALE_1X 64 + +/* See the data sheet for the "Gain Setting" table */ +#define BU27034_GSEL_1X 0x00 /* 00000 */ +#define BU27034_GSEL_4X 0x08 /* 01000 */ +#define BU27034_GSEL_16X 0x0a /* 01010 */ +#define BU27034_GSEL_32X 0x0b /* 01011 */ +#define BU27034_GSEL_64X 0x0c /* 01100 */ +#define BU27034_GSEL_256X 0x18 /* 11000 */ +#define BU27034_GSEL_512X 0x19 /* 11001 */ +#define BU27034_GSEL_1024X 0x1a /* 11010 */ +#define BU27034_GSEL_2048X 0x1b /* 11011 */ +#define BU27034_GSEL_4096X 0x1c /* 11100 */ + +/* Available gain settings */ +static const struct iio_gain_sel_pair bu27034_gains[] = { + GAIN_SCALE_GAIN(1, BU27034_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27034_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27034_GSEL_16X), + GAIN_SCALE_GAIN(32, BU27034_GSEL_32X), + GAIN_SCALE_GAIN(64, BU27034_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27034_GSEL_256X), + GAIN_SCALE_GAIN(512, BU27034_GSEL_512X), + GAIN_SCALE_GAIN(1024, BU27034_GSEL_1024X), + GAIN_SCALE_GAIN(2048, BU27034_GSEL_2048X), + GAIN_SCALE_GAIN(4096, BU27034_GSEL_4096X), +}; + +/* + * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits + * the data collection to data0-channel only and cuts the supported range to + * 10 bit. It is not supported by the driver. + * + * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct + * multiplying impact to the register values (similar to gain). + * + * This means that if meas-mode is changed for example from 400 => 200, + * the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8. + */ +#define BU27034_MEAS_MODE_100MS 0 +#define BU27034_MEAS_MODE_55MS 1 +#define BU27034_MEAS_MODE_200MS 2 +#define BU27034_MEAS_MODE_400MS 4 + +static const struct iio_itime_sel_mul bu27034_itimes[] = { + GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8), + GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4), + GAIN_SCALE_ITIME_US(100000, BU27034_MEAS_MODE_100MS, 2), + GAIN_SCALE_ITIME_US(55000, BU27034_MEAS_MODE_55MS, 1), +}; + +#define BU27034_CHAN_DATA(_name, _ch2) \ +{ \ + .type = IIO_INTENSITY, \ + .channel = BU27034_CHAN_##_name, \ + .channel2 = (_ch2), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .address = BU27034_REG_##_name##_LO, \ + .scan_index = BU27034_CHAN_##_name, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec bu27034_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = BU27034_CHAN_ALS, + .scan_index = BU27034_CHAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + /* + * The BU27034 DATA0 and DATA1 channels are both on the visible light + * area (mostly). The data0 sensitivity peaks at 500nm, DATA1 at 600nm. + * These wave lengths are pretty much on the border of colours making + * these a poor candidates for R/G/B standardization. Hence they're both + * marked as clear channels + */ + BU27034_CHAN_DATA(DATA0, IIO_MOD_LIGHT_CLEAR), + BU27034_CHAN_DATA(DATA1, IIO_MOD_LIGHT_CLEAR), + BU27034_CHAN_DATA(DATA2, IIO_MOD_LIGHT_IR), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +struct bu27034_data { + struct regmap *regmap; + struct device *dev; + /* + * Protect gain and time during scale adjustment and data reading. + * Protect measurement enabling/disabling. + */ + struct mutex mutex; + struct iio_gts gts; + struct task_struct *task; + __le16 raw[3]; + struct { + u32 mlux; + __le16 channels[3]; + s64 ts __aligned(8); + } scan; +}; + +struct bu27034_result { + u16 ch0; + u16 ch1; + u16 ch2; +}; + +static const struct regmap_range bu27034_volatile_ranges[] = { + { + .range_min = BU27034_REG_MODE_CONTROL4, + .range_max = BU27034_REG_MODE_CONTROL4, + }, { + .range_min = BU27034_REG_DATA0_LO, + .range_max = BU27034_REG_DATA2_HI, + }, +}; + +static const struct regmap_access_table bu27034_volatile_regs = { + .yes_ranges = &bu27034_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bu27034_volatile_ranges), +}; + +static const struct regmap_range bu27034_read_only_ranges[] = { + { + .range_min = BU27034_REG_DATA0_LO, + .range_max = BU27034_REG_DATA2_HI, + }, { + .range_min = BU27034_REG_MANUFACTURER_ID, + .range_max = BU27034_REG_MANUFACTURER_ID, + } +}; + +static const struct regmap_access_table bu27034_ro_regs = { + .no_ranges = &bu27034_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(bu27034_read_only_ranges), +}; + +static const struct regmap_config bu27034_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BU27034_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &bu27034_volatile_regs, + .wr_table = &bu27034_ro_regs, +}; + +struct bu27034_gain_check { + int old_gain; + int new_gain; + int chan; +}; + +static int bu27034_get_gain_sel(struct bu27034_data *data, int chan) +{ + int ret, val; + + switch (chan) { + case BU27034_CHAN_DATA0: + case BU27034_CHAN_DATA1: + { + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; + ret = regmap_read(data->regmap, reg[chan], &val); + if (ret) + return ret; + + return FIELD_GET(BU27034_MASK_D01_GAIN, val); + } + case BU27034_CHAN_DATA2: + { + int d2_lo_bits = fls(BU27034_MASK_D2_GAIN_LO); + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL2, &val); + if (ret) + return ret; + + /* + * The data2 channel gain is composed by 5 non continuous bits + * [7:6], [2:0]. Thus when we combine the 5-bit 'selector' + * from register value we must right shift the high bits by 3. + */ + return FIELD_GET(BU27034_MASK_D2_GAIN_HI, val) << d2_lo_bits | + FIELD_GET(BU27034_MASK_D2_GAIN_LO, val); + } + default: + return -EINVAL; + } +} + +static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain) +{ + int ret, sel; + + ret = bu27034_get_gain_sel(data, chan); + if (ret < 0) + return ret; + + sel = ret; + + ret = iio_gts_find_gain_by_sel(&data->gts, sel); + if (ret < 0) { + dev_err(data->dev, "chan %u: unknown gain value 0x%x\n", chan, + sel); + + return ret; + } + + *gain = ret; + + return 0; +} + +static int bu27034_get_int_time(struct bu27034_data *data) +{ + int ret, sel; + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel); + if (ret) + return ret; + + return iio_gts_find_int_time_by_sel(&data->gts, + sel & BU27034_MASK_MEAS_MODE); +} + +static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val, + int *val2) +{ + int gain, ret; + + ret = bu27034_get_gain(data, channel, &gain); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + return iio_gts_get_scale(&data->gts, gain, ret, val, val2); +} + +static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val, + int *val2) +{ + int ret; + + if (channel == BU27034_CHAN_ALS) { + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + } + + mutex_lock(&data->mutex); + ret = _bu27034_get_scale(data, channel, val, val2); + mutex_unlock(&data->mutex); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_NANO; +} + +/* Caller should hold the lock to protect lux reading */ +static int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel) +{ + static const int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; + int mask, val; + + if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) + return -EINVAL; + + val = FIELD_PREP(BU27034_MASK_D01_GAIN, sel); + + mask = BU27034_MASK_D01_GAIN; + + if (chan == BU27034_CHAN_DATA0) { + /* + * We keep the same gain for channel 2 as we set for channel 0 + * We can't allow them to be individually controlled because + * setting one will impact also the other. Also, if we don't + * always update both gains we may result unsupported bit + * combinations. + * + * This is not nice but this is yet another place where the + * user space must be prepared to surprizes. Namely, see chan 2 + * gain changed when chan 0 gain is changed. + * + * This is not fatal for most users though. I don't expect the + * channel 2 to be used in any generic cases - the intensity + * values provided by the sensor for IR area are not openly + * documented. Also, channel 2 is not used for visible light. + * + * So, if there is application which is written to utilize the + * channel 2 - then it is probably specifically targeted to this + * sensor and knows how to utilize those values. It is safe to + * hope such user can also cope with the gain changes. + */ + mask |= BU27034_MASK_D2_GAIN_LO; + + /* + * The D2 gain bits are directly the lowest bits of selector. + * Just do add those bits to the value + */ + val |= sel & BU27034_MASK_D2_GAIN_LO; + } + + return regmap_update_bits(data->regmap, reg[chan], mask, val); +} + +static int bu27034_set_gain(struct bu27034_data *data, int chan, int gain) +{ + int ret; + + /* + * We don't allow setting channel 2 gain as it messes up the + * gain for channel 0 - which shares the high bits + */ + if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) + return -EINVAL; + + ret = iio_gts_find_sel_by_gain(&data->gts, gain); + if (ret < 0) + return ret; + + return bu27034_write_gain_sel(data, chan, ret); +} + +/* Caller should hold the lock to protect data->int_time */ +static int bu27034_set_int_time(struct bu27034_data *data, int time) +{ + int ret; + + ret = iio_gts_find_sel_by_int_time(&data->gts, time); + if (ret < 0) + return ret; + + return regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, + BU27034_MASK_MEAS_MODE, ret); +} + +/* + * We try to change the time in such way that the scale is maintained for + * given channels by adjusting gain so that it compensates the time change. + */ +static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) +{ + struct bu27034_gain_check gains[] = { + { .chan = BU27034_CHAN_DATA0 }, + { .chan = BU27034_CHAN_DATA1 }, + }; + int numg = ARRAY_SIZE(gains); + int ret, int_time_old, i; + + mutex_lock(&data->mutex); + ret = bu27034_get_int_time(data); + if (ret < 0) + goto unlock_out; + + int_time_old = ret; + + if (!iio_gts_valid_time(&data->gts, time_us)) { + dev_err(data->dev, "Unsupported integration time %u\n", + time_us); + ret = -EINVAL; + + goto unlock_out; + } + + if (time_us == int_time_old) { + ret = 0; + goto unlock_out; + } + + for (i = 0; i < numg; i++) { + ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain); + if (ret) + goto unlock_out; + + ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, + gains[i].old_gain, + int_time_old, time_us, + &gains[i].new_gain); + if (ret) { + int scale1, scale2; + bool ok; + + _bu27034_get_scale(data, gains[i].chan, &scale1, &scale2); + dev_dbg(data->dev, + "chan %u, can't support time %u with scale %u %u\n", + gains[i].chan, time_us, scale1, scale2); + + if (gains[i].new_gain < 0) + goto unlock_out; + + /* + * If caller requests for integration time change and we + * can't support the scale - then the caller should be + * prepared to 'pick up the pieces and deal with the + * fact that the scale changed'. + */ + ret = iio_find_closest_gain_low(&data->gts, + gains[i].new_gain, &ok); + + if (!ok) + dev_dbg(data->dev, + "optimal gain out of range for chan %u\n", + gains[i].chan); + + if (ret < 0) { + dev_dbg(data->dev, + "Total gain increase. Risk of saturation"); + ret = iio_gts_get_min_gain(&data->gts); + if (ret < 0) + goto unlock_out; + } + dev_dbg(data->dev, "chan %u scale changed\n", + gains[i].chan); + gains[i].new_gain = ret; + dev_dbg(data->dev, "chan %u new gain %u\n", + gains[i].chan, gains[i].new_gain); + } + } + + for (i = 0; i < numg; i++) { + ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain); + if (ret) + goto unlock_out; + } + + ret = bu27034_set_int_time(data, time_us); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bu27034_set_scale(struct bu27034_data *data, int chan, + int val, int val2) +{ + int ret, time_sel, gain_sel, i; + bool found = false; + + if (chan == BU27034_CHAN_DATA2) + return -EINVAL; + + if (chan == BU27034_CHAN_ALS) { + if (val == 0 && val2 == 1000) + return 0; + + return -EINVAL; + } + + mutex_lock(&data->mutex); + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel); + if (ret) + goto unlock_out; + + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, + val, val2 * 1000, &gain_sel); + if (ret) { + /* + * Could not support scale with given time. Need to change time. + * We still want to maintain the scale for all channels + */ + struct bu27034_gain_check gain; + int new_time_sel; + + /* + * Populate information for the other channel which should also + * maintain the scale. (Due to the HW limitations the chan2 + * gets the same gain as chan0, so we only need to explicitly + * set the chan 0 and 1). + */ + if (chan == BU27034_CHAN_DATA0) + gain.chan = BU27034_CHAN_DATA1; + else if (chan == BU27034_CHAN_DATA1) + gain.chan = BU27034_CHAN_DATA0; + + ret = bu27034_get_gain(data, gain.chan, &gain.old_gain); + if (ret) + goto unlock_out; + + /* + * Iterate through all the times to see if we find one which + * can support requested scale for requested channel, while + * maintaining the scale for other channels + */ + for (i = 0; i < data->gts.num_itime; i++) { + new_time_sel = data->gts.itime_table[i].sel; + + if (new_time_sel == time_sel) + continue; + + /* Can we provide requested scale with this time? */ + ret = iio_gts_find_gain_sel_for_scale_using_time( + &data->gts, new_time_sel, val, val2 * 1000, + &gain_sel); + if (ret) + continue; + + /* Can the other channel(s) maintain scale? */ + ret = iio_gts_find_new_gain_sel_by_old_gain_time( + &data->gts, gain.old_gain, time_sel, + new_time_sel, &gain.new_gain); + if (!ret) { + /* Yes - we found suitable time */ + found = true; + break; + } + } + if (!found) { + dev_dbg(data->dev, + "Can't set scale maintaining other channels\n"); + ret = -EINVAL; + + goto unlock_out; + } + + ret = bu27034_set_gain(data, gain.chan, gain.new_gain); + if (ret) + goto unlock_out; + + ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, + BU27034_MASK_MEAS_MODE, new_time_sel); + if (ret) + goto unlock_out; + } + + ret = bu27034_write_gain_sel(data, chan, gain_sel); +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +/* + * for (D1/D0 < 0.87): + * lx = 0.004521097 * D1 - 0.002663996 * D0 + + * 0.00012213 * D1 * D1 / D0 + * + * => 115.7400832 * ch1 / gain1 / mt - + * 68.1982976 * ch0 / gain0 / mt + + * 0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 * + * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) + * + * A = 0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 * + * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) + * => 0.00012213 * 25600 * (ch1 /gain1 / mt) * + * (ch1 /gain1 / mt) / (ch0 / gain0 / mt) + * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) / + * (ch0 / gain0) + * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) * + * gain0 / ch0 + * => 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0 + * + * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / + * mt + A + * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / + * mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt / + * ch0 + * + * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 + + * 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) / + * mt + * + * For (0.87 <= D1/D0 < 1.00) + * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1) + * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * + * 100 * ch1 / gain1 / mt) * ((D1/D0 - 0.87) * (0.385) + 1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * ((D1/D0 - 0.87) * (0.385) + 1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (0.385 * D1/D0 - 0.66505) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505) + * => 13.118336 * ch1 / (gain1 * mt) + * + 22.66064768 * ch0 / (gain0 * mt) + * + 8931.90144 * ch1 * ch1 * gain0 / + * (25600 * ch0 * gain1 * gain1 * mt) + * + 0.602694912 * ch1 / (gain1 * mt) + * + * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + * + 22.66064768 * ch0 / gain0 + * + 13.721030912 * ch1 / gain1 + * ] / mt + * + * For (D1/D0 >= 1.00) + * + * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1) + * => (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1) + * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * + * 100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1) + * => (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0) + * + 34.0736 * 1.1 * ch0 / (gain0 * mt) + * + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0) + * + 1.1 * 0.90624 * ch1 / (gain1 * mt) + * => -43614.208 * ch1 / (gain1 * mt * 25600) + * + 37.48096 ch0 / (gain0 * mt) + * - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0) + * + 0.996864 ch1 / (gain1 * mt) + * => [ + * - 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0) + * - 0.706816 * ch1 / gain1 + * + 37.48096 ch0 /gain0 + * ] * mt + * + * + * So, the first case (D1/D0 < 0.87) can be computed to a form: + * + * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * 115.7400832 * ch1 / gain1 + + * -68.1982976 * ch0 / gain0 + * / mt + * + * Second case (0.87 <= D1/D0 < 1.00) goes to form: + * + * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * 13.721030912 * ch1 / gain1 + + * 22.66064768 * ch0 / gain0 + * ] / mt + * + * Third case (D1/D0 >= 1.00) goes to form: + * => [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * -0.706816 * ch1 / gain1 + + * 37.48096 ch0 /(gain0 + * ] / mt + * + * This can be unified to format: + * lx = [ + * A * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * B * ch1 / gain1 + + * C * ch0 / gain0 + * ] / mt + * + * For case 1: + * A = 3.126528, + * B = 115.7400832 + * C = -68.1982976 + * + * For case 2: + * A = 0.3489024 + * B = 13.721030912 + * C = 22.66064768 + * + * For case 3: + * A = -0.045312 + * B = -0.706816 + * C = 37.48096 + */ + +struct bu27034_lx_coeff { + unsigned int A; + unsigned int B; + unsigned int C; + /* Indicate which of the coefficients above are negative */ + bool is_neg[3]; +}; + +static inline u64 gain_mul_div_helper(u64 val, unsigned int gain, + unsigned int div) +{ + /* + * Max gain for a channel is 4096. The max u64 (0xffffffffffffffffULL) + * divided by 4096 is 0xFFFFFFFFFFFFF (GENMASK_ULL(51, 0)) (floored). + * Thus, the 0xFFFFFFFFFFFFF is the largest value we can safely multiply + * with the gain, no matter what gain is set. + * + * So, multiplication with max gain may overflow if val is greater than + * 0xFFFFFFFFFFFFF (52 bits set).. + * + * If this is the case we divide first. + */ + if (val < GENMASK_ULL(51, 0)) { + val *= gain; + do_div(val, div); + } else { + do_div(val, div); + val *= gain; + } + + return val; +} + +static u64 bu27034_fixp_calc_t1_64bit(unsigned int coeff, unsigned int ch0, + unsigned int ch1, unsigned int gain0, + unsigned int gain1) +{ + unsigned int helper; + u64 helper64; + + helper64 = (u64)coeff * (u64)ch1 * (u64)ch1; + + helper = gain1 * gain1; + if (helper > ch0) { + do_div(helper64, helper); + + return gain_mul_div_helper(helper64, gain0, ch0); + } + + do_div(helper64, ch0); + + return gain_mul_div_helper(helper64, gain0, helper); + +} + +static u64 bu27034_fixp_calc_t1(unsigned int coeff, unsigned int ch0, + unsigned int ch1, unsigned int gain0, + unsigned int gain1) +{ + unsigned int helper, tmp; + + /* + * Here we could overflow even the 64bit value. Hence we + * multiply with gain0 only after the divisions - even though + * it may result loss of accuracy + */ + helper = coeff * ch1 * ch1; + tmp = helper * gain0; + + helper = ch1 * ch1; + + if (check_mul_overflow(helper, coeff, &helper)) + return bu27034_fixp_calc_t1_64bit(coeff, ch0, ch1, gain0, gain1); + + if (check_mul_overflow(helper, gain0, &tmp)) + return bu27034_fixp_calc_t1_64bit(coeff, ch0, ch1, gain0, gain1); + + return tmp / (gain1 * gain1) / ch0; + +} + +static u64 bu27034_fixp_calc_t23(unsigned int coeff, unsigned int ch, + unsigned int gain) +{ + unsigned int helper; + u64 helper64; + + if (!check_mul_overflow(coeff, ch, &helper)) + return helper / gain; + + helper64 = (u64)coeff * (u64)ch; + do_div(helper64, gain); + + return helper64; +} + +static int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1, + unsigned int gain0, unsigned int gain1, + unsigned int meastime, int coeff_idx) +{ + static const struct bu27034_lx_coeff coeff[] = { + { + .A = 31265280, /* 3.126528 */ + .B = 1157400832, /*115.7400832 */ + .C = 681982976, /* -68.1982976 */ + .is_neg = {false, false, true}, + }, { + .A = 3489024, /* 0.3489024 */ + .B = 137210309, /* 13.721030912 */ + .C = 226606476, /* 22.66064768 */ + /* All terms positive */ + }, { + .A = 453120, /* -0.045312 */ + .B = 7068160, /* -0.706816 */ + .C = 374809600, /* 37.48096 */ + .is_neg = {true, true, false}, + } + }; + const struct bu27034_lx_coeff *c = &coeff[coeff_idx]; + u64 res = 0, terms[3]; + int i; + + if (coeff_idx >= ARRAY_SIZE(coeff)) + return -EINVAL; + + terms[0] = bu27034_fixp_calc_t1(c->A, ch0, ch1, gain0, gain1); + terms[1] = bu27034_fixp_calc_t23(c->B, ch1, gain1); + terms[2] = bu27034_fixp_calc_t23(c->C, ch0, gain0); + + /* First, add positive terms */ + for (i = 0; i < 3; i++) + if (!c->is_neg[i]) + res += terms[i]; + + /* No positive term => zero lux */ + if (!res) + return 0; + + /* Then, subtract negative terms (if any) */ + for (i = 0; i < 3; i++) + if (c->is_neg[i]) { + /* + * If the negative term is greater than positive - then + * the darkness has taken over and we are all doomed! Eh, + * I mean, then we can just return 0 lx and go out + */ + if (terms[i] >= res) + return 0; + + res -= terms[i]; + } + + meastime *= 10; + do_div(res, meastime); + + return (int) res; +} + +static bool bu27034_has_valid_sample(struct bu27034_data *data) +{ + int ret, val; + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL4, &val); + if (ret) { + dev_err(data->dev, "Read failed %d\n", ret); + + return false; + } + + return val & BU27034_MASK_VALID; +} + +/* + * Reading the register where VALID bit is clears this bit. (So does changing + * any gain / integration time configuration registers) The bit gets + * set when we have acquired new data. We use this bit to indicate data + * validity. + */ +static void bu27034_invalidate_read_data(struct bu27034_data *data) +{ + bu27034_has_valid_sample(data); +} + +static int bu27034_read_result(struct bu27034_data *data, int chan, int *res) +{ + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_DATA0_LO, + [BU27034_CHAN_DATA1] = BU27034_REG_DATA1_LO, + [BU27034_CHAN_DATA2] = BU27034_REG_DATA2_LO, + }; + int valid, ret; + __le16 val; + + ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4, + valid, (valid & BU27034_MASK_VALID), + BU27034_DATA_WAIT_TIME_US, 0); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, reg[chan], &val, sizeof(val)); + if (ret) + return ret; + + *res = le16_to_cpu(val); + + return 0; +} + +static int bu27034_get_result_unlocked(struct bu27034_data *data, __le16 *res, + int size) +{ + int ret = 0, retry_cnt = 0; + +retry: + /* Get new value from sensor if data is ready */ + if (bu27034_has_valid_sample(data)) { + ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO, + res, size); + if (ret) + return ret; + + bu27034_invalidate_read_data(data); + } else { + /* No new data in sensor. Wait and retry */ + retry_cnt++; + + if (retry_cnt > BU27034_RETRY_LIMIT) { + dev_err(data->dev, "No data from sensor\n"); + + return -ETIMEDOUT; + } + + msleep(25); + + goto retry; + } + + return ret; +} + +static int bu27034_meas_set(struct bu27034_data *data, bool en) +{ + if (en) + return regmap_set_bits(data->regmap, BU27034_REG_MODE_CONTROL4, + BU27034_MASK_MEAS_EN); + + return regmap_clear_bits(data->regmap, BU27034_REG_MODE_CONTROL4, + BU27034_MASK_MEAS_EN); +} + +static int bu27034_get_single_result(struct bu27034_data *data, int chan, + int *val) +{ + int ret; + + if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA2) + return -EINVAL; + + ret = bu27034_meas_set(data, true); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + msleep(ret / 1000); + + return bu27034_read_result(data, chan, val); +} + +/* + * The formula given by vendor for computing luxes out of data0 and data1 + * (in open air) is as follows: + * + * Let's mark: + * D0 = data0/ch0_gain/meas_time_ms * 25600 + * D1 = data1/ch1_gain/meas_time_ms * 25600 + * + * Then: + * if (D1/D0 < 0.87) + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1) + * else if (D1/D0 < 1) + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1) + * else + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1) + * + * We use it here. Users who have for example some colored lens + * need to modify the calculation but I hope this gives a starting point for + * those working with such devices. + */ + +static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val) +{ + unsigned int gain0, gain1, meastime; + unsigned int d1_d0_ratio_scaled; + u16 ch0, ch1; + u64 helper64; + int ret; + + /* + * We return 0 lux if calculation fails. This should be reasonably + * easy to spot from the buffers especially if raw-data channels show + * valid values + */ + *val = 0; + + ch0 = max_t(u16, 1, le16_to_cpu(res[0])); + ch1 = max_t(u16, 1, le16_to_cpu(res[1])); + + ret = bu27034_get_gain(data, BU27034_CHAN_DATA0, &gain0); + if (ret) + return ret; + + ret = bu27034_get_gain(data, BU27034_CHAN_DATA1, &gain1); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + meastime = ret; + + d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100; + helper64 = (u64)ch1 * (u64)gain0 * 100LLU; + + if (helper64 != d1_d0_ratio_scaled) { + unsigned int div = (unsigned int)ch0 * gain1; + + do_div(helper64, div); + d1_d0_ratio_scaled = helper64; + } else { + d1_d0_ratio_scaled /= ch0 * gain1; + } + + if (d1_d0_ratio_scaled < 87) + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 0); + else if (d1_d0_ratio_scaled < 100) + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1); + else + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 2); + + if (ret < 0) + return ret; + + *val = ret; + + return 0; + +} + +static int bu27034_get_mlux(struct bu27034_data *data, int chan, int *val) +{ + __le16 res[3]; + int ret; + + ret = bu27034_meas_set(data, true); + if (ret) + return ret; + + ret = bu27034_get_result_unlocked(data, &res[0], sizeof(res)); + if (ret) + return ret; + + ret = bu27034_calc_mlux(data, res, val); + if (ret) + return ret; + + ret = bu27034_meas_set(data, false); + if (ret) + dev_err(data->dev, "failed to disable measurement\n"); + + return 0; +} + +static int bu27034_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *val = bu27034_get_int_time(data); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return bu27034_get_scale(data, chan->channel, val, val2); + + case IIO_CHAN_INFO_RAW: + { + int (*result_get)(struct bu27034_data *data, int chan, int *val); + + if (chan->type == IIO_INTENSITY) + result_get = bu27034_get_single_result; + else if (chan->type == IIO_LIGHT) + result_get = bu27034_get_mlux; + else + return -EINVAL; + + /* Don't mess with measurement enabling while buffering */ + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + mutex_lock(&data->mutex); + /* + * Reading one channel at a time is inefficient but we + * don't care here. Buffered version should be used if + * performance is an issue. + */ + ret = result_get(data, chan->channel, val); + + mutex_unlock(&data->mutex); + iio_device_release_direct_mode(idev); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int bu27034_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = bu27034_set_scale(data, chan->channel, val, val2); + break; + case IIO_CHAN_INFO_INT_TIME: + ret = bu27034_try_set_int_time(data, val); + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(idev); + + return ret; +} + +static int bu27034_read_avail(struct iio_dev *idev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return iio_gts_avail_times(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + default: + return -EINVAL; + } +} + +static const struct iio_info bu27034_info = { + .read_raw = &bu27034_read_raw, + .write_raw = &bu27034_write_raw, + .read_avail = &bu27034_read_avail, +}; + +static int bu27034_chip_init(struct bu27034_data *data) +{ + int ret, sel; + + /* Reset */ + ret = regmap_update_bits(data->regmap, BU27034_REG_SYSTEM_CONTROL, + BU27034_MASK_SW_RESET, BU27034_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + msleep(1); + /* + * Read integration time here to ensure it is in regmap cache. We do + * this to speed-up the int-time acquisition in the start of the buffer + * handling thread where longer delays could make it more likely we end + * up skipping a sample, and where the longer delays make timestamps + * less accurate. + */ + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel); + if (ret) + dev_err(data->dev, "reading integration time failed\n"); + + return 0; +} + +static int bu27034_wait_for_data(struct bu27034_data *data) +{ + int ret, val; + + ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4, + val, val & BU27034_MASK_VALID, + BU27034_DATA_WAIT_TIME_US, + BU27034_TOTAL_DATA_WAIT_TIME_US); + if (ret) { + dev_err(data->dev, "data polling %s\n", + !(val & BU27034_MASK_VALID) ? "timeout" : "fail"); + + return ret; + } + + ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO, + &data->scan.channels[0], + sizeof(data->scan.channels)); + if (ret) + return ret; + + bu27034_invalidate_read_data(data); + + return 0; +} + +static int bu27034_buffer_thread(void *arg) +{ + struct iio_dev *idev = arg; + struct bu27034_data *data; + int wait_ms; + + data = iio_priv(idev); + + wait_ms = bu27034_get_int_time(data); + wait_ms /= 1000; + + wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS; + + while (!kthread_should_stop()) { + int ret; + int64_t tstamp; + + msleep(wait_ms); + ret = bu27034_wait_for_data(data); + if (ret) + continue; + + tstamp = iio_get_time_ns(idev); + + if (test_bit(BU27034_CHAN_ALS, idev->active_scan_mask)) { + int mlux; + + ret = bu27034_calc_mlux(data, &data->scan.channels[0], + &mlux); + if (ret) + dev_err(data->dev, "failed to calculate lux\n"); + + /* + * The maximum Milli lux value we get with gain 1x time + * 55mS data ch0 = 0xffff ch1 = 0xffff fits in 26 bits + * so there should be no problem returning int from + * computations and casting it to u32 + */ + data->scan.mlux = (u32)mlux; + } + iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp); + } + + return 0; +} + +static int bu27034_buffer_enable(struct iio_dev *idev) +{ + struct bu27034_data *data = iio_priv(idev); + struct task_struct *task; + int ret; + + mutex_lock(&data->mutex); + ret = bu27034_meas_set(data, true); + if (ret) + goto unlock_out; + + task = kthread_run(bu27034_buffer_thread, idev, + "bu27034-buffering-%u", + iio_device_id(idev)); + if (IS_ERR(task)) { + ret = PTR_ERR(task); + goto unlock_out; + } + + data->task = task; + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bu27034_buffer_disable(struct iio_dev *idev) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + mutex_lock(&data->mutex); + if (data->task) { + kthread_stop(data->task); + data->task = NULL; + } + + ret = bu27034_meas_set(data, false); + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_buffer_setup_ops bu27034_buffer_ops = { + .postenable = &bu27034_buffer_enable, + .predisable = &bu27034_buffer_disable, +}; + +static int bu27034_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct bu27034_data *data; + struct regmap *regmap; + struct iio_dev *idev; + unsigned int part_id, reg; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &bu27034_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); + + data = iio_priv(idev); + + ret = regmap_read(regmap, BU27034_REG_SYSTEM_CONTROL, ®); + if (ret) + return dev_err_probe(dev, ret, "Failed to access sensor\n"); + + part_id = FIELD_GET(BU27034_MASK_PART_ID, reg); + + if (part_id != BU27034_ID) + dev_warn(dev, "unknown device 0x%x\n", part_id); + + ret = devm_iio_init_iio_gts(dev, BU27034_SCALE_1X, 0, bu27034_gains, + ARRAY_SIZE(bu27034_gains), bu27034_itimes, + ARRAY_SIZE(bu27034_itimes), &data->gts); + if (ret) + return ret; + + mutex_init(&data->mutex); + data->regmap = regmap; + data->dev = dev; + + idev->channels = bu27034_channels; + idev->num_channels = ARRAY_SIZE(bu27034_channels); + idev->name = "bu27034"; + idev->info = &bu27034_info; + + idev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + idev->available_scan_masks = bu27034_scan_masks; + + ret = bu27034_chip_init(data); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, idev, &bu27034_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "buffer setup failed\n"); + + ret = devm_iio_device_register(dev, idev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Unable to register iio device\n"); + + return ret; +} + +static const struct of_device_id bu27034_of_match[] = { + { .compatible = "rohm,bu27034" }, + { } +}; +MODULE_DEVICE_TABLE(of, bu27034_of_match); + +static struct i2c_driver bu27034_i2c_driver = { + .driver = { + .name = "bu27034-als", + .of_match_table = bu27034_of_match, + }, + .probe_new = bu27034_probe, +}; +module_i2c_driver(bu27034_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("ROHM BU27034 ambient light sensor driver"); +MODULE_IMPORT_NS(IIO_GTS_HELPER); -- cgit v1.2.3 From c86b0e73f0bebbb0245ef2bac4cf269d61ff828c Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 31 Mar 2023 15:42:17 +0300 Subject: MAINTAINERS: Add ROHM BU27034 Add myself as a maintainer for ROHM BU27034 ALS driver. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/eac4b5f7fc8adcaac59ffa73e46cd7bb9c90edfa.1680263956.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 169d1dd45c1a..7b9c0c841884 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18098,6 +18098,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/bh1750.yaml F: drivers/iio/light/bh1750.c +ROHM BU27034 AMBIENT LIGHT SENSOR DRIVER +M: Matti Vaittinen +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/light/rohm-bu27034.c + ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS M: Marek Vasut L: linux-kernel@vger.kernel.org -- cgit v1.2.3 From 4308c6878acfb58eda46483644b516abd732b6dd Mon Sep 17 00:00:00 2001 From: Vivek Pernamitta Date: Tue, 4 Apr 2023 15:14:16 +0530 Subject: bus: mhi: host: Avoid ringing EV DB if there are no elements to process Currently, mhi_process_data_event_ring()/mhi_process_ctrl_ev_ring() APIs are ringing DB even if there are no ring elements to process. This could cause the device to process the DB event in the absence of ring elements. So to avoid this unnecessary device processing, let's ring event DB only if there are any ring elements to process. Signed-off-by: Vivek Pernamitta Reviewed-by: Jeffrey Hugo Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1680601458-9105-1-git-send-email-quic_vpernami@quicinc.com [mani: massaged the commit message a bit] Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 4fa0969472fc..c7eb7b8be9d6 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -961,7 +961,9 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, } read_lock_bh(&mhi_cntrl->pm_lock); - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) + + /* Ring EV DB only if there is any pending element to process */ + if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count) mhi_ring_er_db(mhi_event); read_unlock_bh(&mhi_cntrl->pm_lock); @@ -1031,7 +1033,9 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, count++; } read_lock_bh(&mhi_cntrl->pm_lock); - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) + + /* Ring EV DB only if there is any pending element to process */ + if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count) mhi_ring_er_db(mhi_event); read_unlock_bh(&mhi_cntrl->pm_lock); -- cgit v1.2.3 From d469d9448a0f1a33c175d3280b1542fa0158ad7a Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 10 Apr 2023 09:58:11 -0600 Subject: bus: mhi: host: Remove duplicate ee check for syserr If we detect a system error via intvec, we only process the syserr if the current ee is different than the last observed ee. The reason for this check is to prevent bhie from running multiple times, but with the single queue handling syserr, that is not possible. The check can cause an issue with device recovery. If PBL loads a bad SBL via BHI, but that SBL hangs before notifying the host of an ee change, then issuing soc_reset to crash the device and retry (after supplying a fixed SBL) will not recover the device as the host will observe a PBL->PBL transition and not process the syserr. The device will be stuck until either the driver is reloaded, or the host is rebooted. Instead, remove the check so that we can attempt to recover the device. Fixes: ef2126c4e2ea ("bus: mhi: core: Process execution environment changes serially") Cc: stable@vger.kernel.org Signed-off-by: Jeffrey Hugo Reviewed-by: Carl Vanderlip Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1681142292-27571-2-git-send-email-quic_jhugo@quicinc.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index c7eb7b8be9d6..74a75439c713 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -503,7 +503,7 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) } write_unlock_irq(&mhi_cntrl->pm_lock); - if (pm_state != MHI_PM_SYS_ERR_DETECT || ee == mhi_cntrl->ee) + if (pm_state != MHI_PM_SYS_ERR_DETECT) goto exit_intvec; switch (ee) { -- cgit v1.2.3 From 1d1493bdc25f498468a606a4ece947d155cfa3a9 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 10 Apr 2023 09:58:12 -0600 Subject: bus: mhi: host: Use mhi_tryset_pm_state() for setting fw error state If firmware loading fails, the controller's pm_state is updated to MHI_PM_FW_DL_ERR unconditionally. This can corrupt the pm_state as the update is not done under the proper lock, and also does not validate the state transition. The firmware loading can fail due to a detected syserr, but if MHI_PM_FW_DL_ERR is unconditionally set as the pm_state, the handling of the syserr can break when it attempts to transition from syserr detect, to syserr process. By grabbing the lock, we ensure we don't race with some other pm_state update. By using mhi_try_set_pm_state(), we check that the transition to MHI_PM_FW_DL_ERR is valid via the state machine logic. If it is not valid, then some other transition is occurring like syserr processing, and we assume that will resolve the firmware loading error. Fixes: 12e050c77be0 ("bus: mhi: core: Move to an error state on any firmware load failure") Cc: stable@vger.kernel.org Signed-off-by: Jeffrey Hugo Reviewed-by: Carl Vanderlip Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1681142292-27571-3-git-send-email-quic_jhugo@quicinc.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/boot.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index 1c69feee1703..d2a19b07ccb8 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -391,6 +391,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) { const struct firmware *firmware = NULL; struct device *dev = &mhi_cntrl->mhi_dev->dev; + enum mhi_pm_state new_state; const char *fw_name; void *buf; dma_addr_t dma_addr; @@ -508,14 +509,18 @@ error_ready_state: } error_fw_load: - mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR; - wake_up_all(&mhi_cntrl->state_event); + write_lock_irq(&mhi_cntrl->pm_lock); + new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR); + write_unlock_irq(&mhi_cntrl->pm_lock); + if (new_state == MHI_PM_FW_DL_ERR) + wake_up_all(&mhi_cntrl->state_event); } int mhi_download_amss_image(struct mhi_controller *mhi_cntrl) { struct image_info *image_info = mhi_cntrl->fbc_image; struct device *dev = &mhi_cntrl->mhi_dev->dev; + enum mhi_pm_state new_state; int ret; if (!image_info) @@ -526,8 +531,11 @@ int mhi_download_amss_image(struct mhi_controller *mhi_cntrl) &image_info->mhi_buf[image_info->entries - 1]); if (ret) { dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret); - mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR; - wake_up_all(&mhi_cntrl->state_event); + write_lock_irq(&mhi_cntrl->pm_lock); + new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR); + write_unlock_irq(&mhi_cntrl->pm_lock); + if (new_state == MHI_PM_FW_DL_ERR) + wake_up_all(&mhi_cntrl->state_event); } return ret; -- cgit v1.2.3 From bd66b6acd20ba72d1779fa1abe55eb0fcc20f2c3 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 30 Jan 2023 20:10:17 +0000 Subject: iio: imu: lsm6dsx: Support SMO8B30 ACPI ID for LSM6DS3TR-C ID seen in the wild and it is a valid ST micro ID. An offset of 1 for the device ID enum is needed when adding support for retrieving the ID from device_get_match_data() to allow detection of NULL pointer and fallback to i2c_device_id table. DSDT chunk cropped for relevant parts. Scope (_SB.PCI0.I2C5) { Device (DEV) { Name (_HID, EisaId ("SMO8B30")) // _HID: Hardware ID Name (_CID, EisaId ("SMO8B30")) // _CID: Compatible ID Name (_UID, Zero) // _UID: Unique ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (RBUF, ResourceTemplate () { I2cSerialBusV2 (0x006A, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\_SB.PCI0.I2C5", 0x00, ResourceConsumer, , Exclusive, ) }) Return (RBUF) /* \_SB_.PCI0.I2C5.DEV_._CRS.RBUF */ } Method (ROTM, 0, NotSerialized) { Name (RBUF, Package (0x03) { "0 -1 0", "1 0 0", "0 0 1" }) Return (RBUF) /* \_SB_.PCI0.I2C5.DEV_.ROTM.RBUF */ } ... Link: https://lore.kernel.org/all/20230129182441.082f29d0@jic23-huawei/ Reported-by: Darrell Kavanagh Tested-by: Darrell Kavanagh Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20230130201018.981024-2-jic23@kernel.org --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 9dfe4917a386..c19237717e81 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -40,7 +40,7 @@ #define ST_ASM330LHB_DEV_NAME "asm330lhb" enum st_lsm6dsx_hw_id { - ST_LSM6DS3_ID, + ST_LSM6DS3_ID = 1, ST_LSM6DS3H_ID, ST_LSM6DSL_ID, ST_LSM6DSM_ID, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index ccdc4ceffd85..020717f92363 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -23,10 +23,15 @@ static const struct regmap_config st_lsm6dsx_i2c_regmap_config = { static int st_lsm6dsx_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); - int hw_id = id->driver_data; + int hw_id; struct regmap *regmap; + hw_id = (kernel_ulong_t)device_get_match_data(&client->dev); + if (!hw_id) + hw_id = i2c_client_get_device_id(client)->driver_data; + if (!hw_id) + return -EINVAL; + regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); @@ -133,6 +138,12 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); +static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { + { "SMO8B30", ST_LSM6DS3TRC_ID, }, + {} +}; +MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); + static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, @@ -166,6 +177,7 @@ static struct i2c_driver st_lsm6dsx_driver = { .name = "st_lsm6dsx_i2c", .pm = pm_sleep_ptr(&st_lsm6dsx_pm_ops), .of_match_table = st_lsm6dsx_i2c_of_match, + .acpi_match_table = st_lsm6dsx_i2c_acpi_match, }, .probe_new = st_lsm6dsx_i2c_probe, .id_table = st_lsm6dsx_i2c_id_table, -- cgit v1.2.3 From dc3d25f22b889a326abc8331075849ea11b4f2bb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 30 Jan 2023 20:10:18 +0000 Subject: iio: imu: lsm6dsx: Add ACPI mount matrix retrieval DSDT ROTM method seen in the wild with SMO8B30 _HID. Making assumption it is similar to that used for bmc150 plus information from Darrell that the rotation is out by 90 degrees at boot. Method (ROTM, 0, NotSerialized) { Name (RBUF, Package (0x03) { "0 -1 0", "1 0 0", "0 0 1" }) Return (RBUF) /* \_SB_.PCI0.I2C5.DEV_.ROTM.RBUF */ } Reported-by: Darrell Kavanagh Signed-off-by: Jonathan Cameron Tested-by: Darrell Kavanagh Link: https://lore.kernel.org/r/20230130201018.981024-3-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 77 ++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 455e5edd3a80..6a18b363cf73 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -56,6 +56,7 @@ #include #include +#include #include #include #include @@ -2624,6 +2625,73 @@ static int st_lsm6dsx_init_regulators(struct device *dev) return 0; } +#ifdef CONFIG_ACPI + +static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, + struct iio_mount_matrix *orientation) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device *adev = ACPI_COMPANION(dev); + union acpi_object *obj, *elements; + acpi_status status; + int i, j, val[3]; + char *str; + + if (!has_acpi_companion(dev)) + return -EINVAL; + + if (!acpi_has_method(adev->handle, "ROTM")) + return -EINVAL; + + status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "Failed to get ACPI mount matrix: %d\n", status); + return -EINVAL; + } + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) + goto unknown_format; + + elements = obj->package.elements; + for (i = 0; i < 3; i++) { + if (elements[i].type != ACPI_TYPE_STRING) + goto unknown_format; + + str = elements[i].string.pointer; + if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) + goto unknown_format; + + for (j = 0; j < 3; j++) { + switch (val[j]) { + case -1: str = "-1"; break; + case 0: str = "0"; break; + case 1: str = "1"; break; + default: goto unknown_format; + } + orientation->rotation[i * 3 + j] = str; + } + } + + kfree(buffer.pointer); + return 0; + +unknown_format: + dev_warn(dev, "Unknown ACPI mount matrix format, ignoring\n"); + kfree(buffer.pointer); + return -EINVAL; +} + +#else + +static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, + struct iio_mount_matrix *orientation) +{ + return false; +} + +#endif + int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { @@ -2698,9 +2766,12 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - err = iio_read_mount_matrix(hw->dev, &hw->orientation); - if (err) - return err; + err = lsm6dsx_get_acpi_mount_matrix(hw->dev, &hw->orientation); + if (err) { + err = iio_read_mount_matrix(hw->dev, &hw->orientation); + if (err) + return err; + } for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { if (!hw->iio_devs[i]) -- cgit v1.2.3 From 6d52b0e706989d48e1aa3b5263f6185770ab4af3 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 18 Mar 2023 16:30:39 +0000 Subject: iio: adc: palmas: Take probe fully device managed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review of a recent fix highlighted that this driver could be trivially converted to be entirely devm managed. That fix should be applied to resolve the fix in a fashion easy to back port even though this change removes the relevant code. [1] https://patchwork.kernel.org/project/linux-iio/patch/20230313205029.1881745-1-risca@dalakolonin.se/ Signed-off-by: Jonathan Cameron Tested-by: Patrik Dahlström Reviewed-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230318163039.56115-1-jic23@kernel.org --- drivers/iio/adc/palmas_gpadc.c | 110 ++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 849a697a467e..2921186458e0 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -493,6 +493,11 @@ static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, return 0; } +static void palmas_disable_wakeup(void *dev) +{ + device_wakeup_disable(dev); +} + static int palmas_gpadc_probe(struct platform_device *pdev) { struct palmas_gpadc *adc; @@ -532,36 +537,30 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); - if (adc->irq < 0) { - dev_err(adc->dev, - "get virq failed: %d\n", adc->irq); - ret = adc->irq; - goto out; - } - ret = request_threaded_irq(adc->irq, NULL, - palmas_gpadc_irq, - IRQF_ONESHOT, dev_name(adc->dev), - adc); - if (ret < 0) { - dev_err(adc->dev, - "request irq %d failed: %d\n", adc->irq, ret); - goto out; - } + if (adc->irq < 0) + return dev_err_probe(adc->dev, adc->irq, "get virq failed\n"); + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq, NULL, + palmas_gpadc_irq, + IRQF_ONESHOT, dev_name(adc->dev), + adc); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request irq %d failed\n", adc->irq); if (gpadc_pdata->adc_wakeup1_data) { memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data, sizeof(adc->wakeup1_data)); adc->wakeup1_enable = true; adc->irq_auto_0 = platform_get_irq(pdev, 1); - ret = request_threaded_irq(adc->irq_auto_0, NULL, - palmas_gpadc_irq_auto, - IRQF_ONESHOT, - "palmas-adc-auto-0", adc); - if (ret < 0) { - dev_err(adc->dev, "request auto0 irq %d failed: %d\n", - adc->irq_auto_0, ret); - goto out_irq_free; - } + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, + NULL, palmas_gpadc_irq_auto, + IRQF_ONESHOT, + "palmas-adc-auto-0", adc); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto0 irq %d failed\n", + adc->irq_auto_0); } if (gpadc_pdata->adc_wakeup2_data) { @@ -569,15 +568,14 @@ static int palmas_gpadc_probe(struct platform_device *pdev) sizeof(adc->wakeup2_data)); adc->wakeup2_enable = true; adc->irq_auto_1 = platform_get_irq(pdev, 2); - ret = request_threaded_irq(adc->irq_auto_1, NULL, - palmas_gpadc_irq_auto, - IRQF_ONESHOT, - "palmas-adc-auto-1", adc); - if (ret < 0) { - dev_err(adc->dev, "request auto1 irq %d failed: %d\n", - adc->irq_auto_1, ret); - goto out_irq_auto0_free; - } + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, + NULL, palmas_gpadc_irq_auto, + IRQF_ONESHOT, + "palmas-adc-auto-1", adc); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto1 irq %d failed\n", + adc->irq_auto_1); } /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ @@ -608,11 +606,10 @@ static int palmas_gpadc_probe(struct platform_device *pdev) indio_dev->channels = palmas_gpadc_iio_channel; indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel); - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(adc->dev, "iio_device_register() failed: %d\n", ret); - goto out_irq_auto1_free; - } + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "iio_device_register() failed\n"); device_set_wakeup_capable(&pdev->dev, 1); for (i = 0; i < PALMAS_ADC_CH_MAX; i++) { @@ -620,36 +617,14 @@ static int palmas_gpadc_probe(struct platform_device *pdev) palmas_gpadc_calibrate(adc, i); } - if (adc->wakeup1_enable || adc->wakeup2_enable) + if (adc->wakeup1_enable || adc->wakeup2_enable) { device_wakeup_enable(&pdev->dev); - - return 0; - -out_irq_auto1_free: - if (gpadc_pdata->adc_wakeup2_data) - free_irq(adc->irq_auto_1, adc); -out_irq_auto0_free: - if (gpadc_pdata->adc_wakeup1_data) - free_irq(adc->irq_auto_0, adc); -out_irq_free: - free_irq(adc->irq, adc); -out: - return ret; -} - -static int palmas_gpadc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); - struct palmas_gpadc *adc = iio_priv(indio_dev); - - if (adc->wakeup1_enable || adc->wakeup2_enable) - device_wakeup_disable(&pdev->dev); - iio_device_unregister(indio_dev); - free_irq(adc->irq, adc); - if (adc->wakeup1_enable) - free_irq(adc->irq_auto_0, adc); - if (adc->wakeup2_enable) - free_irq(adc->irq_auto_1, adc); + ret = devm_add_action_or_reset(&pdev->dev, + palmas_disable_wakeup, + &pdev->dev); + if (ret) + return ret; + } return 0; } @@ -834,7 +809,6 @@ MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl); static struct platform_driver palmas_gpadc_driver = { .probe = palmas_gpadc_probe, - .remove = palmas_gpadc_remove, .driver = { .name = MOD_NAME, .pm = pm_sleep_ptr(&palmas_pm_ops), -- cgit v1.2.3 From 79d9622d622d49d6c14c51edec2059b8591e7975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:19 +0200 Subject: iio: adc: palmas: remove adc_wakeupX_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It does not seem to be used by anyone and later patches in this series are made simpler by first removing this. There is now a lot of dead code that cannot be reached, until later patches revive it. Arguably, this is preferred over removing the code only to add it again. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-4-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 50 +++++------------------------------------- include/linux/mfd/palmas.h | 8 ------- 2 files changed, 6 insertions(+), 52 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 2921186458e0..03af6cd73ec8 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -76,6 +76,12 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true), }; +struct palmas_adc_wakeup_property { + int adc_channel_number; + int adc_high_threshold; + int adc_low_threshold; +}; + /* * struct palmas_gpadc - the palmas_gpadc structure * @ch0_current: channel 0 current source setting @@ -493,11 +499,6 @@ static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, return 0; } -static void palmas_disable_wakeup(void *dev) -{ - device_wakeup_disable(dev); -} - static int palmas_gpadc_probe(struct platform_device *pdev) { struct palmas_gpadc *adc; @@ -548,36 +549,6 @@ static int palmas_gpadc_probe(struct platform_device *pdev) return dev_err_probe(adc->dev, ret, "request irq %d failed\n", adc->irq); - if (gpadc_pdata->adc_wakeup1_data) { - memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data, - sizeof(adc->wakeup1_data)); - adc->wakeup1_enable = true; - adc->irq_auto_0 = platform_get_irq(pdev, 1); - ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, - NULL, palmas_gpadc_irq_auto, - IRQF_ONESHOT, - "palmas-adc-auto-0", adc); - if (ret < 0) - return dev_err_probe(adc->dev, ret, - "request auto0 irq %d failed\n", - adc->irq_auto_0); - } - - if (gpadc_pdata->adc_wakeup2_data) { - memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data, - sizeof(adc->wakeup2_data)); - adc->wakeup2_enable = true; - adc->irq_auto_1 = platform_get_irq(pdev, 2); - ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, - NULL, palmas_gpadc_irq_auto, - IRQF_ONESHOT, - "palmas-adc-auto-1", adc); - if (ret < 0) - return dev_err_probe(adc->dev, ret, - "request auto1 irq %d failed\n", - adc->irq_auto_1); - } - /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ if (gpadc_pdata->ch0_current <= 1) adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0; @@ -617,15 +588,6 @@ static int palmas_gpadc_probe(struct platform_device *pdev) palmas_gpadc_calibrate(adc, i); } - if (adc->wakeup1_enable || adc->wakeup2_enable) { - device_wakeup_enable(&pdev->dev); - ret = devm_add_action_or_reset(&pdev->dev, - palmas_disable_wakeup, - &pdev->dev); - if (ret) - return ret; - } - return 0; } diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 117d02708439..eda1ffd99c1a 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -128,12 +128,6 @@ struct palmas_pmic_driver_data { struct regulator_config config); }; -struct palmas_adc_wakeup_property { - int adc_channel_number; - int adc_high_threshold; - int adc_low_threshold; -}; - struct palmas_gpadc_platform_data { /* Channel 3 current source is only enabled during conversion */ int ch3_current; /* 0: off; 1: 10uA; 2: 400uA; 3: 800 uA */ @@ -152,8 +146,6 @@ struct palmas_gpadc_platform_data { int start_polarity; int auto_conversion_period_ms; - struct palmas_adc_wakeup_property *adc_wakeup1_data; - struct palmas_adc_wakeup_property *adc_wakeup2_data; }; struct palmas_reg_init { -- cgit v1.2.3 From d2ab4eea732dd2e235ba2a488344612a07a20334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:20 +0200 Subject: iio: adc: palmas: replace "wakeup" with "event" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The palmas gpadc block has support for monitoring up to 2 ADC channels and issue an interrupt if they reach past a set threshold. This is currently used to wake up the system from sleep, but the functionality is more generic than that. As such, change the naming of functions and variables to refer to it as events instead, except during suspend and resume where wakeup still make sense. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-5-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 03af6cd73ec8..55fdf59ef711 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -76,7 +76,7 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true), }; -struct palmas_adc_wakeup_property { +struct palmas_adc_event { int adc_channel_number; int adc_high_threshold; int adc_low_threshold; @@ -117,10 +117,10 @@ struct palmas_gpadc { int irq_auto_1; struct palmas_gpadc_info *adc_info; struct completion conv_completion; - struct palmas_adc_wakeup_property wakeup1_data; - struct palmas_adc_wakeup_property wakeup2_data; - bool wakeup1_enable; - bool wakeup2_enable; + struct palmas_adc_event event0; + struct palmas_adc_event event1; + bool event0_enable; + bool event1_enable; int auto_conversion_period; struct mutex lock; }; @@ -591,7 +591,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) return 0; } -static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) +static int palmas_adc_configure_events(struct palmas_gpadc *adc) { int adc_period, conv; int i; @@ -617,16 +617,16 @@ static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) } conv = 0; - if (adc->wakeup1_enable) { + if (adc->event0_enable) { int polarity; - ch0 = adc->wakeup1_data.adc_channel_number; + ch0 = adc->event0.adc_channel_number; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; - if (adc->wakeup1_data.adc_high_threshold > 0) { - thres = adc->wakeup1_data.adc_high_threshold; + if (adc->event0.adc_high_threshold > 0) { + thres = adc->event0.adc_high_threshold; polarity = 0; } else { - thres = adc->wakeup1_data.adc_low_threshold; + thres = adc->event0.adc_low_threshold; polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; } @@ -648,16 +648,16 @@ static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) } } - if (adc->wakeup2_enable) { + if (adc->event1_enable) { int polarity; - ch1 = adc->wakeup2_data.adc_channel_number; + ch1 = adc->event1.adc_channel_number; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; - if (adc->wakeup2_data.adc_high_threshold > 0) { - thres = adc->wakeup2_data.adc_high_threshold; + if (adc->event1.adc_high_threshold > 0) { + thres = adc->event1.adc_high_threshold; polarity = 0; } else { - thres = adc->wakeup2_data.adc_low_threshold; + thres = adc->event1.adc_low_threshold; polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; } @@ -696,7 +696,7 @@ static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) return ret; } -static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc) +static int palmas_adc_reset_events(struct palmas_gpadc *adc) { int ret; @@ -718,20 +718,20 @@ static int palmas_gpadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; + int wakeup = adc->event0_enable || adc->event1_enable; int ret; if (!device_may_wakeup(dev) || !wakeup) return 0; - ret = palmas_adc_wakeup_configure(adc); + ret = palmas_adc_configure_events(adc); if (ret < 0) return ret; - if (adc->wakeup1_enable) + if (adc->event0_enable) enable_irq_wake(adc->irq_auto_0); - if (adc->wakeup2_enable) + if (adc->event1_enable) enable_irq_wake(adc->irq_auto_1); return 0; @@ -741,20 +741,20 @@ static int palmas_gpadc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; + int wakeup = adc->event0_enable || adc->event1_enable; int ret; if (!device_may_wakeup(dev) || !wakeup) return 0; - ret = palmas_adc_wakeup_reset(adc); + ret = palmas_adc_reset_events(adc); if (ret < 0) return ret; - if (adc->wakeup1_enable) + if (adc->event0_enable) disable_irq_wake(adc->irq_auto_0); - if (adc->wakeup2_enable) + if (adc->event1_enable) disable_irq_wake(adc->irq_auto_1); return 0; -- cgit v1.2.3 From 7501a3a97e4f000c1881f228132ead7a2fa5725c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:21 +0200 Subject: iio: adc: palmas: use iio_event_direction for threshold polarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having high_threshold > 0 as an indicator for upper threshold event and lower threshold event otherwise, use enum iio_event_direction instead. This is hopefully less ambiguous. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-6-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 55fdf59ef711..205a7628b235 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -77,9 +77,9 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { }; struct palmas_adc_event { - int adc_channel_number; - int adc_high_threshold; - int adc_low_threshold; + int channel; + int raw_thresh; + enum iio_event_direction direction; }; /* @@ -618,16 +618,21 @@ static int palmas_adc_configure_events(struct palmas_gpadc *adc) conv = 0; if (adc->event0_enable) { + struct palmas_adc_event *ev = &adc->event0; int polarity; - ch0 = adc->event0.adc_channel_number; + ch0 = ev->channel; + thres = ev->raw_thresh; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; - if (adc->event0.adc_high_threshold > 0) { - thres = adc->event0.adc_high_threshold; + switch (ev->direction) { + case IIO_EV_DIR_RISING: polarity = 0; - } else { - thres = adc->event0.adc_low_threshold; + break; + case IIO_EV_DIR_FALLING: polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; + break; + default: + return -EINVAL; } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, @@ -649,16 +654,21 @@ static int palmas_adc_configure_events(struct palmas_gpadc *adc) } if (adc->event1_enable) { + struct palmas_adc_event *ev = &adc->event1; int polarity; - ch1 = adc->event1.adc_channel_number; + ch1 = ev->channel; + thres = ev->raw_thresh; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; - if (adc->event1.adc_high_threshold > 0) { - thres = adc->event1.adc_high_threshold; + switch (ev->direction) { + case IIO_EV_DIR_RISING: polarity = 0; - } else { - thres = adc->event1.adc_low_threshold; + break; + case IIO_EV_DIR_FALLING: polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; + break; + default: + return -EINVAL; } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, -- cgit v1.2.3 From 2d48dbdfc7d4714973de48e601d69854b21b69de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:22 +0200 Subject: iio: adc: palmas: move eventX_enable into palmas_adc_event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It just makes more sense to have all information regarding adc events in one place. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-7-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 205a7628b235..8c6ea4a3cd2e 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -77,6 +77,7 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { }; struct palmas_adc_event { + bool enabled; int channel; int raw_thresh; enum iio_event_direction direction; @@ -119,8 +120,6 @@ struct palmas_gpadc { struct completion conv_completion; struct palmas_adc_event event0; struct palmas_adc_event event1; - bool event0_enable; - bool event1_enable; int auto_conversion_period; struct mutex lock; }; @@ -617,7 +616,7 @@ static int palmas_adc_configure_events(struct palmas_gpadc *adc) } conv = 0; - if (adc->event0_enable) { + if (adc->event0.enabled) { struct palmas_adc_event *ev = &adc->event0; int polarity; @@ -653,7 +652,7 @@ static int palmas_adc_configure_events(struct palmas_gpadc *adc) } } - if (adc->event1_enable) { + if (adc->event1.enabled) { struct palmas_adc_event *ev = &adc->event1; int polarity; @@ -728,7 +727,7 @@ static int palmas_gpadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->event0_enable || adc->event1_enable; + int wakeup = adc->event0.enabled || adc->event1.enabled; int ret; if (!device_may_wakeup(dev) || !wakeup) @@ -738,10 +737,10 @@ static int palmas_gpadc_suspend(struct device *dev) if (ret < 0) return ret; - if (adc->event0_enable) + if (adc->event0.enabled) enable_irq_wake(adc->irq_auto_0); - if (adc->event1_enable) + if (adc->event1.enabled) enable_irq_wake(adc->irq_auto_1); return 0; @@ -751,7 +750,7 @@ static int palmas_gpadc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->event0_enable || adc->event1_enable; + int wakeup = adc->event0.enabled || adc->event1.enabled; int ret; if (!device_may_wakeup(dev) || !wakeup) @@ -761,10 +760,10 @@ static int palmas_gpadc_resume(struct device *dev) if (ret < 0) return ret; - if (adc->event0_enable) + if (adc->event0.enabled) disable_irq_wake(adc->irq_auto_0); - if (adc->event1_enable) + if (adc->event1.enabled) disable_irq_wake(adc->irq_auto_1); return 0; -- cgit v1.2.3 From 773597aeee455d646bff12ce68a68884bc2d9113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:23 +0200 Subject: iio: adc: palmas: always reset events on unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prevents leaving the adc in freerunning mode when removing the driver. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-8-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 8c6ea4a3cd2e..da4908608a27 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -498,6 +498,13 @@ static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, return 0; } +static void palmas_gpadc_reset(void *data) +{ + struct palmas_gpadc *adc = data; + if (adc->event0.enabled || adc->event1.enabled) + palmas_adc_reset_events(adc); +} + static int palmas_gpadc_probe(struct platform_device *pdev) { struct palmas_gpadc *adc; @@ -587,6 +594,10 @@ static int palmas_gpadc_probe(struct platform_device *pdev) palmas_gpadc_calibrate(adc, i); } + ret = devm_add_action(&pdev->dev, palmas_gpadc_reset, adc); + if (ret) + return ret; + return 0; } -- cgit v1.2.3 From a99544c6c883f5fdd1b326b245572ca22cabc421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:24 +0200 Subject: iio: adc: palmas: add support for iio threshold events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The palmas gpadc block has support for monitoring up to 2 ADC channels and issue an interrupt if they reach past a set threshold. This change hooks into the IIO events system and exposes to userspace the ability to configure these threshold values for each channel, but only allow up to 2 such thresholds to be enabled at any given time. Trying to enable a third channel will result in an error. Userspace is expected to input calibrated, as opposed to raw, values as threshold. However, it is not enough to do the opposite of what is done when converting the other way around. To account for tolerances in the ADC, the calculated raw threshold should be adjusted based on the ADC specifications for the device. These specifications include the integral nonlinearity (INL), offset, and gain error. To adjust the high threshold, use the following equation: (calibrated value + INL) * Gain error + offset = maximum value [1] Likewise, use the following equation for the low threshold: (calibrated value - INL) * Gain error - offset = minimum value The gain error is a combination of gain error, as listed in the datasheet, and gain error drift due to temperature and supply. The exact values for these specifications vary between palmas devices. This patch sets the values found in TWL6035, TWL6037 datasheet. [1] TI Application Report, SLIA087A, Guide to Using the GPADC in TPS65903x, TPS65917-Q1, TPS65919-Q1, and TPS65916 Devices. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-9-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 443 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 419 insertions(+), 24 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index da4908608a27..071795c8de1d 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -79,10 +80,14 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { struct palmas_adc_event { bool enabled; int channel; - int raw_thresh; enum iio_event_direction direction; }; +struct palmas_gpadc_thresholds { + int high; + int low; +}; + /* * struct palmas_gpadc - the palmas_gpadc structure * @ch0_current: channel 0 current source setting @@ -120,10 +125,31 @@ struct palmas_gpadc { struct completion conv_completion; struct palmas_adc_event event0; struct palmas_adc_event event1; + struct palmas_gpadc_thresholds thresholds[PALMAS_ADC_CH_MAX]; int auto_conversion_period; struct mutex lock; }; +static struct palmas_adc_event *palmas_gpadc_get_event(struct palmas_gpadc *adc, + int adc_chan, + enum iio_event_direction dir) +{ + if (adc_chan == adc->event0.channel && dir == adc->event0.direction) + return &adc->event0; + + if (adc_chan == adc->event1.channel && dir == adc->event1.direction) + return &adc->event1; + + return NULL; +} + +static bool palmas_gpadc_channel_is_freerunning(struct palmas_gpadc *adc, + int adc_chan) +{ + return palmas_gpadc_get_event(adc, adc_chan, IIO_EV_DIR_RISING) || + palmas_gpadc_get_event(adc, adc_chan, IIO_EV_DIR_FALLING); +} + /* * GPADC lock issue in AUTO mode. * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO @@ -193,11 +219,24 @@ static irqreturn_t palmas_gpadc_irq(int irq, void *data) static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data) { - struct palmas_gpadc *adc = data; + struct iio_dev *indio_dev = data; + struct palmas_gpadc *adc = iio_priv(indio_dev); + struct palmas_adc_event *ev; dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq); palmas_disable_auto_conversion(adc); + ev = (irq == adc->irq_auto_0) ? &adc->event0 : &adc->event1; + if (ev->channel != -1) { + enum iio_event_direction dir; + u64 code; + + dir = ev->direction; + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, ev->channel, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, code, iio_get_time_ns(indio_dev)); + } + return IRQ_HANDLED; } @@ -285,6 +324,9 @@ static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan) { int ret; + if (palmas_gpadc_channel_is_freerunning(adc, adc_chan)) + return 0; /* ADC already running */ + ret = palmas_gpadc_enable(adc, adc_chan, true); if (ret < 0) return ret; @@ -344,28 +386,43 @@ static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan) unsigned int val; int ret; - init_completion(&adc->conv_completion); - ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, - PALMAS_GPADC_SW_SELECT, - PALMAS_GPADC_SW_SELECT_SW_START_CONV0, - PALMAS_GPADC_SW_SELECT_SW_START_CONV0); - if (ret < 0) { - dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); - return ret; - } + if (palmas_gpadc_channel_is_freerunning(adc, adc_chan)) { + int event = (adc_chan == adc->event0.channel) ? 0 : 1; + unsigned int reg = (event == 0) ? + PALMAS_GPADC_AUTO_CONV0_LSB : + PALMAS_GPADC_AUTO_CONV1_LSB; - ret = wait_for_completion_timeout(&adc->conv_completion, - PALMAS_ADC_CONVERSION_TIMEOUT); - if (ret == 0) { - dev_err(adc->dev, "conversion not completed\n"); - return -ETIMEDOUT; - } + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + reg, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CONV%x_LSB read failed: %d\n", + event, ret); + return ret; + } + } else { + init_completion(&adc->conv_completion); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0); + if (ret < 0) { + dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); + return ret; + } - ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, - PALMAS_GPADC_SW_CONV0_LSB, &val, 2); - if (ret < 0) { - dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); - return ret; + ret = wait_for_completion_timeout(&adc->conv_completion, + PALMAS_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_err(adc->dev, "conversion not completed\n"); + return -ETIMEDOUT; + } + + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_CONV0_LSB, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); + return ret; + } } ret = val & 0xFFF; @@ -391,6 +448,98 @@ static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, return val; } +/* + * The high and low threshold values are calculated based on the advice given + * in TI Application Report SLIA087A, "Guide to Using the GPADC in PS65903x, + * TPS65917-Q1, TPS65919-Q1, and TPS65916 Devices". This document recommend + * taking ADC tolerances into account and is based on the device integral non- + * linearity (INL), offset error and gain error: + * + * raw high threshold = (ideal threshold + INL) * gain error + offset error + * + * The gain error include both gain error, as specified in the datasheet, and + * the gain error drift. These paramenters vary depending on device and whether + * the the channel is calibrated (trimmed) or not. + */ +static int palmas_gpadc_threshold_with_tolerance(int val, const int INL, + const int gain_error, + const int offset_error) +{ + val = ((val + INL) * (1000 + gain_error)) / 1000 + offset_error; + + return clamp(val, 0, 0xFFF); +} + +/* + * The values below are taken from the datasheet of TWL6035, TWL6037. + * todo: get max INL, gain error, and offset error from OF. + */ +static int palmas_gpadc_get_high_threshold_raw(struct palmas_gpadc *adc, + struct palmas_adc_event *ev) +{ + const int adc_chan = ev->channel; + int val = adc->thresholds[adc_chan].high; + /* integral nonlinearity, measured in LSB */ + const int max_INL = 2; + /* measured in LSB */ + int max_offset_error; + /* 0.2% when calibrated */ + int max_gain_error = 2; + + val = (val * 1000) / adc->adc_info[adc_chan].gain; + + if (adc->adc_info[adc_chan].is_uncalibrated) { + /* 2% worse */ + max_gain_error += 20; + max_offset_error = 36; + } else { + val = (val * adc->adc_info[adc_chan].gain_error + + adc->adc_info[adc_chan].offset) / + 1000; + max_offset_error = 2; + } + + return palmas_gpadc_threshold_with_tolerance(val, + max_INL, + max_gain_error, + max_offset_error); +} + +/* + * The values below are taken from the datasheet of TWL6035, TWL6037. + * todo: get min INL, gain error, and offset error from OF. + */ +static int palmas_gpadc_get_low_threshold_raw(struct palmas_gpadc *adc, + struct palmas_adc_event *ev) +{ + const int adc_chan = ev->channel; + int val = adc->thresholds[adc_chan].low; + /* integral nonlinearity, measured in LSB */ + const int min_INL = -2; + /* measured in LSB */ + int min_offset_error; + /* -0.6% when calibrated */ + int min_gain_error = -6; + + val = (val * 1000) / adc->adc_info[adc_chan].gain; + + if (adc->adc_info[adc_chan].is_uncalibrated) { + /* 2% worse */ + min_gain_error -= 20; + min_offset_error = -36; + } else { + val = (val * adc->adc_info[adc_chan].gain_error - + adc->adc_info[adc_chan].offset) / + 1000; + min_offset_error = -2; + } + + return palmas_gpadc_threshold_with_tolerance(val, + min_INL, + min_gain_error, + min_offset_error); +} + static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { @@ -437,8 +586,217 @@ out: return ret; } +static int palmas_gpadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + if (palmas_gpadc_get_event(adc, adc_chan, dir)) + ret = 1; + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_adc_configure_events(struct palmas_gpadc *adc); +static int palmas_adc_reset_events(struct palmas_gpadc *adc); + +static int palmas_gpadc_reconfigure_event_channels(struct palmas_gpadc *adc) +{ + return (adc->event0.enabled || adc->event1.enabled) ? + palmas_adc_configure_events(adc) : + palmas_adc_reset_events(adc); +} + +static int palmas_gpadc_enable_event_config(struct palmas_gpadc *adc, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + struct palmas_adc_event *ev; + int adc_chan = chan->channel; + + if (palmas_gpadc_get_event(adc, adc_chan, dir)) + /* already enabled */ + return 0; + + if (adc->event0.channel == -1) { + ev = &adc->event0; + } else if (adc->event1.channel == -1) { + /* event0 has to be the lowest channel */ + if (adc_chan < adc->event0.channel) { + adc->event1 = adc->event0; + ev = &adc->event0; + } else { + ev = &adc->event1; + } + } else { /* both AUTO channels already in use */ + dev_warn(adc->dev, "event0 - %d, event1 - %d\n", + adc->event0.channel, adc->event1.channel); + return -EBUSY; + } + + ev->enabled = true; + ev->channel = adc_chan; + ev->direction = dir; + + return palmas_gpadc_reconfigure_event_channels(adc); +} + +static int palmas_gpadc_disable_event_config(struct palmas_gpadc *adc, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + int adc_chan = chan->channel; + struct palmas_adc_event *ev = palmas_gpadc_get_event(adc, adc_chan, dir); + + if (!ev) + return 0; + + if (ev == &adc->event0) { + adc->event0 = adc->event1; + ev = &adc->event1; + } + + ev->enabled = false; + ev->channel = -1; + ev->direction = IIO_EV_DIR_NONE; + + return palmas_gpadc_reconfigure_event_channels(adc); +} + +static int palmas_gpadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + if (state) + ret = palmas_gpadc_enable_event_config(adc, chan, dir); + else + ret = palmas_gpadc_disable_event_config(adc, chan, dir); + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = (dir == IIO_EV_DIR_RISING) ? + adc->thresholds[adc_chan].high : + adc->thresholds[adc_chan].low; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int old; + int ret; + + if (adc_chan > PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0 || val > 0xFFF) { + ret = -EINVAL; + goto out_unlock; + } + if (dir == IIO_EV_DIR_RISING) { + old = adc->thresholds[adc_chan].high; + adc->thresholds[adc_chan].high = val; + } else { + old = adc->thresholds[adc_chan].low; + adc->thresholds[adc_chan].low = val; + } + ret = 0; + break; + default: + ret = -EINVAL; + goto out_unlock; + } + + if (val != old && palmas_gpadc_get_event(adc, adc_chan, dir)) + ret = palmas_gpadc_reconfigure_event_channels(adc); + +out_unlock: + mutex_unlock(&adc->lock); + + return ret; +} + static const struct iio_info palmas_gpadc_iio_info = { .read_raw = palmas_gpadc_read_raw, + .read_event_config = palmas_gpadc_read_event_config, + .write_event_config = palmas_gpadc_write_event_config, + .read_event_value = palmas_gpadc_read_event_value, + .write_event_value = palmas_gpadc_write_event_value, +}; + +static const struct iio_event_spec palmas_gpadc_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, }; #define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \ @@ -449,6 +807,8 @@ static const struct iio_info palmas_gpadc_iio_info = { BIT(chan_info), \ .indexed = 1, \ .channel = PALMAS_ADC_CH_##chan, \ + .event_spec = palmas_gpadc_events, \ + .num_event_specs = ARRAY_SIZE(palmas_gpadc_events) \ } static const struct iio_chan_spec palmas_gpadc_iio_channel[] = { @@ -555,6 +915,39 @@ static int palmas_gpadc_probe(struct platform_device *pdev) return dev_err_probe(adc->dev, ret, "request irq %d failed\n", adc->irq); + adc->irq_auto_0 = platform_get_irq(pdev, 1); + if (adc->irq_auto_0 < 0) + return dev_err_probe(adc->dev, adc->irq_auto_0, + "get auto0 irq failed\n"); + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL, + palmas_gpadc_irq_auto, IRQF_ONESHOT, + "palmas-adc-auto-0", indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto0 irq %d failed\n", + adc->irq_auto_0); + + adc->irq_auto_1 = platform_get_irq(pdev, 2); + if (adc->irq_auto_1 < 0) + return dev_err_probe(adc->dev, adc->irq_auto_1, + "get auto1 irq failed\n"); + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL, + palmas_gpadc_irq_auto, IRQF_ONESHOT, + "palmas-adc-auto-1", indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto1 irq %d failed\n", + adc->irq_auto_1); + + adc->event0.enabled = false; + adc->event0.channel = -1; + adc->event0.direction = IIO_EV_DIR_NONE; + adc->event1.enabled = false; + adc->event1.channel = -1; + adc->event1.direction = IIO_EV_DIR_NONE; + /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ if (gpadc_pdata->ch0_current <= 1) adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0; @@ -632,13 +1025,14 @@ static int palmas_adc_configure_events(struct palmas_gpadc *adc) int polarity; ch0 = ev->channel; - thres = ev->raw_thresh; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; switch (ev->direction) { case IIO_EV_DIR_RISING: + thres = palmas_gpadc_get_high_threshold_raw(adc, ev); polarity = 0; break; case IIO_EV_DIR_FALLING: + thres = palmas_gpadc_get_low_threshold_raw(adc, ev); polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; break; default: @@ -668,13 +1062,14 @@ static int palmas_adc_configure_events(struct palmas_gpadc *adc) int polarity; ch1 = ev->channel; - thres = ev->raw_thresh; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; switch (ev->direction) { case IIO_EV_DIR_RISING: + thres = palmas_gpadc_get_high_threshold_raw(adc, ev); polarity = 0; break; case IIO_EV_DIR_FALLING: + thres = palmas_gpadc_get_low_threshold_raw(adc, ev); polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; break; default: -- cgit v1.2.3 From 52cc189b4fc6af6accc45fe7b7053d76d8724059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Sat, 8 Apr 2023 13:48:25 +0200 Subject: iio: adc: palmas: don't alter event config on suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The event config is controlled through the IIO events subsystem and device wakeup is controlled by /sys/devices/.../power/wakeup. Let's keep those two knobs independent. Signed-off-by: Patrik Dahlström Link: https://lore.kernel.org/r/20230408114825.824505-10-risca@dalakolonin.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 071795c8de1d..c1c439215aeb 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -1133,16 +1133,10 @@ static int palmas_gpadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->event0.enabled || adc->event1.enabled; - int ret; - if (!device_may_wakeup(dev) || !wakeup) + if (!device_may_wakeup(dev)) return 0; - ret = palmas_adc_configure_events(adc); - if (ret < 0) - return ret; - if (adc->event0.enabled) enable_irq_wake(adc->irq_auto_0); @@ -1156,16 +1150,10 @@ static int palmas_gpadc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct palmas_gpadc *adc = iio_priv(indio_dev); - int wakeup = adc->event0.enabled || adc->event1.enabled; - int ret; - if (!device_may_wakeup(dev) || !wakeup) + if (!device_may_wakeup(dev)) return 0; - ret = palmas_adc_reset_events(adc); - if (ret < 0) - return ret; - if (adc->event0.enabled) disable_irq_wake(adc->irq_auto_0); -- cgit v1.2.3 From dd8f7f463394f92c2f402e29cb39f5265ef97776 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 4 Apr 2023 12:11:17 +0100 Subject: MAINTAINERS: Remove Mathieu Poirier as coresight maintainer Mathieu Poirier is no longer involved in maintainig the CoreSight self-hosted tracing subsystem. Mathieu, Thank you very much creating and maintaing the subsystem all these years ! Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20230404111117.569795-1-suzuki.poulose@arm.com --- CREDITS | 5 +++++ MAINTAINERS | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index 847059166a15..216e86f56dd9 100644 --- a/CREDITS +++ b/CREDITS @@ -3459,6 +3459,11 @@ D: several improvements to system programs S: Oldenburg S: Germany +N: Mathieu Poirier +E: mathieu.poirier@linaro.org +D: CoreSight kernel subsystem, Maintainer 2014-2022 +D: Perf tool support for CoreSight + N: Robert Schwebel E: robert@schwebel.de W: https://www.schwebel.de diff --git a/MAINTAINERS b/MAINTAINERS index 1dc8bd26b6cf..c16edd182e7b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2091,7 +2091,6 @@ F: arch/arm/boot/dts/cx92755* N: digicolor ARM/CORESIGHT FRAMEWORK AND DRIVERS -M: Mathieu Poirier M: Suzuki K Poulose R: Mike Leach R: Leo Yan -- cgit v1.2.3 From 18996a113f2567aef3057e300e3193ce2df1684c Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Wed, 5 Apr 2023 10:49:22 +0100 Subject: coresight: etm_pmu: Set the module field struct pmu::module must be set to the module owning the PMU driver. Set this for the coresight etm_pmu. Fixes: 8e264c52e1dab ("coresight: core: Allow the coresight core driver to be built as a module") Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20230405094922.667834-1-suzuki.poulose@arm.com --- drivers/hwtracing/coresight/coresight-etm-perf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index a48c97da8165..711f451b6946 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -901,6 +901,7 @@ int __init etm_perf_init(void) etm_pmu.addr_filters_sync = etm_addr_filters_sync; etm_pmu.addr_filters_validate = etm_addr_filters_validate; etm_pmu.nr_addr_filters = ETM_ADDR_CMP_MAX; + etm_pmu.module = THIS_MODULE; ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1); if (ret == 0) -- cgit v1.2.3 From e1d6ca042e62c2a69513235f8629eb6e62ca79c5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Apr 2023 17:27:03 +0300 Subject: firmware: stratix10-svc: Fix an NULL vs IS_ERR() bug in probe The svc_create_memory_pool() function returns error pointers. It never returns NULL. Fix the check. Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/5f9a8cb4-5a4f-460b-9cdc-2fae6c5b7922@kili.mountain Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index bde1f543f529..80f4e2d14e04 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1133,8 +1133,8 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) return ret; genpool = svc_create_memory_pool(pdev, sh_memory); - if (!genpool) - return -ENOMEM; + if (IS_ERR(genpool)) + return PTR_ERR(genpool); /* allocate service controller and supporting channel */ controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); -- cgit v1.2.3 From 6f36ff32809aeaa7276d5f663d7adf9298a8bf5c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 11:38:56 +0200 Subject: MAINTAINERS: w1: add Krzysztof Kozlowski as maintainer Evgeniy wrote that he no longer takes patches for 1-Wire/W1 subsystem, so add Krzysztof Kozlowski to help reviewing and handling these. Link: https://lore.kernel.org/all/bdcf41d5-cd61-1e95-0b21-b8fe401644bd@ioremap.net/ Cc: Evgeniy Polyakov Cc: Arnd Bergmann Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415093856.41948-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e314ae0614b7..78944f72b6fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22436,7 +22436,7 @@ S: Orphan F: drivers/mmc/host/vub300.c W1 DALLAS'S 1-WIRE BUS -M: Evgeniy Polyakov +M: Krzysztof Kozlowski S: Maintained F: Documentation/devicetree/bindings/w1/ F: Documentation/w1/ -- cgit v1.2.3 From 22ab6c515d9602e5ddf41998ce5352e8887f4543 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 6 Apr 2023 12:31:36 +0200 Subject: dt-bindings: w1: Add DS2482/DS2484 I2C to 1-W bridges This adds a dedicated devicetree binding for the Maxim DS2482/DS2484 I2C to 1-W bridges, which can be extended later for further features (e.g. sleep mode control GPIO). Since one wire is a bus, child nodes needs to be allowed here. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Stefan Wahren Link: https://lore.kernel.org/r/20230406103137.6092-2-stefan.wahren@chargebyte.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/w1/maxim,ds2482.yaml | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/w1/maxim,ds2482.yaml diff --git a/Documentation/devicetree/bindings/w1/maxim,ds2482.yaml b/Documentation/devicetree/bindings/w1/maxim,ds2482.yaml new file mode 100644 index 000000000000..422becc6e1fa --- /dev/null +++ b/Documentation/devicetree/bindings/w1/maxim,ds2482.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/w1/maxim,ds2482.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim One wire bus master controller + +maintainers: + - Stefan Wahren + +description: | + I2C to 1-wire bridges + + https://www.analog.com/media/en/technical-documentation/data-sheets/ds2482-100.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/DS2482-800.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/DS2484.pdf + +properties: + compatible: + enum: + - maxim,ds2482 + - maxim,ds2484 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: + type: object + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + onewire@18 { + compatible = "maxim,ds2484"; + reg = <0x18>; + }; + }; -- cgit v1.2.3 From 58ecb03fc74a5ec4b8c8222c5b4bac572a264c47 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 6 Apr 2023 12:31:37 +0200 Subject: w1: ds2482: add i2c id for DS2484 The DS2484 is compatible to the DS2482-100, but also supports a pin-controlled power-saving sleep mode. Link: https://www.analog.com/media/en/technical-documentation/data-sheets/DS2484.pdf Signed-off-by: Stefan Wahren Link: https://lore.kernel.org/r/20230406103137.6092-3-stefan.wahren@chargebyte.com Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2482.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 3d8b51316bef..1665fd28b964 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -544,6 +544,7 @@ static void ds2482_remove(struct i2c_client *client) */ static const struct i2c_device_id ds2482_id[] = { { "ds2482", 0 }, + { "ds2484", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ds2482_id); -- cgit v1.2.3 From 45c85d974f60aa519769c1933859e84aa96fa3f1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:49 +0200 Subject: w1: ds2408: drop kerneldoc annotation Drop kerneldoc annotation from comment which is not a kerneldoc to fix: w1_ds2408.c:210: warning: This comment starts with '/**', but isn't a kernel-doc comment Reviewed-by: Randy Dunlap Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_ds2408.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index ad102c577122..7fa911989193 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -206,7 +206,7 @@ out: } -/** +/* * Writing to the activity file resets the activity latches. */ static ssize_t activity_write(struct file *filp, struct kobject *kobj, -- cgit v1.2.3 From a866ce3b4fde3d0fb399cb5a81fd12cc3ff6e5c1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:50 +0200 Subject: w1: ds2433: correct kerneldoc annotation Correct kerneldoc comments (or drop annotation) to fix: w1_ds2433.c:46: warning: This comment starts with '/**', but isn't a kernel-doc comment. w1_ds2433.c:141: warning: This comment starts with '/**', but isn't a kernel-doc comment. Reviewed-by: Randy Dunlap Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-2-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_ds2433.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 0f72df15a024..6134ca2f37fe 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -42,7 +42,7 @@ struct w1_f23_data { u32 validcrc; }; -/** +/* * Check the file size bounds and adjusts count as needed. * This would not be needed if the file size didn't reset to 0 after a write. */ @@ -138,16 +138,17 @@ out_up: } /** - * Writes to the scratchpad and reads it back for verification. + * w1_f23_write() - Writes to the scratchpad and reads it back for verification. + * @sl: The slave structure + * @addr: Address for the write + * @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) + * @data: The data to write + * * Then copies the scratchpad to EEPROM. * The data must be on one page. * The master must be locked. * - * @param sl The slave structure - * @param addr Address for the write - * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) - * @param data The data to write - * @return 0=Success -1=failure + * Return: 0=Success, -1=failure */ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) { -- cgit v1.2.3 From a2809664ca296ca89c6c8cc52ce728c1088d5af1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:51 +0200 Subject: w1: ds28e04: correct kerneldoc annotation Correct kerneldoc comments (or drop annotation) to fix: w1_ds28e04.c:57: warning: This comment starts with '/**', but isn't a kernel-doc comment. w1_ds28e04.c:149: warning: This comment starts with '/**', but isn't a kernel-doc comment. Reviewed-by: Randy Dunlap Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-3-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_ds28e04.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index 6cef6e2edb89..f54d6afd79c3 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -53,7 +53,7 @@ struct w1_f1C_data { u32 validcrc; }; -/** +/* * Check the file size bounds and adjusts count as needed. * This would not be needed if the file size didn't reset to 0 after a write. */ @@ -146,16 +146,17 @@ out_up: } /** - * Writes to the scratchpad and reads it back for verification. + * w1_f1C_write() - Writes to the scratchpad and reads it back for verification. + * @sl: The slave structure + * @addr: Address for the write + * @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) + * @data: The data to write + * * Then copies the scratchpad to EEPROM. * The data must be on one page. * The master must be locked. * - * @param sl The slave structure - * @param addr Address for the write - * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) - * @param data The data to write - * @return 0=Success -1=failure + * Return: 0=Success, -1=failure */ static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data) { -- cgit v1.2.3 From ad9c36be1f78f2990310cdcf902eba64cd2b2cf3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:52 +0200 Subject: w1: minor white-space and code style fixes Correct several coding convention violations around white-spaces: ERROR: spaces required around that '=' (ctx:VxV) WARNING: Missing a blank line after declarations ERROR: "foo* bar" should be "foo *bar" ERROR: "(foo*)" should be "(foo *)" WARNING: Block comments use * on subsequent lines WARNING: Block comments use a trailing */ on a separate line WARNING: please, no space before tabs WARNING: Missing a blank line after declarations ERROR: open brace '{' following struct go on the same line ERROR: that open brace { should be on the previous line Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-4-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2482.c | 6 ++---- drivers/w1/masters/ds2490.c | 13 +++++++++---- drivers/w1/masters/matrox_w1.c | 3 +-- drivers/w1/slaves/w1_ds2406.c | 31 ++++++++++++++++--------------- drivers/w1/slaves/w1_ds2408.c | 10 +++++----- drivers/w1/slaves/w1_ds2413.c | 6 ++++-- drivers/w1/slaves/w1_ds2433.c | 2 +- drivers/w1/slaves/w1_ds2780.c | 1 + drivers/w1/slaves/w1_ds2781.c | 1 + drivers/w1/slaves/w1_ds28e04.c | 6 ++++-- drivers/w1/slaves/w1_ds28e17.c | 2 +- 11 files changed, 45 insertions(+), 36 deletions(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 1665fd28b964..104bdf5c8064 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -78,10 +78,8 @@ MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8 * To set the channel, write the value at the index of the channel. * Read and compare against the corresponding value to verify the change. */ -static const u8 ds2482_chan_wr[8] = - { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 }; -static const u8 ds2482_chan_rd[8] = - { 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 }; +static const u8 ds2482_chan_wr[8] = { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 }; +static const u8 ds2482_chan_rd[8] = { 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 }; /* diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 0eb560fc0153..5f5b97e24700 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -304,6 +304,7 @@ static void ds_reset_device(struct ds_device *dev) if (dev->spu_sleep) { /* lower 4 bits are 0, see ds_set_pullup */ u8 del = dev->spu_sleep>>4; + if (ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del)) dev_err(&dev->udev->dev, "%s: Error setting duration\n", __func__); @@ -731,7 +732,8 @@ static void ds9490r_search(void *data, struct w1_master *master, break; if (st.data_in_buffer_status) { - /* Bulk in can receive partial ids, but when it does + /* + * Bulk in can receive partial ids, but when it does * they fail crc and will be discarded anyway. * That has only been seen when status in buffer * is 0 and bulk is read anyway, so don't read @@ -743,8 +745,10 @@ static void ds9490r_search(void *data, struct w1_master *master, break; for (i = 0; i < err/8; ++i) { found_ids[found++] = buf[i]; - /* can't know if there will be a discrepancy - * value after until the next id */ + /* + * can't know if there will be a discrepancy + * value after until the next id + */ if (found == search_limit) { master->search_id = buf[i]; break; @@ -760,7 +764,8 @@ static void ds9490r_search(void *data, struct w1_master *master, if (found <= search_limit) { master->search_id = 0; } else if (!test_bit(W1_WARN_MAX_COUNT, &master->flags)) { - /* Only max_slave_count will be scanned in a search, + /* + * Only max_slave_count will be scanned in a search, * but it will start where it left off next search * until all ids are identified and then it will start * over. A continued search will report the previous diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index ee716c715710..94a1f3fa2dc3 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -39,8 +39,7 @@ #define MATROX_GET_DATA 0x2B #define MATROX_CURSOR_CTL 0x06 -struct matrox_device -{ +struct matrox_device { void __iomem *base_addr; void __iomem *port_index; void __iomem *port_data; diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c index 6c269af73c80..3bb88b51fe2b 100644 --- a/drivers/w1/slaves/w1_ds2406.c +++ b/drivers/w1/slaves/w1_ds2406.c @@ -27,11 +27,11 @@ static ssize_t w1_f12_read_state( struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - u8 w1_buf[6]={W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; + u8 w1_buf[6] = {W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; struct w1_slave *sl = kobj_to_w1_slave(kobj); - u16 crc=0; + u16 crc = 0; int i; - ssize_t rtnval=1; + ssize_t rtnval = 1; if (off != 0) return 0; @@ -47,12 +47,12 @@ static ssize_t w1_f12_read_state( w1_write_block(sl->master, w1_buf, 3); w1_read_block(sl->master, w1_buf+3, 3); - for (i=0; i<6; i++) - crc=crc16_byte(crc, w1_buf[i]); - if (crc==0xb001) /* good read? */ - *buf=((w1_buf[3]>>5)&3)|0x30; + for (i = 0; i < 6; i++) + crc = crc16_byte(crc, w1_buf[i]); + if (crc == 0xb001) /* good read? */ + *buf = ((w1_buf[3]>>5)&3)|0x30; else - rtnval=-EIO; + rtnval = -EIO; mutex_unlock(&sl->master->bus_mutex); @@ -65,10 +65,10 @@ static ssize_t w1_f12_write_output( char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); - u8 w1_buf[6]={W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; - u16 crc=0; + u8 w1_buf[6] = {W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; + u16 crc = 0; int i; - ssize_t rtnval=1; + ssize_t rtnval = 1; if (count != 1 || off != 0) return -EFAULT; @@ -83,12 +83,12 @@ static ssize_t w1_f12_write_output( w1_buf[3] = (((*buf)&3)<<5)|0x1F; w1_write_block(sl->master, w1_buf, 4); w1_read_block(sl->master, w1_buf+4, 2); - for (i=0; i<6; i++) - crc=crc16_byte(crc, w1_buf[i]); - if (crc==0xb001) /* good read? */ + for (i = 0; i < 6; i++) + crc = crc16_byte(crc, w1_buf[i]); + if (crc == 0xb001) /* good read? */ w1_write_8(sl->master, 0xFF); else - rtnval=-EIO; + rtnval = -EIO; mutex_unlock(&sl->master->bus_mutex); return rtnval; @@ -133,6 +133,7 @@ static int w1_f12_add_slave(struct w1_slave *sl) static void w1_f12_remove_slave(struct w1_slave *sl) { int i; + for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) sysfs_remove_bin_file(&sl->dev.kobj, &(w1_f12_sysfs_bin_files[i])); diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 7fa911989193..56f822a1dfdb 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -35,12 +35,12 @@ #define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA -static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) +static int _read_reg(struct w1_slave *sl, u8 address, unsigned char *buf) { u8 wrbuf[3]; - dev_dbg(&sl->dev, - "Reading with slave: %p, reg addr: %0#4x, buff addr: %p", - sl, (unsigned int)address, buf); + + dev_dbg(&sl->dev, "Reading with slave: %p, reg addr: %0#4x, buff addr: %p", + sl, (unsigned int)address, buf); if (!buf) return -EINVAL; @@ -292,7 +292,7 @@ static int w1_f29_disable_test_mode(struct w1_slave *sl) { int res; u8 magic[10] = {0x96, }; - u64 rn = le64_to_cpu(*((u64*)&sl->reg_num)); + u64 rn = le64_to_cpu(*((u64 *)&sl->reg_num)); memcpy(&magic[1], &rn, 8); magic[9] = 0x3C; diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index c8cfac555b48..bf4a72aae28a 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -99,8 +99,10 @@ static ssize_t output_write(struct file *filp, struct kobject *kobj, if (w1_reset_select_slave(sl)) goto out; - /* according to the DS2413 datasheet the most significant 6 bits - should be set to "1"s, so do it now */ + /* + * according to the DS2413 datasheet the most significant 6 bits + * should be set to "1"s, so do it now + */ *buf = *buf | 0xFC; while (retries--) { diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 6134ca2f37fe..23cd099d032b 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -115,7 +115,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, } memcpy(buf, &data->memory[off], count); -#else /* CONFIG_W1_SLAVE_DS2433_CRC */ +#else /* CONFIG_W1_SLAVE_DS2433_CRC */ /* read directly from the EEPROM */ if (w1_reset_select_slave(sl)) { diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index 9dcb5a54f7fc..3cde1bb1886b 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -91,6 +91,7 @@ static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); + return w1_ds2780_io(dev, buf, off, count, 0); } diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c index 2cb7c020b607..e418484b4a49 100644 --- a/drivers/w1/slaves/w1_ds2781.c +++ b/drivers/w1/slaves/w1_ds2781.c @@ -88,6 +88,7 @@ static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); + return w1_ds2781_io(dev, buf, off, count, 0); } diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index f54d6afd79c3..2854b8b9e93f 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -198,8 +198,10 @@ static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data) wrbuf[3] = es; for (i = 0; i < sizeof(wrbuf); ++i) { - /* issue 10ms strong pullup (or delay) on the last byte - for writing the data from the scratchpad to EEPROM */ + /* + * issue 10ms strong pullup (or delay) on the last byte + * for writing the data from the scratchpad to EEPROM + */ if (w1_strong_pullup && i == sizeof(wrbuf)-1) w1_next_pullup(sl->master, tm); diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index aed10b72fc99..fdbaf77620ac 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -59,7 +59,7 @@ MODULE_PARM_DESC(stretch, "Default I2C stretch value to be set when a DS28E17 is /* * Maximum number of I2C bytes to transfer within one CRC16 protected onewire * command. - * */ + */ #define W1_F19_WRITE_DATA_LIMIT 255 /* Maximum number of I2C bytes to read with one onewire command. */ -- cgit v1.2.3 From 921e0f2fb09d8decfa8f19eae01f0b68e322363e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:54 +0200 Subject: w1: use octal for file permissions Improve code readability and checkpatch warnings: WARNING: Symbolic permissions 'S_IRUGO' are not preferred. Consider using octal permissions '0444'. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-6-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2482.c | 2 +- drivers/w1/masters/omap_hdq.c | 2 +- drivers/w1/slaves/w1_ds2406.c | 4 ++-- drivers/w1/slaves/w1_ds2413.c | 2 +- drivers/w1/slaves/w1_ds2805.c | 2 +- drivers/w1/slaves/w1_ds28e17.c | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 104bdf5c8064..b1c76a96cc8d 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -36,7 +36,7 @@ MODULE_PARM_DESC(active_pullup, "Active pullup (apply to all buses): " \ /* extra configurations - e.g. 1WS */ static int extra_config; -module_param(extra_config, int, S_IRUGO | S_IWUSR); +module_param(extra_config, int, 0644); MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8=1WS"); /* diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index bf2ec59c1f9d..ae3e504cd543 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -48,7 +48,7 @@ static DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue); static int w1_id; -module_param(w1_id, int, S_IRUSR); +module_param(w1_id, int, 0400); MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection in HDQ mode"); struct hdq_data { diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c index 3bb88b51fe2b..2f5926859b8b 100644 --- a/drivers/w1/slaves/w1_ds2406.c +++ b/drivers/w1/slaves/w1_ds2406.c @@ -99,7 +99,7 @@ static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { { .attr = { .name = "state", - .mode = S_IRUGO, + .mode = 0444, }, .size = 1, .read = w1_f12_read_state, @@ -107,7 +107,7 @@ static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { { .attr = { .name = "output", - .mode = S_IRUGO | S_IWUSR | S_IWGRP, + .mode = 0664, }, .size = 1, .write = w1_f12_write_output, diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index bf4a72aae28a..739009806467 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -128,7 +128,7 @@ out: return bytes_written; } -static BIN_ATTR(output, S_IRUGO | S_IWUSR | S_IWGRP, NULL, output_write, 1); +static BIN_ATTR(output, 0664, NULL, output_write, 1); static struct bin_attribute *w1_f3a_bin_attrs[] = { &bin_attr_state, diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c index 6b5d12ba1b65..4c1a2c515317 100644 --- a/drivers/w1/slaves/w1_ds2805.c +++ b/drivers/w1/slaves/w1_ds2805.c @@ -264,7 +264,7 @@ out_up: static struct bin_attribute w1_f0d_bin_attr = { .attr = { .name = "eeprom", - .mode = S_IRUGO | S_IWUSR, + .mode = 0644, }, .size = W1_F0D_EEPROM_SIZE, .read = w1_f0d_read_bin, diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index fdbaf77620ac..52261b54d842 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -31,12 +31,12 @@ MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E17)); /* Default I2C speed to be set when a DS28E17 is detected. */ static int i2c_speed = 100; -module_param_named(speed, i2c_speed, int, (S_IRUSR | S_IWUSR)); +module_param_named(speed, i2c_speed, int, 0600); MODULE_PARM_DESC(speed, "Default I2C speed to be set when a DS28E17 is detected"); /* Default I2C stretch value to be set when a DS28E17 is detected. */ static char i2c_stretch = 1; -module_param_named(stretch, i2c_stretch, byte, (S_IRUSR | S_IWUSR)); +module_param_named(stretch, i2c_stretch, byte, 0600); MODULE_PARM_DESC(stretch, "Default I2C stretch value to be set when a DS28E17 is detected"); /* DS28E17 device command codes. */ -- cgit v1.2.3 From 39bbfdacaebbe2576fb0f591e218e76a117886cf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:55 +0200 Subject: w1: ds2433: do not use assignment in if condition Assignments in if condition are less readable and error-prone. Fixes also checkpatch warning: ERROR: do not use assignment in if condition Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-7-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_ds2433.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 23cd099d032b..9f21fd98f799 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -98,7 +98,8 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, u8 wrbuf[3]; #endif - if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) + count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE); + if (!count) return 0; mutex_lock(&sl->master->bus_mutex); @@ -208,7 +209,8 @@ static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, struct w1_slave *sl = kobj_to_w1_slave(kobj); int addr, len, idx; - if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) + count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE); + if (!count) return 0; #ifdef CONFIG_W1_SLAVE_DS2433_CRC -- cgit v1.2.3 From 076909c24ffa747a56d9c0167c74ee167dd81cd1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:56 +0200 Subject: w1: ds2482: drop unnecessary header linux/delay.h is already included, so drop asm header to also fix checkpatch: WARNING: Use #include instead of Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-8-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2482.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index b1c76a96cc8d..cc5938fb6239 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -15,7 +15,6 @@ #include #include #include -#include #include -- cgit v1.2.3 From 787e19ae1fa96cfbbb943e647253e966b4d53407 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:57 +0200 Subject: w1: ds2482: do not use assignment in if condition Assignments in if condition are less readable and error-prone. Fixes also checkpatch warning: ERROR: do not use assignment in if condition Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-9-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2482.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index cc5938fb6239..c1de8a92e144 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -451,7 +451,8 @@ static int ds2482_probe(struct i2c_client *client) I2C_FUNC_SMBUS_BYTE)) return -ENODEV; - if (!(data = kzalloc(sizeof(struct ds2482_data), GFP_KERNEL))) { + data = kzalloc(sizeof(struct ds2482_data), GFP_KERNEL); + if (!data) { err = -ENOMEM; goto exit; } -- cgit v1.2.3 From 01bc199b42594e31e5c4fe335f4feb40f976db1f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:58 +0200 Subject: w1: matrox: switch from asm to linux header linux/io.h is preferred over asm/io.h: WARNING: Use #include instead of Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-10-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/matrox_w1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index 94a1f3fa2dc3..662108b1d806 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include -- cgit v1.2.3 From 115ddc758bfbd24e077e2e96b307d174622e1584 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:42:59 +0200 Subject: w1: matrox: use inline over __inline__ Switch to inline as preferred by coding style: WARNING: plain inline is preferred over __inline__ Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-11-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/matrox_w1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index 662108b1d806..fb38d92b2f43 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -63,7 +63,7 @@ struct matrox_device { * * Port mapping. */ -static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg) +static inline u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg) { u8 ret; @@ -74,7 +74,7 @@ static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg) return ret; } -static __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val) +static inline void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val) { writeb(reg, dev->port_index); writeb(val, dev->port_data); -- cgit v1.2.3 From 4f4129e4e28fb5f5e84a2d9719666968049d95e3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:43:00 +0200 Subject: w1: matrox: remove unnecessary ENOMEM messages Core already prints detailed reports on out of memory: WARNING: Possible unnecessary 'out of memory' message Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-12-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/matrox_w1.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index fb38d92b2f43..2852cd2dc67c 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -122,13 +122,8 @@ static int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent dev = kzalloc(sizeof(struct matrox_device) + sizeof(struct w1_bus_master), GFP_KERNEL); - if (!dev) { - dev_err(&pdev->dev, - "%s: Failed to create new matrox_device object.\n", - __func__); + if (!dev) return -ENOMEM; - } - dev->bus_master = (struct w1_bus_master *)(dev + 1); -- cgit v1.2.3 From 0cb90771913922e4b34c296e96d80d451766d942 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:43:01 +0200 Subject: w1: omap-hdq: allow compile testing omap-hdq does not depend on anything from ARCH_OMAP for building. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-13-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 692cac3ff0ee..ad316573288a 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -53,7 +53,7 @@ config W1_MASTER_GPIO config HDQ_MASTER_OMAP tristate "OMAP HDQ driver" - depends on ARCH_OMAP + depends on ARCH_OMAP || COMPILE_TEST help Say Y here if you want support for the 1-wire or HDQ Interface on an OMAP processor. -- cgit v1.2.3 From 752144d8939a5b303b753da8528e14d14c2a235c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:43:02 +0200 Subject: w1: omap-hdq: add SPDX tag Replace GPLv2 license text with SPDX and drop unnecessary file name in comment to fix checkpatch warnings: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1 WARNING: It's generally not useful to have the filename in the file Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-14-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index ae3e504cd543..826207c24bd0 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -1,12 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/w1/masters/omap_hdq.c - * * Copyright (C) 2007,2012 Texas Instruments, 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. - * */ #include #include -- cgit v1.2.3 From 1e5d6652154aca23da856ac7926f23217239ea60 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:43:03 +0200 Subject: w1: omap-hdq: remove unnecessary ENOMEM messages Core already prints detailed reports on out of memory: WARNING: Possible unnecessary 'out of memory' message Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-15-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 826207c24bd0..6a39b71eb718 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -573,10 +573,8 @@ static int omap_hdq_probe(struct platform_device *pdev) const char *mode; hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL); - if (!hdq_data) { - dev_dbg(&pdev->dev, "unable to allocate memory\n"); + if (!hdq_data) return -ENOMEM; - } hdq_data->dev = dev; platform_set_drvdata(pdev, hdq_data); -- cgit v1.2.3 From 4ccb0fc2a212d11e5491a4a1e705d6d84463c3a5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 15 Apr 2023 12:43:04 +0200 Subject: w1: gpio: remove unnecessary ENOMEM messages Core already prints detailed reports on out of memory: WARNING: Possible unnecessary 'out of memory' message Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230415104304.104134-16-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/w1-gpio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index d4632aace402..ac7ad63f8f8d 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -101,10 +101,8 @@ static int w1_gpio_probe(struct platform_device *pdev) master = devm_kzalloc(dev, sizeof(struct w1_bus_master), GFP_KERNEL); - if (!master) { - dev_err(dev, "Out of memory\n"); + if (!master) return -ENOMEM; - } pdata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags); if (IS_ERR(pdata->gpiod)) { -- cgit v1.2.3 From 15f44da0ab041e6f4d2218468eec792ebfa25665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 13 Apr 2023 15:38:29 -0700 Subject: spmi: hisi-spmi-controller: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230306073446.2194048-2-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20230413223834.4084793-2-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/hisi-spmi-controller.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c index 5bd23262abd6..9cbd473487cb 100644 --- a/drivers/spmi/hisi-spmi-controller.c +++ b/drivers/spmi/hisi-spmi-controller.c @@ -324,13 +324,12 @@ err_put_controller: return ret; } -static int spmi_del_controller(struct platform_device *pdev) +static void spmi_del_controller(struct platform_device *pdev) { struct spmi_controller *ctrl = platform_get_drvdata(pdev); spmi_controller_remove(ctrl); spmi_controller_put(ctrl); - return 0; } static const struct of_device_id spmi_controller_match_table[] = { @@ -343,7 +342,7 @@ MODULE_DEVICE_TABLE(of, spmi_controller_match_table); static struct platform_driver spmi_controller_driver = { .probe = spmi_controller_probe, - .remove = spmi_del_controller, + .remove_new = spmi_del_controller, .driver = { .name = "hisi_spmi_controller", .of_match_table = spmi_controller_match_table, -- cgit v1.2.3 From 75fbbd8b53b46fa1c36d055854d7c0c849abf9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 13 Apr 2023 15:38:30 -0700 Subject: spmi: mtk-pmif: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230306073446.2194048-3-u.kleine-koenig@pengutronix.de Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Uwe Kleine-König Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20230413223834.4084793-3-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-mtk-pmif.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c index ad511f2c3324..fbcb3921e70c 100644 --- a/drivers/spmi/spmi-mtk-pmif.c +++ b/drivers/spmi/spmi-mtk-pmif.c @@ -503,7 +503,7 @@ err_put_ctrl: return err; } -static int mtk_spmi_remove(struct platform_device *pdev) +static void mtk_spmi_remove(struct platform_device *pdev) { struct spmi_controller *ctrl = platform_get_drvdata(pdev); struct pmif *arb = spmi_controller_get_drvdata(ctrl); @@ -511,7 +511,6 @@ static int mtk_spmi_remove(struct platform_device *pdev) clk_bulk_disable_unprepare(arb->nclks, arb->clks); spmi_controller_remove(ctrl); spmi_controller_put(ctrl); - return 0; } static const struct of_device_id mtk_spmi_match_table[] = { @@ -533,7 +532,7 @@ static struct platform_driver mtk_spmi_driver = { .of_match_table = of_match_ptr(mtk_spmi_match_table), }, .probe = mtk_spmi_probe, - .remove = mtk_spmi_remove, + .remove_new = mtk_spmi_remove, }; module_platform_driver(mtk_spmi_driver); -- cgit v1.2.3 From 019fe19bd4079bc9775fb034e49da68b1b8db06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 13 Apr 2023 15:38:31 -0700 Subject: spmi: pmic-arb: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230306073446.2194048-4-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20230413223834.4084793-4-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 8b6a42ab816f..42a593418aad 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1674,7 +1674,7 @@ err_put_ctrl: return err; } -static int spmi_pmic_arb_remove(struct platform_device *pdev) +static void spmi_pmic_arb_remove(struct platform_device *pdev) { struct spmi_controller *ctrl = platform_get_drvdata(pdev); struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); @@ -1682,7 +1682,6 @@ static int spmi_pmic_arb_remove(struct platform_device *pdev) irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); irq_domain_remove(pmic_arb->domain); spmi_controller_put(ctrl); - return 0; } static const struct of_device_id spmi_pmic_arb_match_table[] = { @@ -1693,7 +1692,7 @@ MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); static struct platform_driver spmi_pmic_arb_driver = { .probe = spmi_pmic_arb_probe, - .remove = spmi_pmic_arb_remove, + .remove_new = spmi_pmic_arb_remove, .driver = { .name = "spmi_pmic_arb", .of_match_table = spmi_pmic_arb_match_table, -- cgit v1.2.3 From 77982a7f4970dcabf4bbcbcae9d1dc85a731ce6b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 13 Apr 2023 15:38:32 -0700 Subject: spmi: mtk-pmif: Drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it is not relevant here). drivers/spmi/spmi-mtk-pmif.c:517:34: error: ‘mtk_spmi_match_table’ defined but not used [-Werror=unused-const-variable=] Link: https://lore.kernel.org/r/20230310222857.315629-2-krzysztof.kozlowski@linaro.org Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20230413223834.4084793-5-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-mtk-pmif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c index fbcb3921e70c..b3c991e1ea40 100644 --- a/drivers/spmi/spmi-mtk-pmif.c +++ b/drivers/spmi/spmi-mtk-pmif.c @@ -529,7 +529,7 @@ MODULE_DEVICE_TABLE(of, mtk_spmi_match_table); static struct platform_driver mtk_spmi_driver = { .driver = { .name = "spmi-mtk", - .of_match_table = of_match_ptr(mtk_spmi_match_table), + .of_match_table = mtk_spmi_match_table, }, .probe = mtk_spmi_probe, .remove_new = mtk_spmi_remove, -- cgit v1.2.3 From 80c606a842e6b6b6f4a2b53a734a9d3be2cdddff Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 13 Apr 2023 15:38:33 -0700 Subject: spmi: fix W=1 kernel-doc warnings Fix all W=1 kernel-doc warnings in drivers/spmi/: drivers/spmi/spmi.c:414: warning: expecting prototype for spmi_controller_alloc(). Prototype was for spmi_device_alloc() instead drivers/spmi/spmi.c:592: warning: expecting prototype for spmi_driver_register(). Prototype was for __spmi_driver_register() instead drivers/spmi/spmi.c:592: warning: Function parameter or member 'owner' not described in '__spmi_driver_register' drivers/spmi/spmi-pmic-arb.c:155: warning: cannot understand function prototype: 'struct spmi_pmic_arb ' drivers/spmi/spmi-pmic-arb.c:203: warning: cannot understand function prototype: 'struct pmic_arb_ver_ops ' drivers/spmi/spmi-pmic-arb.c:219: warning: expecting prototype for struct pmic_arb_ver. Prototype was for struct pmic_arb_ver_ops instead Link: https://lore.kernel.org/r/20230113064040.26801-1-rdunlap@infradead.org Cc: Stephen Boyd Signed-off-by: Randy Dunlap Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20230413223834.4084793-6-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 4 ++-- drivers/spmi/spmi.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 42a593418aad..dcb675d980d4 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -126,7 +126,7 @@ struct apid_data { }; /** - * spmi_pmic_arb - SPMI PMIC Arbiter object + * struct spmi_pmic_arb - SPMI PMIC Arbiter object * * @rd_base: on v1 "core", on v2 "observer" register base off DT. * @wr_base: on v1 "core", on v2 "chnls" register base off DT. @@ -180,7 +180,7 @@ struct spmi_pmic_arb { }; /** - * pmic_arb_ver: version dependent functionality. + * struct pmic_arb_ver_ops - version dependent functionality. * * @ver_str: version string. * @ppid_to_apid: finds the apid for a given ppid. diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 73551531ed43..5705151013b3 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -404,7 +404,7 @@ struct spmi_device *spmi_device_from_of(struct device_node *np) EXPORT_SYMBOL_GPL(spmi_device_from_of); /** - * spmi_controller_alloc() - Allocate a new SPMI device + * spmi_device_alloc() - Allocate a new SPMI device * @ctrl: associated controller * * Caller is responsible for either calling spmi_device_add() to add the @@ -582,8 +582,9 @@ void spmi_controller_remove(struct spmi_controller *ctrl) EXPORT_SYMBOL_GPL(spmi_controller_remove); /** - * spmi_driver_register() - Register client driver with SPMI core + * __spmi_driver_register() - Register client driver with SPMI core * @sdrv: client driver to be associated with client-device. + * @owner: module owner * * This API will register the client driver with the SPMI framework. * It is typically called from the driver's module-init function. -- cgit v1.2.3 From b56eef3e16d888883fefab47425036de80dd38fc Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Thu, 13 Apr 2023 15:38:34 -0700 Subject: spmi: Add a check for remove callback when removing a SPMI driver When removing a SPMI driver, there can be a crash due to NULL pointer dereference if it does not have a remove callback defined. This is one such call trace observed when removing the QCOM SPMI PMIC driver: dump_backtrace.cfi_jt+0x0/0x8 dump_stack_lvl+0xd8/0x16c panic+0x188/0x498 __cfi_slowpath+0x0/0x214 __cfi_slowpath+0x1dc/0x214 spmi_drv_remove+0x16c/0x1e0 device_release_driver_internal+0x468/0x79c driver_detach+0x11c/0x1a0 bus_remove_driver+0xc4/0x124 driver_unregister+0x58/0x84 cleanup_module+0x1c/0xc24 [qcom_spmi_pmic] __do_sys_delete_module+0x3ec/0x53c __arm64_sys_delete_module+0x18/0x28 el0_svc_common+0xdc/0x294 el0_svc+0x38/0x9c el0_sync_handler+0x8c/0xf0 el0_sync+0x1b4/0x1c0 If a driver has all its resources allocated through devm_() APIs and does not need any other explicit cleanup, it would not require a remove callback to be defined. Hence, add a check for remove callback presence before calling it when removing a SPMI driver. Link: https://lore.kernel.org/r/1671601032-18397-2-git-send-email-quic_jprakash@quicinc.com Fixes: 6f00f8c8635f ("mfd: qcom-spmi-pmic: Use devm_of_platform_populate()") Fixes: 5a86bf343976 ("spmi: Linux driver framework for SPMI") Signed-off-by: Jishnu Prakash Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20230413223834.4084793-7-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 5705151013b3..7313d4c18a04 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -350,7 +350,8 @@ static void spmi_drv_remove(struct device *dev) const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); pm_runtime_get_sync(dev); - sdrv->remove(to_spmi_device(dev)); + if (sdrv->remove) + sdrv->remove(to_spmi_device(dev)); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); -- cgit v1.2.3 From 32118bdc9471f6d28dbf3d55b2bf0f912be0c62b Mon Sep 17 00:00:00 2001 From: Deming Wang Date: Wed, 12 Apr 2023 03:30:41 -0400 Subject: virt: acrn: Replace obsolete memalign() with posix_memalign() memalign() is obsolete according to its manpage. Replace memalign() with posix_memalign() and remove malloc.h include that was there for memalign(). As a pointer is passed into posix_memalign(), initialize *p to NULL to silence a warning about the function's return value being used as uninitialized (which is not valid anyway because the error is properly checked before p is returned). Signed-off-by: Deming Wang Link: https://lore.kernel.org/r/20230412073041.2168-1-wangdeming@inspur.com Signed-off-by: Greg Kroah-Hartman --- samples/acrn/vm-sample.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/samples/acrn/vm-sample.c b/samples/acrn/vm-sample.c index 7abd68b20153..704402c64ea3 100644 --- a/samples/acrn/vm-sample.c +++ b/samples/acrn/vm-sample.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -54,8 +53,8 @@ int main(int argc, char **argv) argc = argc; argv = argv; - guest_memory = memalign(4096, GUEST_MEMORY_SIZE); - if (!guest_memory) { + ret = posix_memalign(&guest_memory, 4096, GUEST_MEMORY_SIZE); + if (ret < 0) { printf("No enough memory!\n"); return -1; } -- cgit v1.2.3 From 4daa669efcfa5147e4bd5ecb3f1e10c0c96eef27 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 11 Apr 2023 18:04:09 +0000 Subject: kernel/configs: Drop Android config fragments In the old days where each device had a custom kernel, the android config fragments were useful to provide the required and reccomended options expected by userland. However, these days devices are expected to use the GKI kernel, so these config fragments no longer needed, and out of date, so they seem to only cause confusion. So lets drop them. If folks are curious what configs are expected by the Android environment, check out the gki_defconfig file in the latest android common kernel tree. Cc: Rob Herring Cc: Amit Pundir Cc: Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20230411180409.1706067-1-jstultz@google.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 5 - kernel/configs/android-base.config | 159 ------------------------------ kernel/configs/android-recommended.config | 127 ------------------------ 3 files changed, 291 deletions(-) delete mode 100644 kernel/configs/android-base.config delete mode 100644 kernel/configs/android-recommended.config diff --git a/MAINTAINERS b/MAINTAINERS index 78944f72b6fe..5408ca5771a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1436,11 +1436,6 @@ S: Supported F: drivers/clk/analogbits/* F: include/linux/clk/analogbits* -ANDROID CONFIG FRAGMENTS -M: Rob Herring -S: Supported -F: kernel/configs/android* - ANDROID DRIVERS M: Greg Kroah-Hartman M: Arve Hjønnevåg diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config deleted file mode 100644 index 44b0f0146a3f..000000000000 --- a/kernel/configs/android-base.config +++ /dev/null @@ -1,159 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_DEVMEM is not set -# CONFIG_FHANDLE is not set -# CONFIG_INET_LRO is not set -# CONFIG_NFSD is not set -# CONFIG_NFS_FS is not set -# CONFIG_OABI_COMPAT is not set -# CONFIG_SYSVIPC is not set -# CONFIG_USELIB is not set -CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder -CONFIG_ANDROID_LOW_MEMORY_KILLER=y -CONFIG_ARMV8_DEPRECATED=y -CONFIG_ASHMEM=y -CONFIG_AUDIT=y -CONFIG_BLK_DEV_INITRD=y -CONFIG_CGROUPS=y -CONFIG_CGROUP_BPF=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_DEBUG=y -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_SCHED=y -CONFIG_CP15_BARRIER_EMULATION=y -CONFIG_DEFAULT_SECURITY_SELINUX=y -CONFIG_EMBEDDED=y -CONFIG_FB=y -CONFIG_HARDENED_USERCOPY=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_INET=y -CONFIG_INET_DIAG_DESTROY=y -CONFIG_INET_ESP=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y -CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IPV6=y -CONFIG_IPV6_MIP6=y -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_NAT=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_SECURITY=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_NETMAP=y -CONFIG_IP_NF_TARGET_REDIRECT=y -CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_NET=y -CONFIG_NETDEVICES=y -CONFIG_NETFILTER=y -CONFIG_NETFILTER_TPROXY=y -CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y -CONFIG_NETFILTER_XT_MATCH_IPRANGE=y -CONFIG_NETFILTER_XT_MATCH_LENGTH=y -CONFIG_NETFILTER_XT_MATCH_LIMIT=y -CONFIG_NETFILTER_XT_MATCH_MAC=y -CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_POLICY=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y -CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y -CONFIG_NETFILTER_XT_MATCH_STATISTIC=y -CONFIG_NETFILTER_XT_MATCH_STRING=y -CONFIG_NETFILTER_XT_MATCH_TIME=y -CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_SECMARK=y -CONFIG_NETFILTER_XT_TARGET_TCPMSS=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -CONFIG_NET_CLS_ACT=y -CONFIG_NET_CLS_U32=y -CONFIG_NET_EMATCH=y -CONFIG_NET_EMATCH_U32=y -CONFIG_NET_KEY=y -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_HTB=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IPV4=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_NAT=y -CONFIG_NO_HZ=y -CONFIG_PACKET=y -CONFIG_PM_AUTOSLEEP=y -CONFIG_PM_WAKELOCKS=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_MPPE=y -CONFIG_PREEMPT=y -CONFIG_QUOTA=y -CONFIG_RANDOMIZE_BASE=y -CONFIG_RTC_CLASS=y -CONFIG_RT_GROUP_SCHED=y -CONFIG_SECCOMP=y -CONFIG_SECURITY=y -CONFIG_SECURITY_NETWORK=y -CONFIG_SECURITY_SELINUX=y -CONFIG_SETEND_EMULATION=y -CONFIG_STAGING=y -CONFIG_SWP_EMULATION=y -CONFIG_SYNC=y -CONFIG_TUN=y -CONFIG_UNIX=y -CONFIG_USB_GADGET=y -CONFIG_USB_CONFIGFS=y -CONFIG_USB_CONFIGFS_F_FS=y -CONFIG_USB_CONFIGFS_F_MIDI=y -CONFIG_USB_OTG_WAKELOCK=y -CONFIG_XFRM_USER=y diff --git a/kernel/configs/android-recommended.config b/kernel/configs/android-recommended.config deleted file mode 100644 index e400fbbc8aba..000000000000 --- a/kernel/configs/android-recommended.config +++ /dev/null @@ -1,127 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_LEGACY_PTYS is not set -# CONFIG_NF_CONNTRACK_SIP is not set -# CONFIG_PM_WAKELOCKS_GC is not set -# CONFIG_VT is not set -CONFIG_ARM64_SW_TTBR0_PAN=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BLK_DEV_DM=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_STACKPROTECTOR_STRONG=y -CONFIG_COMPACTION=y -CONFIG_CPU_SW_DOMAIN_PAN=y -CONFIG_DM_CRYPT=y -CONFIG_DM_UEVENT=y -CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y -CONFIG_DRAGONRISE_FF=y -CONFIG_ENABLE_DEFAULT_TRACERS=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_SECURITY=y -CONFIG_FUSE_FS=y -CONFIG_GREENASIA_FF=y -CONFIG_HIDRAW=y -CONFIG_HID_A4TECH=y -CONFIG_HID_ACRUX=y -CONFIG_HID_ACRUX_FF=y -CONFIG_HID_APPLE=y -CONFIG_HID_BELKIN=y -CONFIG_HID_CHERRY=y -CONFIG_HID_CHICONY=y -CONFIG_HID_CYPRESS=y -CONFIG_HID_DRAGONRISE=y -CONFIG_HID_ELECOM=y -CONFIG_HID_EMS_FF=y -CONFIG_HID_EZKEY=y -CONFIG_HID_GREENASIA=y -CONFIG_HID_GYRATION=y -CONFIG_HID_HOLTEK=y -CONFIG_HID_KENSINGTON=y -CONFIG_HID_KEYTOUCH=y -CONFIG_HID_KYE=y -CONFIG_HID_LCPOWER=y -CONFIG_HID_LOGITECH=y -CONFIG_HID_LOGITECH_DJ=y -CONFIG_HID_MAGICMOUSE=y -CONFIG_HID_MICROSOFT=y -CONFIG_HID_MONTEREY=y -CONFIG_HID_MULTITOUCH=y -CONFIG_HID_NTRIG=y -CONFIG_HID_ORTEK=y -CONFIG_HID_PANTHERLORD=y -CONFIG_HID_PETALYNX=y -CONFIG_HID_PICOLCD=y -CONFIG_HID_PRIMAX=y -CONFIG_HID_PRODIKEYS=y -CONFIG_HID_ROCCAT=y -CONFIG_HID_SAITEK=y -CONFIG_HID_SAMSUNG=y -CONFIG_HID_SMARTJOYPLUS=y -CONFIG_HID_SONY=y -CONFIG_HID_SPEEDLINK=y -CONFIG_HID_SUNPLUS=y -CONFIG_HID_THRUSTMASTER=y -CONFIG_HID_TIVO=y -CONFIG_HID_TOPSEED=y -CONFIG_HID_TWINHAN=y -CONFIG_HID_UCLOGIC=y -CONFIG_HID_WACOM=y -CONFIG_HID_WALTOP=y -CONFIG_HID_WIIMOTE=y -CONFIG_HID_ZEROPLUS=y -CONFIG_HID_ZYDACRON=y -CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_GPIO=y -CONFIG_INPUT_JOYSTICK=y -CONFIG_INPUT_MISC=y -CONFIG_INPUT_TABLET=y -CONFIG_INPUT_UINPUT=y -CONFIG_JOYSTICK_XPAD=y -CONFIG_JOYSTICK_XPAD_FF=y -CONFIG_JOYSTICK_XPAD_LEDS=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KSM=y -CONFIG_LOGIG940_FF=y -CONFIG_LOGIRUMBLEPAD2_FF=y -CONFIG_LOGITECH_FF=y -CONFIG_MD=y -CONFIG_MEDIA_SUPPORT=y -CONFIG_MSDOS_FS=y -CONFIG_PANIC_TIMEOUT=5 -CONFIG_PANTHERLORD_FF=y -CONFIG_PERF_EVENTS=y -CONFIG_PM_DEBUG=y -CONFIG_PM_RUNTIME=y -CONFIG_PM_WAKELOCKS_LIMIT=0 -CONFIG_POWER_SUPPLY=y -CONFIG_PSTORE=y -CONFIG_PSTORE_CONSOLE=y -CONFIG_PSTORE_RAM=y -CONFIG_SCHEDSTATS=y -CONFIG_SMARTJOYPLUS_FF=y -CONFIG_SND=y -CONFIG_SOUND=y -CONFIG_STRICT_KERNEL_RWX=y -CONFIG_SUSPEND_TIME=y -CONFIG_TABLET_USB_ACECAD=y -CONFIG_TABLET_USB_AIPTEK=y -CONFIG_TABLET_USB_HANWANG=y -CONFIG_TABLET_USB_KBTAB=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_TASK_XACCT=y -CONFIG_TIMER_STATS=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_UHID=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_HIDDEV=y -CONFIG_USB_USBNET=y -CONFIG_VFAT_FS=y -- cgit v1.2.3 From a889c276d33d333ae96697510f33533f6e9d9591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodr=C3=ADguez=20Barbarin=2C=20Jos=C3=A9=20Javier?= Date: Tue, 11 Apr 2023 10:33:27 +0200 Subject: mcb: Return actual parsed size when reading chameleon table The function chameleon_parse_cells() returns the number of cells parsed which has an undetermined size. This return value is only used for error checking but the number of cells is never used. Change return value to be number of bytes parsed to allow for memory management improvements. Co-developed-by: Jorge Sanjuan Garcia Signed-off-by: Jorge Sanjuan Garcia Signed-off-by: Javier Rodriguez Signed-off-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20230411083329.4506-2-jth@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-parse.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c index aa6938da0db8..2aef990f379f 100644 --- a/drivers/mcb/mcb-parse.c +++ b/drivers/mcb/mcb-parse.c @@ -130,7 +130,7 @@ static void chameleon_parse_bar(void __iomem *base, } } -static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase, +static int chameleon_get_bar(void __iomem **base, phys_addr_t mapbase, struct chameleon_bar **cb) { struct chameleon_bar *c; @@ -179,12 +179,13 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, { struct chameleon_fpga_header *header; struct chameleon_bar *cb; - char __iomem *p = base; + void __iomem *p = base; int num_cells = 0; uint32_t dtype; int bar_count; int ret; u32 hsize; + u32 table_size; hsize = sizeof(struct chameleon_fpga_header); @@ -239,12 +240,16 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, num_cells++; } - if (num_cells == 0) - num_cells = -EINVAL; + if (num_cells == 0) { + ret = -EINVAL; + goto free_bar; + } + table_size = p - base; + pr_debug("%d cell(s) found. Chameleon table size: 0x%04x bytes\n", num_cells, table_size); kfree(cb); kfree(header); - return num_cells; + return table_size; free_bar: kfree(cb); -- cgit v1.2.3 From 9be24faadd085c284890c3afcec7a0184642315a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodr=C3=ADguez=20Barbarin=2C=20Jos=C3=A9=20Javier?= Date: Tue, 11 Apr 2023 10:33:28 +0200 Subject: mcb-pci: Reallocate memory region to avoid memory overlapping mcb-pci requests a fixed-size memory region to parse the chameleon table, however, if the chameleon table is smaller that the allocated region, it could overlap with the IP Cores' memory regions. After parsing the chameleon table, drop/reallocate the memory region with the actual chameleon table size. Co-developed-by: Jorge Sanjuan Garcia Signed-off-by: Jorge Sanjuan Garcia Signed-off-by: Javier Rodriguez Signed-off-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20230411083329.4506-3-jth@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-pci.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index dc88232d9af8..53d9202ff9a7 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -31,7 +31,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct resource *res; struct priv *priv; - int ret; + int ret, table_size; unsigned long flags; priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL); @@ -90,7 +90,30 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret < 0) goto out_mcb_bus; - dev_dbg(&pdev->dev, "Found %d cells\n", ret); + table_size = ret; + + if (table_size < CHAM_HEADER_SIZE) { + /* Release the previous resources */ + devm_iounmap(&pdev->dev, priv->base); + devm_release_mem_region(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE); + + /* Then, allocate it again with the actual chameleon table size */ + res = devm_request_mem_region(&pdev->dev, priv->mapbase, + table_size, + KBUILD_MODNAME); + if (!res) { + dev_err(&pdev->dev, "Failed to request PCI memory\n"); + ret = -EBUSY; + goto out_mcb_bus; + } + + priv->base = devm_ioremap(&pdev->dev, priv->mapbase, table_size); + if (!priv->base) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + ret = -ENOMEM; + goto out_mcb_bus; + } + } mcb_bus_add_devices(priv->bus); -- cgit v1.2.3 From 2025b2ca8004c04861903d076c67a73a0ec6dfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodr=C3=ADguez=20Barbarin=2C=20Jos=C3=A9=20Javier?= Date: Tue, 11 Apr 2023 10:33:29 +0200 Subject: mcb-lpc: Reallocate memory region to avoid memory overlapping mcb-lpc requests a fixed-size memory region to parse the chameleon table, however, if the chameleon table is smaller that the allocated region, it could overlap with the IP Cores' memory regions. After parsing the chameleon table, drop/reallocate the memory region with the actual chameleon table size. Co-developed-by: Jorge Sanjuan Garcia Signed-off-by: Jorge Sanjuan Garcia Signed-off-by: Javier Rodriguez Signed-off-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20230411083329.4506-4-jth@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-lpc.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c index 53decd89876e..a851e0236464 100644 --- a/drivers/mcb/mcb-lpc.c +++ b/drivers/mcb/mcb-lpc.c @@ -23,7 +23,7 @@ static int mcb_lpc_probe(struct platform_device *pdev) { struct resource *res; struct priv *priv; - int ret = 0; + int ret = 0, table_size; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -58,16 +58,43 @@ static int mcb_lpc_probe(struct platform_device *pdev) ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base); if (ret < 0) { - mcb_release_bus(priv->bus); - return ret; + goto out_mcb_bus; } - dev_dbg(&pdev->dev, "Found %d cells\n", ret); + table_size = ret; + + if (table_size < CHAM_HEADER_SIZE) { + /* Release the previous resources */ + devm_iounmap(&pdev->dev, priv->base); + devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem)); + + /* Then, allocate it again with the actual chameleon table size */ + res = devm_request_mem_region(&pdev->dev, priv->mem->start, + table_size, + KBUILD_MODNAME); + if (!res) { + dev_err(&pdev->dev, "Failed to request PCI memory\n"); + ret = -EBUSY; + goto out_mcb_bus; + } + + priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size); + if (!priv->base) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + ret = -ENOMEM; + goto out_mcb_bus; + } + + platform_set_drvdata(pdev, priv); + } mcb_bus_add_devices(priv->bus); return 0; +out_mcb_bus: + mcb_release_bus(priv->bus); + return ret; } static int mcb_lpc_remove(struct platform_device *pdev) -- cgit v1.2.3