diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-28 22:27:35 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-28 22:27:35 +0300 |
commit | 02e2af20f4f9f2aa0c84e9a30a35c02f0fbb7daa (patch) | |
tree | d126449a7d2ea2270627183f7cebd726fbe56a9d /drivers | |
parent | ff61bc81b3feebcef4d0431a92e2e40e8d4fe8b3 (diff) | |
parent | 37fd83916da2e4cae03d350015c82a67b1b334c4 (diff) | |
download | linux-02e2af20f4f9f2aa0c84e9a30a35c02f0fbb7daa.tar.xz |
Merge tag 'char-misc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc and other driver updates from Greg KH:
"Here is the big set of char/misc and other small driver subsystem
updates for 5.18-rc1.
Included in here are merges from driver subsystems which contain:
- iio driver updates and new drivers
- fsi driver updates
- fpga driver updates
- habanalabs driver updates and support for new hardware
- soundwire driver updates and new drivers
- phy driver updates and new drivers
- coresight driver updates
- icc driver updates
Individual changes include:
- mei driver updates
- interconnect driver updates
- new PECI driver subsystem added
- vmci driver updates
- lots of tiny misc/char driver updates
All of these have been in linux-next for a while with no reported
problems"
* tag 'char-misc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (556 commits)
firmware: google: Properly state IOMEM dependency
kgdbts: fix return value of __setup handler
firmware: sysfb: fix platform-device leak in error path
firmware: stratix10-svc: add missing callback parameter on RSU
arm64: dts: qcom: add non-secure domain property to fastrpc nodes
misc: fastrpc: Add dma handle implementation
misc: fastrpc: Add fdlist implementation
misc: fastrpc: Add helper function to get list and page
misc: fastrpc: Add support to secure memory map
dt-bindings: misc: add fastrpc domain vmid property
misc: fastrpc: check before loading process to the DSP
misc: fastrpc: add secure domain support
dt-bindings: misc: add property to support non-secure DSP
misc: fastrpc: Add support to get DSP capabilities
misc: fastrpc: add support for FASTRPC_IOCTL_MEM_MAP/UNMAP
misc: fastrpc: separate fastrpc device from channel context
dt-bindings: nvmem: brcm,nvram: add basic NVMEM cells
dt-bindings: nvmem: make "reg" property optional
nvmem: brcm_nvram: parse NVRAM content into NVMEM cells
nvmem: dt-bindings: Fix the error of dt-bindings check
...
Diffstat (limited to 'drivers')
456 files changed, 23167 insertions, 6123 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 0d399ddaa185..8d6cd5d08722 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -236,4 +236,7 @@ source "drivers/interconnect/Kconfig" source "drivers/counter/Kconfig" source "drivers/most/Kconfig" + +source "drivers/peci/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index a110338c860c..020780b6b4d2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -187,3 +187,4 @@ obj-$(CONFIG_GNSS) += gnss/ obj-$(CONFIG_INTERCONNECT) += interconnect/ obj-$(CONFIG_COUNTER) += counter/ obj-$(CONFIG_MOST) += most/ +obj-$(CONFIG_PECI) += peci/ diff --git a/drivers/accessibility/speakup/speakup_audptr.c b/drivers/accessibility/speakup/speakup_audptr.c index e89fd72579e6..a0c3b8ae17a1 100644 --- a/drivers/accessibility/speakup/speakup_audptr.c +++ b/drivers/accessibility/speakup/speakup_audptr.c @@ -126,20 +126,22 @@ static void synth_flush(struct spk_synth *synth) static void synth_version(struct spk_synth *synth) { - unsigned char test = 0; - char synth_id[40] = ""; + unsigned i; + char synth_id[33]; synth->synth_immediate(synth, "\x05[Q]"); - synth_id[test] = synth->io_ops->synth_in(synth); - if (synth_id[test] == 'A') { - do { - /* read version string from synth */ - synth_id[++test] = synth->io_ops->synth_in(synth); - } while (synth_id[test] != '\n' && test < 32); - synth_id[++test] = 0x00; + synth_id[0] = synth->io_ops->synth_in(synth); + if (synth_id[0] != 'A') + return; + + for (i = 1; i < sizeof(synth_id) - 1; i++) { + /* read version string from synth */ + synth_id[i] = synth->io_ops->synth_in(synth); + if (synth_id[i] == '\n') + break; } - if (synth_id[0] == 'A') - pr_info("%s version: %s", synth->long_name, synth_id); + synth_id[i] = '\0'; + pr_info("%s version: %s", synth->long_name, synth_id); } static int synth_probe(struct spk_synth *synth) diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c index 2b8699673bac..eea2a2fa4f01 100644 --- a/drivers/accessibility/speakup/synth.c +++ b/drivers/accessibility/speakup/synth.c @@ -348,7 +348,7 @@ struct var_t synth_time_vars[] = { { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } }, { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } }, { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } }, - { FLUSH, .u.n = {NULL, 4000, 100, 4000, 0, 0, NULL } }, + { FLUSH, .u.n = {NULL, 4000, 10, 4000, 0, 0, NULL } }, V_LAST_VAR }; diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 47bc74a8c7b6..2ac1008a5f39 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -1049,18 +1049,14 @@ err_get_alloc_mutex_failed: static unsigned long binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) { - unsigned long ret = list_lru_count(&binder_alloc_lru); - return ret; + return list_lru_count(&binder_alloc_lru); } static unsigned long binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { - unsigned long ret; - - ret = list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, + return list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, NULL, sc->nr_to_scan); - return ret; } static struct shrinker binder_shrinker = { diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 5badbaca05a0..6ead58c1b6e5 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -240,8 +240,6 @@ enum { as default lpm_policy */ AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during suspend/resume */ - AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = (1 << 27), /* ignore -EOPNOTSUPP - from phy_power_on() */ AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */ /* ap->flags bits */ diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index 991413a272e6..22ecc4f3ae79 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data ahci_mvebu_armada_380_plat_data = { static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = { .plat_config = ahci_mvebu_armada_3700_config, - .flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON, + .flags = AHCI_HFLAG_SUSPEND_PHYS, }; static const struct of_device_id ahci_mvebu_of_match[] = { diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 65227ef6b846..32495ae96567 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) } rc = phy_power_on(hpriv->phys[i]); - if (rc && !(rc == -EOPNOTSUPP && (hpriv->flags & AHCI_HFLAG_IGN_NOTSUPP_POWER_ON))) { + if (rc) { phy_exit(hpriv->phys[i]); goto disable_phys; } diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 52c2f35a26a9..16da51130d1a 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -39,4 +39,4 @@ obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o # MHI -obj-$(CONFIG_MHI_BUS) += mhi/ +obj-y += mhi/ diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig index da5cd0c9fc62..4748df7f9cd5 100644 --- a/drivers/bus/mhi/Kconfig +++ b/drivers/bus/mhi/Kconfig @@ -2,30 +2,7 @@ # # MHI bus # -# Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +# Copyright (c) 2021, Linaro Ltd. # -config MHI_BUS - tristate "Modem Host Interface (MHI) bus" - help - Bus driver for MHI protocol. Modem Host Interface (MHI) is a - communication protocol used by the host processors to control - and communicate with modem devices over a high speed peripheral - bus or shared memory. - -config MHI_BUS_DEBUG - bool "Debugfs support for the MHI bus" - depends on MHI_BUS && DEBUG_FS - help - Enable debugfs support for use with the MHI transport. Allows - reading and/or modifying some values within the MHI controller - for debug and test purposes. - -config MHI_BUS_PCI_GENERIC - tristate "MHI PCI controller driver" - depends on MHI_BUS - depends on PCI - help - This driver provides MHI PCI controller driver for devices such as - Qualcomm SDX55 based PCIe modems. - +source "drivers/bus/mhi/host/Kconfig" diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile index 0a2d778d6fb4..5f5708a249f5 100644 --- a/drivers/bus/mhi/Makefile +++ b/drivers/bus/mhi/Makefile @@ -1,6 +1,2 @@ -# core layer -obj-y += core/ - -obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o -mhi_pci_generic-y += pci_generic.o - +# Host MHI stack +obj-y += host/ diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h new file mode 100644 index 000000000000..b4ef9acd3ce7 --- /dev/null +++ b/drivers/bus/mhi/common.h @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Linaro Ltd. + * + */ + +#ifndef _MHI_COMMON_H +#define _MHI_COMMON_H + +#include <linux/bitfield.h> +#include <linux/mhi.h> + +/* MHI registers */ +#define MHIREGLEN 0x00 +#define MHIVER 0x08 +#define MHICFG 0x10 +#define CHDBOFF 0x18 +#define ERDBOFF 0x20 +#define BHIOFF 0x28 +#define BHIEOFF 0x2c +#define DEBUGOFF 0x30 +#define MHICTRL 0x38 +#define MHISTATUS 0x48 +#define CCABAP_LOWER 0x58 +#define CCABAP_HIGHER 0x5c +#define ECABAP_LOWER 0x60 +#define ECABAP_HIGHER 0x64 +#define CRCBAP_LOWER 0x68 +#define CRCBAP_HIGHER 0x6c +#define CRDB_LOWER 0x70 +#define CRDB_HIGHER 0x74 +#define MHICTRLBASE_LOWER 0x80 +#define MHICTRLBASE_HIGHER 0x84 +#define MHICTRLLIMIT_LOWER 0x88 +#define MHICTRLLIMIT_HIGHER 0x8c +#define MHIDATABASE_LOWER 0x98 +#define MHIDATABASE_HIGHER 0x9c +#define MHIDATALIMIT_LOWER 0xa0 +#define MHIDATALIMIT_HIGHER 0xa4 + +/* MHI BHI registers */ +#define BHI_BHIVERSION_MINOR 0x00 +#define BHI_BHIVERSION_MAJOR 0x04 +#define BHI_IMGADDR_LOW 0x08 +#define BHI_IMGADDR_HIGH 0x0c +#define BHI_IMGSIZE 0x10 +#define BHI_RSVD1 0x14 +#define BHI_IMGTXDB 0x18 +#define BHI_RSVD2 0x1c +#define BHI_INTVEC 0x20 +#define BHI_RSVD3 0x24 +#define BHI_EXECENV 0x28 +#define BHI_STATUS 0x2c +#define BHI_ERRCODE 0x30 +#define BHI_ERRDBG1 0x34 +#define BHI_ERRDBG2 0x38 +#define BHI_ERRDBG3 0x3c +#define BHI_SERIALNU 0x40 +#define BHI_SBLANTIROLLVER 0x44 +#define BHI_NUMSEG 0x48 +#define BHI_MSMHWID(n) (0x4c + (0x4 * (n))) +#define BHI_OEMPKHASH(n) (0x64 + (0x4 * (n))) +#define BHI_RSVD5 0xc4 + +/* BHI register bits */ +#define BHI_TXDB_SEQNUM_BMSK GENMASK(29, 0) +#define BHI_TXDB_SEQNUM_SHFT 0 +#define BHI_STATUS_MASK GENMASK(31, 30) +#define BHI_STATUS_ERROR 0x03 +#define BHI_STATUS_SUCCESS 0x02 +#define BHI_STATUS_RESET 0x00 + +/* MHI BHIE registers */ +#define BHIE_MSMSOCID_OFFS 0x00 +#define BHIE_TXVECADDR_LOW_OFFS 0x2c +#define BHIE_TXVECADDR_HIGH_OFFS 0x30 +#define BHIE_TXVECSIZE_OFFS 0x34 +#define BHIE_TXVECDB_OFFS 0x3c +#define BHIE_TXVECSTATUS_OFFS 0x44 +#define BHIE_RXVECADDR_LOW_OFFS 0x60 +#define BHIE_RXVECADDR_HIGH_OFFS 0x64 +#define BHIE_RXVECSIZE_OFFS 0x68 +#define BHIE_RXVECDB_OFFS 0x70 +#define BHIE_RXVECSTATUS_OFFS 0x78 + +/* BHIE register bits */ +#define BHIE_TXVECDB_SEQNUM_BMSK GENMASK(29, 0) +#define BHIE_TXVECDB_SEQNUM_SHFT 0 +#define BHIE_TXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0) +#define BHIE_TXVECSTATUS_SEQNUM_SHFT 0 +#define BHIE_TXVECSTATUS_STATUS_BMSK GENMASK(31, 30) +#define BHIE_TXVECSTATUS_STATUS_SHFT 30 +#define BHIE_TXVECSTATUS_STATUS_RESET 0x00 +#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL 0x02 +#define BHIE_TXVECSTATUS_STATUS_ERROR 0x03 +#define BHIE_RXVECDB_SEQNUM_BMSK GENMASK(29, 0) +#define BHIE_RXVECDB_SEQNUM_SHFT 0 +#define BHIE_RXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0) +#define BHIE_RXVECSTATUS_SEQNUM_SHFT 0 +#define BHIE_RXVECSTATUS_STATUS_BMSK GENMASK(31, 30) +#define BHIE_RXVECSTATUS_STATUS_SHFT 30 +#define BHIE_RXVECSTATUS_STATUS_RESET 0x00 +#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL 0x02 +#define BHIE_RXVECSTATUS_STATUS_ERROR 0x03 + +/* MHI register bits */ +#define MHICFG_NHWER_MASK GENMASK(31, 24) +#define MHICFG_NER_MASK GENMASK(23, 16) +#define MHICFG_NHWCH_MASK GENMASK(15, 8) +#define MHICFG_NCH_MASK GENMASK(7, 0) +#define MHICTRL_MHISTATE_MASK GENMASK(15, 8) +#define MHICTRL_RESET_MASK BIT(1) +#define MHISTATUS_MHISTATE_MASK GENMASK(15, 8) +#define MHISTATUS_SYSERR_MASK BIT(2) +#define MHISTATUS_READY_MASK BIT(0) + +/* Command Ring Element macros */ +/* No operation command */ +#define MHI_TRE_CMD_NOOP_PTR 0 +#define MHI_TRE_CMD_NOOP_DWORD0 0 +#define MHI_TRE_CMD_NOOP_DWORD1 cpu_to_le32(FIELD_PREP(GENMASK(23, 16), MHI_CMD_NOP)) + +/* Channel reset command */ +#define MHI_TRE_CMD_RESET_PTR 0 +#define MHI_TRE_CMD_RESET_DWORD0 0 +#define MHI_TRE_CMD_RESET_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \ + FIELD_PREP(GENMASK(23, 16), \ + MHI_CMD_RESET_CHAN)) + +/* Channel stop command */ +#define MHI_TRE_CMD_STOP_PTR 0 +#define MHI_TRE_CMD_STOP_DWORD0 0 +#define MHI_TRE_CMD_STOP_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \ + FIELD_PREP(GENMASK(23, 16), \ + MHI_CMD_STOP_CHAN)) + +/* Channel start command */ +#define MHI_TRE_CMD_START_PTR 0 +#define MHI_TRE_CMD_START_DWORD0 0 +#define MHI_TRE_CMD_START_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \ + FIELD_PREP(GENMASK(23, 16), \ + MHI_CMD_START_CHAN)) + +#define MHI_TRE_GET_DWORD(tre, word) le32_to_cpu((tre)->dword[(word)]) +#define MHI_TRE_GET_CMD_CHID(tre) FIELD_GET(GENMASK(31, 24), MHI_TRE_GET_DWORD(tre, 1)) +#define MHI_TRE_GET_CMD_TYPE(tre) FIELD_GET(GENMASK(23, 16), MHI_TRE_GET_DWORD(tre, 1)) + +/* Event descriptor macros */ +#define MHI_TRE_EV_PTR(ptr) cpu_to_le64(ptr) +#define MHI_TRE_EV_DWORD0(code, len) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), code) | \ + FIELD_PREP(GENMASK(15, 0), len)) +#define MHI_TRE_EV_DWORD1(chid, type) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \ + FIELD_PREP(GENMASK(23, 16), type)) +#define MHI_TRE_GET_EV_PTR(tre) le64_to_cpu((tre)->ptr) +#define MHI_TRE_GET_EV_CODE(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0))) +#define MHI_TRE_GET_EV_LEN(tre) FIELD_GET(GENMASK(15, 0), (MHI_TRE_GET_DWORD(tre, 0))) +#define MHI_TRE_GET_EV_CHID(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 1))) +#define MHI_TRE_GET_EV_TYPE(tre) FIELD_GET(GENMASK(23, 16), (MHI_TRE_GET_DWORD(tre, 1))) +#define MHI_TRE_GET_EV_STATE(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0))) +#define MHI_TRE_GET_EV_EXECENV(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0))) +#define MHI_TRE_GET_EV_SEQ(tre) MHI_TRE_GET_DWORD(tre, 0) +#define MHI_TRE_GET_EV_TIME(tre) MHI_TRE_GET_EV_PTR(tre) +#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits(MHI_TRE_GET_EV_PTR(tre)) +#define MHI_TRE_GET_EV_VEID(tre) FIELD_GET(GENMASK(23, 16), (MHI_TRE_GET_DWORD(tre, 0))) +#define MHI_TRE_GET_EV_LINKSPEED(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 1))) +#define MHI_TRE_GET_EV_LINKWIDTH(tre) FIELD_GET(GENMASK(7, 0), (MHI_TRE_GET_DWORD(tre, 0))) + +/* Transfer descriptor macros */ +#define MHI_TRE_DATA_PTR(ptr) cpu_to_le64(ptr) +#define MHI_TRE_DATA_DWORD0(len) cpu_to_le32(FIELD_PREP(GENMASK(15, 0), len)) +#define MHI_TRE_TYPE_TRANSFER 2 +#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) cpu_to_le32(FIELD_PREP(GENMASK(23, 16), \ + MHI_TRE_TYPE_TRANSFER) | \ + FIELD_PREP(BIT(10), bei) | \ + FIELD_PREP(BIT(9), ieot) | \ + FIELD_PREP(BIT(8), ieob) | \ + FIELD_PREP(BIT(0), chain)) + +/* RSC transfer descriptor macros */ +#define MHI_RSCTRE_DATA_PTR(ptr, len) cpu_to_le64(FIELD_PREP(GENMASK(64, 48), len) | ptr) +#define MHI_RSCTRE_DATA_DWORD0(cookie) cpu_to_le32(cookie) +#define MHI_RSCTRE_DATA_DWORD1 cpu_to_le32(FIELD_PREP(GENMASK(23, 16), \ + MHI_PKT_TYPE_COALESCING)) + +enum mhi_pkt_type { + MHI_PKT_TYPE_INVALID = 0x0, + MHI_PKT_TYPE_NOOP_CMD = 0x1, + MHI_PKT_TYPE_TRANSFER = 0x2, + MHI_PKT_TYPE_COALESCING = 0x8, + MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10, + MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11, + MHI_PKT_TYPE_START_CHAN_CMD = 0x12, + MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20, + MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21, + MHI_PKT_TYPE_TX_EVENT = 0x22, + MHI_PKT_TYPE_RSC_TX_EVENT = 0x28, + MHI_PKT_TYPE_EE_EVENT = 0x40, + MHI_PKT_TYPE_TSYNC_EVENT = 0x48, + MHI_PKT_TYPE_BW_REQ_EVENT = 0x50, + MHI_PKT_TYPE_STALE_EVENT, /* internal event */ +}; + +/* MHI transfer completion events */ +enum mhi_ev_ccs { + MHI_EV_CC_INVALID = 0x0, + MHI_EV_CC_SUCCESS = 0x1, + MHI_EV_CC_EOT = 0x2, /* End of transfer event */ + MHI_EV_CC_OVERFLOW = 0x3, + MHI_EV_CC_EOB = 0x4, /* End of block event */ + MHI_EV_CC_OOB = 0x5, /* Out of block event */ + MHI_EV_CC_DB_MODE = 0x6, + MHI_EV_CC_UNDEFINED_ERR = 0x10, + MHI_EV_CC_BAD_TRE = 0x11, +}; + +/* Channel state */ +enum mhi_ch_state { + MHI_CH_STATE_DISABLED, + MHI_CH_STATE_ENABLED, + MHI_CH_STATE_RUNNING, + MHI_CH_STATE_SUSPENDED, + MHI_CH_STATE_STOP, + MHI_CH_STATE_ERROR, +}; + +enum mhi_cmd_type { + MHI_CMD_NOP = 1, + MHI_CMD_RESET_CHAN = 16, + MHI_CMD_STOP_CHAN = 17, + MHI_CMD_START_CHAN = 18, +}; + +#define EV_CTX_RESERVED_MASK GENMASK(7, 0) +#define EV_CTX_INTMODC_MASK GENMASK(15, 8) +#define EV_CTX_INTMODT_MASK GENMASK(31, 16) +struct mhi_event_ctxt { + __le32 intmod; + __le32 ertype; + __le32 msivec; + + __le64 rbase __packed __aligned(4); + __le64 rlen __packed __aligned(4); + __le64 rp __packed __aligned(4); + __le64 wp __packed __aligned(4); +}; + +#define CHAN_CTX_CHSTATE_MASK GENMASK(7, 0) +#define CHAN_CTX_BRSTMODE_MASK GENMASK(9, 8) +#define CHAN_CTX_POLLCFG_MASK GENMASK(15, 10) +#define CHAN_CTX_RESERVED_MASK GENMASK(31, 16) +struct mhi_chan_ctxt { + __le32 chcfg; + __le32 chtype; + __le32 erindex; + + __le64 rbase __packed __aligned(4); + __le64 rlen __packed __aligned(4); + __le64 rp __packed __aligned(4); + __le64 wp __packed __aligned(4); +}; + +struct mhi_cmd_ctxt { + __le32 reserved0; + __le32 reserved1; + __le32 reserved2; + + __le64 rbase __packed __aligned(4); + __le64 rlen __packed __aligned(4); + __le64 rp __packed __aligned(4); + __le64 wp __packed __aligned(4); +}; + +struct mhi_ring_element { + __le64 ptr; + __le32 dword[2]; +}; + +static inline const char *mhi_state_str(enum mhi_state state) +{ + switch (state) { + case MHI_STATE_RESET: + return "RESET"; + case MHI_STATE_READY: + return "READY"; + case MHI_STATE_M0: + return "M0"; + case MHI_STATE_M1: + return "M1"; + case MHI_STATE_M2: + return "M2"; + case MHI_STATE_M3: + return "M3"; + case MHI_STATE_M3_FAST: + return "M3 FAST"; + case MHI_STATE_BHI: + return "BHI"; + case MHI_STATE_SYS_ERR: + return "SYS ERROR"; + default: + return "Unknown state"; + } +}; + +#endif /* _MHI_COMMON_H */ diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h deleted file mode 100644 index e2e10474a9d9..000000000000 --- a/drivers/bus/mhi/core/internal.h +++ /dev/null @@ -1,722 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. - * - */ - -#ifndef _MHI_INT_H -#define _MHI_INT_H - -#include <linux/mhi.h> - -extern struct bus_type mhi_bus_type; - -#define MHIREGLEN (0x0) -#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF) -#define MHIREGLEN_MHIREGLEN_SHIFT (0) - -#define MHIVER (0x8) -#define MHIVER_MHIVER_MASK (0xFFFFFFFF) -#define MHIVER_MHIVER_SHIFT (0) - -#define MHICFG (0x10) -#define MHICFG_NHWER_MASK (0xFF000000) -#define MHICFG_NHWER_SHIFT (24) -#define MHICFG_NER_MASK (0xFF0000) -#define MHICFG_NER_SHIFT (16) -#define MHICFG_NHWCH_MASK (0xFF00) -#define MHICFG_NHWCH_SHIFT (8) -#define MHICFG_NCH_MASK (0xFF) -#define MHICFG_NCH_SHIFT (0) - -#define CHDBOFF (0x18) -#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF) -#define CHDBOFF_CHDBOFF_SHIFT (0) - -#define ERDBOFF (0x20) -#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF) -#define ERDBOFF_ERDBOFF_SHIFT (0) - -#define BHIOFF (0x28) -#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF) -#define BHIOFF_BHIOFF_SHIFT (0) - -#define BHIEOFF (0x2C) -#define BHIEOFF_BHIEOFF_MASK (0xFFFFFFFF) -#define BHIEOFF_BHIEOFF_SHIFT (0) - -#define DEBUGOFF (0x30) -#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF) -#define DEBUGOFF_DEBUGOFF_SHIFT (0) - -#define MHICTRL (0x38) -#define MHICTRL_MHISTATE_MASK (0x0000FF00) -#define MHICTRL_MHISTATE_SHIFT (8) -#define MHICTRL_RESET_MASK (0x2) -#define MHICTRL_RESET_SHIFT (1) - -#define MHISTATUS (0x48) -#define MHISTATUS_MHISTATE_MASK (0x0000FF00) -#define MHISTATUS_MHISTATE_SHIFT (8) -#define MHISTATUS_SYSERR_MASK (0x4) -#define MHISTATUS_SYSERR_SHIFT (2) -#define MHISTATUS_READY_MASK (0x1) -#define MHISTATUS_READY_SHIFT (0) - -#define CCABAP_LOWER (0x58) -#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF) -#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0) - -#define CCABAP_HIGHER (0x5C) -#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF) -#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0) - -#define ECABAP_LOWER (0x60) -#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF) -#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0) - -#define ECABAP_HIGHER (0x64) -#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF) -#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0) - -#define CRCBAP_LOWER (0x68) -#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF) -#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0) - -#define CRCBAP_HIGHER (0x6C) -#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF) -#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0) - -#define CRDB_LOWER (0x70) -#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF) -#define CRDB_LOWER_CRDB_LOWER_SHIFT (0) - -#define CRDB_HIGHER (0x74) -#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF) -#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0) - -#define MHICTRLBASE_LOWER (0x80) -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF) -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0) - -#define MHICTRLBASE_HIGHER (0x84) -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF) -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0) - -#define MHICTRLLIMIT_LOWER (0x88) -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF) -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0) - -#define MHICTRLLIMIT_HIGHER (0x8C) -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF) -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0) - -#define MHIDATABASE_LOWER (0x98) -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF) -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0) - -#define MHIDATABASE_HIGHER (0x9C) -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF) -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0) - -#define MHIDATALIMIT_LOWER (0xA0) -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF) -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0) - -#define MHIDATALIMIT_HIGHER (0xA4) -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF) -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0) - -/* Host request register */ -#define MHI_SOC_RESET_REQ_OFFSET (0xB0) -#define MHI_SOC_RESET_REQ BIT(0) - -/* MHI BHI offfsets */ -#define BHI_BHIVERSION_MINOR (0x00) -#define BHI_BHIVERSION_MAJOR (0x04) -#define BHI_IMGADDR_LOW (0x08) -#define BHI_IMGADDR_HIGH (0x0C) -#define BHI_IMGSIZE (0x10) -#define BHI_RSVD1 (0x14) -#define BHI_IMGTXDB (0x18) -#define BHI_TXDB_SEQNUM_BMSK (0x3FFFFFFF) -#define BHI_TXDB_SEQNUM_SHFT (0) -#define BHI_RSVD2 (0x1C) -#define BHI_INTVEC (0x20) -#define BHI_RSVD3 (0x24) -#define BHI_EXECENV (0x28) -#define BHI_STATUS (0x2C) -#define BHI_ERRCODE (0x30) -#define BHI_ERRDBG1 (0x34) -#define BHI_ERRDBG2 (0x38) -#define BHI_ERRDBG3 (0x3C) -#define BHI_SERIALNU (0x40) -#define BHI_SBLANTIROLLVER (0x44) -#define BHI_NUMSEG (0x48) -#define BHI_MSMHWID(n) (0x4C + (0x4 * (n))) -#define BHI_OEMPKHASH(n) (0x64 + (0x4 * (n))) -#define BHI_RSVD5 (0xC4) -#define BHI_STATUS_MASK (0xC0000000) -#define BHI_STATUS_SHIFT (30) -#define BHI_STATUS_ERROR (3) -#define BHI_STATUS_SUCCESS (2) -#define BHI_STATUS_RESET (0) - -/* MHI BHIE offsets */ -#define BHIE_MSMSOCID_OFFS (0x0000) -#define BHIE_TXVECADDR_LOW_OFFS (0x002C) -#define BHIE_TXVECADDR_HIGH_OFFS (0x0030) -#define BHIE_TXVECSIZE_OFFS (0x0034) -#define BHIE_TXVECDB_OFFS (0x003C) -#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_TXVECDB_SEQNUM_SHFT (0) -#define BHIE_TXVECSTATUS_OFFS (0x0044) -#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0) -#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000) -#define BHIE_TXVECSTATUS_STATUS_SHFT (30) -#define BHIE_TXVECSTATUS_STATUS_RESET (0x00) -#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02) -#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03) -#define BHIE_RXVECADDR_LOW_OFFS (0x0060) -#define BHIE_RXVECADDR_HIGH_OFFS (0x0064) -#define BHIE_RXVECSIZE_OFFS (0x0068) -#define BHIE_RXVECDB_OFFS (0x0070) -#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_RXVECDB_SEQNUM_SHFT (0) -#define BHIE_RXVECSTATUS_OFFS (0x0078) -#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0) -#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000) -#define BHIE_RXVECSTATUS_STATUS_SHFT (30) -#define BHIE_RXVECSTATUS_STATUS_RESET (0x00) -#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02) -#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03) - -#define SOC_HW_VERSION_OFFS (0x224) -#define SOC_HW_VERSION_FAM_NUM_BMSK (0xF0000000) -#define SOC_HW_VERSION_FAM_NUM_SHFT (28) -#define SOC_HW_VERSION_DEV_NUM_BMSK (0x0FFF0000) -#define SOC_HW_VERSION_DEV_NUM_SHFT (16) -#define SOC_HW_VERSION_MAJOR_VER_BMSK (0x0000FF00) -#define SOC_HW_VERSION_MAJOR_VER_SHFT (8) -#define SOC_HW_VERSION_MINOR_VER_BMSK (0x000000FF) -#define SOC_HW_VERSION_MINOR_VER_SHFT (0) - -#define EV_CTX_RESERVED_MASK GENMASK(7, 0) -#define EV_CTX_INTMODC_MASK GENMASK(15, 8) -#define EV_CTX_INTMODC_SHIFT 8 -#define EV_CTX_INTMODT_MASK GENMASK(31, 16) -#define EV_CTX_INTMODT_SHIFT 16 -struct mhi_event_ctxt { - __u32 intmod; - __u32 ertype; - __u32 msivec; - - __u64 rbase __packed __aligned(4); - __u64 rlen __packed __aligned(4); - __u64 rp __packed __aligned(4); - __u64 wp __packed __aligned(4); -}; - -#define CHAN_CTX_CHSTATE_MASK GENMASK(7, 0) -#define CHAN_CTX_CHSTATE_SHIFT 0 -#define CHAN_CTX_BRSTMODE_MASK GENMASK(9, 8) -#define CHAN_CTX_BRSTMODE_SHIFT 8 -#define CHAN_CTX_POLLCFG_MASK GENMASK(15, 10) -#define CHAN_CTX_POLLCFG_SHIFT 10 -#define CHAN_CTX_RESERVED_MASK GENMASK(31, 16) -struct mhi_chan_ctxt { - __u32 chcfg; - __u32 chtype; - __u32 erindex; - - __u64 rbase __packed __aligned(4); - __u64 rlen __packed __aligned(4); - __u64 rp __packed __aligned(4); - __u64 wp __packed __aligned(4); -}; - -struct mhi_cmd_ctxt { - __u32 reserved0; - __u32 reserved1; - __u32 reserved2; - - __u64 rbase __packed __aligned(4); - __u64 rlen __packed __aligned(4); - __u64 rp __packed __aligned(4); - __u64 wp __packed __aligned(4); -}; - -struct mhi_ctxt { - struct mhi_event_ctxt *er_ctxt; - struct mhi_chan_ctxt *chan_ctxt; - struct mhi_cmd_ctxt *cmd_ctxt; - dma_addr_t er_ctxt_addr; - dma_addr_t chan_ctxt_addr; - dma_addr_t cmd_ctxt_addr; -}; - -struct mhi_tre { - u64 ptr; - u32 dword[2]; -}; - -struct bhi_vec_entry { - u64 dma_addr; - u64 size; -}; - -enum mhi_cmd_type { - MHI_CMD_NOP = 1, - MHI_CMD_RESET_CHAN = 16, - MHI_CMD_STOP_CHAN = 17, - MHI_CMD_START_CHAN = 18, -}; - -/* No operation command */ -#define MHI_TRE_CMD_NOOP_PTR (0) -#define MHI_TRE_CMD_NOOP_DWORD0 (0) -#define MHI_TRE_CMD_NOOP_DWORD1 (MHI_CMD_NOP << 16) - -/* Channel reset command */ -#define MHI_TRE_CMD_RESET_PTR (0) -#define MHI_TRE_CMD_RESET_DWORD0 (0) -#define MHI_TRE_CMD_RESET_DWORD1(chid) ((chid << 24) | \ - (MHI_CMD_RESET_CHAN << 16)) - -/* Channel stop command */ -#define MHI_TRE_CMD_STOP_PTR (0) -#define MHI_TRE_CMD_STOP_DWORD0 (0) -#define MHI_TRE_CMD_STOP_DWORD1(chid) ((chid << 24) | \ - (MHI_CMD_STOP_CHAN << 16)) - -/* Channel start command */ -#define MHI_TRE_CMD_START_PTR (0) -#define MHI_TRE_CMD_START_DWORD0 (0) -#define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \ - (MHI_CMD_START_CHAN << 16)) - -#define MHI_TRE_GET_CMD_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF) -#define MHI_TRE_GET_CMD_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) - -/* Event descriptor macros */ -#define MHI_TRE_EV_PTR(ptr) (ptr) -#define MHI_TRE_EV_DWORD0(code, len) ((code << 24) | len) -#define MHI_TRE_EV_DWORD1(chid, type) ((chid << 24) | (type << 16)) -#define MHI_TRE_GET_EV_PTR(tre) ((tre)->ptr) -#define MHI_TRE_GET_EV_CODE(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_LEN(tre) ((tre)->dword[0] & 0xFFFF) -#define MHI_TRE_GET_EV_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) -#define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_SEQ(tre) ((tre)->dword[0]) -#define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr) -#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr) -#define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF) -#define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF) - -/* Transfer descriptor macros */ -#define MHI_TRE_DATA_PTR(ptr) (ptr) -#define MHI_TRE_DATA_DWORD0(len) (len & MHI_MAX_MTU) -#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) ((2 << 16) | (bei << 10) \ - | (ieot << 9) | (ieob << 8) | chain) - -/* RSC transfer descriptor macros */ -#define MHI_RSCTRE_DATA_PTR(ptr, len) (((u64)len << 48) | ptr) -#define MHI_RSCTRE_DATA_DWORD0(cookie) (cookie) -#define MHI_RSCTRE_DATA_DWORD1 (MHI_PKT_TYPE_COALESCING << 16) - -enum mhi_pkt_type { - MHI_PKT_TYPE_INVALID = 0x0, - MHI_PKT_TYPE_NOOP_CMD = 0x1, - MHI_PKT_TYPE_TRANSFER = 0x2, - MHI_PKT_TYPE_COALESCING = 0x8, - MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10, - MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11, - MHI_PKT_TYPE_START_CHAN_CMD = 0x12, - MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20, - MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21, - MHI_PKT_TYPE_TX_EVENT = 0x22, - MHI_PKT_TYPE_RSC_TX_EVENT = 0x28, - MHI_PKT_TYPE_EE_EVENT = 0x40, - MHI_PKT_TYPE_TSYNC_EVENT = 0x48, - MHI_PKT_TYPE_BW_REQ_EVENT = 0x50, - MHI_PKT_TYPE_STALE_EVENT, /* internal event */ -}; - -/* MHI transfer completion events */ -enum mhi_ev_ccs { - MHI_EV_CC_INVALID = 0x0, - MHI_EV_CC_SUCCESS = 0x1, - MHI_EV_CC_EOT = 0x2, /* End of transfer event */ - MHI_EV_CC_OVERFLOW = 0x3, - MHI_EV_CC_EOB = 0x4, /* End of block event */ - MHI_EV_CC_OOB = 0x5, /* Out of block event */ - MHI_EV_CC_DB_MODE = 0x6, - MHI_EV_CC_UNDEFINED_ERR = 0x10, - MHI_EV_CC_BAD_TRE = 0x11, -}; - -enum mhi_ch_state { - MHI_CH_STATE_DISABLED = 0x0, - MHI_CH_STATE_ENABLED = 0x1, - MHI_CH_STATE_RUNNING = 0x2, - MHI_CH_STATE_SUSPENDED = 0x3, - MHI_CH_STATE_STOP = 0x4, - MHI_CH_STATE_ERROR = 0x5, -}; - -enum mhi_ch_state_type { - MHI_CH_STATE_TYPE_RESET, - MHI_CH_STATE_TYPE_STOP, - MHI_CH_STATE_TYPE_START, - MHI_CH_STATE_TYPE_MAX, -}; - -extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX]; -#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \ - "INVALID_STATE" : \ - mhi_ch_state_type_str[(state)]) - -#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \ - mode != MHI_DB_BRST_ENABLE) - -extern const char * const mhi_ee_str[MHI_EE_MAX]; -#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \ - "INVALID_EE" : mhi_ee_str[ee]) - -#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \ - ee == MHI_EE_EDL) -#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS) -#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL) -#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \ - ee == MHI_EE_FP) - -enum dev_st_transition { - DEV_ST_TRANSITION_PBL, - DEV_ST_TRANSITION_READY, - DEV_ST_TRANSITION_SBL, - DEV_ST_TRANSITION_MISSION_MODE, - DEV_ST_TRANSITION_FP, - DEV_ST_TRANSITION_SYS_ERR, - DEV_ST_TRANSITION_DISABLE, - DEV_ST_TRANSITION_MAX, -}; - -extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; -#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ - "INVALID_STATE" : dev_state_tran_str[state]) - -extern const char * const mhi_state_str[MHI_STATE_MAX]; -#define TO_MHI_STATE_STR(state) ((state >= MHI_STATE_MAX || \ - !mhi_state_str[state]) ? \ - "INVALID_STATE" : mhi_state_str[state]) - -/* internal power states */ -enum mhi_pm_state { - MHI_PM_STATE_DISABLE, - MHI_PM_STATE_POR, - MHI_PM_STATE_M0, - MHI_PM_STATE_M2, - MHI_PM_STATE_M3_ENTER, - MHI_PM_STATE_M3, - MHI_PM_STATE_M3_EXIT, - MHI_PM_STATE_FW_DL_ERR, - MHI_PM_STATE_SYS_ERR_DETECT, - MHI_PM_STATE_SYS_ERR_PROCESS, - MHI_PM_STATE_SHUTDOWN_PROCESS, - MHI_PM_STATE_LD_ERR_FATAL_DETECT, - MHI_PM_STATE_MAX -}; - -#define MHI_PM_DISABLE BIT(0) -#define MHI_PM_POR BIT(1) -#define MHI_PM_M0 BIT(2) -#define MHI_PM_M2 BIT(3) -#define MHI_PM_M3_ENTER BIT(4) -#define MHI_PM_M3 BIT(5) -#define MHI_PM_M3_EXIT BIT(6) -/* firmware download failure state */ -#define MHI_PM_FW_DL_ERR BIT(7) -#define MHI_PM_SYS_ERR_DETECT BIT(8) -#define MHI_PM_SYS_ERR_PROCESS BIT(9) -#define MHI_PM_SHUTDOWN_PROCESS BIT(10) -/* link not accessible */ -#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11) - -#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \ - MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \ - MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \ - MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) -#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) -#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) -#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & \ - mhi_cntrl->db_access) -#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \ - MHI_PM_M2 | MHI_PM_M3_EXIT)) -#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2) -#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state) -#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \ - MHI_PM_IN_ERROR_STATE(pm_state)) -#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \ - (MHI_PM_M3_ENTER | MHI_PM_M3)) - -#define NR_OF_CMD_RINGS 1 -#define CMD_EL_PER_RING 128 -#define PRIMARY_CMD_RING 0 -#define MHI_DEV_WAKE_DB 127 -#define MHI_MAX_MTU 0xffff -#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1) - -enum mhi_er_type { - MHI_ER_TYPE_INVALID = 0x0, - MHI_ER_TYPE_VALID = 0x1, -}; - -struct db_cfg { - bool reset_req; - bool db_mode; - u32 pollcfg; - enum mhi_db_brst_mode brstmode; - dma_addr_t db_val; - void (*process_db)(struct mhi_controller *mhi_cntrl, - struct db_cfg *db_cfg, void __iomem *io_addr, - dma_addr_t db_val); -}; - -struct mhi_pm_transitions { - enum mhi_pm_state from_state; - u32 to_states; -}; - -struct state_transition { - struct list_head node; - enum dev_st_transition state; -}; - -struct mhi_ring { - dma_addr_t dma_handle; - dma_addr_t iommu_base; - u64 *ctxt_wp; /* point to ctxt wp */ - void *pre_aligned; - void *base; - void *rp; - void *wp; - size_t el_size; - size_t len; - size_t elements; - size_t alloc_size; - void __iomem *db_addr; -}; - -struct mhi_cmd { - struct mhi_ring ring; - spinlock_t lock; -}; - -struct mhi_buf_info { - void *v_addr; - void *bb_addr; - void *wp; - void *cb_buf; - dma_addr_t p_addr; - size_t len; - enum dma_data_direction dir; - bool used; /* Indicates whether the buffer is used or not */ - bool pre_mapped; /* Already pre-mapped by client */ -}; - -struct mhi_event { - struct mhi_controller *mhi_cntrl; - struct mhi_chan *mhi_chan; /* dedicated to channel */ - u32 er_index; - u32 intmod; - u32 irq; - int chan; /* this event ring is dedicated to a channel (optional) */ - u32 priority; - enum mhi_er_data_type data_type; - struct mhi_ring ring; - struct db_cfg db_cfg; - struct tasklet_struct task; - spinlock_t lock; - int (*process_event)(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, - u32 event_quota); - bool hw_ring; - bool cl_manage; - bool offload_ev; /* managed by a device driver */ -}; - -struct mhi_chan { - const char *name; - /* - * Important: When consuming, increment tre_ring first and when - * releasing, decrement buf_ring first. If tre_ring has space, buf_ring - * is guranteed to have space so we do not need to check both rings. - */ - struct mhi_ring buf_ring; - struct mhi_ring tre_ring; - u32 chan; - u32 er_index; - u32 intmod; - enum mhi_ch_type type; - enum dma_data_direction dir; - struct db_cfg db_cfg; - enum mhi_ch_ee_mask ee_mask; - enum mhi_ch_state ch_state; - enum mhi_ev_ccs ccs; - struct mhi_device *mhi_dev; - void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result); - struct mutex mutex; - struct completion completion; - rwlock_t lock; - struct list_head node; - bool lpm_notify; - bool configured; - bool offload_ch; - bool pre_alloc; - bool wake_capable; -}; - -/* Default MHI timeout */ -#define MHI_TIMEOUT_MS (1000) - -/* debugfs related functions */ -#ifdef CONFIG_MHI_BUS_DEBUG -void mhi_create_debugfs(struct mhi_controller *mhi_cntrl); -void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl); -void mhi_debugfs_init(void); -void mhi_debugfs_exit(void); -#else -static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl) -{ -} - -static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl) -{ -} - -static inline void mhi_debugfs_init(void) -{ -} - -static inline void mhi_debugfs_exit(void) -{ -} -#endif - -struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl); - -int mhi_destroy_device(struct device *dev, void *data); -void mhi_create_devices(struct mhi_controller *mhi_cntrl); - -int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info **image_info, size_t alloc_size); -void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info *image_info); - -/* Power management APIs */ -enum mhi_pm_state __must_check mhi_tryset_pm_state( - struct mhi_controller *mhi_cntrl, - enum mhi_pm_state state); -const char *to_mhi_pm_state_str(enum mhi_pm_state state); -int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl, - enum dev_st_transition state); -void mhi_pm_st_worker(struct work_struct *work); -void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl); -int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl); -int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl); -void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl); -int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl); -int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); -int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, - enum mhi_cmd_type cmd); -int mhi_download_amss_image(struct mhi_controller *mhi_cntrl); -static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl) -{ - return (mhi_cntrl->dev_state >= MHI_STATE_M0 && - mhi_cntrl->dev_state <= MHI_STATE_M3_FAST); -} - -static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl) -{ - pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0); - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); -} - -/* Register access methods */ -void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg, - void __iomem *db_addr, dma_addr_t db_val); -void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl, - struct db_cfg *db_mode, void __iomem *db_addr, - dma_addr_t db_val); -int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, - void __iomem *base, u32 offset, u32 *out); -int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, - void __iomem *base, u32 offset, u32 mask, - u32 shift, u32 *out); -int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl, - void __iomem *base, u32 offset, u32 mask, - u32 shift, u32 val, u32 delayus); -void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, - u32 offset, u32 val); -void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, - u32 offset, u32 mask, u32 shift, u32 val); -void mhi_ring_er_db(struct mhi_event *mhi_event); -void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr, - dma_addr_t db_val); -void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd); -void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); - -/* Initialization methods */ -int mhi_init_mmio(struct mhi_controller *mhi_cntrl); -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl); -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl); -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl); -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl); -void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, - struct image_info *img_info); -void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl); - -/* Automatically allocate and queue inbound buffers */ -#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0) -int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan, unsigned int flags); - -int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); -void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); -void mhi_reset_chan(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); - -/* Event processing methods */ -void mhi_ctrl_ev_task(unsigned long data); -void mhi_ev_task(unsigned long data); -int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, u32 event_quota); -int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, u32 event_quota); - -/* ISR handlers */ -irqreturn_t mhi_irq_handler(int irq_number, void *dev); -irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev); -irqreturn_t mhi_intvec_handler(int irq_number, void *dev); - -int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, - struct mhi_buf_info *info, enum mhi_flags flags); -int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); -int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); -void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); -void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); - -#endif /* _MHI_INT_H */ diff --git a/drivers/bus/mhi/host/Kconfig b/drivers/bus/mhi/host/Kconfig new file mode 100644 index 000000000000..da5cd0c9fc62 --- /dev/null +++ b/drivers/bus/mhi/host/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# MHI bus +# +# Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +# + +config MHI_BUS + tristate "Modem Host Interface (MHI) bus" + help + Bus driver for MHI protocol. Modem Host Interface (MHI) is a + communication protocol used by the host processors to control + and communicate with modem devices over a high speed peripheral + bus or shared memory. + +config MHI_BUS_DEBUG + bool "Debugfs support for the MHI bus" + depends on MHI_BUS && DEBUG_FS + help + Enable debugfs support for use with the MHI transport. Allows + reading and/or modifying some values within the MHI controller + for debug and test purposes. + +config MHI_BUS_PCI_GENERIC + tristate "MHI PCI controller driver" + depends on MHI_BUS + depends on PCI + help + This driver provides MHI PCI controller driver for devices such as + Qualcomm SDX55 based PCIe modems. + diff --git a/drivers/bus/mhi/core/Makefile b/drivers/bus/mhi/host/Makefile index c3feb4130aa3..859c2f38451c 100644 --- a/drivers/bus/mhi/core/Makefile +++ b/drivers/bus/mhi/host/Makefile @@ -1,4 +1,6 @@ obj-$(CONFIG_MHI_BUS) += mhi.o - mhi-y := init.o main.o pm.o boot.o mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o + +obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o +mhi_pci_generic-y += pci_generic.o diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/host/boot.c index 74295d3cc662..b0da7ca4519c 100644 --- a/drivers/bus/mhi/core/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -46,8 +46,7 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK); mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS, - BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT, - sequence_id); + BHIE_RXVECDB_SEQNUM_BMSK, sequence_id); dev_dbg(dev, "Address: %p and len: 0x%zx sequence: %u\n", &mhi_buf->dma_addr, mhi_buf->len, sequence_id); @@ -68,7 +67,7 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) dev_dbg(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), + mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(mhi_cntrl->ee)); /* @@ -127,9 +126,7 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) while (retry--) { ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, - BHIE_RXVECSTATUS_STATUS_BMSK, - BHIE_RXVECSTATUS_STATUS_SHFT, - &rx_status); + BHIE_RXVECSTATUS_STATUS_BMSK, &rx_status); if (ret) return -EIO; @@ -168,7 +165,6 @@ int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic) mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, BHIE_RXVECSTATUS_STATUS_BMSK, - BHIE_RXVECSTATUS_STATUS_SHFT, &rx_status) || rx_status, msecs_to_jiffies(mhi_cntrl->timeout_ms)); @@ -203,8 +199,7 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl, mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len); mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS, - BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT, - sequence_id); + BHIE_TXVECDB_SEQNUM_BMSK, sequence_id); read_unlock_bh(pm_lock); /* Wait for the image download to complete */ @@ -213,7 +208,6 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl, mhi_read_reg_field(mhi_cntrl, base, BHIE_TXVECSTATUS_OFFS, BHIE_TXVECSTATUS_STATUS_BMSK, - BHIE_TXVECSTATUS_STATUS_SHFT, &tx_status) || tx_status, msecs_to_jiffies(mhi_cntrl->timeout_ms)); if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || @@ -265,8 +259,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl, ret = wait_event_timeout(mhi_cntrl->state_event, MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS, - BHI_STATUS_MASK, BHI_STATUS_SHIFT, - &tx_status) || tx_status, + BHI_STATUS_MASK, &tx_status) || tx_status, msecs_to_jiffies(mhi_cntrl->timeout_ms)); if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) goto invalid_pm_state; diff --git a/drivers/bus/mhi/core/debugfs.c b/drivers/bus/mhi/host/debugfs.c index 858d7516410b..cfec7811dfbb 100644 --- a/drivers/bus/mhi/core/debugfs.c +++ b/drivers/bus/mhi/host/debugfs.c @@ -20,7 +20,7 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d) seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), mhi_is_active(mhi_cntrl) ? "Active" : "Inactive", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), + mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_cntrl->wake_set ? "true" : "false"); @@ -60,16 +60,16 @@ static int mhi_debugfs_events_show(struct seq_file *m, void *d) } seq_printf(m, "Index: %d intmod count: %lu time: %lu", - i, (er_ctxt->intmod & EV_CTX_INTMODC_MASK) >> - EV_CTX_INTMODC_SHIFT, - (er_ctxt->intmod & EV_CTX_INTMODT_MASK) >> - EV_CTX_INTMODT_SHIFT); + i, (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODC_MASK) >> + __ffs(EV_CTX_INTMODC_MASK), + (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODT_MASK) >> + __ffs(EV_CTX_INTMODT_MASK)); - seq_printf(m, " base: 0x%0llx len: 0x%llx", er_ctxt->rbase, - er_ctxt->rlen); + seq_printf(m, " base: 0x%0llx len: 0x%llx", le64_to_cpu(er_ctxt->rbase), + le64_to_cpu(er_ctxt->rlen)); - seq_printf(m, " rp: 0x%llx wp: 0x%llx", er_ctxt->rp, - er_ctxt->wp); + seq_printf(m, " rp: 0x%llx wp: 0x%llx", le64_to_cpu(er_ctxt->rp), + le64_to_cpu(er_ctxt->wp)); seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp, &mhi_event->db_cfg.db_val); @@ -106,18 +106,18 @@ static int mhi_debugfs_channels_show(struct seq_file *m, void *d) seq_printf(m, "%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx", - mhi_chan->name, mhi_chan->chan, (chan_ctxt->chcfg & - CHAN_CTX_CHSTATE_MASK) >> CHAN_CTX_CHSTATE_SHIFT, - (chan_ctxt->chcfg & CHAN_CTX_BRSTMODE_MASK) >> - CHAN_CTX_BRSTMODE_SHIFT, (chan_ctxt->chcfg & - CHAN_CTX_POLLCFG_MASK) >> CHAN_CTX_POLLCFG_SHIFT); + mhi_chan->name, mhi_chan->chan, (le32_to_cpu(chan_ctxt->chcfg) & + CHAN_CTX_CHSTATE_MASK) >> __ffs(CHAN_CTX_CHSTATE_MASK), + (le32_to_cpu(chan_ctxt->chcfg) & CHAN_CTX_BRSTMODE_MASK) >> + __ffs(CHAN_CTX_BRSTMODE_MASK), (le32_to_cpu(chan_ctxt->chcfg) & + CHAN_CTX_POLLCFG_MASK) >> __ffs(CHAN_CTX_POLLCFG_MASK)); - seq_printf(m, " type: 0x%x event ring: %u", chan_ctxt->chtype, - chan_ctxt->erindex); + seq_printf(m, " type: 0x%x event ring: %u", le32_to_cpu(chan_ctxt->chtype), + le32_to_cpu(chan_ctxt->erindex)); seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx", - chan_ctxt->rbase, chan_ctxt->rlen, chan_ctxt->rp, - chan_ctxt->wp); + le64_to_cpu(chan_ctxt->rbase), le64_to_cpu(chan_ctxt->rlen), + le64_to_cpu(chan_ctxt->rp), le64_to_cpu(chan_ctxt->wp)); seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n", ring->rp, ring->wp, @@ -206,13 +206,13 @@ static int mhi_debugfs_regdump_show(struct seq_file *m, void *d) seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), + mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(mhi_cntrl->ee)); state = mhi_get_mhi_state(mhi_cntrl); ee = mhi_get_exec_env(mhi_cntrl); seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee), - TO_MHI_STATE_STR(state)); + mhi_state_str(state)); for (i = 0; regs[i].name; i++) { if (!regs[i].base) diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/host/init.c index 046f407dc5d6..a665b8e92408 100644 --- a/drivers/bus/mhi/core/init.c +++ b/drivers/bus/mhi/host/init.c @@ -4,6 +4,7 @@ * */ +#include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/dma-direction.h> @@ -44,18 +45,6 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = { [DEV_ST_TRANSITION_DISABLE] = "DISABLE", }; -const char * const mhi_state_str[MHI_STATE_MAX] = { - [MHI_STATE_RESET] = "RESET", - [MHI_STATE_READY] = "READY", - [MHI_STATE_M0] = "M0", - [MHI_STATE_M1] = "M1", - [MHI_STATE_M2] = "M2", - [MHI_STATE_M3] = "M3", - [MHI_STATE_M3_FAST] = "M3 FAST", - [MHI_STATE_BHI] = "BHI", - [MHI_STATE_SYS_ERR] = "SYS ERROR", -}; - const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = { [MHI_CH_STATE_TYPE_RESET] = "RESET", [MHI_CH_STATE_TYPE_STOP] = "STOP", @@ -77,12 +66,14 @@ static const char * const mhi_pm_state_str[] = { [MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect", }; -const char *to_mhi_pm_state_str(enum mhi_pm_state state) +const char *to_mhi_pm_state_str(u32 state) { - unsigned long pm_state = state; - int index = find_last_bit(&pm_state, 32); + int index; + + if (state) + index = __fls(state); - if (index >= ARRAY_SIZE(mhi_pm_state_str)) + if (!state || index >= ARRAY_SIZE(mhi_pm_state_str)) return "Invalid State"; return mhi_pm_state_str[index]; @@ -291,17 +282,17 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) if (mhi_chan->offload_ch) continue; - tmp = chan_ctxt->chcfg; + tmp = le32_to_cpu(chan_ctxt->chcfg); tmp &= ~CHAN_CTX_CHSTATE_MASK; - tmp |= (MHI_CH_STATE_DISABLED << CHAN_CTX_CHSTATE_SHIFT); + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED); tmp &= ~CHAN_CTX_BRSTMODE_MASK; - tmp |= (mhi_chan->db_cfg.brstmode << CHAN_CTX_BRSTMODE_SHIFT); + tmp |= FIELD_PREP(CHAN_CTX_BRSTMODE_MASK, mhi_chan->db_cfg.brstmode); tmp &= ~CHAN_CTX_POLLCFG_MASK; - tmp |= (mhi_chan->db_cfg.pollcfg << CHAN_CTX_POLLCFG_SHIFT); - chan_ctxt->chcfg = tmp; + tmp |= FIELD_PREP(CHAN_CTX_POLLCFG_MASK, mhi_chan->db_cfg.pollcfg); + chan_ctxt->chcfg = cpu_to_le32(tmp); - chan_ctxt->chtype = mhi_chan->type; - chan_ctxt->erindex = mhi_chan->er_index; + chan_ctxt->chtype = cpu_to_le32(mhi_chan->type); + chan_ctxt->erindex = cpu_to_le32(mhi_chan->er_index); mhi_chan->ch_state = MHI_CH_STATE_DISABLED; mhi_chan->tre_ring.db_addr = (void __iomem *)&chan_ctxt->wp; @@ -326,17 +317,17 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) if (mhi_event->offload_ev) continue; - tmp = er_ctxt->intmod; + tmp = le32_to_cpu(er_ctxt->intmod); tmp &= ~EV_CTX_INTMODC_MASK; tmp &= ~EV_CTX_INTMODT_MASK; - tmp |= (mhi_event->intmod << EV_CTX_INTMODT_SHIFT); - er_ctxt->intmod = tmp; + tmp |= FIELD_PREP(EV_CTX_INTMODT_MASK, mhi_event->intmod); + er_ctxt->intmod = cpu_to_le32(tmp); - er_ctxt->ertype = MHI_ER_TYPE_VALID; - er_ctxt->msivec = mhi_event->irq; + er_ctxt->ertype = cpu_to_le32(MHI_ER_TYPE_VALID); + er_ctxt->msivec = cpu_to_le32(mhi_event->irq); mhi_event->db_cfg.db_mode = true; - ring->el_size = sizeof(struct mhi_tre); + ring->el_size = sizeof(struct mhi_ring_element); ring->len = ring->el_size * ring->elements; ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); if (ret) @@ -347,9 +338,9 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) * ring is empty */ ring->rp = ring->wp = ring->base; - er_ctxt->rbase = ring->iommu_base; + er_ctxt->rbase = cpu_to_le64(ring->iommu_base); er_ctxt->rp = er_ctxt->wp = er_ctxt->rbase; - er_ctxt->rlen = ring->len; + er_ctxt->rlen = cpu_to_le64(ring->len); ring->ctxt_wp = &er_ctxt->wp; } @@ -368,7 +359,7 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) { struct mhi_ring *ring = &mhi_cmd->ring; - ring->el_size = sizeof(struct mhi_tre); + ring->el_size = sizeof(struct mhi_ring_element); ring->elements = CMD_EL_PER_RING; ring->len = ring->el_size * ring->elements; ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); @@ -376,9 +367,9 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) goto error_alloc_cmd; ring->rp = ring->wp = ring->base; - cmd_ctxt->rbase = ring->iommu_base; + cmd_ctxt->rbase = cpu_to_le64(ring->iommu_base); cmd_ctxt->rp = cmd_ctxt->wp = cmd_ctxt->rbase; - cmd_ctxt->rlen = ring->len; + cmd_ctxt->rlen = cpu_to_le64(ring->len); ring->ctxt_wp = &cmd_ctxt->wp; } @@ -435,71 +426,70 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) struct { u32 offset; u32 mask; - u32 shift; u32 val; } reg_info[] = { { - CCABAP_HIGHER, U32_MAX, 0, + CCABAP_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr), }, { - CCABAP_LOWER, U32_MAX, 0, + CCABAP_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr), }, { - ECABAP_HIGHER, U32_MAX, 0, + ECABAP_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr), }, { - ECABAP_LOWER, U32_MAX, 0, + ECABAP_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr), }, { - CRCBAP_HIGHER, U32_MAX, 0, + CRCBAP_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr), }, { - CRCBAP_LOWER, U32_MAX, 0, + CRCBAP_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr), }, { - MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, + MHICFG, MHICFG_NER_MASK, mhi_cntrl->total_ev_rings, }, { - MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT, + MHICFG, MHICFG_NHWER_MASK, mhi_cntrl->hw_ev_rings, }, { - MHICTRLBASE_HIGHER, U32_MAX, 0, + MHICTRLBASE_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->iova_start), }, { - MHICTRLBASE_LOWER, U32_MAX, 0, + MHICTRLBASE_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->iova_start), }, { - MHIDATABASE_HIGHER, U32_MAX, 0, + MHIDATABASE_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->iova_start), }, { - MHIDATABASE_LOWER, U32_MAX, 0, + MHIDATABASE_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->iova_start), }, { - MHICTRLLIMIT_HIGHER, U32_MAX, 0, + MHICTRLLIMIT_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->iova_stop), }, { - MHICTRLLIMIT_LOWER, U32_MAX, 0, + MHICTRLLIMIT_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->iova_stop), }, { - MHIDATALIMIT_HIGHER, U32_MAX, 0, + MHIDATALIMIT_HIGHER, U32_MAX, upper_32_bits(mhi_cntrl->iova_stop), }, { - MHIDATALIMIT_LOWER, U32_MAX, 0, + MHIDATALIMIT_LOWER, U32_MAX, lower_32_bits(mhi_cntrl->iova_stop), }, { 0, 0, 0 } @@ -508,8 +498,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) dev_dbg(dev, "Initializing MHI registers\n"); /* Read channel db offset */ - ret = mhi_read_reg_field(mhi_cntrl, base, CHDBOFF, CHDBOFF_CHDBOFF_MASK, - CHDBOFF_CHDBOFF_SHIFT, &val); + ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val); if (ret) { dev_err(dev, "Unable to read CHDBOFF register\n"); return -EIO; @@ -525,8 +514,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) mhi_chan->tre_ring.db_addr = base + val; /* Read event ring db offset */ - ret = mhi_read_reg_field(mhi_cntrl, base, ERDBOFF, ERDBOFF_ERDBOFF_MASK, - ERDBOFF_ERDBOFF_SHIFT, &val); + ret = mhi_read_reg(mhi_cntrl, base, ERDBOFF, &val); if (ret) { dev_err(dev, "Unable to read ERDBOFF register\n"); return -EIO; @@ -547,8 +535,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) /* Write to MMIO registers */ for (i = 0; reg_info[i].offset; i++) mhi_write_reg_field(mhi_cntrl, base, reg_info[i].offset, - reg_info[i].mask, reg_info[i].shift, - reg_info[i].val); + reg_info[i].mask, reg_info[i].val); return 0; } @@ -579,10 +566,10 @@ void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, chan_ctxt->rp = 0; chan_ctxt->wp = 0; - tmp = chan_ctxt->chcfg; + tmp = le32_to_cpu(chan_ctxt->chcfg); tmp &= ~CHAN_CTX_CHSTATE_MASK; - tmp |= (MHI_CH_STATE_DISABLED << CHAN_CTX_CHSTATE_SHIFT); - chan_ctxt->chcfg = tmp; + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED); + chan_ctxt->chcfg = cpu_to_le32(tmp); /* Update to all cores */ smp_wmb(); @@ -599,7 +586,7 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, buf_ring = &mhi_chan->buf_ring; tre_ring = &mhi_chan->tre_ring; - tre_ring->el_size = sizeof(struct mhi_tre); + tre_ring->el_size = sizeof(struct mhi_ring_element); tre_ring->len = tre_ring->el_size * tre_ring->elements; chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan]; ret = mhi_alloc_aligned_ring(mhi_cntrl, tre_ring, tre_ring->len); @@ -616,14 +603,14 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, return -ENOMEM; } - tmp = chan_ctxt->chcfg; + tmp = le32_to_cpu(chan_ctxt->chcfg); tmp &= ~CHAN_CTX_CHSTATE_MASK; - tmp |= (MHI_CH_STATE_ENABLED << CHAN_CTX_CHSTATE_SHIFT); - chan_ctxt->chcfg = tmp; + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_ENABLED); + chan_ctxt->chcfg = cpu_to_le32(tmp); - chan_ctxt->rbase = tre_ring->iommu_base; + chan_ctxt->rbase = cpu_to_le64(tre_ring->iommu_base); chan_ctxt->rp = chan_ctxt->wp = chan_ctxt->rbase; - chan_ctxt->rlen = tre_ring->len; + chan_ctxt->rlen = cpu_to_le64(tre_ring->len); tre_ring->ctxt_wp = &chan_ctxt->wp; tre_ring->rp = tre_ring->wp = tre_ring->base; @@ -962,14 +949,10 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, if (ret) goto err_destroy_wq; - mhi_cntrl->family_number = (soc_info & SOC_HW_VERSION_FAM_NUM_BMSK) >> - SOC_HW_VERSION_FAM_NUM_SHFT; - mhi_cntrl->device_number = (soc_info & SOC_HW_VERSION_DEV_NUM_BMSK) >> - SOC_HW_VERSION_DEV_NUM_SHFT; - mhi_cntrl->major_version = (soc_info & SOC_HW_VERSION_MAJOR_VER_BMSK) >> - SOC_HW_VERSION_MAJOR_VER_SHFT; - mhi_cntrl->minor_version = (soc_info & SOC_HW_VERSION_MINOR_VER_BMSK) >> - SOC_HW_VERSION_MINOR_VER_SHFT; + mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info); + mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info); + mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info); + mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info); mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL); if (mhi_cntrl->index < 0) { diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h new file mode 100644 index 000000000000..b47d8ef2624a --- /dev/null +++ b/drivers/bus/mhi/host/internal.h @@ -0,0 +1,382 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * + */ + +#ifndef _MHI_INT_H +#define _MHI_INT_H + +#include "../common.h" + +extern struct bus_type mhi_bus_type; + +/* Host request register */ +#define MHI_SOC_RESET_REQ_OFFSET 0xb0 +#define MHI_SOC_RESET_REQ BIT(0) + +#define SOC_HW_VERSION_OFFS 0x224 +#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28) +#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16) +#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8) +#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0) + +struct mhi_ctxt { + struct mhi_event_ctxt *er_ctxt; + struct mhi_chan_ctxt *chan_ctxt; + struct mhi_cmd_ctxt *cmd_ctxt; + dma_addr_t er_ctxt_addr; + dma_addr_t chan_ctxt_addr; + dma_addr_t cmd_ctxt_addr; +}; + +struct bhi_vec_entry { + u64 dma_addr; + u64 size; +}; + +enum mhi_ch_state_type { + MHI_CH_STATE_TYPE_RESET, + MHI_CH_STATE_TYPE_STOP, + MHI_CH_STATE_TYPE_START, + MHI_CH_STATE_TYPE_MAX, +}; + +extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX]; +#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \ + "INVALID_STATE" : \ + mhi_ch_state_type_str[(state)]) + +#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \ + mode != MHI_DB_BRST_ENABLE) + +extern const char * const mhi_ee_str[MHI_EE_MAX]; +#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \ + "INVALID_EE" : mhi_ee_str[ee]) + +#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \ + ee == MHI_EE_EDL) +#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS) +#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL) +#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \ + ee == MHI_EE_FP) + +enum dev_st_transition { + DEV_ST_TRANSITION_PBL, + DEV_ST_TRANSITION_READY, + DEV_ST_TRANSITION_SBL, + DEV_ST_TRANSITION_MISSION_MODE, + DEV_ST_TRANSITION_FP, + DEV_ST_TRANSITION_SYS_ERR, + DEV_ST_TRANSITION_DISABLE, + DEV_ST_TRANSITION_MAX, +}; + +extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; +#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ + "INVALID_STATE" : dev_state_tran_str[state]) + +/* internal power states */ +enum mhi_pm_state { + MHI_PM_STATE_DISABLE, + MHI_PM_STATE_POR, + MHI_PM_STATE_M0, + MHI_PM_STATE_M2, + MHI_PM_STATE_M3_ENTER, + MHI_PM_STATE_M3, + MHI_PM_STATE_M3_EXIT, + MHI_PM_STATE_FW_DL_ERR, + MHI_PM_STATE_SYS_ERR_DETECT, + MHI_PM_STATE_SYS_ERR_PROCESS, + MHI_PM_STATE_SHUTDOWN_PROCESS, + MHI_PM_STATE_LD_ERR_FATAL_DETECT, + MHI_PM_STATE_MAX +}; + +#define MHI_PM_DISABLE BIT(0) +#define MHI_PM_POR BIT(1) +#define MHI_PM_M0 BIT(2) +#define MHI_PM_M2 BIT(3) +#define MHI_PM_M3_ENTER BIT(4) +#define MHI_PM_M3 BIT(5) +#define MHI_PM_M3_EXIT BIT(6) +/* firmware download failure state */ +#define MHI_PM_FW_DL_ERR BIT(7) +#define MHI_PM_SYS_ERR_DETECT BIT(8) +#define MHI_PM_SYS_ERR_PROCESS BIT(9) +#define MHI_PM_SHUTDOWN_PROCESS BIT(10) +/* link not accessible */ +#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11) + +#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \ + MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \ + MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \ + MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) +#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) +#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) +#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access) +#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \ + MHI_PM_M2 | MHI_PM_M3_EXIT)) +#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2) +#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state) +#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \ + MHI_PM_IN_ERROR_STATE(pm_state)) +#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \ + (MHI_PM_M3_ENTER | MHI_PM_M3)) + +#define NR_OF_CMD_RINGS 1 +#define CMD_EL_PER_RING 128 +#define PRIMARY_CMD_RING 0 +#define MHI_DEV_WAKE_DB 127 +#define MHI_MAX_MTU 0xffff +#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1) + +enum mhi_er_type { + MHI_ER_TYPE_INVALID = 0x0, + MHI_ER_TYPE_VALID = 0x1, +}; + +struct db_cfg { + bool reset_req; + bool db_mode; + u32 pollcfg; + enum mhi_db_brst_mode brstmode; + dma_addr_t db_val; + void (*process_db)(struct mhi_controller *mhi_cntrl, + struct db_cfg *db_cfg, void __iomem *io_addr, + dma_addr_t db_val); +}; + +struct mhi_pm_transitions { + enum mhi_pm_state from_state; + u32 to_states; +}; + +struct state_transition { + struct list_head node; + enum dev_st_transition state; +}; + +struct mhi_ring { + dma_addr_t dma_handle; + dma_addr_t iommu_base; + __le64 *ctxt_wp; /* point to ctxt wp */ + void *pre_aligned; + void *base; + void *rp; + void *wp; + size_t el_size; + size_t len; + size_t elements; + size_t alloc_size; + void __iomem *db_addr; +}; + +struct mhi_cmd { + struct mhi_ring ring; + spinlock_t lock; +}; + +struct mhi_buf_info { + void *v_addr; + void *bb_addr; + void *wp; + void *cb_buf; + dma_addr_t p_addr; + size_t len; + enum dma_data_direction dir; + bool used; /* Indicates whether the buffer is used or not */ + bool pre_mapped; /* Already pre-mapped by client */ +}; + +struct mhi_event { + struct mhi_controller *mhi_cntrl; + struct mhi_chan *mhi_chan; /* dedicated to channel */ + u32 er_index; + u32 intmod; + u32 irq; + int chan; /* this event ring is dedicated to a channel (optional) */ + u32 priority; + enum mhi_er_data_type data_type; + struct mhi_ring ring; + struct db_cfg db_cfg; + struct tasklet_struct task; + spinlock_t lock; + int (*process_event)(struct mhi_controller *mhi_cntrl, + struct mhi_event *mhi_event, + u32 event_quota); + bool hw_ring; + bool cl_manage; + bool offload_ev; /* managed by a device driver */ +}; + +struct mhi_chan { + const char *name; + /* + * Important: When consuming, increment tre_ring first and when + * releasing, decrement buf_ring first. If tre_ring has space, buf_ring + * is guranteed to have space so we do not need to check both rings. + */ + struct mhi_ring buf_ring; + struct mhi_ring tre_ring; + u32 chan; + u32 er_index; + u32 intmod; + enum mhi_ch_type type; + enum dma_data_direction dir; + struct db_cfg db_cfg; + enum mhi_ch_ee_mask ee_mask; + enum mhi_ch_state ch_state; + enum mhi_ev_ccs ccs; + struct mhi_device *mhi_dev; + void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result); + struct mutex mutex; + struct completion completion; + rwlock_t lock; + struct list_head node; + bool lpm_notify; + bool configured; + bool offload_ch; + bool pre_alloc; + bool wake_capable; +}; + +/* Default MHI timeout */ +#define MHI_TIMEOUT_MS (1000) + +/* debugfs related functions */ +#ifdef CONFIG_MHI_BUS_DEBUG +void mhi_create_debugfs(struct mhi_controller *mhi_cntrl); +void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl); +void mhi_debugfs_init(void); +void mhi_debugfs_exit(void); +#else +static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl) +{ +} + +static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl) +{ +} + +static inline void mhi_debugfs_init(void) +{ +} + +static inline void mhi_debugfs_exit(void) +{ +} +#endif + +struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl); + +int mhi_destroy_device(struct device *dev, void *data); +void mhi_create_devices(struct mhi_controller *mhi_cntrl); + +int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, + struct image_info **image_info, size_t alloc_size); +void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, + struct image_info *image_info); + +/* Power management APIs */ +enum mhi_pm_state __must_check mhi_tryset_pm_state( + struct mhi_controller *mhi_cntrl, + enum mhi_pm_state state); +const char *to_mhi_pm_state_str(u32 state); +int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl, + enum dev_st_transition state); +void mhi_pm_st_worker(struct work_struct *work); +void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl); +int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl); +int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl); +void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl); +int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl); +int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); +int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, + enum mhi_cmd_type cmd); +int mhi_download_amss_image(struct mhi_controller *mhi_cntrl); +static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl) +{ + return (mhi_cntrl->dev_state >= MHI_STATE_M0 && + mhi_cntrl->dev_state <= MHI_STATE_M3_FAST); +} + +static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl) +{ + pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0); + mhi_cntrl->runtime_get(mhi_cntrl); + mhi_cntrl->runtime_put(mhi_cntrl); +} + +/* Register access methods */ +void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg, + void __iomem *db_addr, dma_addr_t db_val); +void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl, + struct db_cfg *db_mode, void __iomem *db_addr, + dma_addr_t db_val); +int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, + void __iomem *base, u32 offset, u32 *out); +int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, + void __iomem *base, u32 offset, u32 mask, + u32 *out); +int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl, + void __iomem *base, u32 offset, u32 mask, + u32 val, u32 delayus); +void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, + u32 offset, u32 val); +void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, + u32 offset, u32 mask, u32 val); +void mhi_ring_er_db(struct mhi_event *mhi_event); +void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr, + dma_addr_t db_val); +void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd); +void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan); + +/* Initialization methods */ +int mhi_init_mmio(struct mhi_controller *mhi_cntrl); +int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl); +void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl); +int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl); +void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl); +void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, + struct image_info *img_info); +void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl); + +/* Automatically allocate and queue inbound buffers */ +#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0) +int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan, unsigned int flags); + +int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan); +void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan); +void mhi_reset_chan(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan); + +/* Event processing methods */ +void mhi_ctrl_ev_task(unsigned long data); +void mhi_ev_task(unsigned long data); +int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, + struct mhi_event *mhi_event, u32 event_quota); +int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, + struct mhi_event *mhi_event, u32 event_quota); + +/* ISR handlers */ +irqreturn_t mhi_irq_handler(int irq_number, void *dev); +irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev); +irqreturn_t mhi_intvec_handler(int irq_number, void *dev); + +int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, + struct mhi_buf_info *info, enum mhi_flags flags); +int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl, + struct mhi_buf_info *buf_info); +int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl, + struct mhi_buf_info *buf_info); +void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl, + struct mhi_buf_info *buf_info); +void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl, + struct mhi_buf_info *buf_info); + +#endif /* _MHI_INT_H */ diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/host/main.c index ffde617f93a3..9021be7f2359 100644 --- a/drivers/bus/mhi/core/main.c +++ b/drivers/bus/mhi/host/main.c @@ -24,7 +24,7 @@ int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, - u32 mask, u32 shift, u32 *out) + u32 mask, u32 *out) { u32 tmp; int ret; @@ -33,21 +33,20 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, if (ret) return ret; - *out = (tmp & mask) >> shift; + *out = (tmp & mask) >> __ffs(mask); return 0; } int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, - u32 mask, u32 shift, u32 val, u32 delayus) + u32 mask, u32 val, u32 delayus) { int ret; u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus; while (retry--) { - ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, shift, - &out); + ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out); if (ret) return ret; @@ -67,7 +66,7 @@ void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, } void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, - u32 offset, u32 mask, u32 shift, u32 val) + u32 offset, u32 mask, u32 val) { int ret; u32 tmp; @@ -77,7 +76,7 @@ void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, return; tmp &= ~mask; - tmp |= (val << shift); + tmp |= (val << __ffs(mask)); mhi_write_reg(mhi_cntrl, base, offset, tmp); } @@ -114,7 +113,7 @@ void mhi_ring_er_db(struct mhi_event *mhi_event) struct mhi_ring *ring = &mhi_event->ring; mhi_event->db_cfg.process_db(mhi_event->mhi_cntrl, &mhi_event->db_cfg, - ring->db_addr, *ring->ctxt_wp); + ring->db_addr, le64_to_cpu(*ring->ctxt_wp)); } void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd) @@ -123,7 +122,7 @@ void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd) struct mhi_ring *ring = &mhi_cmd->ring; db = ring->iommu_base + (ring->wp - ring->base); - *ring->ctxt_wp = db; + *ring->ctxt_wp = cpu_to_le64(db); mhi_write_db(mhi_cntrl, ring->db_addr, db); } @@ -140,7 +139,7 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, * before letting h/w know there is new element to fetch. */ dma_wmb(); - *ring->ctxt_wp = db; + *ring->ctxt_wp = cpu_to_le64(db); mhi_chan->db_cfg.process_db(mhi_cntrl, &mhi_chan->db_cfg, ring->db_addr, db); @@ -159,8 +158,7 @@ enum mhi_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl) { u32 state; int ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS, - MHISTATUS_MHISTATE_MASK, - MHISTATUS_MHISTATE_SHIFT, &state); + MHISTATUS_MHISTATE_MASK, &state); return ret ? MHI_STATE_MAX : state; } EXPORT_SYMBOL_GPL(mhi_get_mhi_state); @@ -432,7 +430,7 @@ irqreturn_t mhi_irq_handler(int irq_number, void *dev) struct mhi_event_ctxt *er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; struct mhi_ring *ev_ring = &mhi_event->ring; - dma_addr_t ptr = er_ctxt->rp; + dma_addr_t ptr = le64_to_cpu(er_ctxt->rp); void *dev_rp; if (!is_valid_ring_ptr(ev_ring, ptr)) { @@ -479,8 +477,8 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) ee = mhi_get_exec_env(mhi_cntrl); dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(ee), TO_MHI_STATE_STR(state)); + mhi_state_str(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(ee), mhi_state_str(state)); if (state == MHI_STATE_SYS_ERR) { dev_dbg(dev, "System error detected\n"); @@ -537,14 +535,14 @@ static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl, /* Update the WP */ ring->wp += ring->el_size; - ctxt_wp = *ring->ctxt_wp + ring->el_size; + ctxt_wp = le64_to_cpu(*ring->ctxt_wp) + ring->el_size; if (ring->wp >= (ring->base + ring->len)) { ring->wp = ring->base; ctxt_wp = ring->iommu_base; } - *ring->ctxt_wp = ctxt_wp; + *ring->ctxt_wp = cpu_to_le64(ctxt_wp); /* Update the RP */ ring->rp += ring->el_size; @@ -556,7 +554,7 @@ static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl, } static int parse_xfer_event(struct mhi_controller *mhi_cntrl, - struct mhi_tre *event, + struct mhi_ring_element *event, struct mhi_chan *mhi_chan) { struct mhi_ring *buf_ring, *tre_ring; @@ -592,7 +590,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, case MHI_EV_CC_EOT: { dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event); - struct mhi_tre *local_rp, *ev_tre; + struct mhi_ring_element *local_rp, *ev_tre; void *dev_rp; struct mhi_buf_info *buf_info; u16 xfer_len; @@ -691,7 +689,7 @@ end_process_tx_event: } static int parse_rsc_event(struct mhi_controller *mhi_cntrl, - struct mhi_tre *event, + struct mhi_ring_element *event, struct mhi_chan *mhi_chan) { struct mhi_ring *buf_ring, *tre_ring; @@ -755,12 +753,12 @@ end_process_rsc_event: } static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, - struct mhi_tre *tre) + struct mhi_ring_element *tre) { dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre); struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; struct mhi_ring *mhi_ring = &cmd_ring->ring; - struct mhi_tre *cmd_pkt; + struct mhi_ring_element *cmd_pkt; struct mhi_chan *mhi_chan; u32 chan; @@ -793,7 +791,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota) { - struct mhi_tre *dev_rp, *local_rp; + struct mhi_ring_element *dev_rp, *local_rp; struct mhi_ring *ev_ring = &mhi_event->ring; struct mhi_event_ctxt *er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; @@ -801,7 +799,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, struct device *dev = &mhi_cntrl->mhi_dev->dev; u32 chan; int count = 0; - dma_addr_t ptr = er_ctxt->rp; + dma_addr_t ptr = le64_to_cpu(er_ctxt->rp); /* * This is a quick check to avoid unnecessary event processing @@ -846,7 +844,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, new_state = MHI_TRE_GET_EV_STATE(local_rp); dev_dbg(dev, "State change event to state: %s\n", - TO_MHI_STATE_STR(new_state)); + mhi_state_str(new_state)); switch (new_state) { case MHI_STATE_M0: @@ -873,7 +871,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, } default: dev_err(dev, "Invalid state: %s\n", - TO_MHI_STATE_STR(new_state)); + mhi_state_str(new_state)); } break; @@ -940,7 +938,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); local_rp = ev_ring->rp; - ptr = er_ctxt->rp; + ptr = le64_to_cpu(er_ctxt->rp); if (!is_valid_ring_ptr(ev_ring, ptr)) { dev_err(&mhi_cntrl->mhi_dev->dev, "Event ring rp points outside of the event ring\n"); @@ -963,14 +961,14 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota) { - struct mhi_tre *dev_rp, *local_rp; + struct mhi_ring_element *dev_rp, *local_rp; struct mhi_ring *ev_ring = &mhi_event->ring; struct mhi_event_ctxt *er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; int count = 0; u32 chan; struct mhi_chan *mhi_chan; - dma_addr_t ptr = er_ctxt->rp; + dma_addr_t ptr = le64_to_cpu(er_ctxt->rp); if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) return -EIO; @@ -1011,7 +1009,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); local_rp = ev_ring->rp; - ptr = er_ctxt->rp; + ptr = le64_to_cpu(er_ctxt->rp); if (!is_valid_ring_ptr(ev_ring, ptr)) { dev_err(&mhi_cntrl->mhi_dev->dev, "Event ring rp points outside of the event ring\n"); @@ -1187,7 +1185,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, struct mhi_buf_info *info, enum mhi_flags flags) { struct mhi_ring *buf_ring, *tre_ring; - struct mhi_tre *mhi_tre; + struct mhi_ring_element *mhi_tre; struct mhi_buf_info *buf_info; int eot, eob, chain, bei; int ret; @@ -1258,7 +1256,7 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, enum mhi_cmd_type cmd) { - struct mhi_tre *cmd_tre = NULL; + struct mhi_ring_element *cmd_tre = NULL; struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; struct mhi_ring *ring = &mhi_cmd->ring; struct device *dev = &mhi_cntrl->mhi_dev->dev; @@ -1520,7 +1518,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, int chan) { - struct mhi_tre *dev_rp, *local_rp; + struct mhi_ring_element *dev_rp, *local_rp; struct mhi_ring *ev_ring; struct device *dev = &mhi_cntrl->mhi_dev->dev; unsigned long flags; @@ -1533,7 +1531,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, /* mark all stale events related to channel as STALE event */ spin_lock_irqsave(&mhi_event->lock, flags); - ptr = er_ctxt->rp; + ptr = le64_to_cpu(er_ctxt->rp); if (!is_valid_ring_ptr(ev_ring, ptr)) { dev_err(&mhi_cntrl->mhi_dev->dev, "Event ring rp points outside of the event ring\n"); diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index b79895810c52..9527b7d63840 100644 --- a/drivers/bus/mhi/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -327,6 +327,7 @@ static const struct mhi_pci_dev_info mhi_quectel_em1xx_info = { .config = &modem_quectel_em1xx_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, + .mru_default = 32768, .sideband_wake = true, }; diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/host/pm.c index 4aae0baea008..3d90b8ecd3d9 100644 --- a/drivers/bus/mhi/core/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -131,11 +131,10 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, enum mhi_state state) { if (state == MHI_STATE_RESET) { mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1); + MHICTRL_RESET_MASK, 1); } else { mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_MHISTATE_MASK, - MHICTRL_MHISTATE_SHIFT, state); + MHICTRL_MHISTATE_MASK, state); } } @@ -167,16 +166,14 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) /* Wait for RESET to be cleared and READY bit to be set by the device */ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0, - interval_us); + MHICTRL_RESET_MASK, 0, interval_us); if (ret) { dev_err(dev, "Device failed to clear MHI Reset\n"); return ret; } ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS, - MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT, 1, - interval_us); + MHISTATUS_READY_MASK, 1, interval_us); if (ret) { dev_err(dev, "Device failed to enter MHI Ready\n"); return ret; @@ -218,7 +215,7 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) continue; ring->wp = ring->base + ring->len - ring->el_size; - *ring->ctxt_wp = ring->iommu_base + ring->len - ring->el_size; + *ring->ctxt_wp = cpu_to_le64(ring->iommu_base + ring->len - ring->el_size); /* Update all cores */ smp_wmb(); @@ -420,7 +417,7 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) continue; ring->wp = ring->base + ring->len - ring->el_size; - *ring->ctxt_wp = ring->iommu_base + ring->len - ring->el_size; + *ring->ctxt_wp = cpu_to_le64(ring->iommu_base + ring->len - ring->el_size); /* Update to all cores */ smp_wmb(); @@ -470,8 +467,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) /* Wait for the reset bit to be cleared by the device */ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0, - 25000); + MHICTRL_RESET_MASK, 0, 25000); if (ret) dev_err(dev, "Device failed to clear MHI Reset\n"); @@ -545,7 +541,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state)); + mhi_state_str(mhi_cntrl->dev_state)); mutex_unlock(&mhi_cntrl->pm_mutex); } @@ -602,7 +598,6 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) mhi_cntrl->regs, MHICTRL, MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT, &in_reset) || !in_reset, timeout); if (!ret || in_reset) { @@ -689,7 +684,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) exit_sys_error_transition: dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state)); + mhi_state_str(mhi_cntrl->dev_state)); mutex_unlock(&mhi_cntrl->pm_mutex); } @@ -864,7 +859,7 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { dev_err(dev, "Did not enter M3 state, MHI state: %s, PM state: %s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), + mhi_state_str(mhi_cntrl->dev_state), to_mhi_pm_state_str(mhi_cntrl->pm_state)); return -EIO; } @@ -890,7 +885,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) dev_dbg(dev, "Entered with PM state: %s, MHI state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state)); + mhi_state_str(mhi_cntrl->dev_state)); if (mhi_cntrl->pm_state == MHI_PM_DISABLE) return 0; @@ -900,7 +895,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { dev_warn(dev, "Resuming from non M3 state (%s)\n", - TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl))); + mhi_state_str(mhi_get_mhi_state(mhi_cntrl))); if (!force) return -EINVAL; } @@ -937,7 +932,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { dev_err(dev, "Did not enter M0 state, MHI state: %s, PM state: %s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), + mhi_state_str(mhi_cntrl->dev_state), to_mhi_pm_state_str(mhi_cntrl->pm_state)); return -EIO; } @@ -1088,13 +1083,12 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) state = mhi_get_mhi_state(mhi_cntrl); dev_dbg(dev, "Attempting power on with EE: %s, state: %s\n", - TO_MHI_EXEC_STR(current_ee), TO_MHI_STATE_STR(state)); + TO_MHI_EXEC_STR(current_ee), mhi_state_str(state)); if (state == MHI_STATE_SYS_ERR) { mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0, - interval_us); + MHICTRL_RESET_MASK, 0, interval_us); if (ret) { dev_info(dev, "Failed to reset MHI due to syserr state\n"); goto error_exit; diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index cce2af5df7b4..d5f943938427 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -60,7 +60,7 @@ struct bsr_dev { }; static unsigned total_bsr_devs; -static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs); +static LIST_HEAD(bsr_devs); static struct class *bsr_class; static int bsr_major; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 563dfae3b8da..ee71376f174b 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -16,6 +16,7 @@ #include <linux/ioport.h> #include <linux/fcntl.h> #include <linux/init.h> +#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/poll.h> #include <linux/mm.h> #include <linux/proc_fs.h> @@ -120,22 +121,6 @@ static struct hpets *hpets; #define HPET_PERIODIC 0x0004 #define HPET_SHARED_IRQ 0x0008 - -#ifndef readq -static inline unsigned long long readq(void __iomem *addr) -{ - return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL); -} -#endif - -#ifndef writeq -static inline void writeq(unsigned long long v, void __iomem *addr) -{ - writel(v & 0xffffffff, addr); - writel(v >> 32, addr + 4); -} -#endif - static irqreturn_t hpet_interrupt(int irq, void *data) { struct hpet_dev *devp; @@ -268,9 +253,9 @@ static int hpet_open(struct inode *inode, struct file *file) for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next) for (i = 0; i < hpetp->hp_ntimer; i++) - if (hpetp->hp_dev[i].hd_flags & HPET_OPEN) + if (hpetp->hp_dev[i].hd_flags & HPET_OPEN) { continue; - else { + } else { devp = &hpetp->hp_dev[i]; break; } @@ -317,9 +302,9 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) devp->hd_irqdata = 0; spin_unlock_irq(&hpet_lock); - if (data) + if (data) { break; - else if (file->f_flags & O_NONBLOCK) { + } else if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto out; } else if (signal_pending(current)) { @@ -982,7 +967,8 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) break; irq = acpi_register_gsi(NULL, irqp->interrupts[i], - irqp->triggering, irqp->polarity); + irqp->triggering, + irqp->polarity); if (irq < 0) return AE_ERROR; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e3c430539a17..9fa3c76a267f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2245,7 +2245,7 @@ static struct virtio_driver virtio_rproc_serial = { .remove = virtcons_remove, }; -static int __init init(void) +static int __init virtio_console_init(void) { int err; @@ -2280,7 +2280,7 @@ free: return err; } -static void __exit fini(void) +static void __exit virtio_console_fini(void) { reclaim_dma_bufs(); @@ -2290,8 +2290,8 @@ static void __exit fini(void) class_destroy(pdrvdata.class); debugfs_remove_recursive(pdrvdata.debugfs_dir); } -module_init(init); -module_exit(fini); +module_init(virtio_console_init); +module_exit(virtio_console_fini); MODULE_DESCRIPTION("Virtio console driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c index 02225eb19cf6..619f3a30ec55 100644 --- a/drivers/char/xilinx_hwicap/fifo_icap.c +++ b/drivers/char/xilinx_hwicap/fifo_icap.c @@ -111,7 +111,7 @@ static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata) } /** - * fifo_icap_set_read_size - Set the the size register. + * fifo_icap_set_read_size - Set the size register. * @drvdata: a pointer to the drvdata. * @data: the size of the following read transaction, in words. **/ diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 067396bedf22..74a4928aea1d 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -241,7 +241,7 @@ static int hwicap_command_desync(struct hwicap_drvdata *drvdata) buffer[index++] = XHI_NOOP_PACKET; /* - * Write the data to the FIFO and intiate the transfer of data present + * Write the data to the FIFO and initiate the transfer of data present * in the FIFO to the ICAP device. */ return drvdata->config->set_configuration(drvdata, @@ -297,7 +297,7 @@ static int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata, buffer[index++] = XHI_NOOP_PACKET; /* - * Write the data to the FIFO and intiate the transfer of data present + * Write the data to the FIFO and initiate the transfer of data present * in the FIFO to the ICAP device. */ status = drvdata->config->set_configuration(drvdata, @@ -384,7 +384,7 @@ hwicap_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) drvdata->read_buffer + bytes_to_read, 4 - bytes_to_read); } else { - /* Get new data from the ICAP, and return was was requested. */ + /* Get new data from the ICAP, and return what was requested. */ kbuf = (u32 *) get_zeroed_page(GFP_KERNEL); if (!kbuf) { status = -ENOMEM; diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index 937a69ce0977..728dc02156c8 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -961,7 +961,7 @@ static const struct comedi_lrange *das16_ai_range(struct comedi_device *dev, /* allocate single-range range table */ lrange = comedi_alloc_spriv(s, - sizeof(*lrange) + sizeof(*krange)); + struct_size(lrange, range, 1)); if (!lrange) return &range_unknown; @@ -995,7 +995,7 @@ static const struct comedi_lrange *das16_ao_range(struct comedi_device *dev, /* allocate single-range range table */ lrange = comedi_alloc_spriv(s, - sizeof(*lrange) + sizeof(*krange)); + struct_size(lrange, range, 1)); if (!lrange) return &range_unknown; diff --git a/drivers/comedi/drivers/ni_routes.c b/drivers/comedi/drivers/ni_routes.c index f24eeb464eba..295a3a9ee0c9 100644 --- a/drivers/comedi/drivers/ni_routes.c +++ b/drivers/comedi/drivers/ni_routes.c @@ -56,8 +56,7 @@ static const u8 *ni_find_route_values(const char *device_family) int i; for (i = 0; ni_all_route_values[i]; ++i) { - if (memcmp(ni_all_route_values[i]->family, device_family, - strnlen(device_family, 30)) == 0) { + if (!strcmp(ni_all_route_values[i]->family, device_family)) { rv = &ni_all_route_values[i]->register_values[0][0]; break; } @@ -75,8 +74,7 @@ ni_find_valid_routes(const char *board_name) int i; for (i = 0; ni_device_routes_list[i]; ++i) { - if (memcmp(ni_device_routes_list[i]->device, board_name, - strnlen(board_name, 30)) == 0) { + if (!strcmp(ni_device_routes_list[i]->device, board_name)) { dr = ni_device_routes_list[i]; break; } diff --git a/drivers/comedi/drivers/pcm3724.c b/drivers/comedi/drivers/pcm3724.c index e4103f9eeced..ca8bef54dacc 100644 --- a/drivers/comedi/drivers/pcm3724.c +++ b/drivers/comedi/drivers/pcm3724.c @@ -93,7 +93,6 @@ static void do_3724_config(struct comedi_device *dev, unsigned long port_8255_cfg; config = I8255_CTRL_CW; - buffer_config = 0; /* 1 in io_bits indicates output, 1 in config indicates input */ if (!(s->io_bits & 0x0000ff)) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 3dcdb681c4e4..5edd155f1911 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -14,7 +14,7 @@ if COUNTER config 104_QUAD_8 tristate "ACCES 104-QUAD-8 driver" - depends on PC104 && X86 + depends on (PC104 && X86) || COMPILE_TEST select ISA_BUS_API help Say yes here to build support for the ACCES 104-QUAD-8 quadrature diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index b7c62f957a6a..69d340be9c93 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -477,6 +477,8 @@ static int counter_get_data(struct counter_device *const counter, case COUNTER_SCOPE_COUNT: ret = comp->count_u8_read(counter, parent, &value_u8); break; + default: + return -EINVAL; } *value = value_u8; return ret; @@ -496,6 +498,8 @@ static int counter_get_data(struct counter_device *const counter, case COUNTER_SCOPE_COUNT: ret = comp->count_u32_read(counter, parent, &value_u32); break; + default: + return -EINVAL; } *value = value_u32; return ret; diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 869894b74741..938651f9e9e0 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -22,6 +22,8 @@ #include "counter-chrdev.h" #include "counter-sysfs.h" +#define COUNTER_NAME "counter" + /* Provides a unique ID for each counter device */ static DEFINE_IDA(counter_ida); @@ -113,8 +115,15 @@ struct counter_device *counter_alloc(size_t sizeof_priv) device_initialize(dev); + err = dev_set_name(dev, COUNTER_NAME "%d", dev->id); + if (err) + goto err_dev_set_name; + return counter; +err_dev_set_name: + + counter_chrdev_remove(counter); err_chrdev_add: ida_free(&counter_ida, dev->id); @@ -247,7 +256,8 @@ static int __init counter_init(void) if (err < 0) return err; - err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter"); + err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, + COUNTER_NAME); if (err < 0) goto err_unregister_bus; diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 9e99702470c2..3b13f56bbb11 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -26,10 +26,13 @@ struct interrupt_cnt_priv { static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id) { - struct interrupt_cnt_priv *priv = dev_id; + struct counter_device *counter = dev_id; + struct interrupt_cnt_priv *priv = counter_priv(counter); atomic_inc(&priv->count); + counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 0); + return IRQ_HANDLED; } @@ -209,7 +212,7 @@ static int interrupt_cnt_probe(struct platform_device *pdev) irq_set_status_flags(priv->irq, IRQ_NOAUTOEN); ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, - dev_name(dev), priv); + dev_name(dev), counter); if (ret) return ret; diff --git a/drivers/dio/dio.c b/drivers/dio/dio.c index 4c06c93c93d3..005a82f671c3 100644 --- a/drivers/dio/dio.c +++ b/drivers/dio/dio.c @@ -2,27 +2,27 @@ /* Code to support devices on the DIO and DIO-II bus * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk> * Copyright (C) 2004 Jochen Friedrich <jochen@scram.de> - * + * * This code has basically these routines at the moment: * int dio_find(u_int deviceid) * Search the list of DIO devices and return the select code * of the next unconfigured device found that matches the given device ID. * Note that the deviceid parameter should be the encoded ID. - * This means that framebuffers should pass it as + * This means that framebuffers should pass it as * DIO_ENCODE_ID(DIO_ID_FBUFFER,DIO_ID2_TOPCAT) * (or whatever); everybody else just uses DIO_ID_FOOBAR. * unsigned long dio_scodetophysaddr(int scode) * Return the physical address corresponding to the given select code. * int dio_scodetoipl(int scode) - * Every DIO card has a fixed interrupt priority level. This function + * Every DIO card has a fixed interrupt priority level. This function * returns it, whatever it is. * const char *dio_scodetoname(int scode) - * Return a character string describing this board [might be "" if + * Return a character string describing this board [might be "" if * not CONFIG_DIO_CONSTANTS] * void dio_config_board(int scode) mark board as configured in the list * void dio_unconfig_board(int scode) mark board as no longer configured * - * This file is based on the way the Amiga port handles Zorro II cards, + * This file is based on the way the Amiga port handles Zorro II cards, * although we aren't so complicated... */ #include <linux/module.h> @@ -33,7 +33,7 @@ #include <linux/dio.h> #include <linux/slab.h> /* kmalloc() */ #include <linux/uaccess.h> -#include <asm/io.h> /* readb() */ +#include <linux/io.h> /* readb() */ struct dio_bus dio_bus = { .resources = { @@ -52,38 +52,36 @@ struct dio_bus dio_bus = { /* We associate each numeric ID with an appropriate descriptive string * using a constant array of these structs. * FIXME: we should be able to arrange to throw away most of the strings - * using the initdata stuff. Then we wouldn't need to worry about + * using the initdata stuff. Then we wouldn't need to worry about * carrying them around... - * I think we do this by copying them into newly kmalloc()ed memory and + * I think we do this by copying them into newly kmalloc()ed memory and * marking the names[] array as .initdata ? */ -struct dioname -{ - int id; - const char *name; +struct dioname { + int id; + const char *name; }; /* useful macro */ #define DIONAME(x) { DIO_ID_##x, DIO_DESC_##x } -#define DIOFBNAME(x) { DIO_ENCODE_ID( DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x } - -static struct dioname names[] = -{ - DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM), - DIONAME(DCM), DIONAME(DCMREM), - DIONAME(LAN), - DIONAME(FHPIB), DIONAME(NHPIB), - DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3), - DIONAME(FBUFFER), - DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM), - DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3), - DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7), - DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11), - DIONAME(MISC12), DIONAME(MISC13), - DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE), - DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE), - DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION), - DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS) +#define DIOFBNAME(x) { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x } + +static struct dioname names[] = { + DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM), + DIONAME(DCM), DIONAME(DCMREM), + DIONAME(LAN), + DIONAME(FHPIB), DIONAME(NHPIB), + DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3), + DIONAME(FBUFFER), + DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM), + DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3), + DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7), + DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11), + DIONAME(MISC12), DIONAME(MISC13), + DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE), + DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE), + DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION), + DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS) }; #undef DIONAME @@ -94,13 +92,14 @@ static const char unknowndioname[] static const char *dio_getname(int id) { - /* return pointer to a constant string describing the board with given ID */ + /* return pointer to a constant string describing the board with given ID */ unsigned int i; + for (i = 0; i < ARRAY_SIZE(names); i++) - if (names[i].id == id) - return names[i].name; + if (names[i].id == id) + return names[i].name; - return unknowndioname; + return unknowndioname; } #else @@ -122,10 +121,10 @@ int __init dio_find(int deviceid) void *va; unsigned long pa; - if (DIO_SCINHOLE(scode)) - continue; + if (DIO_SCINHOLE(scode)) + continue; - pa = dio_scodetophysaddr(scode); + pa = dio_scodetophysaddr(scode); if (!pa) continue; @@ -139,15 +138,15 @@ int __init dio_find(int deviceid) (unsigned char *)va + DIO_IDOFF, 1)) { if (scode >= DIOII_SCBASE) iounmap(va); - continue; /* no board present at that select code */ + continue; /* no board present at that select code */ } prid = DIO_ID(va); - if (DIO_NEEDSSECID(prid)) { - secid = DIO_SECID(va); - id = DIO_ENCODE_ID(prid, secid); - } else + if (DIO_NEEDSSECID(prid)) { + secid = DIO_SECID(va); + id = DIO_ENCODE_ID(prid, secid); + } else id = prid; if (id == deviceid) { @@ -175,7 +174,7 @@ static int __init dio_init(void) printk(KERN_INFO "Scanning for DIO devices...\n"); - /* Initialize the DIO bus */ + /* Initialize the DIO bus */ INIT_LIST_HEAD(&dio_bus.devices); dev_set_name(&dio_bus.dev, "dio"); error = device_register(&dio_bus.dev); @@ -190,14 +189,13 @@ static int __init dio_init(void) request_resource(&iomem_resource, &dio_bus.resources[i]); /* Register all devices */ - for (scode = 0; scode < DIO_SCMAX; ++scode) - { - u_char prid, secid = 0; /* primary, secondary ID bytes */ - u_char *va; + for (scode = 0; scode < DIO_SCMAX; ++scode) { + u_char prid, secid = 0; /* primary, secondary ID bytes */ + u_char *va; unsigned long pa; - - if (DIO_SCINHOLE(scode)) - continue; + + if (DIO_SCINHOLE(scode)) + continue; pa = dio_scodetophysaddr(scode); @@ -213,10 +211,10 @@ static int __init dio_init(void) (unsigned char *)va + DIO_IDOFF, 1)) { if (scode >= DIOII_SCBASE) iounmap(va); - continue; /* no board present at that select code */ + continue; /* no board present at that select code */ } - /* Found a board, allocate it an entry in the list */ + /* Found a board, allocate it an entry in the list */ dev = kzalloc(sizeof(struct dio_dev), GFP_KERNEL); if (!dev) return -ENOMEM; @@ -229,19 +227,19 @@ static int __init dio_init(void) dev->resource.end = pa + DIO_SIZE(scode, va); dev_set_name(&dev->dev, "%02x", scode); - /* read the ID byte(s) and encode if necessary. */ + /* read the ID byte(s) and encode if necessary. */ prid = DIO_ID(va); - if (DIO_NEEDSSECID(prid)) { - secid = DIO_SECID(va); - dev->id = DIO_ENCODE_ID(prid, secid); - } else - dev->id = prid; + if (DIO_NEEDSSECID(prid)) { + secid = DIO_SECID(va); + dev->id = DIO_ENCODE_ID(prid, secid); + } else + dev->id = prid; - dev->ipl = DIO_IPL(va); - strcpy(dev->name,dio_getname(dev->id)); + dev->ipl = DIO_IPL(va); + strcpy(dev->name, dio_getname(dev->id)); printk(KERN_INFO "select code %3d: ipl %d: ID %02X", dev->scode, dev->ipl, prid); - if (DIO_NEEDSSECID(prid)) + if (DIO_NEEDSSECID(prid)) printk(":%02X", secid); printk(": %s\n", dev->name); @@ -256,7 +254,7 @@ static int __init dio_init(void) error = dio_create_sysfs_dev_files(dev); if (error) dev_err(&dev->dev, "Error creating sysfs files\n"); - } + } return 0; } @@ -267,12 +265,12 @@ subsys_initcall(dio_init); */ unsigned long dio_scodetophysaddr(int scode) { - if (scode >= DIOII_SCBASE) { - return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE); - } else if (scode > DIO_SCMAX || scode < 0) - return 0; - else if (DIO_SCINHOLE(scode)) - return 0; - - return (DIO_BASE + scode * DIO_DEVSIZE); + if (scode >= DIOII_SCBASE) + return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE); + else if (scode > DIO_SCMAX || scode < 0) + return 0; + else if (DIO_SCINHOLE(scode)) + return 0; + + return (DIO_BASE + scode * DIO_DEVSIZE); } diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 931544c9f63d..983e07dc022e 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -21,7 +21,7 @@ config GOOGLE_SMI config GOOGLE_COREBOOT_TABLE tristate "Coreboot Table Access" - depends on ACPI || OF + depends on HAS_IOMEM && (ACPI || OF) help This option enables the coreboot_table module, which provides other firmware modules access to the coreboot table. The coreboot table diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 29c0a616b317..8177a0fae11d 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -306,6 +306,7 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, break; case COMMAND_RSU_RETRY: case COMMAND_RSU_MAX_RETRY: + case COMMAND_FIRMWARE_VERSION: cb_data->status = BIT(SVC_STATUS_OK); cb_data->kaddr1 = &res.a1; break; @@ -422,6 +423,11 @@ static int svc_normal_to_secure_thread(void *data) a1 = 0; a2 = 0; break; + case COMMAND_FIRMWARE_VERSION: + a0 = INTEL_SIP_SMC_FIRMWARE_VERSION; + a1 = 0; + a2 = 0; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -477,7 +483,7 @@ static int svc_normal_to_secure_thread(void *data) case INTEL_SIP_SMC_RSU_ERROR: pr_err("%s: STATUS_ERROR\n", __func__); cbdata->status = BIT(SVC_STATUS_ERROR); - cbdata->kaddr1 = NULL; + cbdata->kaddr1 = &res.a1; cbdata->kaddr2 = NULL; cbdata->kaddr3 = NULL; pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); @@ -491,7 +497,8 @@ static int svc_normal_to_secure_thread(void *data) */ if ((pdata->command == COMMAND_RSU_RETRY) || (pdata->command == COMMAND_RSU_MAX_RETRY) || - (pdata->command == COMMAND_RSU_NOTIFY)) { + (pdata->command == COMMAND_RSU_NOTIFY) || + (pdata->command == COMMAND_FIRMWARE_VERSION)) { cbdata->status = BIT(SVC_STATUS_NO_SUPPORT); cbdata->kaddr1 = NULL; diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c index 76c4abc42a30..bda8712bfd8c 100644 --- a/drivers/firmware/sysfb_simplefb.c +++ b/drivers/firmware/sysfb_simplefb.c @@ -113,16 +113,21 @@ __init int sysfb_create_simplefb(const struct screen_info *si, sysfb_apply_efi_quirks(pd); ret = platform_device_add_resources(pd, &res, 1); - if (ret) { - platform_device_put(pd); - return ret; - } + if (ret) + goto err_put_device; ret = platform_device_add_data(pd, mode, sizeof(*mode)); - if (ret) { - platform_device_put(pd); - return ret; - } + if (ret) + goto err_put_device; + + ret = platform_device_add(pd); + if (ret) + goto err_put_device; + + return 0; + +err_put_device: + platform_device_put(pd); - return platform_device_add(pd); + return ret; } diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 5e5b0bb2e4e0..f21ece56695e 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -42,6 +42,16 @@ static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); static struct platform_device *em_dev; /** + * struct zynqmp_devinfo - Structure for Zynqmp device instance + * @dev: Device Pointer + * @feature_conf_id: Feature conf id + */ +struct zynqmp_devinfo { + struct device *dev; + u32 feature_conf_id; +}; + +/** * struct pm_api_feature_data - PM API Feature data * @pm_api_id: PM API Id, used as key to index into hashmap * @feature_status: status of PM API feature: valid, invalid @@ -1183,6 +1193,33 @@ int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype) } /** + * zynqmp_pm_set_feature_config - PM call to request IOCTL for feature config + * @id: The config ID of the feature to be configured + * @value: The config value of the feature to be configured + * + * Return: Returns 0 on success or error value on failure. + */ +int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value) +{ + return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_FEATURE_CONFIG, + id, value, NULL); +} + +/** + * zynqmp_pm_get_feature_config - PM call to get value of configured feature + * @id: The config id of the feature to be queried + * @payload: Returned value array + * + * Return: Returns 0 on success or error value on failure. + */ +int zynqmp_pm_get_feature_config(enum pm_feature_config_id id, + u32 *payload) +{ + return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_GET_FEATURE_CONFIG, + id, 0, payload); +} + +/** * struct zynqmp_pm_shutdown_scope - Struct for shutdown scope * @subtype: Shutdown subtype * @name: Matching string for scope argument @@ -1450,6 +1487,78 @@ static DEVICE_ATTR_RW(pggs1); static DEVICE_ATTR_RW(pggs2); static DEVICE_ATTR_RW(pggs3); +static ssize_t feature_config_id_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct zynqmp_devinfo *devinfo = dev_get_drvdata(device); + + return sysfs_emit(buf, "%d\n", devinfo->feature_conf_id); +} + +static ssize_t feature_config_id_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 config_id; + int ret; + struct zynqmp_devinfo *devinfo = dev_get_drvdata(device); + + if (!buf) + return -EINVAL; + + ret = kstrtou32(buf, 10, &config_id); + if (ret) + return ret; + + devinfo->feature_conf_id = config_id; + + return count; +} + +static DEVICE_ATTR_RW(feature_config_id); + +static ssize_t feature_config_value_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + struct zynqmp_devinfo *devinfo = dev_get_drvdata(device); + + ret = zynqmp_pm_get_feature_config(devinfo->feature_conf_id, + ret_payload); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", ret_payload[1]); +} + +static ssize_t feature_config_value_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 value; + int ret; + struct zynqmp_devinfo *devinfo = dev_get_drvdata(device); + + if (!buf) + return -EINVAL; + + ret = kstrtou32(buf, 10, &value); + if (ret) + return ret; + + ret = zynqmp_pm_set_feature_config(devinfo->feature_conf_id, + value); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(feature_config_value); + static struct attribute *zynqmp_firmware_attrs[] = { &dev_attr_ggs0.attr, &dev_attr_ggs1.attr, @@ -1461,6 +1570,8 @@ static struct attribute *zynqmp_firmware_attrs[] = { &dev_attr_pggs3.attr, &dev_attr_shutdown_scope.attr, &dev_attr_health_status.attr, + &dev_attr_feature_config_id.attr, + &dev_attr_feature_config_value.attr, NULL, }; @@ -1470,6 +1581,7 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np; + struct zynqmp_devinfo *devinfo; int ret; np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); @@ -1486,6 +1598,14 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) if (ret) return ret; + devinfo = devm_kzalloc(dev, sizeof(*devinfo), GFP_KERNEL); + if (!devinfo) + return -ENOMEM; + + devinfo->dev = dev; + + platform_set_drvdata(pdev, devinfo); + /* Check PM API version number */ ret = zynqmp_pm_get_api_version(&pm_api_version); if (ret) diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c index 4d68719e608f..717ac9715970 100644 --- a/drivers/fpga/dfl-pci.c +++ b/drivers/fpga/dfl-pci.c @@ -15,6 +15,7 @@ */ #include <linux/pci.h> +#include <linux/dma-mapping.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/module.h> @@ -354,16 +355,10 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) pci_set_master(pcidev); - if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) { - ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64)); - if (ret) - goto disable_error_report_exit; - } else if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(32))) { - ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32)); - if (ret) - goto disable_error_report_exit; - } else { - ret = -EIO; + ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64)); + if (ret) + ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)); + if (ret) { dev_err(&pcidev->dev, "No suitable DMA support available.\n"); goto disable_error_report_exit; } diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 59ddc9fd5bca..3a7b78e36701 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -24,9 +24,6 @@ #include "fsi-master.h" -#define CREATE_TRACE_POINTS -#include <trace/events/fsi.h> - #define FSI_SLAVE_CONF_NEXT_MASK GENMASK(31, 31) #define FSI_SLAVE_CONF_SLOTS_MASK GENMASK(23, 16) #define FSI_SLAVE_CONF_SLOTS_SHIFT 16 @@ -95,6 +92,9 @@ struct fsi_slave { u8 t_echo_delay; }; +#define CREATE_TRACE_POINTS +#include <trace/events/fsi.h> + #define to_fsi_master(d) container_of(d, struct fsi_master, dev) #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev) @@ -524,6 +524,8 @@ static int fsi_slave_scan(struct fsi_slave *slave) dev->addr = engine_addr; dev->size = slots * engine_page_size; + trace_fsi_dev_init(dev); + dev_dbg(&slave->dev, "engine[%i]: type %x, version %x, addr %x size %x\n", dev->unit, dev->engine_type, version, @@ -1006,6 +1008,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) crc = crc4(0, cfam_id, 32); if (crc) { + trace_fsi_slave_invalid_cfam(master, link, cfam_id); dev_warn(&master->dev, "slave %02x:%02x invalid cfam id CRC!\n", link, id); return -EIO; @@ -1080,6 +1083,8 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) if (rc) goto err_free; + trace_fsi_slave_init(slave); + /* Create chardev for userspace access */ cdev_init(&slave->cdev, &cfam_fops); rc = cdev_device_add(&slave->cdev, &slave->dev); diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c index 8606e55c1721..7cec1772820d 100644 --- a/drivers/fsi/fsi-master-aspeed.c +++ b/drivers/fsi/fsi-master-aspeed.c @@ -449,11 +449,13 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att { struct fsi_master_aspeed *aspeed = dev_get_drvdata(dev); + trace_fsi_master_aspeed_cfam_reset(true); mutex_lock(&aspeed->lock); gpiod_set_value(aspeed->cfam_reset_gpio, 1); usleep_range(900, 1000); gpiod_set_value(aspeed->cfam_reset_gpio, 0); mutex_unlock(&aspeed->lock); + trace_fsi_master_aspeed_cfam_reset(false); return count; } @@ -542,25 +544,28 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev) return rc; } - aspeed = devm_kzalloc(&pdev->dev, sizeof(*aspeed), GFP_KERNEL); + aspeed = kzalloc(sizeof(*aspeed), GFP_KERNEL); if (!aspeed) return -ENOMEM; aspeed->dev = &pdev->dev; aspeed->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(aspeed->base)) - return PTR_ERR(aspeed->base); + if (IS_ERR(aspeed->base)) { + rc = PTR_ERR(aspeed->base); + goto err_free_aspeed; + } aspeed->clk = devm_clk_get(aspeed->dev, NULL); if (IS_ERR(aspeed->clk)) { dev_err(aspeed->dev, "couldn't get clock\n"); - return PTR_ERR(aspeed->clk); + rc = PTR_ERR(aspeed->clk); + goto err_free_aspeed; } rc = clk_prepare_enable(aspeed->clk); if (rc) { dev_err(aspeed->dev, "couldn't enable clock\n"); - return rc; + goto err_free_aspeed; } rc = setup_cfam_reset(aspeed); @@ -595,7 +600,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev) rc = opb_readl(aspeed, ctrl_base + FSI_MVER, &raw); if (rc) { dev_err(&pdev->dev, "failed to read hub version\n"); - return rc; + goto err_release; } reg = be32_to_cpu(raw); @@ -634,6 +639,8 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev) err_release: clk_disable_unprepare(aspeed->clk); +err_free_aspeed: + kfree(aspeed); return rc; } diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index 7eaab1be0aa4..c9cc75fbdfb9 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -451,6 +451,14 @@ static int occ_trigger_attn(struct occ *occ) return rc; } +static bool fsi_occ_response_not_ready(struct occ_response *resp, u8 seq_no, + u8 cmd_type) +{ + return resp->return_status == OCC_RESP_CMD_IN_PRG || + resp->return_status == OCC_RESP_CRIT_INIT || + resp->seq_no != seq_no || resp->cmd_type != cmd_type; +} + int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, void *response, size_t *resp_len) { @@ -461,10 +469,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, struct occ_response *resp = response; size_t user_resp_len = *resp_len; u8 seq_no; + u8 cmd_type; u16 checksum = 0; u16 resp_data_length; const u8 *byte_request = (const u8 *)request; - unsigned long start; + unsigned long end; int rc; size_t i; @@ -478,6 +487,8 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, return -EINVAL; } + cmd_type = byte_request[1]; + /* Checksum the request, ignoring first byte (sequence number). */ for (i = 1; i < req_len - 2; ++i) checksum += byte_request[i]; @@ -509,51 +520,61 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, if (rc) goto done; - /* Read occ response header */ - start = jiffies; - do { + end = jiffies + timeout; + while (true) { + /* Read occ response header */ rc = occ_getsram(occ, 0, resp, 8); if (rc) goto done; - if (resp->return_status == OCC_RESP_CMD_IN_PRG || - resp->return_status == OCC_RESP_CRIT_INIT || - resp->seq_no != seq_no) { - rc = -ETIMEDOUT; - - if (time_after(jiffies, start + timeout)) { - dev_err(occ->dev, "resp timeout status=%02x " - "resp seq_no=%d our seq_no=%d\n", + if (fsi_occ_response_not_ready(resp, seq_no, cmd_type)) { + if (time_after(jiffies, end)) { + dev_err(occ->dev, + "resp timeout status=%02x seq=%d cmd=%d, our seq=%d cmd=%d\n", resp->return_status, resp->seq_no, - seq_no); + resp->cmd_type, seq_no, cmd_type); + rc = -ETIMEDOUT; goto done; } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(wait_time); - } - } while (rc); - - /* Extract size of response data */ - resp_data_length = get_unaligned_be16(&resp->data_length); + } else { + /* Extract size of response data */ + resp_data_length = + get_unaligned_be16(&resp->data_length); + + /* + * Message size is data length + 5 bytes header + 2 + * bytes checksum + */ + if ((resp_data_length + 7) > user_resp_len) { + rc = -EMSGSIZE; + goto done; + } - /* Message size is data length + 5 bytes header + 2 bytes checksum */ - if ((resp_data_length + 7) > user_resp_len) { - rc = -EMSGSIZE; - goto done; + /* + * Get the entire response including the header again, + * in case it changed + */ + if (resp_data_length > 1) { + rc = occ_getsram(occ, 0, resp, + resp_data_length + 7); + if (rc) + goto done; + + if (!fsi_occ_response_not_ready(resp, seq_no, + cmd_type)) + break; + } else { + break; + } + } } dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n", resp->return_status, resp_data_length); - /* Grab the rest */ - if (resp_data_length > 1) { - /* already got 3 bytes resp, also need 2 bytes checksum */ - rc = occ_getsram(occ, 8, &resp->data[3], resp_data_length - 1); - if (rc) - goto done; - } - occ->client_response_size = resp_data_length + 7; rc = occ_verify_checksum(occ, resp, resp_data_length); @@ -598,7 +619,11 @@ static int occ_probe(struct platform_device *pdev) occ->version = (uintptr_t)of_device_get_match_data(dev); occ->dev = dev; occ->sbefifo = dev->parent; - occ->sequence_number = 1; + /* + * Quickly derive a pseudo-random number from jiffies so that + * re-probing the driver doesn't accidentally overlap sequence numbers. + */ + occ->sequence_number = (u8)((jiffies % 0xff) + 1); mutex_init(&occ->occ_lock); if (dev->of_node) { diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index 52328adef643..f52a912cdf16 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -32,6 +32,8 @@ #include <linux/vmalloc.h> #include <linux/mm.h> +#include <uapi/linux/fsi.h> + /* * The SBEFIFO is a pipe-like FSI device for communicating with * the self boot engine on POWER processors. @@ -125,6 +127,7 @@ struct sbefifo { bool dead; bool async_ffdc; bool timed_out; + u32 timeout_start_rsp_ms; }; struct sbefifo_user { @@ -133,6 +136,7 @@ struct sbefifo_user { void *cmd_page; void *pending_cmd; size_t pending_len; + u32 read_timeout_ms; }; static DEFINE_MUTEX(sbefifo_ffdc_mutex); @@ -549,7 +553,7 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo dev_vdbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response)); - timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_RSP); + timeout = msecs_to_jiffies(sbefifo->timeout_start_rsp_ms); for (;;) { /* Grab FIFO status (this will handle parity errors) */ rc = sbefifo_wait(sbefifo, false, &status, timeout); @@ -795,6 +799,7 @@ static int sbefifo_user_open(struct inode *inode, struct file *file) return -ENOMEM; } mutex_init(&user->file_lock); + user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP; return 0; } @@ -837,7 +842,9 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf, rc = mutex_lock_interruptible(&sbefifo->lock); if (rc) goto bail; + sbefifo->timeout_start_rsp_ms = user->read_timeout_ms; rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter); + sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP; mutex_unlock(&sbefifo->lock); if (rc < 0) goto bail; @@ -927,12 +934,55 @@ static int sbefifo_user_release(struct inode *inode, struct file *file) return 0; } +static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp) +{ + struct device *dev = &user->sbefifo->dev; + u32 timeout; + + if (get_user(timeout, (__u32 __user *)argp)) + return -EFAULT; + + if (timeout == 0) { + user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP; + dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms); + return 0; + } + + if (timeout < 10 || timeout > 120) + return -EINVAL; + + user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */ + + dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms); + + return 0; +} + +static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sbefifo_user *user = file->private_data; + int rc = -ENOTTY; + + if (!user) + return -EINVAL; + + mutex_lock(&user->file_lock); + switch (cmd) { + case FSI_SBEFIFO_READ_TIMEOUT_SECONDS: + rc = sbefifo_read_timeout(user, (void __user *)arg); + break; + } + mutex_unlock(&user->file_lock); + return rc; +} + static const struct file_operations sbefifo_fops = { .owner = THIS_MODULE, .open = sbefifo_user_open, .read = sbefifo_user_read, .write = sbefifo_user_write, .release = sbefifo_user_release, + .unlocked_ioctl = sbefifo_user_ioctl, }; static void sbefifo_free(struct device *dev) @@ -972,6 +1022,7 @@ static int sbefifo_probe(struct device *dev) sbefifo->fsi_dev = fsi_dev; dev_set_drvdata(dev, sbefifo); mutex_init(&sbefifo->lock); + sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP; /* * Try cleaning up the FIFO. If this fails, we still register the diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c index da1486bb6a14..bcb756dc9866 100644 --- a/drivers/fsi/fsi-scom.c +++ b/drivers/fsi/fsi-scom.c @@ -145,7 +145,7 @@ static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value, uint64_t addr, uint32_t *status) { uint64_t ind_data, ind_addr; - int rc, retries, err = 0; + int rc, err; if (value & ~XSCOM_DATA_IND_DATA) return -EINVAL; @@ -156,19 +156,14 @@ static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value, if (rc || (*status & SCOM_STATUS_ANY_ERR)) return rc; - for (retries = 0; retries < SCOM_MAX_IND_RETRIES; retries++) { - rc = __get_scom(scom, &ind_data, addr, status); - if (rc || (*status & SCOM_STATUS_ANY_ERR)) - return rc; + rc = __get_scom(scom, &ind_data, addr, status); + if (rc || (*status & SCOM_STATUS_ANY_ERR)) + return rc; - err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT; - *status = err << SCOM_STATUS_PIB_RESP_SHIFT; - if ((ind_data & XSCOM_DATA_IND_COMPLETE) || (err != SCOM_PIB_BLOCKED)) - return 0; + err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT; + *status = err << SCOM_STATUS_PIB_RESP_SHIFT; - msleep(1); - } - return rc; + return 0; } static int put_indirect_scom_form1(struct scom_device *scom, uint64_t value, @@ -188,7 +183,7 @@ static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value, uint64_t addr, uint32_t *status) { uint64_t ind_data, ind_addr; - int rc, retries, err = 0; + int rc, err; ind_addr = addr & XSCOM_ADDR_DIRECT_PART; ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | XSCOM_DATA_IND_READ; @@ -196,21 +191,15 @@ static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value, if (rc || (*status & SCOM_STATUS_ANY_ERR)) return rc; - for (retries = 0; retries < SCOM_MAX_IND_RETRIES; retries++) { - rc = __get_scom(scom, &ind_data, addr, status); - if (rc || (*status & SCOM_STATUS_ANY_ERR)) - return rc; - - err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT; - *status = err << SCOM_STATUS_PIB_RESP_SHIFT; - *value = ind_data & XSCOM_DATA_IND_DATA; + rc = __get_scom(scom, &ind_data, addr, status); + if (rc || (*status & SCOM_STATUS_ANY_ERR)) + return rc; - if ((ind_data & XSCOM_DATA_IND_COMPLETE) || (err != SCOM_PIB_BLOCKED)) - return 0; + err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT; + *status = err << SCOM_STATUS_PIB_RESP_SHIFT; + *value = ind_data & XSCOM_DATA_IND_DATA; - msleep(1); - } - return rc; + return 0; } static int raw_put_scom(struct scom_device *scom, uint64_t value, @@ -289,7 +278,7 @@ static int put_scom(struct scom_device *scom, uint64_t value, int rc; rc = raw_put_scom(scom, value, addr, &status); - if (rc == -ENODEV) + if (rc) return rc; rc = handle_fsi2pib_status(scom, status); @@ -308,7 +297,7 @@ static int get_scom(struct scom_device *scom, uint64_t *value, int rc; rc = raw_get_scom(scom, value, addr, &status); - if (rc == -ENODEV) + if (rc) return rc; rc = handle_fsi2pib_status(scom, status); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b2d313fe3fc5..45764ec3b2eb 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1356,6 +1356,18 @@ config GPIO_TIMBERDALE help Add support for the GPIO IP in the timberdale FPGA. +config GPIO_TN48M_CPLD + tristate "Delta Networks TN48M switch CPLD GPIO driver" + depends on MFD_TN48M_CPLD + select GPIO_REGMAP + help + This enables support for the GPIOs found on the Delta + Networks TN48M switch Lattice CPLD. It provides 12 pins in total, + they are input-only or output-only type. + + This driver can also be built as a module. If so, the + module will be called gpio-tn48m. + config GPIO_TPS65086 tristate "TI TPS65086 GPO" depends on MFD_TPS65086 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1a14e248bdbd..14352f6dfe8e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -149,6 +149,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o +obj-$(CONFIG_GPIO_TN48M_CPLD) += gpio-tn48m.o obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o diff --git a/drivers/gpio/gpio-tn48m.c b/drivers/gpio/gpio-tn48m.c new file mode 100644 index 000000000000..cd4a80b22794 --- /dev/null +++ b/drivers/gpio/gpio-tn48m.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Delta TN48M CPLD GPIO driver + * + * Copyright (C) 2021 Sartura Ltd. + * + * Author: Robert Marko <robert.marko@sartura.hr> + */ + +#include <linux/device.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +enum tn48m_gpio_type { + TN48M_GP0 = 1, + TN48M_GPI, +}; + +struct tn48m_gpio_config { + int ngpio; + int ngpio_per_reg; + enum tn48m_gpio_type type; +}; + +static const struct tn48m_gpio_config tn48m_gpo_config = { + .ngpio = 4, + .ngpio_per_reg = 4, + .type = TN48M_GP0, +}; + +static const struct tn48m_gpio_config tn48m_gpi_config = { + .ngpio = 4, + .ngpio_per_reg = 4, + .type = TN48M_GPI, +}; + +static int tn48m_gpio_probe(struct platform_device *pdev) +{ + const struct tn48m_gpio_config *gpio_config; + struct gpio_regmap_config config = {}; + struct regmap *regmap; + u32 base; + int ret; + + if (!pdev->dev.parent) + return -ENODEV; + + gpio_config = device_get_match_data(&pdev->dev); + if (!gpio_config) + return -ENODEV; + + ret = device_property_read_u32(&pdev->dev, "reg", &base); + if (ret) + return ret; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) + return -ENODEV; + + config.regmap = regmap; + config.parent = &pdev->dev; + config.ngpio = gpio_config->ngpio; + config.ngpio_per_reg = gpio_config->ngpio_per_reg; + switch (gpio_config->type) { + case TN48M_GP0: + config.reg_set_base = base; + break; + case TN48M_GPI: + config.reg_dat_base = base; + break; + default: + return -EINVAL; + } + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config)); +} + +static const struct of_device_id tn48m_gpio_of_match[] = { + { .compatible = "delta,tn48m-gpo", .data = &tn48m_gpo_config }, + { .compatible = "delta,tn48m-gpi", .data = &tn48m_gpi_config }, + { } +}; +MODULE_DEVICE_TABLE(of, tn48m_gpio_of_match); + +static struct platform_driver tn48m_gpio_driver = { + .driver = { + .name = "delta-tn48m-gpio", + .of_match_table = tn48m_gpio_of_match, + }, + .probe = tn48m_gpio_probe, +}; +module_platform_driver(tn48m_gpio_driver); + +MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); +MODULE_DESCRIPTION("Delta TN48M CPLD GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/greybus/svc.c b/drivers/greybus/svc.c index ce7740ef449b..56d2b44d6fef 100644 --- a/drivers/greybus/svc.c +++ b/drivers/greybus/svc.c @@ -861,16 +861,26 @@ static int gb_svc_hello(struct gb_operation *op) ret = gb_svc_watchdog_create(svc); if (ret) { dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); - goto err_unregister_device; + goto err_deregister_svc; } + /* + * FIXME: This is a temporary hack to reconfigure the link at HELLO + * (which abuses the deferred request processing mechanism). + */ + ret = gb_svc_queue_deferred_request(op); + if (ret) + goto err_destroy_watchdog; + gb_svc_debugfs_init(svc); - return gb_svc_queue_deferred_request(op); + return 0; -err_unregister_device: +err_destroy_watchdog: gb_svc_watchdog_destroy(svc); +err_deregister_svc: device_del(&svc->dev); + return ret; } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index db8bc55e5f50..68a8a27ab3b7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1555,6 +1555,8 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. +source "drivers/hwmon/peci/Kconfig" + source "drivers/hwmon/pmbus/Kconfig" config SENSORS_PWM_FAN diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 7fd5e94d88f6..8a03289e2aa4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -211,6 +211,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o obj-$(CONFIG_SENSORS_OCC) += occ/ +obj-$(CONFIG_SENSORS_PECI) += peci/ obj-$(CONFIG_PMBUS) += pmbus/ ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig new file mode 100644 index 000000000000..9d32a57badfe --- /dev/null +++ b/drivers/hwmon/peci/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SENSORS_PECI_CPUTEMP + tristate "PECI CPU temperature monitoring client" + depends on PECI + select SENSORS_PECI + select PECI_CPU + help + If you say yes here you get support for the generic Intel PECI + cputemp driver which provides Digital Thermal Sensor (DTS) thermal + readings of the CPU package and CPU cores that are accessible via + the processor PECI interface. + + This driver can also be built as a module. If so, the module + will be called peci-cputemp. + +config SENSORS_PECI_DIMMTEMP + tristate "PECI DIMM temperature monitoring client" + depends on PECI + select SENSORS_PECI + select PECI_CPU + help + If you say yes here you get support for the generic Intel PECI hwmon + driver which provides Temperature Sensor on DIMM readings that are + accessible via the processor PECI interface. + + This driver can also be built as a module. If so, the module + will be called peci-dimmtemp. + +config SENSORS_PECI + tristate diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile new file mode 100644 index 000000000000..191cfa0227f3 --- /dev/null +++ b/drivers/hwmon/peci/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only + +peci-cputemp-y := cputemp.o +peci-dimmtemp-y := dimmtemp.o + +obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h new file mode 100644 index 000000000000..734506b0eca2 --- /dev/null +++ b/drivers/hwmon/peci/common.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2021 Intel Corporation */ + +#include <linux/mutex.h> +#include <linux/types.h> + +#ifndef __PECI_HWMON_COMMON_H +#define __PECI_HWMON_COMMON_H + +#define PECI_HWMON_UPDATE_INTERVAL HZ + +/** + * struct peci_sensor_state - PECI state information + * @valid: flag to indicate the sensor value is valid + * @last_updated: time of the last update in jiffies + * @lock: mutex to protect sensor access + */ +struct peci_sensor_state { + bool valid; + unsigned long last_updated; + struct mutex lock; /* protect sensor access */ +}; + +/** + * struct peci_sensor_data - PECI sensor information + * @value: sensor value in milli units + * @state: sensor update state + */ + +struct peci_sensor_data { + s32 value; + struct peci_sensor_state state; +}; + +/** + * peci_sensor_need_update() - check whether sensor update is needed or not + * @sensor: pointer to sensor data struct + * + * Return: true if update is needed, false if not. + */ + +static inline bool peci_sensor_need_update(struct peci_sensor_state *state) +{ + return !state->valid || + time_after(jiffies, state->last_updated + PECI_HWMON_UPDATE_INTERVAL); +} + +/** + * peci_sensor_mark_updated() - mark the sensor is updated + * @sensor: pointer to sensor data struct + */ +static inline void peci_sensor_mark_updated(struct peci_sensor_state *state) +{ + state->valid = true; + state->last_updated = jiffies; +} + +#endif /* __PECI_HWMON_COMMON_H */ diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c new file mode 100644 index 000000000000..12156328f5cf --- /dev/null +++ b/drivers/hwmon/peci/cputemp.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/peci.h> +#include <linux/peci-cpu.h> +#include <linux/units.h> + +#include "common.h" + +#define CORE_NUMS_MAX 64 + +#define BASE_CHANNEL_NUMS 5 +#define CPUTEMP_CHANNEL_NUMS (BASE_CHANNEL_NUMS + CORE_NUMS_MAX) + +#define TEMP_TARGET_FAN_TEMP_MASK GENMASK(15, 8) +#define TEMP_TARGET_REF_TEMP_MASK GENMASK(23, 16) +#define TEMP_TARGET_TJ_OFFSET_MASK GENMASK(29, 24) + +#define DTS_MARGIN_MASK GENMASK(15, 0) +#define PCS_MODULE_TEMP_MASK GENMASK(15, 0) + +struct resolved_cores_reg { + u8 bus; + u8 dev; + u8 func; + u8 offset; +}; + +struct cpu_info { + struct resolved_cores_reg *reg; + u8 min_peci_revision; + s32 (*thermal_margin_to_millidegree)(u16 val); +}; + +struct peci_temp_target { + s32 tcontrol; + s32 tthrottle; + s32 tjmax; + struct peci_sensor_state state; +}; + +enum peci_temp_target_type { + tcontrol_type, + tthrottle_type, + tjmax_type, + crit_hyst_type, +}; + +struct peci_cputemp { + struct peci_device *peci_dev; + struct device *dev; + const char *name; + const struct cpu_info *gen_info; + struct { + struct peci_temp_target target; + struct peci_sensor_data die; + struct peci_sensor_data dts; + struct peci_sensor_data core[CORE_NUMS_MAX]; + } temp; + const char **coretemp_label; + DECLARE_BITMAP(core_mask, CORE_NUMS_MAX); +}; + +enum cputemp_channels { + channel_die, + channel_dts, + channel_tcontrol, + channel_tthrottle, + channel_tjmax, + channel_core, +}; + +static const char * const cputemp_label[BASE_CHANNEL_NUMS] = { + "Die", + "DTS", + "Tcontrol", + "Tthrottle", + "Tjmax", +}; + +static int update_temp_target(struct peci_cputemp *priv) +{ + s32 tthrottle_offset, tcontrol_margin; + u32 pcs; + int ret; + + if (!peci_sensor_need_update(&priv->temp.target.state)) + return 0; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs); + if (ret) + return ret; + + priv->temp.target.tjmax = + FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * MILLIDEGREE_PER_DEGREE; + + tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs); + tcontrol_margin = sign_extend32(tcontrol_margin, 7) * MILLIDEGREE_PER_DEGREE; + priv->temp.target.tcontrol = priv->temp.target.tjmax - tcontrol_margin; + + tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * MILLIDEGREE_PER_DEGREE; + priv->temp.target.tthrottle = priv->temp.target.tjmax - tthrottle_offset; + + peci_sensor_mark_updated(&priv->temp.target.state); + + return 0; +} + +static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type type, long *val) +{ + int ret; + + mutex_lock(&priv->temp.target.state.lock); + + ret = update_temp_target(priv); + if (ret) + goto unlock; + + switch (type) { + case tcontrol_type: + *val = priv->temp.target.tcontrol; + break; + case tthrottle_type: + *val = priv->temp.target.tthrottle; + break; + case tjmax_type: + *val = priv->temp.target.tjmax; + break; + case crit_hyst_type: + *val = priv->temp.target.tjmax - priv->temp.target.tcontrol; + break; + default: + ret = -EOPNOTSUPP; + break; + } +unlock: + mutex_unlock(&priv->temp.target.state.lock); + + return ret; +} + +/* + * Error codes: + * 0x8000: General sensor error + * 0x8001: Reserved + * 0x8002: Underflow on reading value + * 0x8003-0x81ff: Reserved + */ +static bool dts_valid(u16 val) +{ + return val < 0x8000 || val > 0x81ff; +} + +/* + * Processors return a value of DTS reading in S10.6 fixed point format + * (16 bits: 10-bit signed magnitude, 6-bit fraction). + */ +static s32 dts_ten_dot_six_to_millidegree(u16 val) +{ + return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 64; +} + +/* + * For older processors, thermal margin reading is returned in S8.8 fixed + * point format (16 bits: 8-bit signed magnitude, 8-bit fraction). + */ +static s32 dts_eight_dot_eight_to_millidegree(u16 val) +{ + return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 256; +} + +static int get_die_temp(struct peci_cputemp *priv, long *val) +{ + int ret = 0; + long tjmax; + u16 temp; + + mutex_lock(&priv->temp.die.state.lock); + if (!peci_sensor_need_update(&priv->temp.die.state)) + goto skip_update; + + ret = peci_temp_read(priv->peci_dev, &temp); + if (ret) + goto err_unlock; + + if (!dts_valid(temp)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tjmax_type, &tjmax); + if (ret) + goto err_unlock; + + priv->temp.die.value = (s32)tjmax + dts_ten_dot_six_to_millidegree(temp); + + peci_sensor_mark_updated(&priv->temp.die.state); + +skip_update: + *val = priv->temp.die.value; +err_unlock: + mutex_unlock(&priv->temp.die.state.lock); + return ret; +} + +static int get_dts(struct peci_cputemp *priv, long *val) +{ + int ret = 0; + u16 thermal_margin; + long tcontrol; + u32 pcs; + + mutex_lock(&priv->temp.dts.state.lock); + if (!peci_sensor_need_update(&priv->temp.dts.state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs); + if (ret) + goto err_unlock; + + thermal_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); + if (!dts_valid(thermal_margin)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tcontrol_type, &tcontrol); + if (ret) + goto err_unlock; + + /* Note that the tcontrol should be available before calling it */ + priv->temp.dts.value = + (s32)tcontrol - priv->gen_info->thermal_margin_to_millidegree(thermal_margin); + + peci_sensor_mark_updated(&priv->temp.dts.state); + +skip_update: + *val = priv->temp.dts.value; +err_unlock: + mutex_unlock(&priv->temp.dts.state.lock); + return ret; +} + +static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) +{ + int ret = 0; + u16 core_dts_margin; + long tjmax; + u32 pcs; + + mutex_lock(&priv->temp.core[core_index].state.lock); + if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs); + if (ret) + goto err_unlock; + + core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); + if (!dts_valid(core_dts_margin)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tjmax_type, &tjmax); + if (ret) + goto err_unlock; + + /* Note that the tjmax should be available before calling it */ + priv->temp.core[core_index].value = + (s32)tjmax + dts_ten_dot_six_to_millidegree(core_dts_margin); + + peci_sensor_mark_updated(&priv->temp.core[core_index].state); + +skip_update: + *val = priv->temp.core[core_index].value; +err_unlock: + mutex_unlock(&priv->temp.core[core_index].state.lock); + return ret; +} + +static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct peci_cputemp *priv = dev_get_drvdata(dev); + + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = channel < channel_core ? + cputemp_label[channel] : priv->coretemp_label[channel - channel_core]; + + return 0; +} + +static int cputemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct peci_cputemp *priv = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case channel_die: + return get_die_temp(priv, val); + case channel_dts: + return get_dts(priv, val); + case channel_tcontrol: + return get_temp_target(priv, tcontrol_type, val); + case channel_tthrottle: + return get_temp_target(priv, tthrottle_type, val); + case channel_tjmax: + return get_temp_target(priv, tjmax_type, val); + default: + return get_core_temp(priv, channel - channel_core, val); + } + break; + case hwmon_temp_max: + return get_temp_target(priv, tcontrol_type, val); + case hwmon_temp_crit: + return get_temp_target(priv, tjmax_type, val); + case hwmon_temp_crit_hyst: + return get_temp_target(priv, crit_hyst_type, val); + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct peci_cputemp *priv = data; + + if (channel > CPUTEMP_CHANNEL_NUMS) + return 0; + + if (channel < channel_core) + return 0444; + + if (test_bit(channel - channel_core, priv->core_mask)) + return 0444; + + return 0; +} + +static int init_core_mask(struct peci_cputemp *priv) +{ + struct peci_device *peci_dev = priv->peci_dev; + struct resolved_cores_reg *reg = priv->gen_info->reg; + u64 core_mask; + u32 data; + int ret; + + /* Get the RESOLVED_CORES register value */ + switch (peci_dev->info.model) { + case INTEL_FAM6_ICELAKE_X: + case INTEL_FAM6_ICELAKE_D: + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, + reg->func, reg->offset + 4, &data); + if (ret) + return ret; + + core_mask = (u64)data << 32; + + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, + reg->func, reg->offset, &data); + if (ret) + return ret; + + core_mask |= data; + + break; + default: + ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev, + reg->func, reg->offset, &data); + if (ret) + return ret; + + core_mask = data; + + break; + } + + if (!core_mask) + return -EIO; + + bitmap_from_u64(priv->core_mask, core_mask); + + return 0; +} + +static int create_temp_label(struct peci_cputemp *priv) +{ + unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX); + int i; + + priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL); + if (!priv->coretemp_label) + return -ENOMEM; + + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) { + priv->coretemp_label[i] = devm_kasprintf(priv->dev, GFP_KERNEL, "Core %d", i); + if (!priv->coretemp_label[i]) + return -ENOMEM; + } + + return 0; +} + +static void check_resolved_cores(struct peci_cputemp *priv) +{ + /* + * Failure to resolve cores is non-critical, we're still able to + * provide other sensor data. + */ + + if (init_core_mask(priv)) + return; + + if (create_temp_label(priv)) + bitmap_zero(priv->core_mask, CORE_NUMS_MAX); +} + +static void sensor_init(struct peci_cputemp *priv) +{ + int i; + + mutex_init(&priv->temp.target.state.lock); + mutex_init(&priv->temp.die.state.lock); + mutex_init(&priv->temp.dts.state.lock); + + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) + mutex_init(&priv->temp.core[i].state.lock); +} + +static const struct hwmon_ops peci_cputemp_ops = { + .is_visible = cputemp_is_visible, + .read_string = cputemp_read_string, + .read = cputemp_read, +}; + +static const u32 peci_cputemp_temp_channel_config[] = { + /* Die temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST, + /* DTS margin */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST, + /* Tcontrol temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, + /* Tthrottle temperature */ + HWMON_T_LABEL | HWMON_T_INPUT, + /* Tjmax temperature */ + HWMON_T_LABEL | HWMON_T_INPUT, + /* Core temperature - for all core channels */ + [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info peci_cputemp_temp_channel = { + .type = hwmon_temp, + .config = peci_cputemp_temp_channel_config, +}; + +static const struct hwmon_channel_info *peci_cputemp_info[] = { + &peci_cputemp_temp_channel, + NULL +}; + +static const struct hwmon_chip_info peci_cputemp_chip_info = { + .ops = &peci_cputemp_ops, + .info = peci_cputemp_info, +}; + +static int peci_cputemp_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct peci_device *peci_dev = to_peci_device(dev->parent); + struct peci_cputemp *priv; + struct device *hwmon_dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d", + peci_dev->info.socket_id); + if (!priv->name) + return -ENOMEM; + + priv->dev = dev; + priv->peci_dev = peci_dev; + priv->gen_info = (const struct cpu_info *)id->driver_data; + + /* + * This is just a sanity check. Since we're using commands that are + * guaranteed to be supported on a given platform, we should never see + * revision lower than expected. + */ + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) + dev_warn(priv->dev, + "Unexpected PECI revision %#x, some features may be unavailable\n", + peci_dev->info.peci_revision); + + check_resolved_cores(priv); + + sensor_init(priv); + + hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, + priv, &peci_cputemp_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* + * RESOLVED_CORES PCI configuration register may have different location on + * different platforms. + */ +static struct resolved_cores_reg resolved_cores_reg_hsx = { + .bus = 1, + .dev = 30, + .func = 3, + .offset = 0xb4, +}; + +static struct resolved_cores_reg resolved_cores_reg_icx = { + .bus = 14, + .dev = 30, + .func = 3, + .offset = 0xd0, +}; + +static const struct cpu_info cpu_hsx = { + .reg = &resolved_cores_reg_hsx, + .min_peci_revision = 0x33, + .thermal_margin_to_millidegree = &dts_eight_dot_eight_to_millidegree, +}; + +static const struct cpu_info cpu_icx = { + .reg = &resolved_cores_reg_icx, + .min_peci_revision = 0x40, + .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, +}; + +static const struct auxiliary_device_id peci_cputemp_ids[] = { + { + .name = "peci_cpu.cputemp.hsx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.bdx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.bdxd", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.skx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.icx", + .driver_data = (kernel_ulong_t)&cpu_icx, + }, + { + .name = "peci_cpu.cputemp.icxd", + .driver_data = (kernel_ulong_t)&cpu_icx, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); + +static struct auxiliary_driver peci_cputemp_driver = { + .probe = peci_cputemp_probe, + .id_table = peci_cputemp_ids, +}; + +module_auxiliary_driver(peci_cputemp_driver); + +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); +MODULE_DESCRIPTION("PECI cputemp driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI_CPU); diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c new file mode 100644 index 000000000000..c8222354c005 --- /dev/null +++ b/drivers/hwmon/peci/dimmtemp.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/peci.h> +#include <linux/peci-cpu.h> +#include <linux/units.h> +#include <linux/workqueue.h> + +#include "common.h" + +#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000) + +/* Max number of channel ranks and DIMM index per channel */ +#define CHAN_RANK_MAX_ON_HSX 8 +#define DIMM_IDX_MAX_ON_HSX 3 +#define CHAN_RANK_MAX_ON_BDX 4 +#define DIMM_IDX_MAX_ON_BDX 3 +#define CHAN_RANK_MAX_ON_BDXD 2 +#define DIMM_IDX_MAX_ON_BDXD 2 +#define CHAN_RANK_MAX_ON_SKX 6 +#define DIMM_IDX_MAX_ON_SKX 2 +#define CHAN_RANK_MAX_ON_ICX 8 +#define DIMM_IDX_MAX_ON_ICX 2 +#define CHAN_RANK_MAX_ON_ICXD 4 +#define DIMM_IDX_MAX_ON_ICXD 2 + +#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX +#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX +#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX) + +#define CPU_SEG_MASK GENMASK(23, 16) +#define GET_CPU_SEG(x) (((x) & CPU_SEG_MASK) >> 16) +#define CPU_BUS_MASK GENMASK(7, 0) +#define GET_CPU_BUS(x) ((x) & CPU_BUS_MASK) + +#define DIMM_TEMP_MAX GENMASK(15, 8) +#define DIMM_TEMP_CRIT GENMASK(23, 16) +#define GET_TEMP_MAX(x) (((x) & DIMM_TEMP_MAX) >> 8) +#define GET_TEMP_CRIT(x) (((x) & DIMM_TEMP_CRIT) >> 16) + +#define NO_DIMM_RETRY_COUNT_MAX 5 + +struct peci_dimmtemp; + +struct dimm_info { + int chan_rank_max; + int dimm_idx_max; + u8 min_peci_revision; + int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order, + int chan_rank, u32 *data); +}; + +struct peci_dimm_thresholds { + long temp_max; + long temp_crit; + struct peci_sensor_state state; +}; + +enum peci_dimm_threshold_type { + temp_max_type, + temp_crit_type, +}; + +struct peci_dimmtemp { + struct peci_device *peci_dev; + struct device *dev; + const char *name; + const struct dimm_info *gen_info; + struct delayed_work detect_work; + struct { + struct peci_sensor_data temp; + struct peci_dimm_thresholds thresholds; + } dimm[DIMM_NUMS_MAX]; + char **dimmtemp_label; + DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); + u8 no_dimm_retry_count; +}; + +static u8 __dimm_temp(u32 reg, int dimm_order) +{ + return (reg >> (dimm_order * 8)) & 0xff; +} + +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val) +{ + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + int ret = 0; + u32 data; + + mutex_lock(&priv->dimm[dimm_no].temp.state.lock); + if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data); + if (ret) + goto unlock; + + priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE; + + peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); + +skip_update: + *val = priv->dimm[dimm_no].temp.value; +unlock: + mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); + return ret; +} + +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) +{ + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + u32 data; + int ret; + + if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state)) + return 0; + + ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data); + if (ret == -ENODATA) /* Use default or previous value */ + return 0; + if (ret) + return ret; + + priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * MILLIDEGREE_PER_DEGREE; + priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * MILLIDEGREE_PER_DEGREE; + + peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); + + return 0; +} + +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_threshold_type type, + int dimm_no, long *val) +{ + int ret; + + mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); + ret = update_thresholds(priv, dimm_no); + if (ret) + goto unlock; + + switch (type) { + case temp_max_type: + *val = priv->dimm[dimm_no].thresholds.temp_max; + break; + case temp_crit_type: + *val = priv->dimm[dimm_no].thresholds.temp_crit; + break; + default: + ret = -EOPNOTSUPP; + break; + } +unlock: + mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); + + return ret; +} + +static int dimmtemp_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct peci_dimmtemp *priv = dev_get_drvdata(dev); + + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = (const char *)priv->dimmtemp_label[channel]; + + return 0; +} + +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct peci_dimmtemp *priv = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + return get_dimm_temp(priv, channel, val); + case hwmon_temp_max: + return get_dimm_thresholds(priv, temp_max_type, channel, val); + case hwmon_temp_crit: + return get_dimm_thresholds(priv, temp_crit_type, channel, val); + default: + break; + } + + return -EOPNOTSUPP; +} + +static umode_t dimmtemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct peci_dimmtemp *priv = data; + + if (test_bit(channel, priv->dimm_mask)) + return 0444; + + return 0; +} + +static const struct hwmon_ops peci_dimmtemp_ops = { + .is_visible = dimmtemp_is_visible, + .read_string = dimmtemp_read_string, + .read = dimmtemp_read, +}; + +static int check_populated_dimms(struct peci_dimmtemp *priv) +{ + int chan_rank_max = priv->gen_info->chan_rank_max; + int dimm_idx_max = priv->gen_info->dimm_idx_max; + u32 chan_rank_empty = 0; + u64 dimm_mask = 0; + int chan_rank, dimm_idx, ret; + u32 pcs; + + BUILD_BUG_ON(BITS_PER_TYPE(chan_rank_empty) < CHAN_RANK_MAX); + BUILD_BUG_ON(BITS_PER_TYPE(dimm_mask) < DIMM_NUMS_MAX); + if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) { + WARN_ONCE(1, "Unsupported number of DIMMs - chan_rank_max: %d, dimm_idx_max: %d", + chan_rank_max, dimm_idx_max); + return -EINVAL; + } + + for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &pcs); + if (ret) { + /* + * Overall, we expect either success or -EINVAL in + * order to determine whether DIMM is populated or not. + * For anything else we fall back to deferring the + * detection to be performed at a later point in time. + */ + if (ret == -EINVAL) { + chan_rank_empty |= BIT(chan_rank); + continue; + } + + return -EAGAIN; + } + + for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) + if (__dimm_temp(pcs, dimm_idx)) + dimm_mask |= BIT(chan_rank * dimm_idx_max + dimm_idx); + } + + /* + * If we got all -EINVALs, it means that the CPU doesn't have any + * DIMMs. Unfortunately, it may also happen at the very start of + * host platform boot. Retrying a couple of times lets us make sure + * that the state is persistent. + */ + if (chan_rank_empty == GENMASK(chan_rank_max - 1, 0)) { + if (priv->no_dimm_retry_count < NO_DIMM_RETRY_COUNT_MAX) { + priv->no_dimm_retry_count++; + + return -EAGAIN; + } + + return -ENODEV; + } + + /* + * It's possible that memory training is not done yet. In this case we + * defer the detection to be performed at a later point in time. + */ + if (!dimm_mask) { + priv->no_dimm_retry_count = 0; + return -EAGAIN; + } + + dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask); + + bitmap_from_u64(priv->dimm_mask, dimm_mask); + + return 0; +} + +static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan) +{ + int rank = chan / priv->gen_info->dimm_idx_max; + int idx = chan % priv->gen_info->dimm_idx_max; + + priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL, + "DIMM %c%d", 'A' + rank, + idx + 1); + if (!priv->dimmtemp_label[chan]) + return -ENOMEM; + + return 0; +} + +static const u32 peci_dimmtemp_temp_channel_config[] = { + [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT, + 0 +}; + +static const struct hwmon_channel_info peci_dimmtemp_temp_channel = { + .type = hwmon_temp, + .config = peci_dimmtemp_temp_channel_config, +}; + +static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = { + &peci_dimmtemp_temp_channel, + NULL +}; + +static const struct hwmon_chip_info peci_dimmtemp_chip_info = { + .ops = &peci_dimmtemp_ops, + .info = peci_dimmtemp_temp_info, +}; + +static int create_dimm_temp_info(struct peci_dimmtemp *priv) +{ + int ret, i, channels; + struct device *dev; + + /* + * We expect to either find populated DIMMs and carry on with creating + * sensors, or find out that there are no DIMMs populated. + * All other states mean that the platform never reached the state that + * allows to check DIMM state - causing us to retry later on. + */ + ret = check_populated_dimms(priv); + if (ret == -ENODEV) { + dev_dbg(priv->dev, "No DIMMs found\n"); + return 0; + } else if (ret) { + schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES); + dev_dbg(priv->dev, "Deferred populating DIMM temp info\n"); + return ret; + } + + channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max; + + priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * sizeof(char *), GFP_KERNEL); + if (!priv->dimmtemp_label) + return -ENOMEM; + + for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) { + ret = create_dimm_temp_label(priv, i); + if (ret) + return ret; + mutex_init(&priv->dimm[i].thresholds.state.lock); + mutex_init(&priv->dimm[i].temp.state.lock); + } + + dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv, + &peci_dimmtemp_chip_info, NULL); + if (IS_ERR(dev)) { + dev_err(priv->dev, "Failed to register hwmon device\n"); + return PTR_ERR(dev); + } + + dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name); + + return 0; +} + +static void create_dimm_temp_info_delayed(struct work_struct *work) +{ + struct peci_dimmtemp *priv = container_of(to_delayed_work(work), + struct peci_dimmtemp, + detect_work); + int ret; + + ret = create_dimm_temp_info(priv); + if (ret && ret != -EAGAIN) + dev_err(priv->dev, "Failed to populate DIMM temp info\n"); +} + +static void remove_delayed_work(void *_priv) +{ + struct peci_dimmtemp *priv = _priv; + + cancel_delayed_work_sync(&priv->detect_work); +} + +static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct peci_device *peci_dev = to_peci_device(dev->parent); + struct peci_dimmtemp *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d", + peci_dev->info.socket_id); + if (!priv->name) + return -ENOMEM; + + priv->dev = dev; + priv->peci_dev = peci_dev; + priv->gen_info = (const struct dimm_info *)id->driver_data; + + /* + * This is just a sanity check. Since we're using commands that are + * guaranteed to be supported on a given platform, we should never see + * revision lower than expected. + */ + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) + dev_warn(priv->dev, + "Unexpected PECI revision %#x, some features may be unavailable\n", + peci_dev->info.peci_revision); + + INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed); + + ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, priv); + if (ret) + return ret; + + ret = create_dimm_temp_info(priv); + if (ret && ret != -EAGAIN) { + dev_err(dev, "Failed to populate DIMM temp info\n"); + return ret; + } + + return 0; +} + +static int +read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 20, Function 0: IMC 0 channel 0 -> rank 0 + * Device 20, Function 1: IMC 0 channel 1 -> rank 1 + * Device 21, Function 0: IMC 0 channel 2 -> rank 2 + * Device 21, Function 1: IMC 0 channel 3 -> rank 3 + * Device 23, Function 0: IMC 1 channel 0 -> rank 4 + * Device 23, Function 1: IMC 1 channel 1 -> rank 5 + * Device 24, Function 0: IMC 1 channel 2 -> rank 6 + * Device 24, Function 1: IMC 1 channel 3 -> rank 7 + */ + dev = 20 + chan_rank / 2 + chan_rank / 4; + func = chan_rank % 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 12, Function 2: IMC 1 channel 0 -> rank 2 + * Device 12, Function 6: IMC 1 channel 1 -> rank 3 + */ + dev = 10 + chan_rank / 2 * 2; + func = (chan_rank % 2) ? 6 : 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 11, Function 2: IMC 0 channel 2 -> rank 2 + * Device 12, Function 2: IMC 1 channel 0 -> rank 3 + * Device 12, Function 6: IMC 1 channel 1 -> rank 4 + * Device 13, Function 2: IMC 1 channel 2 -> rank 5 + */ + dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0); + func = chan_rank % 3 == 1 ? 6 : 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u32 reg_val; + u64 offset; + int ret; + u8 dev; + + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, ®_val); + if (ret || !(reg_val & BIT(31))) + return -ENODATA; /* Use default or previous value */ + + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, ®_val); + if (ret) + return -ENODATA; /* Use default or previous value */ + + /* + * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0 + * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1 + * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2 + * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3 + * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4 + * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5 + * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6 + * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7 + */ + dev = 26 + chan_rank / 2; + offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000; + + ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val), + dev, 0, offset, data); + if (ret) + return ret; + + return 0; +} + +static const struct dimm_info dimm_hsx = { + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_hsx, +}; + +static const struct dimm_info dimm_bdx = { + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_hsx, +}; + +static const struct dimm_info dimm_bdxd = { + .chan_rank_max = CHAN_RANK_MAX_ON_BDXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDXD, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_bdxd, +}; + +static const struct dimm_info dimm_skx = { + .chan_rank_max = CHAN_RANK_MAX_ON_SKX, + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_skx, +}; + +static const struct dimm_info dimm_icx = { + .chan_rank_max = CHAN_RANK_MAX_ON_ICX, + .dimm_idx_max = DIMM_IDX_MAX_ON_ICX, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_icx, +}; + +static const struct dimm_info dimm_icxd = { + .chan_rank_max = CHAN_RANK_MAX_ON_ICXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_ICXD, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_icx, +}; + +static const struct auxiliary_device_id peci_dimmtemp_ids[] = { + { + .name = "peci_cpu.dimmtemp.hsx", + .driver_data = (kernel_ulong_t)&dimm_hsx, + }, + { + .name = "peci_cpu.dimmtemp.bdx", + .driver_data = (kernel_ulong_t)&dimm_bdx, + }, + { + .name = "peci_cpu.dimmtemp.bdxd", + .driver_data = (kernel_ulong_t)&dimm_bdxd, + }, + { + .name = "peci_cpu.dimmtemp.skx", + .driver_data = (kernel_ulong_t)&dimm_skx, + }, + { + .name = "peci_cpu.dimmtemp.icx", + .driver_data = (kernel_ulong_t)&dimm_icx, + }, + { + .name = "peci_cpu.dimmtemp.icxd", + .driver_data = (kernel_ulong_t)&dimm_icxd, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids); + +static struct auxiliary_driver peci_dimmtemp_driver = { + .probe = peci_dimmtemp_probe, + .id_table = peci_dimmtemp_ids, +}; + +module_auxiliary_driver(peci_dimmtemp_driver); + +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); +MODULE_DESCRIPTION("PECI dimmtemp driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI_CPU); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 88653d1c06a4..af00dca8d1ac 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1279,9 +1279,6 @@ ATTRIBUTE_GROUPS(coresight_source); static struct device_type coresight_dev_type[] = { { - .name = "none", - }, - { .name = "sink", .groups = coresight_sink_groups, }, diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index cf64ce73a741..7d413ba8b823 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -340,6 +340,10 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata, config->ctrl = attr->config; + /* Don't trace contextID when runs in non-root PID namespace */ + if (!task_is_in_init_pid_ns(current)) + config->ctrl &= ~ETMCR_CTXID_SIZE; + /* * Possible to have cores with PTM (supports ret stack) and ETM * (never has ret stack) on the same SoC. So if we have a request diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index bf18128cf5de..7f416a12000e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -656,7 +656,9 @@ static int etm4_parse_event_config(struct coresight_device *csdev, config->cfg |= BIT(11); } - if (attr->config & BIT(ETM_OPT_CTXTID)) + /* Only trace contextID when runs in root PID namespace */ + if ((attr->config & BIT(ETM_OPT_CTXTID)) && + task_is_in_init_pid_ns(current)) /* bit[6], Context ID tracing bit */ config->cfg |= BIT(ETM4_CFG_BIT_CTXTID); @@ -670,7 +672,11 @@ static int etm4_parse_event_config(struct coresight_device *csdev, ret = -EINVAL; goto out; } - config->cfg |= BIT(ETM4_CFG_BIT_VMID) | BIT(ETM4_CFG_BIT_VMID_OPT); + + /* Only trace virtual contextID when runs in root PID namespace */ + if (task_is_in_init_pid_ns(current)) + config->cfg |= BIT(ETM4_CFG_BIT_VMID) | + BIT(ETM4_CFG_BIT_VMID_OPT); } /* return stack - enable if selected and supported */ @@ -1091,7 +1097,7 @@ static void etm4_init_arch_data(void *info) etmidr0 = etm4x_relaxed_read32(csa, TRCIDR0); /* INSTP0, bits[2:1] P0 tracing support field */ - if (BMVAL(etmidr0, 1, 1) && BMVAL(etmidr0, 2, 2)) + if (BMVAL(etmidr0, 1, 2) == 0b11) drvdata->instrp0 = true; else drvdata->instrp0 = false; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index a0640fa5c55b..21687cc1e4e2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -367,8 +367,12 @@ static ssize_t mode_store(struct device *dev, mode = ETM_MODE_QELEM(config->mode); /* start by clearing QE bits */ config->cfg &= ~(BIT(13) | BIT(14)); - /* if supported, Q elements with instruction counts are enabled */ - if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) + /* + * if supported, Q elements with instruction counts are enabled. + * Always set the low bit for any requested mode. Valid combos are + * 0b00, 0b01 and 0b11. + */ + if (mode && drvdata->q_support) config->cfg |= BIT(13); /* * if supported, Q elements with and without instruction @@ -2111,7 +2115,16 @@ static ssize_t vmid_val_show(struct device *dev, struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + /* + * Don't use virtual contextID tracing if coming from a PID namespace. + * See comment in ctxid_pid_store(). + */ + if (!task_is_in_init_pid_ns(current)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); val = (unsigned long)config->vmid_val[config->vmid_idx]; + spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -2124,6 +2137,13 @@ static ssize_t vmid_val_store(struct device *dev, struct etmv4_config *config = &drvdata->config; /* + * Don't use virtual contextID tracing if coming from a PID namespace. + * See comment in ctxid_pid_store(). + */ + if (!task_is_in_init_pid_ns(current)) + return -EINVAL; + + /* * only implemented when vmid tracing is enabled, i.e. at least one * vmid comparator is implemented and at least 8 bit vmid size */ @@ -2146,6 +2166,13 @@ static ssize_t vmid_masks_show(struct device *dev, struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + /* + * Don't use virtual contextID tracing if coming from a PID namespace. + * See comment in ctxid_pid_store(). + */ + if (!task_is_in_init_pid_ns(current)) + return -EINVAL; + spin_lock(&drvdata->spinlock); val1 = config->vmid_mask0; val2 = config->vmid_mask1; @@ -2164,6 +2191,13 @@ static ssize_t vmid_masks_store(struct device *dev, int nr_inputs; /* + * Don't use virtual contextID tracing if coming from a PID namespace. + * See comment in ctxid_pid_store(). + */ + if (!task_is_in_init_pid_ns(current)) + return -EINVAL; + + /* * only implemented when vmid tracing is enabled, i.e. at least one * vmid comparator is implemented and at least 8 bit vmid size */ diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index c594f45319fc..475899714104 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -626,7 +626,7 @@ static int acpi_coresight_parse_link(struct acpi_device *adev, const union acpi_object *link, struct coresight_connection *conn) { - int rc, dir; + int dir; const union acpi_object *fields; struct acpi_device *r_adev; struct device *rdev; @@ -643,9 +643,9 @@ static int acpi_coresight_parse_link(struct acpi_device *adev, fields[3].type != ACPI_TYPE_INTEGER) return -EINVAL; - rc = acpi_bus_get_device(fields[2].reference.handle, &r_adev); - if (rc) - return rc; + r_adev = acpi_fetch_acpi_dev(fields[2].reference.handle); + if (!r_adev) + return -ENODEV; dir = fields[3].integer.value; if (dir == ACPI_CORESIGHT_LINK_MASTER) { diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 098fc34c4829..11850fd8c3b5 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -1049,7 +1049,7 @@ static int cscfg_create_device(void) err = device_register(dev); if (err) - cscfg_dev_release(dev); + put_device(dev); create_dev_exit_unlock: mutex_unlock(&cscfg_mutex); diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 276862c07e32..2b386bb848f8 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -91,10 +91,16 @@ struct trbe_buf { */ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0 #define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE 1 +#define TRBE_NEEDS_DRAIN_AFTER_DISABLE 2 +#define TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE 3 +#define TRBE_IS_BROKEN 4 static int trbe_errata_cpucaps[] = { [TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE, [TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE, + [TRBE_NEEDS_DRAIN_AFTER_DISABLE] = ARM64_WORKAROUND_2064142, + [TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE] = ARM64_WORKAROUND_2038923, + [TRBE_IS_BROKEN] = ARM64_WORKAROUND_1902691, -1, /* Sentinel, must be the last entry */ }; @@ -167,6 +173,32 @@ static inline bool trbe_may_write_out_of_range(struct trbe_cpudata *cpudata) return trbe_has_erratum(cpudata, TRBE_WORKAROUND_WRITE_OUT_OF_RANGE); } +static inline bool trbe_needs_drain_after_disable(struct trbe_cpudata *cpudata) +{ + /* + * Errata affected TRBE implementation will need TSB CSYNC and + * DSB in order to prevent subsequent writes into certain TRBE + * system registers from being ignored and not effected. + */ + return trbe_has_erratum(cpudata, TRBE_NEEDS_DRAIN_AFTER_DISABLE); +} + +static inline bool trbe_needs_ctxt_sync_after_enable(struct trbe_cpudata *cpudata) +{ + /* + * Errata affected TRBE implementation will need an additional + * context synchronization in order to prevent an inconsistent + * TRBE prohibited region view on the CPU which could possibly + * corrupt the TRBE buffer or the TRBE state. + */ + return trbe_has_erratum(cpudata, TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE); +} + +static inline bool trbe_is_broken(struct trbe_cpudata *cpudata) +{ + return trbe_has_erratum(cpudata, TRBE_IS_BROKEN); +} + static int trbe_alloc_node(struct perf_event *event) { if (event->cpu == -1) @@ -174,17 +206,31 @@ static int trbe_alloc_node(struct perf_event *event) return cpu_to_node(event->cpu); } -static void trbe_drain_buffer(void) +static inline void trbe_drain_buffer(void) { tsb_csync(); dsb(nsh); } -static void trbe_drain_and_disable_local(void) +static inline void set_trbe_enabled(struct trbe_cpudata *cpudata, u64 trblimitr) { - u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); + /* + * Enable the TRBE without clearing LIMITPTR which + * might be required for fetching the buffer limits. + */ + trblimitr |= TRBLIMITR_ENABLE; + write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1); - trbe_drain_buffer(); + /* Synchronize the TRBE enable event */ + isb(); + + if (trbe_needs_ctxt_sync_after_enable(cpudata)) + isb(); +} + +static inline void set_trbe_disabled(struct trbe_cpudata *cpudata) +{ + u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); /* * Disable the TRBE without clearing LIMITPTR which @@ -192,12 +238,21 @@ static void trbe_drain_and_disable_local(void) */ trblimitr &= ~TRBLIMITR_ENABLE; write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1); + + if (trbe_needs_drain_after_disable(cpudata)) + trbe_drain_buffer(); isb(); } -static void trbe_reset_local(void) +static void trbe_drain_and_disable_local(struct trbe_cpudata *cpudata) { - trbe_drain_and_disable_local(); + trbe_drain_buffer(); + set_trbe_disabled(cpudata); +} + +static void trbe_reset_local(struct trbe_cpudata *cpudata) +{ + trbe_drain_and_disable_local(cpudata); write_sysreg_s(0, SYS_TRBLIMITR_EL1); write_sysreg_s(0, SYS_TRBPTR_EL1); write_sysreg_s(0, SYS_TRBBASER_EL1); @@ -234,7 +289,7 @@ static void trbe_stop_and_truncate_event(struct perf_output_handle *handle) * at event_stop(). So disable the TRBE here and leave * the update_buffer() to return a 0 size. */ - trbe_drain_and_disable_local(); + trbe_drain_and_disable_local(buf->cpudata); perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); perf_aux_output_end(handle, 0); *this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL; @@ -536,9 +591,10 @@ static void clr_trbe_status(void) write_sysreg_s(trbsr, SYS_TRBSR_EL1); } -static void set_trbe_limit_pointer_enabled(unsigned long addr) +static void set_trbe_limit_pointer_enabled(struct trbe_buf *buf) { u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); + unsigned long addr = buf->trbe_limit; WARN_ON(!IS_ALIGNED(addr, (1UL << TRBLIMITR_LIMIT_SHIFT))); WARN_ON(!IS_ALIGNED(addr, PAGE_SIZE)); @@ -566,12 +622,7 @@ static void set_trbe_limit_pointer_enabled(unsigned long addr) trblimitr |= (TRBE_TRIG_MODE_IGNORE & TRBLIMITR_TRIG_MODE_MASK) << TRBLIMITR_TRIG_MODE_SHIFT; trblimitr |= (addr & PAGE_MASK); - - trblimitr |= TRBLIMITR_ENABLE; - write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1); - - /* Synchronize the TRBE enable event */ - isb(); + set_trbe_enabled(buf->cpudata, trblimitr); } static void trbe_enable_hw(struct trbe_buf *buf) @@ -579,8 +630,7 @@ static void trbe_enable_hw(struct trbe_buf *buf) WARN_ON(buf->trbe_hw_base < buf->trbe_base); WARN_ON(buf->trbe_write < buf->trbe_hw_base); WARN_ON(buf->trbe_write >= buf->trbe_limit); - set_trbe_disabled(); - isb(); + set_trbe_disabled(buf->cpudata); clr_trbe_status(); set_trbe_base_pointer(buf->trbe_hw_base); set_trbe_write_pointer(buf->trbe_write); @@ -590,7 +640,7 @@ static void trbe_enable_hw(struct trbe_buf *buf) * till now before enabling the TRBE. */ isb(); - set_trbe_limit_pointer_enabled(buf->trbe_limit); + set_trbe_limit_pointer_enabled(buf); } static enum trbe_fault_action trbe_get_fault_act(struct perf_output_handle *handle, @@ -775,7 +825,7 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, * the TRBE here will ensure that no IRQ could be generated when the perf * handle gets freed in etm_event_stop(). */ - trbe_drain_and_disable_local(); + trbe_drain_and_disable_local(cpudata); /* Check if there is a pending interrupt and handle it here */ status = read_sysreg_s(SYS_TRBSR_EL1); @@ -986,7 +1036,7 @@ static int arm_trbe_disable(struct coresight_device *csdev) if (cpudata->mode != CS_MODE_PERF) return -EINVAL; - trbe_drain_and_disable_local(); + trbe_drain_and_disable_local(cpudata); buf->cpudata = NULL; cpudata->buf = NULL; cpudata->mode = CS_MODE_DISABLED; @@ -995,16 +1045,15 @@ static int arm_trbe_disable(struct coresight_device *csdev) static void trbe_handle_spurious(struct perf_output_handle *handle) { - u64 limitr = read_sysreg_s(SYS_TRBLIMITR_EL1); + struct trbe_buf *buf = etm_perf_sink_config(handle); + u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); /* * If the IRQ was spurious, simply re-enable the TRBE * back without modifying the buffer parameters to * retain the trace collected so far. */ - limitr |= TRBLIMITR_ENABLE; - write_sysreg_s(limitr, SYS_TRBLIMITR_EL1); - isb(); + set_trbe_enabled(buf->cpudata, trblimitr); } static int trbe_handle_overflow(struct perf_output_handle *handle) @@ -1028,7 +1077,7 @@ static int trbe_handle_overflow(struct perf_output_handle *handle) * is able to detect this with a disconnected handle * (handle->event = NULL). */ - trbe_drain_and_disable_local(); + trbe_drain_and_disable_local(buf->cpudata); *this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL; return -EINVAL; } @@ -1062,6 +1111,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) { struct perf_output_handle **handle_ptr = dev; struct perf_output_handle *handle = *handle_ptr; + struct trbe_buf *buf = etm_perf_sink_config(handle); enum trbe_fault_action act; u64 status; bool truncated = false; @@ -1082,7 +1132,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) * Ensure the trace is visible to the CPUs and * any external aborts have been resolved. */ - trbe_drain_and_disable_local(); + trbe_drain_and_disable_local(buf->cpudata); clr_trbe_irq(); isb(); @@ -1167,8 +1217,9 @@ static const struct attribute_group *arm_trbe_groups[] = { static void arm_trbe_enable_cpu(void *info) { struct trbe_drvdata *drvdata = info; + struct trbe_cpudata *cpudata = this_cpu_ptr(drvdata->cpudata); - trbe_reset_local(); + trbe_reset_local(cpudata); enable_percpu_irq(drvdata->irq, IRQ_TYPE_NONE); } @@ -1244,6 +1295,11 @@ static void arm_trbe_probe_cpu(void *info) */ trbe_check_errata(cpudata); + if (trbe_is_broken(cpudata)) { + pr_err("Disabling TRBE on cpu%d due to erratum\n", cpu); + goto cpu_clear; + } + /* * If the TRBE is affected by erratum TRBE_WORKAROUND_OVERWRITE_FILL_MODE, * we must always program the TBRPTR_EL1, 256bytes from a page @@ -1276,7 +1332,7 @@ static void arm_trbe_remove_coresight_cpu(void *info) struct coresight_device *trbe_csdev = coresight_get_percpu_sink(cpu); disable_percpu_irq(drvdata->irq); - trbe_reset_local(); + trbe_reset_local(cpudata); if (trbe_csdev) { coresight_unregister(trbe_csdev); cpudata->drvdata = NULL; @@ -1349,8 +1405,10 @@ static int arm_trbe_cpu_teardown(unsigned int cpu, struct hlist_node *node) struct trbe_drvdata *drvdata = hlist_entry_safe(node, struct trbe_drvdata, hotplug_node); if (cpumask_test_cpu(cpu, &drvdata->supported_cpus)) { + struct trbe_cpudata *cpudata = per_cpu_ptr(drvdata->cpudata, cpu); + disable_percpu_irq(drvdata->irq); - trbe_reset_local(); + trbe_reset_local(cpudata); } return 0; } @@ -1423,6 +1481,12 @@ static int arm_trbe_device_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; + /* Trace capture is not possible with kernel page table isolation */ + if (arm64_kernel_unmapped_at_el0()) { + pr_err("TRBE wouldn't work if kernel gets unmapped at EL0\n"); + return -EOPNOTSUPP; + } + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; @@ -1484,11 +1548,6 @@ static int __init arm_trbe_init(void) { int ret; - if (arm64_kernel_unmapped_at_el0()) { - pr_err("TRBE wouldn't work if kernel gets unmapped at EL0\n"); - return -EOPNOTSUPP; - } - ret = platform_driver_register(&arm_trbe_driver); if (!ret) return 0; diff --git a/drivers/hwtracing/coresight/coresight-trbe.h b/drivers/hwtracing/coresight/coresight-trbe.h index abf3e36082f0..30e4d7db4f8e 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.h +++ b/drivers/hwtracing/coresight/coresight-trbe.h @@ -91,14 +91,6 @@ static inline bool is_trbe_running(u64 trbsr) #define TRBE_FILL_MODE_WRAP 1 #define TRBE_FILL_MODE_CIRCULAR_BUFFER 3 -static inline void set_trbe_disabled(void) -{ - u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); - - trblimitr &= ~TRBLIMITR_ENABLE; - write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1); -} - static inline bool get_trbe_flag_update(u64 trbidr) { return trbidr & TRBIDR_FLAG; diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 49587c992a6d..eac3f02662ae 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -123,6 +123,33 @@ config ADXL355_SPI will be called adxl355_spi and you will also get adxl355_core for the core module. +config ADXL367 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config ADXL367_SPI + tristate "Analog Devices ADXL367 3-Axis Accelerometer SPI Driver" + depends on SPI + select ADXL367 + select REGMAP_SPI + help + Say yes here to add support for the Analog Devices ADXL367 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl367_spi. + +config ADXL367_I2C + tristate "Analog Devices ADXL367 3-Axis Accelerometer I2C Driver" + depends on I2C + select ADXL367 + select REGMAP_I2C + help + Say yes here to add support for the Analog Devices ADXL367 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl367_i2c. + config ADXL372 tristate select IIO_BUFFER @@ -349,8 +376,6 @@ config IIO_ST_ACCEL_3AXIS depends on !SENSORS_LIS3_I2C depends on !SENSORS_LIS3_SPI select IIO_ST_SENSORS_CORE - select IIO_ST_ACCEL_I2C_3AXIS if (I2C) - select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics accelerometers: @@ -358,23 +383,30 @@ config IIO_ST_ACCEL_3AXIS LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL, LNG2DM, LIS3DE, LIS2DE12, LIS2HH12 - This driver can also be built as a module. If so, these modules - will be created: - - st_accel (core functions for the driver [it is mandatory]); - - st_accel_i2c (necessary for the I2C devices [optional*]); - - st_accel_spi (necessary for the SPI devices [optional*]); - - (*) one of these is necessary to do something. + Also need to enable at least one of I2C and SPI interface drivers + below. config IIO_ST_ACCEL_I2C_3AXIS - tristate - depends on IIO_ST_ACCEL_3AXIS - depends on IIO_ST_SENSORS_I2C + tristate "STMicroelectronics accelerometers 3-Axis I2C Interface" + depends on I2C && IIO_ST_ACCEL_3AXIS + default I2C && IIO_ST_ACCEL_3AXIS + select IIO_ST_SENSORS_I2C + help + Build support for STMicroelectronics accelerometers I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_accel_i2c. config IIO_ST_ACCEL_SPI_3AXIS - tristate - depends on IIO_ST_ACCEL_3AXIS - depends on IIO_ST_SENSORS_SPI + tristate "STMicroelectronics accelerometers 3-Axis SPI Interface" + depends on SPI_MASTER && IIO_ST_ACCEL_3AXIS + default SPI_MASTER && IIO_ST_ACCEL_3AXIS + select IIO_ST_SENSORS_SPI + help + Build support for STMicroelectronics accelerometers SPI interface. + + To compile this driver as a module, choose M here. The module + will be called st_accel_spi. config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index d03e2f6bba08..4d8792668838 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -15,6 +15,9 @@ obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o obj-$(CONFIG_ADXL355) += adxl355_core.o obj-$(CONFIG_ADXL355_I2C) += adxl355_i2c.o obj-$(CONFIG_ADXL355_SPI) += adxl355_spi.o +obj-$(CONFIG_ADXL367) += adxl367.o +obj-$(CONFIG_ADXL367_I2C) += adxl367_i2c.o +obj-$(CONFIG_ADXL367_SPI) += adxl367_spi.o obj-$(CONFIG_ADXL372) += adxl372.o obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 7a434e2884d4..dfb8e2e5bdf5 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -300,3 +300,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16201"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index ac08e866d612..5a9c6e2296f1 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -310,3 +310,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16209"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 0d243341f1a7..9e4193e64765 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -26,7 +26,7 @@ const struct regmap_access_table adxl313_readable_regs_table = { .yes_ranges = adxl313_readable_reg_range, .n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range), }; -EXPORT_SYMBOL_GPL(adxl313_readable_regs_table); +EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313); static const struct regmap_range adxl313_writable_reg_range[] = { regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), @@ -41,7 +41,7 @@ const struct regmap_access_table adxl313_writable_regs_table = { .yes_ranges = adxl313_writable_reg_range, .n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range), }; -EXPORT_SYMBOL_GPL(adxl313_writable_regs_table); +EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313); struct adxl313_data { struct regmap *regmap; @@ -325,7 +325,7 @@ int adxl313_core_probe(struct device *dev, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(adxl313_core_probe); +EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313); MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver"); diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c index 82e9fb2db1e6..c329765dbf60 100644 --- a/drivers/iio/accel/adxl313_i2c.c +++ b/drivers/iio/accel/adxl313_i2c.c @@ -64,3 +64,4 @@ module_i2c_driver(adxl313_i2c_driver); MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer I2C driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL313); diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c index a6162f36ef52..a3c6d553462d 100644 --- a/drivers/iio/accel/adxl313_spi.c +++ b/drivers/iio/accel/adxl313_spi.c @@ -90,3 +90,4 @@ module_spi_driver(adxl313_spi_driver); MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL313); diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index af0fdd02c4f2..d7e67cb08538 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -9,11 +9,10 @@ #define _ADXL345_H_ enum adxl345_device_type { - ADXL345, - ADXL375, + ADXL345 = 1, + ADXL375 = 2, }; -int adxl345_core_probe(struct device *dev, struct regmap *regmap, - enum adxl345_device_type type, const char *name); +int adxl345_core_probe(struct device *dev, struct regmap *regmap); #endif /* _ADXL345_H_ */ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 4b275051ef61..370bfec1275a 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -8,6 +8,7 @@ */ #include <linux/module.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/iio/iio.h> @@ -194,7 +195,7 @@ static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( static struct attribute *adxl345_attrs[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, - NULL, + NULL }; static const struct attribute_group adxl345_attrs_group = { @@ -208,30 +209,44 @@ static const struct iio_info adxl345_info = { .write_raw_get_fmt = adxl345_write_raw_get_fmt, }; +static int adxl345_powerup(void *regmap) +{ + return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE); +} + static void adxl345_powerdown(void *regmap) { regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY); } -int adxl345_core_probe(struct device *dev, struct regmap *regmap, - enum adxl345_device_type type, const char *name) +int adxl345_core_probe(struct device *dev, struct regmap *regmap) { + enum adxl345_device_type type; struct adxl345_data *data; struct iio_dev *indio_dev; + const char *name; u32 regval; int ret; - ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); - if (ret < 0) { - dev_err(dev, "Error reading device ID: %d\n", ret); - return ret; + type = (uintptr_t)device_get_match_data(dev); + switch (type) { + case ADXL345: + name = "adxl345"; + break; + case ADXL375: + name = "adxl375"; + break; + default: + return -EINVAL; } - if (regval != ADXL345_DEVID) { - dev_err(dev, "Invalid device ID: %x, expected %x\n", - regval, ADXL345_DEVID); - return -ENODEV; - } + ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading device ID\n"); + + if (regval != ADXL345_DEVID) + return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n", + regval, ADXL345_DEVID); indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -245,10 +260,8 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, data->data_range); - if (ret < 0) { - dev_err(dev, "Failed to set data range: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set data range\n"); indio_dev->name = name; indio_dev->info = &adxl345_info; @@ -257,12 +270,9 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); /* Enable measurement mode */ - ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL, - ADXL345_POWER_CTL_MEASURE); - if (ret < 0) { - dev_err(dev, "Failed to enable measurement mode: %d\n", ret); - return ret; - } + ret = adxl345_powerup(data->regmap); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable measurement mode\n"); ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap); if (ret < 0) @@ -270,7 +280,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(adxl345_core_probe); +EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345); MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver"); diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index a431cba216e6..098cd83f95b2 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -19,23 +19,15 @@ static const struct regmap_config adxl345_i2c_regmap_config = { .val_bits = 8, }; -static int adxl345_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adxl345_i2c_probe(struct i2c_client *client) { struct regmap *regmap; - if (!id) - return -ENODEV; - regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&client->dev, regmap, id->driver_data, - id->name); + return adxl345_core_probe(&client->dev, regmap); } static const struct i2c_device_id adxl345_i2c_id[] = { @@ -43,28 +35,33 @@ static const struct i2c_device_id adxl345_i2c_id[] = { { "adxl375", ADXL375 }, { } }; - MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id); static const struct of_device_id adxl345_of_match[] = { - { .compatible = "adi,adxl345" }, - { .compatible = "adi,adxl375" }, - { }, + { .compatible = "adi,adxl345", .data = (const void *)ADXL345 }, + { .compatible = "adi,adxl375", .data = (const void *)ADXL375 }, + { } }; - MODULE_DEVICE_TABLE(of, adxl345_of_match); +static const struct acpi_device_id adxl345_acpi_match[] = { + { "ADS0345", ADXL345 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match); + static struct i2c_driver adxl345_i2c_driver = { .driver = { .name = "adxl345_i2c", .of_match_table = adxl345_of_match, + .acpi_match_table = adxl345_acpi_match, }, - .probe = adxl345_i2c_probe, + .probe_new = adxl345_i2c_probe, .id_table = adxl345_i2c_id, }; - module_i2c_driver(adxl345_i2c_driver); MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer I2C driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL345); diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index ea559ac2e87d..aaade5808657 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -22,24 +22,18 @@ static const struct regmap_config adxl345_spi_regmap_config = { static int adxl345_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); struct regmap *regmap; /* Bail out if max_speed_hz exceeds 5 MHz */ - if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) { - dev_err(&spi->dev, "SPI CLK, %d Hz exceeds 5 MHz\n", - spi->max_speed_hz); - return -EINVAL; - } + if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) + return dev_err_probe(&spi->dev, -EINVAL, "SPI CLK, %d Hz exceeds 5 MHz\n", + spi->max_speed_hz); regmap = devm_regmap_init_spi(spi, &adxl345_spi_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&spi->dev, regmap, id->driver_data, id->name); + return adxl345_core_probe(&spi->dev, regmap); } static const struct spi_device_id adxl345_spi_id[] = { @@ -47,28 +41,33 @@ static const struct spi_device_id adxl345_spi_id[] = { { "adxl375", ADXL375 }, { } }; - MODULE_DEVICE_TABLE(spi, adxl345_spi_id); static const struct of_device_id adxl345_of_match[] = { - { .compatible = "adi,adxl345" }, - { .compatible = "adi,adxl375" }, - { }, + { .compatible = "adi,adxl345", .data = (const void *)ADXL345 }, + { .compatible = "adi,adxl375", .data = (const void *)ADXL375 }, + { } }; - MODULE_DEVICE_TABLE(of, adxl345_of_match); +static const struct acpi_device_id adxl345_acpi_match[] = { + { "ADS0345", ADXL345 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match); + static struct spi_driver adxl345_spi_driver = { .driver = { .name = "adxl345_spi", .of_match_table = adxl345_of_match, + .acpi_match_table = adxl345_acpi_match, }, .probe = adxl345_spi_probe, .id_table = adxl345_spi_id, }; - module_spi_driver(adxl345_spi_driver); MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL345); diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 4f485909f459..e9c10c8c32f0 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -20,6 +20,8 @@ #include <linux/mod_devicetable.h> #include <linux/of_irq.h> #include <linux/regmap.h> +#include <linux/units.h> + #include <asm/unaligned.h> #include "adxl355.h" @@ -60,9 +62,6 @@ #define ADXL355_PARTID_VAL 0xED #define ADXL355_RESET_CODE 0x52 -#define MEGA 1000000UL -#define TERA 1000000000000ULL - static const struct regmap_range adxl355_read_reg_range[] = { regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG), regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG), @@ -72,7 +71,7 @@ const struct regmap_access_table adxl355_readable_regs_tbl = { .yes_ranges = adxl355_read_reg_range, .n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range), }; -EXPORT_SYMBOL_GPL(adxl355_readable_regs_tbl); +EXPORT_SYMBOL_NS_GPL(adxl355_readable_regs_tbl, IIO_ADXL355); static const struct regmap_range adxl355_write_reg_range[] = { regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG), @@ -82,7 +81,7 @@ const struct regmap_access_table adxl355_writeable_regs_tbl = { .yes_ranges = adxl355_write_reg_range, .n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range), }; -EXPORT_SYMBOL_GPL(adxl355_writeable_regs_tbl); +EXPORT_SYMBOL_NS_GPL(adxl355_writeable_regs_tbl, IIO_ADXL355); enum adxl355_op_mode { ADXL355_MEASUREMENT, @@ -758,7 +757,7 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(adxl355_core_probe); +EXPORT_SYMBOL_NS_GPL(adxl355_core_probe, IIO_ADXL355); MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver"); diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c index 5a987bda9060..f67d57921c81 100644 --- a/drivers/iio/accel/adxl355_i2c.c +++ b/drivers/iio/accel/adxl355_i2c.c @@ -60,3 +60,4 @@ module_i2c_driver(adxl355_i2c_driver); MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer I2C driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL355); diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c index fb225aeb56e3..5fe986ae03f6 100644 --- a/drivers/iio/accel/adxl355_spi.c +++ b/drivers/iio/accel/adxl355_spi.c @@ -63,3 +63,4 @@ module_spi_driver(adxl355_spi_driver); MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL355); diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c new file mode 100644 index 000000000000..62960134ea19 --- /dev/null +++ b/drivers/iio/accel/adxl367.c @@ -0,0 +1,1588 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <asm/unaligned.h> + +#include "adxl367.h" + +#define ADXL367_REG_DEVID 0x00 +#define ADXL367_DEVID_AD 0xAD + +#define ADXL367_REG_STATUS 0x0B +#define ADXL367_STATUS_INACT_MASK BIT(5) +#define ADXL367_STATUS_ACT_MASK BIT(4) +#define ADXL367_STATUS_FIFO_FULL_MASK BIT(2) + +#define ADXL367_FIFO_ENT_H_MASK GENMASK(1, 0) + +#define ADXL367_REG_X_DATA_H 0x0E +#define ADXL367_REG_Y_DATA_H 0x10 +#define ADXL367_REG_Z_DATA_H 0x12 +#define ADXL367_REG_TEMP_DATA_H 0x14 +#define ADXL367_REG_EX_ADC_DATA_H 0x16 +#define ADXL367_DATA_MASK GENMASK(15, 2) + +#define ADXL367_TEMP_25C 165 +#define ADXL367_TEMP_PER_C 54 + +#define ADXL367_VOLTAGE_OFFSET 8192 +#define ADXL367_VOLTAGE_MAX_MV 1000 +#define ADXL367_VOLTAGE_MAX_RAW GENMASK(13, 0) + +#define ADXL367_REG_RESET 0x1F +#define ADXL367_RESET_CODE 0x52 + +#define ADXL367_REG_THRESH_ACT_H 0x20 +#define ADXL367_REG_THRESH_INACT_H 0x23 +#define ADXL367_THRESH_MAX GENMASK(12, 0) +#define ADXL367_THRESH_VAL_H_MASK GENMASK(12, 6) +#define ADXL367_THRESH_H_MASK GENMASK(6, 0) +#define ADXL367_THRESH_VAL_L_MASK GENMASK(5, 0) +#define ADXL367_THRESH_L_MASK GENMASK(7, 2) + +#define ADXL367_REG_TIME_ACT 0x22 +#define ADXL367_REG_TIME_INACT_H 0x25 +#define ADXL367_TIME_ACT_MAX GENMASK(7, 0) +#define ADXL367_TIME_INACT_MAX GENMASK(15, 0) +#define ADXL367_TIME_INACT_VAL_H_MASK GENMASK(15, 8) +#define ADXL367_TIME_INACT_H_MASK GENMASK(7, 0) +#define ADXL367_TIME_INACT_VAL_L_MASK GENMASK(7, 0) +#define ADXL367_TIME_INACT_L_MASK GENMASK(7, 0) + +#define ADXL367_REG_ACT_INACT_CTL 0x27 +#define ADXL367_ACT_EN_MASK GENMASK(1, 0) +#define ADXL367_ACT_LINKLOOP_MASK GENMASK(5, 4) + +#define ADXL367_REG_FIFO_CTL 0x28 +#define ADXL367_FIFO_CTL_FORMAT_MASK GENMASK(6, 3) +#define ADXL367_FIFO_CTL_MODE_MASK GENMASK(1, 0) + +#define ADXL367_REG_FIFO_SAMPLES 0x29 +#define ADXL367_FIFO_SIZE 512 +#define ADXL367_FIFO_MAX_WATERMARK 511 + +#define ADXL367_SAMPLES_VAL_H_MASK BIT(8) +#define ADXL367_SAMPLES_H_MASK BIT(2) +#define ADXL367_SAMPLES_VAL_L_MASK GENMASK(7, 0) +#define ADXL367_SAMPLES_L_MASK GENMASK(7, 0) + +#define ADXL367_REG_INT1_MAP 0x2A +#define ADXL367_INT_INACT_MASK BIT(5) +#define ADXL367_INT_ACT_MASK BIT(4) +#define ADXL367_INT_FIFO_WATERMARK_MASK BIT(2) + +#define ADXL367_REG_FILTER_CTL 0x2C +#define ADXL367_FILTER_CTL_RANGE_MASK GENMASK(7, 6) +#define ADXL367_2G_RANGE_1G 4095 +#define ADXL367_2G_RANGE_100MG 409 +#define ADXL367_FILTER_CTL_ODR_MASK GENMASK(2, 0) + +#define ADXL367_REG_POWER_CTL 0x2D +#define ADXL367_POWER_CTL_MODE_MASK GENMASK(1, 0) + +#define ADXL367_REG_ADC_CTL 0x3C +#define ADXL367_REG_TEMP_CTL 0x3D +#define ADXL367_ADC_EN_MASK BIT(0) + +enum adxl367_range { + ADXL367_2G_RANGE, + ADXL367_4G_RANGE, + ADXL367_8G_RANGE, +}; + +enum adxl367_fifo_mode { + ADXL367_FIFO_MODE_DISABLED = 0b00, + ADXL367_FIFO_MODE_STREAM = 0b10, +}; + +enum adxl367_fifo_format { + ADXL367_FIFO_FORMAT_XYZ, + ADXL367_FIFO_FORMAT_X, + ADXL367_FIFO_FORMAT_Y, + ADXL367_FIFO_FORMAT_Z, + ADXL367_FIFO_FORMAT_XYZT, + ADXL367_FIFO_FORMAT_XT, + ADXL367_FIFO_FORMAT_YT, + ADXL367_FIFO_FORMAT_ZT, + ADXL367_FIFO_FORMAT_XYZA, + ADXL367_FIFO_FORMAT_XA, + ADXL367_FIFO_FORMAT_YA, + ADXL367_FIFO_FORMAT_ZA, +}; + +enum adxl367_op_mode { + ADXL367_OP_STANDBY = 0b00, + ADXL367_OP_MEASURE = 0b10, +}; + +enum adxl367_act_proc_mode { + ADXL367_LOOPED = 0b11, +}; + +enum adxl367_act_en_mode { + ADXL367_ACT_DISABLED = 0b00, + ADCL367_ACT_REF_ENABLED = 0b11, +}; + +enum adxl367_activity_type { + ADXL367_ACTIVITY, + ADXL367_INACTIVITY, +}; + +enum adxl367_odr { + ADXL367_ODR_12P5HZ, + ADXL367_ODR_25HZ, + ADXL367_ODR_50HZ, + ADXL367_ODR_100HZ, + ADXL367_ODR_200HZ, + ADXL367_ODR_400HZ, +}; + +struct adxl367_state { + const struct adxl367_ops *ops; + void *context; + + struct device *dev; + struct regmap *regmap; + + struct regulator_bulk_data regulators[2]; + + /* + * Synchronize access to members of driver state, and ensure atomicity + * of consecutive regmap operations. + */ + struct mutex lock; + + enum adxl367_odr odr; + enum adxl367_range range; + + unsigned int act_threshold; + unsigned int act_time_ms; + unsigned int inact_threshold; + unsigned int inact_time_ms; + + unsigned int fifo_set_size; + unsigned int fifo_watermark; + + __be16 fifo_buf[ADXL367_FIFO_SIZE] ____cacheline_aligned; + __be16 sample_buf; + u8 act_threshold_buf[2]; + u8 inact_time_buf[2]; + u8 status_buf[3]; +}; + +static const unsigned int adxl367_threshold_h_reg_tbl[] = { + [ADXL367_ACTIVITY] = ADXL367_REG_THRESH_ACT_H, + [ADXL367_INACTIVITY] = ADXL367_REG_THRESH_INACT_H, +}; + +static const unsigned int adxl367_act_en_shift_tbl[] = { + [ADXL367_ACTIVITY] = 0, + [ADXL367_INACTIVITY] = 2, +}; + +static const unsigned int adxl367_act_int_mask_tbl[] = { + [ADXL367_ACTIVITY] = ADXL367_INT_ACT_MASK, + [ADXL367_INACTIVITY] = ADXL367_INT_INACT_MASK, +}; + +static const int adxl367_samp_freq_tbl[][2] = { + [ADXL367_ODR_12P5HZ] = {12, 500000}, + [ADXL367_ODR_25HZ] = {25, 0}, + [ADXL367_ODR_50HZ] = {50, 0}, + [ADXL367_ODR_100HZ] = {100, 0}, + [ADXL367_ODR_200HZ] = {200, 0}, + [ADXL367_ODR_400HZ] = {400, 0}, +}; + +/* (g * 2) * 9.80665 * 1000000 / (2^14 - 1) */ +static const int adxl367_range_scale_tbl[][2] = { + [ADXL367_2G_RANGE] = {0, 2394347}, + [ADXL367_4G_RANGE] = {0, 4788695}, + [ADXL367_8G_RANGE] = {0, 9577391}, +}; + +static const int adxl367_range_scale_factor_tbl[] = { + [ADXL367_2G_RANGE] = 1, + [ADXL367_4G_RANGE] = 2, + [ADXL367_8G_RANGE] = 4, +}; + +enum { + ADXL367_X_CHANNEL_INDEX, + ADXL367_Y_CHANNEL_INDEX, + ADXL367_Z_CHANNEL_INDEX, + ADXL367_TEMP_CHANNEL_INDEX, + ADXL367_EX_ADC_CHANNEL_INDEX +}; + +#define ADXL367_X_CHANNEL_MASK BIT(ADXL367_X_CHANNEL_INDEX) +#define ADXL367_Y_CHANNEL_MASK BIT(ADXL367_Y_CHANNEL_INDEX) +#define ADXL367_Z_CHANNEL_MASK BIT(ADXL367_Z_CHANNEL_INDEX) +#define ADXL367_TEMP_CHANNEL_MASK BIT(ADXL367_TEMP_CHANNEL_INDEX) +#define ADXL367_EX_ADC_CHANNEL_MASK BIT(ADXL367_EX_ADC_CHANNEL_INDEX) + +static const enum adxl367_fifo_format adxl367_fifo_formats[] = { + ADXL367_FIFO_FORMAT_X, + ADXL367_FIFO_FORMAT_Y, + ADXL367_FIFO_FORMAT_Z, + ADXL367_FIFO_FORMAT_XT, + ADXL367_FIFO_FORMAT_YT, + ADXL367_FIFO_FORMAT_ZT, + ADXL367_FIFO_FORMAT_XA, + ADXL367_FIFO_FORMAT_YA, + ADXL367_FIFO_FORMAT_ZA, + ADXL367_FIFO_FORMAT_XYZ, + ADXL367_FIFO_FORMAT_XYZT, + ADXL367_FIFO_FORMAT_XYZA, +}; + +static const unsigned long adxl367_channel_masks[] = { + ADXL367_X_CHANNEL_MASK, + ADXL367_Y_CHANNEL_MASK, + ADXL367_Z_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, + ADXL367_Y_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, + ADXL367_Z_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, + ADXL367_Y_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, + ADXL367_Z_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK | + ADXL367_TEMP_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK | + ADXL367_EX_ADC_CHANNEL_MASK, + 0, +}; + +static int adxl367_set_measure_en(struct adxl367_state *st, bool en) +{ + enum adxl367_op_mode op_mode = en ? ADXL367_OP_MEASURE + : ADXL367_OP_STANDBY; + int ret; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_POWER_CTL, + ADXL367_POWER_CTL_MODE_MASK, + FIELD_PREP(ADXL367_POWER_CTL_MODE_MASK, + op_mode)); + if (ret) + return ret; + + /* + * Wait for acceleration output to settle after entering + * measure mode. + */ + if (en) + msleep(100); + + return 0; +} + +static void adxl367_scale_act_thresholds(struct adxl367_state *st, + enum adxl367_range old_range, + enum adxl367_range new_range) +{ + st->act_threshold = st->act_threshold + * adxl367_range_scale_factor_tbl[old_range] + / adxl367_range_scale_factor_tbl[new_range]; + st->inact_threshold = st->inact_threshold + * adxl367_range_scale_factor_tbl[old_range] + / adxl367_range_scale_factor_tbl[new_range]; +} + +static int _adxl367_set_act_threshold(struct adxl367_state *st, + enum adxl367_activity_type act, + unsigned int threshold) +{ + u8 reg = adxl367_threshold_h_reg_tbl[act]; + int ret; + + if (threshold > ADXL367_THRESH_MAX) + return -EINVAL; + + st->act_threshold_buf[0] = FIELD_PREP(ADXL367_THRESH_H_MASK, + FIELD_GET(ADXL367_THRESH_VAL_H_MASK, + threshold)); + st->act_threshold_buf[1] = FIELD_PREP(ADXL367_THRESH_L_MASK, + FIELD_GET(ADXL367_THRESH_VAL_L_MASK, + threshold)); + + ret = regmap_bulk_write(st->regmap, reg, st->act_threshold_buf, + sizeof(st->act_threshold_buf)); + if (ret) + return ret; + + if (act == ADXL367_ACTIVITY) + st->act_threshold = threshold; + else + st->inact_threshold = threshold; + + return 0; +} + +static int adxl367_set_act_threshold(struct adxl367_state *st, + enum adxl367_activity_type act, + unsigned int threshold) +{ + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = _adxl367_set_act_threshold(st, act, threshold); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int adxl367_set_act_proc_mode(struct adxl367_state *st, + enum adxl367_act_proc_mode mode) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL, + ADXL367_ACT_LINKLOOP_MASK, + FIELD_PREP(ADXL367_ACT_LINKLOOP_MASK, + mode)); +} + +static int adxl367_set_act_interrupt_en(struct adxl367_state *st, + enum adxl367_activity_type act, + bool en) +{ + unsigned int mask = adxl367_act_int_mask_tbl[act]; + + return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, + mask, en ? mask : 0); +} + +static int adxl367_get_act_interrupt_en(struct adxl367_state *st, + enum adxl367_activity_type act, + bool *en) +{ + unsigned int mask = adxl367_act_int_mask_tbl[act]; + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val); + if (ret) + return ret; + + *en = !!(val & mask); + + return 0; +} + +static int adxl367_set_act_en(struct adxl367_state *st, + enum adxl367_activity_type act, + enum adxl367_act_en_mode en) +{ + unsigned int ctl_shift = adxl367_act_en_shift_tbl[act]; + + return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL, + ADXL367_ACT_EN_MASK << ctl_shift, + en << ctl_shift); +} + +static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st, + bool en) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, + ADXL367_INT_FIFO_WATERMARK_MASK, + en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0); +} + +static int adxl367_get_fifo_mode(struct adxl367_state *st, + enum adxl367_fifo_mode *fifo_mode) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, ADXL367_REG_FIFO_CTL, &val); + if (ret) + return ret; + + *fifo_mode = FIELD_GET(ADXL367_FIFO_CTL_MODE_MASK, val); + + return 0; +} + +static int adxl367_set_fifo_mode(struct adxl367_state *st, + enum adxl367_fifo_mode fifo_mode) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, + ADXL367_FIFO_CTL_MODE_MASK, + FIELD_PREP(ADXL367_FIFO_CTL_MODE_MASK, + fifo_mode)); +} + +static int adxl367_set_fifo_format(struct adxl367_state *st, + enum adxl367_fifo_format fifo_format) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, + ADXL367_FIFO_CTL_FORMAT_MASK, + FIELD_PREP(ADXL367_FIFO_CTL_FORMAT_MASK, + fifo_format)); +} + +static int adxl367_set_fifo_samples(struct adxl367_state *st, + unsigned int fifo_watermark, + unsigned int fifo_set_size) +{ + unsigned int fifo_samples = fifo_watermark * fifo_set_size; + unsigned int fifo_samples_h, fifo_samples_l; + int ret; + + if (fifo_samples > ADXL367_FIFO_MAX_WATERMARK) + fifo_samples = ADXL367_FIFO_MAX_WATERMARK; + + if (fifo_set_size == 0) + return 0; + + fifo_samples /= fifo_set_size; + + fifo_samples_h = FIELD_PREP(ADXL367_SAMPLES_H_MASK, + FIELD_GET(ADXL367_SAMPLES_VAL_H_MASK, + fifo_samples)); + fifo_samples_l = FIELD_PREP(ADXL367_SAMPLES_L_MASK, + FIELD_GET(ADXL367_SAMPLES_VAL_L_MASK, + fifo_samples)); + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, + ADXL367_SAMPLES_H_MASK, fifo_samples_h); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES, + ADXL367_SAMPLES_L_MASK, fifo_samples_l); +} + +static int adxl367_set_fifo_set_size(struct adxl367_state *st, + unsigned int fifo_set_size) +{ + int ret; + + ret = adxl367_set_fifo_samples(st, st->fifo_watermark, fifo_set_size); + if (ret) + return ret; + + st->fifo_set_size = fifo_set_size; + + return 0; +} + +static int adxl367_set_fifo_watermark(struct adxl367_state *st, + unsigned int fifo_watermark) +{ + int ret; + + ret = adxl367_set_fifo_samples(st, fifo_watermark, st->fifo_set_size); + if (ret) + return ret; + + st->fifo_watermark = fifo_watermark; + + return 0; +} + +static int adxl367_set_range(struct iio_dev *indio_dev, + enum adxl367_range range) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, + ADXL367_FILTER_CTL_RANGE_MASK, + FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, + range)); + if (ret) + goto out; + + adxl367_scale_act_thresholds(st, st->range, range); + + /* Activity thresholds depend on range */ + ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, + st->act_threshold); + if (ret) + goto out; + + ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, + st->inact_threshold); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + if (ret) + goto out; + + st->range = range; + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms) +{ + int freq_hz = adxl367_samp_freq_tbl[st->odr][0]; + int freq_microhz = adxl367_samp_freq_tbl[st->odr][1]; + /* Scale to decihertz to prevent precision loss in 12.5Hz case. */ + int freq_dhz = freq_hz * 10 + freq_microhz / 100000; + + return DIV_ROUND_CLOSEST(ms * freq_dhz, 10000); +} + +static int _adxl367_set_act_time_ms(struct adxl367_state *st, unsigned int ms) +{ + unsigned int val = adxl367_time_ms_to_samples(st, ms); + int ret; + + if (val > ADXL367_TIME_ACT_MAX) + val = ADXL367_TIME_ACT_MAX; + + ret = regmap_write(st->regmap, ADXL367_REG_TIME_ACT, val); + if (ret) + return ret; + + st->act_time_ms = ms; + + return 0; +} + +static int _adxl367_set_inact_time_ms(struct adxl367_state *st, unsigned int ms) +{ + unsigned int val = adxl367_time_ms_to_samples(st, ms); + int ret; + + if (val > ADXL367_TIME_INACT_MAX) + val = ADXL367_TIME_INACT_MAX; + + st->inact_time_buf[0] = FIELD_PREP(ADXL367_TIME_INACT_H_MASK, + FIELD_GET(ADXL367_TIME_INACT_VAL_H_MASK, + val)); + st->inact_time_buf[1] = FIELD_PREP(ADXL367_TIME_INACT_L_MASK, + FIELD_GET(ADXL367_TIME_INACT_VAL_L_MASK, + val)); + + ret = regmap_bulk_write(st->regmap, ADXL367_REG_TIME_INACT_H, + st->inact_time_buf, sizeof(st->inact_time_buf)); + if (ret) + return ret; + + st->inact_time_ms = ms; + + return 0; +} + +static int adxl367_set_act_time_ms(struct adxl367_state *st, + enum adxl367_activity_type act, + unsigned int ms) +{ + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + if (act == ADXL367_ACTIVITY) + ret = _adxl367_set_act_time_ms(st, ms); + else + ret = _adxl367_set_inact_time_ms(st, ms); + + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, + ADXL367_FILTER_CTL_ODR_MASK, + FIELD_PREP(ADXL367_FILTER_CTL_ODR_MASK, + odr)); + if (ret) + return ret; + + /* Activity timers depend on ODR */ + ret = _adxl367_set_act_time_ms(st, st->act_time_ms); + if (ret) + return ret; + + ret = _adxl367_set_inact_time_ms(st, st->inact_time_ms); + if (ret) + return ret; + + st->odr = odr; + + return 0; +} + +static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = _adxl367_set_odr(st, odr); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg, + bool en) +{ + return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK, + en ? ADXL367_ADC_EN_MASK : 0); +} + +static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st, + unsigned int reg, bool en) +{ + int ret; + + switch (reg) { + case ADXL367_REG_TEMP_DATA_H: + ret = adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en); + break; + case ADXL367_REG_EX_ADC_DATA_H: + ret = adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en); + break; + default: + return 0; + } + + if (ret) + return ret; + + if (en) + msleep(100); + + return 0; +} + +static int adxl367_set_temp_adc_mask_en(struct adxl367_state *st, + const unsigned long *active_scan_mask, + bool en) +{ + if (*active_scan_mask & ADXL367_TEMP_CHANNEL_MASK) + return adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en); + else if (*active_scan_mask & ADXL367_EX_ADC_CHANNEL_MASK) + return adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en); + + return 0; +} + +static int adxl367_find_odr(struct adxl367_state *st, int val, int val2, + enum adxl367_odr *odr) +{ + size_t size = ARRAY_SIZE(adxl367_samp_freq_tbl); + int i; + + for (i = 0; i < size; i++) + if (val == adxl367_samp_freq_tbl[i][0] && + val2 == adxl367_samp_freq_tbl[i][1]) + break; + + if (i == size) + return -EINVAL; + + *odr = i; + + return 0; +} + +static int adxl367_find_range(struct adxl367_state *st, int val, int val2, + enum adxl367_range *range) +{ + size_t size = ARRAY_SIZE(adxl367_range_scale_tbl); + int i; + + for (i = 0; i < size; i++) + if (val == adxl367_range_scale_tbl[i][0] && + val2 == adxl367_range_scale_tbl[i][1]) + break; + + if (i == size) + return -EINVAL; + + *range = i; + + return 0; +} + +static int adxl367_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct adxl367_state *st = iio_priv(indio_dev); + u16 sample; + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); + if (ret) + goto out; + + ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, + sizeof(st->sample_buf)); + if (ret) + goto out; + + sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); + *val = sign_extend32(sample, chan->scan_type.realbits - 1); + + ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret ?: IIO_VAL_INT; +} + +static int adxl367_get_status(struct adxl367_state *st, u8 *status, + u16 *fifo_entries) +{ + int ret; + + /* Read STATUS, FIFO_ENT_L and FIFO_ENT_H */ + ret = regmap_bulk_read(st->regmap, ADXL367_REG_STATUS, + st->status_buf, sizeof(st->status_buf)); + if (ret) + return ret; + + st->status_buf[2] &= ADXL367_FIFO_ENT_H_MASK; + + *status = st->status_buf[0]; + *fifo_entries = get_unaligned_le16(&st->status_buf[1]); + + return 0; +} + +static bool adxl367_push_event(struct iio_dev *indio_dev, u8 status) +{ + unsigned int ev_dir; + + if (FIELD_GET(ADXL367_STATUS_ACT_MASK, status)) + ev_dir = IIO_EV_DIR_RISING; + else if (FIELD_GET(ADXL367_STATUS_INACT_MASK, status)) + ev_dir = IIO_EV_DIR_FALLING; + else + return false; + + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, ev_dir), + iio_get_time_ns(indio_dev)); + + return true; +} + +static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status, + u16 fifo_entries) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + int i; + + if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status)) + return false; + + fifo_entries -= fifo_entries % st->fifo_set_size; + + ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries); + if (ret) { + dev_err(st->dev, "Failed to read FIFO: %d\n", ret); + return true; + } + + for (i = 0; i < fifo_entries; i += st->fifo_set_size) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + + return true; +} + +static irqreturn_t adxl367_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct adxl367_state *st = iio_priv(indio_dev); + u16 fifo_entries; + bool handled; + u8 status; + int ret; + + ret = adxl367_get_status(st, &status, &fifo_entries); + if (ret) + return IRQ_NONE; + + handled = adxl367_push_event(indio_dev, status); + handled |= adxl367_push_fifo_data(indio_dev, status, fifo_entries); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static int adxl367_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct adxl367_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static int adxl367_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adxl367_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + return adxl367_read_sample(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + mutex_lock(&st->lock); + *val = adxl367_range_scale_tbl[st->range][0]; + *val2 = adxl367_range_scale_tbl[st->range][1]; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + *val = 1000; + *val2 = ADXL367_TEMP_PER_C; + return IIO_VAL_FRACTIONAL; + case IIO_VOLTAGE: + *val = ADXL367_VOLTAGE_MAX_MV; + *val2 = ADXL367_VOLTAGE_MAX_RAW; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = 25 * ADXL367_TEMP_PER_C - ADXL367_TEMP_25C; + return IIO_VAL_INT; + case IIO_VOLTAGE: + *val = ADXL367_VOLTAGE_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&st->lock); + *val = adxl367_samp_freq_tbl[st->odr][0]; + *val2 = adxl367_samp_freq_tbl[st->odr][1]; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl367_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: { + enum adxl367_odr odr; + + ret = adxl367_find_odr(st, val, val2, &odr); + if (ret) + return ret; + + return adxl367_set_odr(indio_dev, odr); + } + case IIO_CHAN_INFO_SCALE: { + enum adxl367_range range; + + ret = adxl367_find_range(st, val, val2, &range); + if (ret) + return ret; + + return adxl367_set_range(indio_dev, range); + } + default: + return -EINVAL; + } +} + +static int adxl367_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int adxl367_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + *vals = (int *)adxl367_range_scale_tbl; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(adxl367_range_scale_tbl) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)adxl367_samp_freq_tbl; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(adxl367_samp_freq_tbl) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int adxl367_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 adxl367_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: { + switch (dir) { + case IIO_EV_DIR_RISING: + mutex_lock(&st->lock); + *val = st->act_threshold; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + mutex_lock(&st->lock); + *val = st->inact_threshold; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + mutex_lock(&st->lock); + *val = st->act_time_ms; + mutex_unlock(&st->lock); + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_DIR_FALLING: + mutex_lock(&st->lock); + *val = st->inact_time_ms; + mutex_unlock(&st->lock); + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl367_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 adxl367_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0) + return -EINVAL; + + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl367_set_act_threshold(st, ADXL367_ACTIVITY, val); + case IIO_EV_DIR_FALLING: + return adxl367_set_act_threshold(st, ADXL367_INACTIVITY, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + if (val < 0) + return -EINVAL; + + val = val * 1000 + DIV_ROUND_UP(val2, 1000); + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl367_set_act_time_ms(st, ADXL367_ACTIVITY, val); + case IIO_EV_DIR_FALLING: + return adxl367_set_act_time_ms(st, ADXL367_INACTIVITY, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl367_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 adxl367_state *st = iio_priv(indio_dev); + bool en; + int ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = adxl367_get_act_interrupt_en(st, ADXL367_ACTIVITY, &en); + return ret ?: en; + case IIO_EV_DIR_FALLING: + ret = adxl367_get_act_interrupt_en(st, ADXL367_INACTIVITY, &en); + return ret ?: en; + default: + return -EINVAL; + } +} + +static int adxl367_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 adxl367_state *st = iio_priv(indio_dev); + enum adxl367_activity_type act; + int ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + act = ADXL367_ACTIVITY; + break; + case IIO_EV_DIR_FALLING: + act = ADXL367_INACTIVITY; + break; + default: + return -EINVAL; + } + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_act_interrupt_en(st, act, state); + if (ret) + goto out; + + ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED + : ADXL367_ACT_DISABLED); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static ssize_t adxl367_get_fifo_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); + enum adxl367_fifo_mode fifo_mode; + int ret; + + ret = adxl367_get_fifo_mode(st, &fifo_mode); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", fifo_mode != ADXL367_FIFO_MODE_DISABLED); +} + +static ssize_t adxl367_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int fifo_watermark; + + mutex_lock(&st->lock); + fifo_watermark = st->fifo_watermark; + mutex_unlock(&st->lock); + + return sysfs_emit(buf, "%d\n", fifo_watermark); +} + +static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); +static IIO_CONST_ATTR(hwfifo_watermark_max, + __stringify(ADXL367_FIFO_MAX_WATERMARK)); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + adxl367_get_fifo_watermark, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + adxl367_get_fifo_enabled, NULL, 0); + +static const struct attribute *adxl367_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + if (val > ADXL367_FIFO_MAX_WATERMARK) + return -EINVAL; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_watermark(st, val); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask, + enum adxl367_fifo_format *fifo_format) +{ + size_t size = ARRAY_SIZE(adxl367_fifo_formats); + int i; + + for (i = 0; i < size; i++) + if (*scan_mask == adxl367_channel_masks[i]) + break; + + if (i == size) + return false; + + *fifo_format = adxl367_fifo_formats[i]; + + return true; +} + +static int adxl367_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct adxl367_state *st = iio_priv(indio_dev); + enum adxl367_fifo_format fifo_format; + unsigned int fifo_set_size; + int ret; + + if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format)) + return -EINVAL; + + fifo_set_size = bitmap_weight(active_scan_mask, indio_dev->masklength); + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_format(st, fifo_format); + if (ret) + goto out; + + ret = adxl367_set_fifo_set_size(st, fifo_set_size); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int adxl367_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, + true); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_watermark_interrupt_en(st, true); + if (ret) + goto out; + + ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int adxl367_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED); + if (ret) + goto out; + + ret = adxl367_set_fifo_watermark_interrupt_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + if (ret) + goto out; + + ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, + false); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static const struct iio_buffer_setup_ops adxl367_buffer_ops = { + .postenable = adxl367_buffer_postenable, + .predisable = adxl367_buffer_predisable, +}; + +static const struct iio_info adxl367_info = { + .read_raw = adxl367_read_raw, + .write_raw = adxl367_write_raw, + .write_raw_get_fmt = adxl367_write_raw_get_fmt, + .read_avail = adxl367_read_avail, + .read_event_config = adxl367_read_event_config, + .write_event_config = adxl367_write_event_config, + .read_event_value = adxl367_read_event_value, + .write_event_value = adxl367_write_event_value, + .debugfs_reg_access = adxl367_reg_access, + .hwfifo_set_watermark = adxl367_set_watermark, + .update_scan_mode = adxl367_update_scan_mode, +}; + +static const struct iio_event_spec adxl367_events[] = { + { + .type = IIO_EV_TYPE_MAG_REFERENCED, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_MAG_REFERENCED, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_VALUE), + }, +}; + +#define ADXL367_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = (reg), \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = adxl367_events, \ + .num_event_specs = ARRAY_SIZE(adxl367_events), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 14, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADXL367_CHANNEL(index, reg, _type) { \ + .type = (_type), \ + .address = (reg), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 14, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec adxl367_channels[] = { + ADXL367_ACCEL_CHANNEL(ADXL367_X_CHANNEL_INDEX, ADXL367_REG_X_DATA_H, X), + ADXL367_ACCEL_CHANNEL(ADXL367_Y_CHANNEL_INDEX, ADXL367_REG_Y_DATA_H, Y), + ADXL367_ACCEL_CHANNEL(ADXL367_Z_CHANNEL_INDEX, ADXL367_REG_Z_DATA_H, Z), + ADXL367_CHANNEL(ADXL367_TEMP_CHANNEL_INDEX, ADXL367_REG_TEMP_DATA_H, + IIO_TEMP), + ADXL367_CHANNEL(ADXL367_EX_ADC_CHANNEL_INDEX, ADXL367_REG_EX_ADC_DATA_H, + IIO_VOLTAGE), +}; + +static int adxl367_verify_devid(struct adxl367_state *st) +{ + unsigned int val; + int ret; + + ret = regmap_read_poll_timeout(st->regmap, ADXL367_REG_DEVID, val, + val == ADXL367_DEVID_AD, 1000, 10000); + if (ret) + return dev_err_probe(st->dev, -ENODEV, + "Invalid dev id 0x%02X, expected 0x%02X\n", + val, ADXL367_DEVID_AD); + + return 0; +} + +static int adxl367_setup(struct adxl367_state *st) +{ + int ret; + + ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, + ADXL367_2G_RANGE_1G); + if (ret) + return ret; + + ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, + ADXL367_2G_RANGE_100MG); + if (ret) + return ret; + + ret = adxl367_set_act_proc_mode(st, ADXL367_LOOPED); + if (ret) + return ret; + + ret = _adxl367_set_odr(st, ADXL367_ODR_400HZ); + if (ret) + return ret; + + ret = _adxl367_set_act_time_ms(st, 10); + if (ret) + return ret; + + ret = _adxl367_set_inact_time_ms(st, 10000); + if (ret) + return ret; + + return adxl367_set_measure_en(st, true); +} + +static void adxl367_disable_regulators(void *data) +{ + struct adxl367_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); +} + +int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, + void *context, struct regmap *regmap, int irq) +{ + struct iio_dev *indio_dev; + struct adxl367_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->dev = dev; + st->regmap = regmap; + st->context = context; + st->ops = ops; + + mutex_init(&st->lock); + + indio_dev->channels = adxl367_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl367_channels); + indio_dev->available_scan_masks = adxl367_channel_masks; + indio_dev->name = "adxl367"; + indio_dev->info = &adxl367_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + st->regulators[0].supply = "vdd"; + st->regulators[1].supply = "vddio"; + + ret = devm_regulator_bulk_get(st->dev, ARRAY_SIZE(st->regulators), + st->regulators); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(st->dev, adxl367_disable_regulators, st); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to add regulators disable action\n"); + + ret = regmap_write(st->regmap, ADXL367_REG_RESET, ADXL367_RESET_CODE); + if (ret) + return ret; + + ret = adxl367_verify_devid(st); + if (ret) + return ret; + + ret = adxl367_setup(st); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup_ext(st->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &adxl367_buffer_ops, + adxl367_fifo_attributes); + if (ret) + return ret; + + ret = devm_request_threaded_irq(st->dev, irq, NULL, + adxl367_irq_handler, IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(adxl367_probe, IIO_ADXL367); + +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl367.h b/drivers/iio/accel/adxl367.h new file mode 100644 index 000000000000..4a42622149b1 --- /dev/null +++ b/drivers/iio/accel/adxl367.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#ifndef _ADXL367_H_ +#define _ADXL367_H_ + +#include <linux/types.h> + +struct device; +struct regmap; + +struct adxl367_ops { + int (*read_fifo)(void *context, __be16 *fifo_buf, + unsigned int fifo_entries); +}; + +int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, + void *context, struct regmap *regmap, int irq); + +#endif /* _ADXL367_H_ */ diff --git a/drivers/iio/accel/adxl367_i2c.c b/drivers/iio/accel/adxl367_i2c.c new file mode 100644 index 000000000000..3606efa25835 --- /dev/null +++ b/drivers/iio/accel/adxl367_i2c.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl367.h" + +#define ADXL367_I2C_FIFO_DATA 0x42 + +struct adxl367_i2c_state { + struct regmap *regmap; +}; + +static bool adxl367_readable_noinc_reg(struct device *dev, unsigned int reg) +{ + return reg == ADXL367_I2C_FIFO_DATA; +} + +static int adxl367_i2c_read_fifo(void *context, __be16 *fifo_buf, + unsigned int fifo_entries) +{ + struct adxl367_i2c_state *st = context; + + return regmap_noinc_read(st->regmap, ADXL367_I2C_FIFO_DATA, fifo_buf, + fifo_entries * sizeof(*fifo_buf)); +} + +static const struct regmap_config adxl367_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .readable_noinc_reg = adxl367_readable_noinc_reg, +}; + +static const struct adxl367_ops adxl367_i2c_ops = { + .read_fifo = adxl367_i2c_read_fifo, +}; + +static int adxl367_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adxl367_i2c_state *st; + struct regmap *regmap; + + st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &adxl367_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st->regmap = regmap; + + return adxl367_probe(&client->dev, &adxl367_i2c_ops, st, regmap, + client->irq); +} + +static const struct i2c_device_id adxl367_i2c_id[] = { + { "adxl367", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, adxl367_i2c_id); + +static const struct of_device_id adxl367_of_match[] = { + { .compatible = "adi,adxl367" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adxl367_of_match); + +static struct i2c_driver adxl367_i2c_driver = { + .driver = { + .name = "adxl367_i2c", + .of_match_table = adxl367_of_match, + }, + .probe = adxl367_i2c_probe, + .id_table = adxl367_i2c_id, +}; + +module_i2c_driver(adxl367_i2c_driver); + +MODULE_IMPORT_NS(IIO_ADXL367); +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c new file mode 100644 index 000000000000..26dfc821ebbe --- /dev/null +++ b/drivers/iio/accel/adxl367_spi.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "adxl367.h" + +#define ADXL367_SPI_WRITE_COMMAND 0x0A +#define ADXL367_SPI_READ_COMMAND 0x0B +#define ADXL367_SPI_FIFO_COMMAND 0x0D + +struct adxl367_spi_state { + struct spi_device *spi; + + struct spi_message reg_write_msg; + struct spi_transfer reg_write_xfer[2]; + + struct spi_message reg_read_msg; + struct spi_transfer reg_read_xfer[2]; + + struct spi_message fifo_msg; + struct spi_transfer fifo_xfer[2]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 reg_write_tx_buf[1] ____cacheline_aligned; + u8 reg_read_tx_buf[2]; + u8 fifo_tx_buf[1]; +}; + +static int adxl367_read_fifo(void *context, __be16 *fifo_buf, + unsigned int fifo_entries) +{ + struct adxl367_spi_state *st = context; + + st->fifo_xfer[1].rx_buf = fifo_buf; + st->fifo_xfer[1].len = fifo_entries * sizeof(*fifo_buf); + + return spi_sync(st->spi, &st->fifo_msg); +} + +static int adxl367_read(void *context, const void *reg_buf, size_t reg_size, + void *val_buf, size_t val_size) +{ + struct adxl367_spi_state *st = context; + u8 reg = ((const u8 *)reg_buf)[0]; + + st->reg_read_tx_buf[1] = reg; + st->reg_read_xfer[1].rx_buf = val_buf; + st->reg_read_xfer[1].len = val_size; + + return spi_sync(st->spi, &st->reg_read_msg); +} + +static int adxl367_write(void *context, const void *val_buf, size_t val_size) +{ + struct adxl367_spi_state *st = context; + + st->reg_write_xfer[1].tx_buf = val_buf; + st->reg_write_xfer[1].len = val_size; + + return spi_sync(st->spi, &st->reg_write_msg); +} + +static struct regmap_bus adxl367_spi_regmap_bus = { + .read = adxl367_read, + .write = adxl367_write, +}; + +static const struct regmap_config adxl367_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct adxl367_ops adxl367_spi_ops = { + .read_fifo = adxl367_read_fifo, +}; + +static int adxl367_spi_probe(struct spi_device *spi) +{ + struct adxl367_spi_state *st; + struct regmap *regmap; + + st = devm_kzalloc(&spi->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->spi = spi; + + /* + * Xfer: [XFR1] [ XFR2 ] + * Master: 0x0A ADDR DATA0 DATA1 ... DATAN + * Slave: .... .......................... + */ + st->reg_write_tx_buf[0] = ADXL367_SPI_WRITE_COMMAND; + st->reg_write_xfer[0].tx_buf = st->reg_write_tx_buf; + st->reg_write_xfer[0].len = sizeof(st->reg_write_tx_buf); + spi_message_init_with_transfers(&st->reg_write_msg, + st->reg_write_xfer, 2); + + /* + * Xfer: [ XFR1 ] [ XFR2 ] + * Master: 0x0B ADDR ..................... + * Slave: ......... DATA0 DATA1 ... DATAN + */ + st->reg_read_tx_buf[0] = ADXL367_SPI_READ_COMMAND; + st->reg_read_xfer[0].tx_buf = st->reg_read_tx_buf; + st->reg_read_xfer[0].len = sizeof(st->reg_read_tx_buf); + spi_message_init_with_transfers(&st->reg_read_msg, + st->reg_read_xfer, 2); + + /* + * Xfer: [XFR1] [ XFR2 ] + * Master: 0x0D ..................... + * Slave: .... DATA0 DATA1 ... DATAN + */ + st->fifo_tx_buf[0] = ADXL367_SPI_FIFO_COMMAND; + st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; + st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); + spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, 2); + + regmap = devm_regmap_init(&spi->dev, &adxl367_spi_regmap_bus, st, + &adxl367_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adxl367_probe(&spi->dev, &adxl367_spi_ops, st, regmap, spi->irq); +} + +static const struct spi_device_id adxl367_spi_id[] = { + { "adxl367", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, adxl367_spi_id); + +static const struct of_device_id adxl367_of_match[] = { + { .compatible = "adi,adxl367" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adxl367_of_match); + +static struct spi_driver adxl367_spi_driver = { + .driver = { + .name = "adxl367_spi", + .of_match_table = adxl367_of_match, + }, + .probe = adxl367_spi_probe, + .id_table = adxl367_spi_id, +}; + +module_spi_driver(adxl367_spi_driver); + +MODULE_IMPORT_NS(IIO_ADXL367); +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 758952584f8c..e3ecbaee61f7 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1176,7 +1176,7 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) { return (reg == ADXL372_FIFO_DATA); } -EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg); +EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, IIO_ADXL372); int adxl372_probe(struct device *dev, struct regmap *regmap, int irq, const char *name) @@ -1260,7 +1260,7 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(adxl372_probe); +EXPORT_SYMBOL_NS_GPL(adxl372_probe, IIO_ADXL372); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index 9a07ab3d151a..4efb70a5fe40 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -67,3 +67,4 @@ module_i2c_driver(adxl372_i2c_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADXL372); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 1f1352fee99a..2bd267a22f29 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -59,3 +59,4 @@ module_spi_driver(adxl372_spi_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADXL372); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index d8a454c266d5..4f73bc827eec 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -1065,7 +1065,6 @@ static int bma180_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int bma180_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1092,11 +1091,7 @@ static int bma180_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); -#define BMA180_PM_OPS (&bma180_pm_ops) -#else -#define BMA180_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); static const struct i2c_device_id bma180_ids[] = { { "bma023", BMA023 }, @@ -1137,7 +1132,7 @@ MODULE_DEVICE_TABLE(of, bma180_of_match); static struct i2c_driver bma180_driver = { .driver = { .name = "bma180", - .pm = BMA180_PM_OPS, + .pm = pm_sleep_ptr(&bma180_pm_ops), .of_match_table = bma180_of_match, }, .probe = bma180_probe, diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index fd2647b728d3..043002fe6f63 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -136,7 +136,7 @@ const struct regmap_config bma400_regmap_config = { .writeable_reg = bma400_is_writable_reg, .volatile_reg = bma400_is_volatile_reg, }; -EXPORT_SYMBOL(bma400_regmap_config); +EXPORT_SYMBOL_NS(bma400_regmap_config, IIO_BMA400); static const struct iio_mount_matrix * bma400_accel_get_mount_matrix(const struct iio_dev *indio_dev, @@ -826,7 +826,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) return iio_device_register(indio_dev); } -EXPORT_SYMBOL(bma400_probe); +EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400); void bma400_remove(struct device *dev) { @@ -846,7 +846,7 @@ void bma400_remove(struct device *dev) iio_device_unregister(indio_dev); } -EXPORT_SYMBOL(bma400_remove); +EXPORT_SYMBOL_NS(bma400_remove, IIO_BMA400); MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core"); diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c index f50df5310beb..da104ffd3fe0 100644 --- a/drivers/iio/accel/bma400_i2c.c +++ b/drivers/iio/accel/bma400_i2c.c @@ -61,3 +61,4 @@ module_i2c_driver(bma400_i2c_driver); MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (I2C)"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMA400); diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index 9040a717b247..51f23bdc0ea5 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -118,3 +118,4 @@ module_spi_driver(bma400_spi_driver); MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (SPI)"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMA400); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index d11f668016a6..7516d7dde1af 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -203,7 +203,7 @@ const struct regmap_config bmc150_regmap_conf = { .val_bits = 8, .max_register = 0x3f, }; -EXPORT_SYMBOL_GPL(bmc150_regmap_conf); +EXPORT_SYMBOL_NS_GPL(bmc150_regmap_conf, IIO_BMC150); static int bmc150_accel_set_mode(struct bmc150_accel_data *data, enum bmc150_power_modes mode, @@ -1801,7 +1801,7 @@ err_disable_regulators: return ret; } -EXPORT_SYMBOL_GPL(bmc150_accel_core_probe); +EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_probe, IIO_BMC150); void bmc150_accel_core_remove(struct device *dev) { @@ -1824,7 +1824,7 @@ void bmc150_accel_core_remove(struct device *dev) regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); } -EXPORT_SYMBOL_GPL(bmc150_accel_core_remove); +EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_remove, IIO_BMC150); #ifdef CONFIG_PM_SLEEP static int bmc150_accel_suspend(struct device *dev) @@ -1899,7 +1899,7 @@ const struct dev_pm_ops bmc150_accel_pm_ops = { SET_RUNTIME_PM_OPS(bmc150_accel_runtime_suspend, bmc150_accel_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(bmc150_accel_pm_ops); +EXPORT_SYMBOL_NS_GPL(bmc150_accel_pm_ops, IIO_BMC150); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 9e52df9a8f07..dff4d7dd101c 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -280,3 +280,4 @@ module_i2c_driver(bmc150_accel_driver); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("BMC150 I2C accelerometer driver"); +MODULE_IMPORT_NS(IIO_BMC150); diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 80007cc2d044..921fb46be0b8 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -82,3 +82,4 @@ module_spi_driver(bmc150_accel_driver); MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("BMC150 SPI accelerometer driver"); +MODULE_IMPORT_NS(IIO_BMC150); diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index d74465214feb..8b2728bbcade 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -146,7 +146,7 @@ const struct regmap_config bmi088_regmap_conf = { .volatile_table = &bmi088_volatile_table, .cache_type = REGCACHE_RBTREE, }; -EXPORT_SYMBOL_GPL(bmi088_regmap_conf); +EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088); static int bmi088_accel_power_up(struct bmi088_accel_data *data) { @@ -533,7 +533,7 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, return ret; } -EXPORT_SYMBOL_GPL(bmi088_accel_core_probe); +EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088); void bmi088_accel_core_remove(struct device *dev) @@ -547,7 +547,7 @@ void bmi088_accel_core_remove(struct device *dev) pm_runtime_set_suspended(dev); bmi088_accel_power_down(data); } -EXPORT_SYMBOL_GPL(bmi088_accel_core_remove); +EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088); static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) { @@ -571,7 +571,7 @@ const struct dev_pm_ops bmi088_accel_pm_ops = { SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend, bmi088_accel_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(bmi088_accel_pm_ops); +EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088); MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 06d99d9949f3..167c36cf1eb8 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -81,3 +81,4 @@ module_spi_driver(bmi088_accel_driver); MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("BMI088 accelerometer driver (SPI)"); +MODULE_IMPORT_NS(IIO_BMI088); diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 9633bdae5fd4..04e9c5678964 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -153,7 +153,6 @@ static int da280_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int da280_suspend(struct device *dev) { return da280_enable(to_i2c_client(dev), false); @@ -163,9 +162,8 @@ static int da280_resume(struct device *dev) { return da280_enable(to_i2c_client(dev), true); } -#endif -static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); static const struct acpi_device_id da280_acpi_match[] = { {"MIRAACC", da280}, @@ -184,7 +182,7 @@ static struct i2c_driver da280_driver = { .driver = { .name = "da280", .acpi_match_table = ACPI_PTR(da280_acpi_match), - .pm = &da280_pm_ops, + .pm = pm_sleep_ptr(&da280_pm_ops), }, .probe = da280_probe, .id_table = da280_i2c_id, diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index 04e13487e706..ec4e29d260f7 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -256,7 +256,6 @@ static int da311_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int da311_suspend(struct device *dev) { return da311_enable(to_i2c_client(dev), false); @@ -266,9 +265,8 @@ static int da311_resume(struct device *dev) { return da311_enable(to_i2c_client(dev), true); } -#endif -static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume); static const struct i2c_device_id da311_i2c_id[] = { {"da311", 0}, @@ -279,7 +277,7 @@ MODULE_DEVICE_TABLE(i2c, da311_i2c_id); static struct i2c_driver da311_driver = { .driver = { .name = "da311", - .pm = &da311_pm_ops, + .pm = pm_sleep_ptr(&da311_pm_ops), }, .probe = da311_probe, .id_table = da311_i2c_id, diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c index de2868c28d95..4b69c8530f5e 100644 --- a/drivers/iio/accel/dmard06.c +++ b/drivers/iio/accel/dmard06.c @@ -170,7 +170,6 @@ static int dmard06_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int dmard06_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -199,11 +198,8 @@ static int dmard06_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); -#define DMARD06_PM_OPS (&dmard06_pm_ops) -#else -#define DMARD06_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, + dmard06_resume); static const struct i2c_device_id dmard06_id[] = { { "dmard05", 0 }, @@ -227,7 +223,7 @@ static struct i2c_driver dmard06_driver = { .driver = { .name = DMARD06_DRV_NAME, .of_match_table = dmard06_of_match, - .pm = DMARD06_PM_OPS, + .pm = pm_sleep_ptr(&dmard06_pm_ops), }, }; module_i2c_driver(dmard06_driver); diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c index e6e28c964777..53ab6078cb7f 100644 --- a/drivers/iio/accel/dmard09.c +++ b/drivers/iio/accel/dmard09.c @@ -126,7 +126,7 @@ static int dmard09_probe(struct i2c_client *client, } static const struct i2c_device_id dmard09_id[] = { - { "dmard09", 0}, + { "dmard09", 0 }, { }, }; diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c index f9f173eec202..8ac62ec0a04a 100644 --- a/drivers/iio/accel/dmard10.c +++ b/drivers/iio/accel/dmard10.c @@ -218,7 +218,6 @@ static int dmard10_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int dmard10_suspend(struct device *dev) { return dmard10_shutdown(to_i2c_client(dev)); @@ -228,9 +227,9 @@ static int dmard10_resume(struct device *dev) { return dmard10_reset(to_i2c_client(dev)); } -#endif -static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, + dmard10_resume); static const struct i2c_device_id dmard10_i2c_id[] = { {"dmard10", 0}, @@ -241,7 +240,7 @@ MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id); static struct i2c_driver dmard10_driver = { .driver = { .name = "dmard10", - .pm = &dmard10_pm_ops, + .pm = pm_sleep_ptr(&dmard10_pm_ops), }, .probe = dmard10_probe, .id_table = dmard10_i2c_id, diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index f7fd9e046588..a9d2f10d5d45 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -178,7 +178,7 @@ const struct regmap_config fxls8962af_i2c_regmap_conf = { .val_bits = 8, .max_register = FXLS8962AF_MAX_REG, }; -EXPORT_SYMBOL_GPL(fxls8962af_i2c_regmap_conf); +EXPORT_SYMBOL_NS_GPL(fxls8962af_i2c_regmap_conf, IIO_FXLS8962AF); const struct regmap_config fxls8962af_spi_regmap_conf = { .reg_bits = 8, @@ -186,7 +186,7 @@ const struct regmap_config fxls8962af_spi_regmap_conf = { .val_bits = 8, .max_register = FXLS8962AF_MAX_REG, }; -EXPORT_SYMBOL_GPL(fxls8962af_spi_regmap_conf); +EXPORT_SYMBOL_NS_GPL(fxls8962af_spi_regmap_conf, IIO_FXLS8962AF); enum { fxls8962af_idx_x, @@ -1240,7 +1240,7 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(fxls8962af_core_probe); +EXPORT_SYMBOL_NS_GPL(fxls8962af_core_probe, IIO_FXLS8962AF); static int __maybe_unused fxls8962af_runtime_suspend(struct device *dev) { @@ -1306,7 +1306,7 @@ const struct dev_pm_ops fxls8962af_pm_ops = { SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend, fxls8962af_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(fxls8962af_pm_ops); +EXPORT_SYMBOL_NS_GPL(fxls8962af_pm_ops, IIO_FXLS8962AF); MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer driver"); diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c index 6bde9891effb..8fbadfea1620 100644 --- a/drivers/iio/accel/fxls8962af-i2c.c +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -55,3 +55,4 @@ module_i2c_driver(fxls8962af_driver); MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_FXLS8962AF); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c index 6f4dff3238d3..885b3ab7fcb5 100644 --- a/drivers/iio/accel/fxls8962af-spi.c +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -55,3 +55,4 @@ module_spi_driver(fxls8962af_driver); MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_FXLS8962AF); diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index 274b41a6e603..c8dc52f11037 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -65,3 +65,4 @@ module_i2c_driver(kxsd9_i2c_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("KXSD9 accelerometer I2C interface"); +MODULE_IMPORT_NS(IIO_KXSD9); diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 57c451cfb9e5..ec17e35e573e 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -64,3 +64,4 @@ module_spi_driver(kxsd9_spi_driver); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Kionix KXSD9 SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_KXSD9); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 552eba5e8b4f..3975860331a6 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -476,7 +476,7 @@ err_power_down: return ret; } -EXPORT_SYMBOL(kxsd9_common_probe); +EXPORT_SYMBOL_NS(kxsd9_common_probe, IIO_KXSD9); void kxsd9_common_remove(struct device *dev) { @@ -490,7 +490,7 @@ void kxsd9_common_remove(struct device *dev) pm_runtime_disable(dev); kxsd9_power_down(st); } -EXPORT_SYMBOL(kxsd9_common_remove); +EXPORT_SYMBOL_NS(kxsd9_common_remove, IIO_KXSD9); #ifdef CONFIG_PM static int kxsd9_runtime_suspend(struct device *dev) @@ -516,7 +516,7 @@ const struct dev_pm_ops kxsd9_dev_pm_ops = { SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend, kxsd9_runtime_resume, NULL) }; -EXPORT_SYMBOL(kxsd9_dev_pm_ops); +EXPORT_SYMBOL_NS(kxsd9_dev_pm_ops, IIO_KXSD9); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Kionix KXSD9 driver"); diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c index 735002b716f3..679e69cd7657 100644 --- a/drivers/iio/accel/mc3230.c +++ b/drivers/iio/accel/mc3230.c @@ -160,7 +160,6 @@ static int mc3230_remove(struct i2c_client *client) return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY); } -#ifdef CONFIG_PM_SLEEP static int mc3230_suspend(struct device *dev) { struct mc3230_data *data; @@ -178,9 +177,8 @@ static int mc3230_resume(struct device *dev) return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE); } -#endif -static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); static const struct i2c_device_id mc3230_i2c_id[] = { {"mc3230", 0}, @@ -191,7 +189,7 @@ MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id); static struct i2c_driver mc3230_driver = { .driver = { .name = "mc3230", - .pm = &mc3230_pm_ops, + .pm = pm_sleep_ptr(&mc3230_pm_ops), }, .probe = mc3230_probe, .remove = mc3230_remove, diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index e6739ba74edf..a34195b3215d 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -238,7 +238,7 @@ const struct regmap_config mma7455_core_regmap = { .val_bits = 8, .max_register = MMA7455_REG_TW, }; -EXPORT_SYMBOL_GPL(mma7455_core_regmap); +EXPORT_SYMBOL_NS_GPL(mma7455_core_regmap, IIO_MMA7455); int mma7455_core_probe(struct device *dev, struct regmap *regmap, const char *name) @@ -293,7 +293,7 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap, return 0; } -EXPORT_SYMBOL_GPL(mma7455_core_probe); +EXPORT_SYMBOL_NS_GPL(mma7455_core_probe, IIO_MMA7455); void mma7455_core_remove(struct device *dev) { @@ -306,7 +306,7 @@ void mma7455_core_remove(struct device *dev) regmap_write(mma7455->regmap, MMA7455_REG_MCTL, MMA7455_MCTL_MODE_STANDBY); } -EXPORT_SYMBOL_GPL(mma7455_core_remove); +EXPORT_SYMBOL_NS_GPL(mma7455_core_remove, IIO_MMA7455); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver"); diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c index 8a5256516f9f..a3b84e8a3ea8 100644 --- a/drivers/iio/accel/mma7455_i2c.c +++ b/drivers/iio/accel/mma7455_i2c.c @@ -61,3 +61,4 @@ module_i2c_driver(mma7455_i2c_driver); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MMA7455); diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index b746031551a3..fcdde2e8a84b 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -47,3 +47,4 @@ module_spi_driver(mma7455_spi_driver); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MMA7455); diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index 24b83ccdb950..112a5a33c29f 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -222,7 +222,6 @@ static int mma7660_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int mma7660_suspend(struct device *dev) { struct mma7660_data *data; @@ -241,12 +240,8 @@ static int mma7660_resume(struct device *dev) return mma7660_set_mode(data, MMA7660_MODE_ACTIVE); } -static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume); - -#define MMA7660_PM_OPS (&mma7660_pm_ops) -#else -#define MMA7660_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, + mma7660_resume); static const struct i2c_device_id mma7660_i2c_id[] = { {"mma7660", 0}, @@ -270,7 +265,7 @@ MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); static struct i2c_driver mma7660_driver = { .driver = { .name = "mma7660", - .pm = MMA7660_PM_OPS, + .pm = pm_sleep_ptr(&mma7660_pm_ops), .of_match_table = mma7660_of_match, .acpi_match_table = ACPI_PTR(mma7660_acpi_id), }, diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 64b82b4503ad..9c02c681c84c 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -104,6 +104,7 @@ struct mma8452_data { struct i2c_client *client; struct mutex lock; + struct iio_mount_matrix orientation; u8 ctrl_reg1; u8 data_cfg; const struct mma_chip_info *chip_info; @@ -176,6 +177,7 @@ static const struct mma8452_event_regs trans_ev_regs = { * @enabled_events: event flags enabled and handled by this driver */ struct mma_chip_info { + const char *name; u8 chip_id; const struct iio_chan_spec *channels; int num_channels; @@ -379,8 +381,8 @@ static ssize_t mma8452_show_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { - struct mma8452_data *data = iio_priv(i2c_get_clientdata( - to_i2c_client(dev))); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales, ARRAY_SIZE(data->chip_info->mma_scales)); @@ -1189,6 +1191,20 @@ static const struct attribute_group mma8452_event_attribute_group = { .attrs = mma8452_event_attributes, }; +static const struct iio_mount_matrix * +mma8452_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mma8452_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info mma8452_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mma8452_get_mount_matrix), + { } +}; + #define MMA8452_FREEFALL_CHANNEL(modifier) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -1227,6 +1243,7 @@ static const struct attribute_group mma8452_event_attribute_group = { }, \ .event_spec = mma8452_transient_event, \ .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \ + .ext_info = mma8452_ext_info, \ } #define MMA8652_CHANNEL(axis, idx, bits) { \ @@ -1248,6 +1265,7 @@ static const struct attribute_group mma8452_event_attribute_group = { }, \ .event_spec = mma8452_motion_event, \ .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \ + .ext_info = mma8452_ext_info, \ } static const struct iio_chan_spec mma8451_channels[] = { @@ -1301,6 +1319,7 @@ enum { static const struct mma_chip_info mma_chip_info_table[] = { [mma8451] = { + .name = "mma8451", .chip_id = MMA8451_DEVICE_ID, .channels = mma8451_channels, .num_channels = ARRAY_SIZE(mma8451_channels), @@ -1325,6 +1344,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { MMA8452_INT_FF_MT, }, [mma8452] = { + .name = "mma8452", .chip_id = MMA8452_DEVICE_ID, .channels = mma8452_channels, .num_channels = ARRAY_SIZE(mma8452_channels), @@ -1341,6 +1361,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { MMA8452_INT_FF_MT, }, [mma8453] = { + .name = "mma8453", .chip_id = MMA8453_DEVICE_ID, .channels = mma8453_channels, .num_channels = ARRAY_SIZE(mma8453_channels), @@ -1357,6 +1378,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { MMA8452_INT_FF_MT, }, [mma8652] = { + .name = "mma8652", .chip_id = MMA8652_DEVICE_ID, .channels = mma8652_channels, .num_channels = ARRAY_SIZE(mma8652_channels), @@ -1366,6 +1388,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { .enabled_events = MMA8452_INT_FF_MT, }, [mma8653] = { + .name = "mma8653", .chip_id = MMA8653_DEVICE_ID, .channels = mma8653_channels, .num_channels = ARRAY_SIZE(mma8653_channels), @@ -1380,6 +1403,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { .enabled_events = MMA8452_INT_FF_MT, }, [fxls8471] = { + .name = "fxls8471", .chip_id = FXLS8471_DEVICE_ID, .channels = mma8451_channels, .num_channels = ARRAY_SIZE(mma8451_channels), @@ -1522,13 +1546,6 @@ static int mma8452_probe(struct i2c_client *client, struct mma8452_data *data; struct iio_dev *indio_dev; int ret; - const struct of_device_id *match; - - match = of_match_device(mma8452_dt_ids, &client->dev); - if (!match) { - dev_err(&client->dev, "unknown device model\n"); - return -ENODEV; - } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -1537,7 +1554,18 @@ static int mma8452_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; mutex_init(&data->lock); - data->chip_info = match->data; + + data->chip_info = device_get_match_data(&client->dev); + if (!data->chip_info && id) { + data->chip_info = &mma_chip_info_table[id->driver_data]; + } else { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } + + ret = iio_read_mount_matrix(&client->dev, &data->orientation); + if (ret) + return ret; data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(data->vdd_reg)) @@ -1581,11 +1609,11 @@ static int mma8452_probe(struct i2c_client *client, } dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n", - match->compatible, data->chip_info->chip_id); + data->chip_info->name, data->chip_info->chip_id); i2c_set_clientdata(client, indio_dev); indio_dev->info = &mma8452_info; - indio_dev->name = id->name; + indio_dev->name = data->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->num_channels; @@ -1810,7 +1838,7 @@ MODULE_DEVICE_TABLE(i2c, mma8452_id); static struct i2c_driver mma8452_driver = { .driver = { .name = "mma8452", - .of_match_table = of_match_ptr(mma8452_dt_ids), + .of_match_table = mma8452_dt_ids, .pm = &mma8452_pm_ops, }, .probe = mma8452_probe, diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index c53a3398b14c..123cdbbb265c 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -526,7 +526,6 @@ static int mma9551_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM static int mma9551_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -558,9 +557,7 @@ static int mma9551_runtime_resume(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int mma9551_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -586,12 +583,10 @@ static int mma9551_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops mma9551_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) - SET_RUNTIME_PM_OPS(mma9551_runtime_suspend, - mma9551_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) + RUNTIME_PM_OPS(mma9551_runtime_suspend, mma9551_runtime_resume, NULL) }; static const struct acpi_device_id mma9551_acpi_match[] = { @@ -612,7 +607,7 @@ static struct i2c_driver mma9551_driver = { .driver = { .name = MMA9551_DRV_NAME, .acpi_match_table = ACPI_PTR(mma9551_acpi_match), - .pm = &mma9551_pm_ops, + .pm = pm_ptr(&mma9551_pm_ops), }, .probe = mma9551_probe, .remove = mma9551_remove, @@ -625,3 +620,4 @@ MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver"); +MODULE_IMPORT_NS(IIO_MMA9551); diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index fbf2e2c45678..64ca7d7a9673 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -219,7 +219,7 @@ int mma9551_read_config_byte(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG, reg, NULL, 0, val, 1); } -EXPORT_SYMBOL(mma9551_read_config_byte); +EXPORT_SYMBOL_NS(mma9551_read_config_byte, IIO_MMA9551); /** * mma9551_write_config_byte() - write 1 configuration byte @@ -244,7 +244,7 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, &val, 1, NULL, 0); } -EXPORT_SYMBOL(mma9551_write_config_byte); +EXPORT_SYMBOL_NS(mma9551_write_config_byte, IIO_MMA9551); /** * mma9551_read_status_byte() - read 1 status byte @@ -269,7 +269,7 @@ int mma9551_read_status_byte(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS, reg, NULL, 0, val, 1); } -EXPORT_SYMBOL(mma9551_read_status_byte); +EXPORT_SYMBOL_NS(mma9551_read_status_byte, IIO_MMA9551); /** * mma9551_read_config_word() - read 1 config word @@ -300,7 +300,7 @@ int mma9551_read_config_word(struct i2c_client *client, u8 app_id, return ret; } -EXPORT_SYMBOL(mma9551_read_config_word); +EXPORT_SYMBOL_NS(mma9551_read_config_word, IIO_MMA9551); /** * mma9551_write_config_word() - write 1 config word @@ -327,7 +327,7 @@ int mma9551_write_config_word(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, (u8 *)&v, 2, NULL, 0); } -EXPORT_SYMBOL(mma9551_write_config_word); +EXPORT_SYMBOL_NS(mma9551_write_config_word, IIO_MMA9551); /** * mma9551_read_status_word() - read 1 status word @@ -358,7 +358,7 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id, return ret; } -EXPORT_SYMBOL(mma9551_read_status_word); +EXPORT_SYMBOL_NS(mma9551_read_status_word, IIO_MMA9551); /** * mma9551_read_config_words() - read multiple config words @@ -397,7 +397,7 @@ int mma9551_read_config_words(struct i2c_client *client, u8 app_id, return 0; } -EXPORT_SYMBOL(mma9551_read_config_words); +EXPORT_SYMBOL_NS(mma9551_read_config_words, IIO_MMA9551); /** * mma9551_read_status_words() - read multiple status words @@ -436,7 +436,7 @@ int mma9551_read_status_words(struct i2c_client *client, u8 app_id, return 0; } -EXPORT_SYMBOL(mma9551_read_status_words); +EXPORT_SYMBOL_NS(mma9551_read_status_words, IIO_MMA9551); /** * mma9551_write_config_words() - write multiple config words @@ -471,7 +471,7 @@ int mma9551_write_config_words(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, (u8 *)be_buf, len * sizeof(u16), NULL, 0); } -EXPORT_SYMBOL(mma9551_write_config_words); +EXPORT_SYMBOL_NS(mma9551_write_config_words, IIO_MMA9551); /** * mma9551_update_config_bits() - update bits in register @@ -507,7 +507,7 @@ int mma9551_update_config_bits(struct i2c_client *client, u8 app_id, return mma9551_write_config_byte(client, app_id, reg, tmp); } -EXPORT_SYMBOL(mma9551_update_config_bits); +EXPORT_SYMBOL_NS(mma9551_update_config_bits, IIO_MMA9551); /** * mma9551_gpio_config() - configure gpio @@ -586,7 +586,7 @@ int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin, return ret; } -EXPORT_SYMBOL(mma9551_gpio_config); +EXPORT_SYMBOL_NS(mma9551_gpio_config, IIO_MMA9551); /** * mma9551_read_version() - read device version information @@ -616,7 +616,7 @@ int mma9551_read_version(struct i2c_client *client) return 0; } -EXPORT_SYMBOL(mma9551_read_version); +EXPORT_SYMBOL_NS(mma9551_read_version, IIO_MMA9551); /** * mma9551_set_device_state() - sets HW power mode @@ -646,7 +646,7 @@ int mma9551_set_device_state(struct i2c_client *client, bool enable) MMA9551_SLEEP_CFG_FLEEN : MMA9551_SLEEP_CFG_SNCEN); } -EXPORT_SYMBOL(mma9551_set_device_state); +EXPORT_SYMBOL_NS(mma9551_set_device_state, IIO_MMA9551); /** * mma9551_set_power_state() - sets runtime PM state @@ -680,7 +680,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on) return 0; } -EXPORT_SYMBOL(mma9551_set_power_state); +EXPORT_SYMBOL_NS(mma9551_set_power_state, IIO_MMA9551); /** * mma9551_sleep() - sleep @@ -699,7 +699,7 @@ void mma9551_sleep(int freq) else msleep_interruptible(sleep_val); } -EXPORT_SYMBOL(mma9551_sleep); +EXPORT_SYMBOL_NS(mma9551_sleep, IIO_MMA9551); /** * mma9551_read_accel_chan() - read accelerometer channel @@ -755,7 +755,7 @@ out_poweroff: mma9551_set_power_state(client, false); return ret; } -EXPORT_SYMBOL(mma9551_read_accel_chan); +EXPORT_SYMBOL_NS(mma9551_read_accel_chan, IIO_MMA9551); /** * mma9551_read_accel_scale() - read accelerometer scale @@ -773,7 +773,7 @@ int mma9551_read_accel_scale(int *val, int *val2) return IIO_VAL_INT_PLUS_MICRO; } -EXPORT_SYMBOL(mma9551_read_accel_scale); +EXPORT_SYMBOL_NS(mma9551_read_accel_scale, IIO_MMA9551); /** * mma9551_app_reset() - reset application @@ -792,7 +792,7 @@ int mma9551_app_reset(struct i2c_client *client, u32 app_mask) MMA9551_RSC_OFFSET(app_mask), MMA9551_RSC_VAL(app_mask)); } -EXPORT_SYMBOL(mma9551_app_reset); +EXPORT_SYMBOL_NS(mma9551_app_reset, IIO_MMA9551); MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>"); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 5ff6bc70708b..09df58d4be33 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1165,7 +1165,6 @@ static int mma9553_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM static int mma9553_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1197,9 +1196,7 @@ static int mma9553_runtime_resume(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int mma9553_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1225,12 +1222,10 @@ static int mma9553_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops mma9553_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume) - SET_RUNTIME_PM_OPS(mma9553_runtime_suspend, - mma9553_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume) + RUNTIME_PM_OPS(mma9553_runtime_suspend, mma9553_runtime_resume, NULL) }; static const struct acpi_device_id mma9553_acpi_match[] = { @@ -1251,7 +1246,7 @@ static struct i2c_driver mma9553_driver = { .driver = { .name = MMA9553_DRV_NAME, .acpi_match_table = ACPI_PTR(mma9553_acpi_match), - .pm = &mma9553_pm_ops, + .pm = pm_ptr(&mma9553_pm_ops), }, .probe = mma9553_probe, .remove = mma9553_remove, @@ -1263,3 +1258,4 @@ module_i2c_driver(mma9553_driver); MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MMA9553L pedometer platform driver"); +MODULE_IMPORT_NS(IIO_MMA9551); diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index 04dcb2b657ee..a1164b439f41 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -142,3 +142,4 @@ module_platform_driver(ssp_accel_driver); MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>"); MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_SSP_SENSORS); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 8750dea56fcb..00e056c21bfc 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -36,6 +36,7 @@ enum st_accel_type { LIS3DHH, LIS2DE12, LIS2HH12, + SC7A20, ST_ACCEL_MAX, }; @@ -61,6 +62,7 @@ enum st_accel_type { #define LIS3DE_ACCEL_DEV_NAME "lis3de" #define LIS2DE12_ACCEL_DEV_NAME "lis2de12" #define LIS2HH12_ACCEL_DEV_NAME "lis2hh12" +#define SC7A20_ACCEL_DEV_NAME "sc7a20" #ifdef CONFIG_IIO_BUFFER int st_accel_allocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c index fc82fa83f1fb..b2977ae19b69 100644 --- a/drivers/iio/accel/st_accel_buffer.c +++ b/drivers/iio/accel/st_accel_buffer.c @@ -7,7 +7,6 @@ * Denis Ciocca <denis.ciocca@st.com> */ -#include <linux/module.h> #include <linux/kernel.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> @@ -65,7 +64,3 @@ int st_accel_allocate_ring(struct iio_dev *indio_dev) return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, NULL, &st_sensors_trigger_handler, &st_accel_buffer_setup_ops); } - -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 31ea19d0ba71..5c5da6fdb490 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1087,6 +1087,89 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = true, .bootime = 2, }, + { + /* + * Not an ST part. Register-compatible with the LIS2DH, even + * though the WAI value is different. + */ + .wai = 0x11, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = SC7A20_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = 0x20, + .mask = 0xf0, + .odr_avl = { + { .hz = 1, .value = 0x01, }, + { .hz = 10, .value = 0x02, }, + { .hz = 25, .value = 0x03, }, + { .hz = 50, .value = 0x04, }, + { .hz = 100, .value = 0x05, }, + { .hz = 200, .value = 0x06, }, + { .hz = 400, .value = 0x07, }, + { .hz = 1600, .value = 0x08, }, + }, + }, + .pw = { + .addr = 0x20, + .mask = 0xf0, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = 0x23, + .mask = 0x30, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = 0x00, + .gain = IIO_G_TO_M_S_2(1000), + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = 0x01, + .gain = IIO_G_TO_M_S_2(2000), + }, + [2] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = 0x02, + .gain = IIO_G_TO_M_S_2(4000), + }, + [3] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = 0x03, + .gain = IIO_G_TO_M_S_2(12000), + }, + }, + }, + .bdu = { + .addr = 0x23, + .mask = 0x80, + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = 0x10, + }, + .addr_ihl = 0x25, + .mask_ihl = 0x02, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, + }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, }; /* Default accel DRDY is available on INT1 pin */ @@ -1329,7 +1412,7 @@ const struct st_sensor_settings *st_accel_get_settings(const char *name) return &st_accel_sensors_settings[index]; } -EXPORT_SYMBOL(st_accel_get_settings); +EXPORT_SYMBOL_NS(st_accel_get_settings, IIO_ST_SENSORS); int st_accel_common_probe(struct iio_dev *indio_dev) { @@ -1383,8 +1466,9 @@ int st_accel_common_probe(struct iio_dev *indio_dev) return devm_iio_device_register(parent, indio_dev); } -EXPORT_SYMBOL(st_accel_common_probe); +EXPORT_SYMBOL_NS(st_accel_common_probe, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index c0ce78eebad9..96adc4344f4a 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -107,6 +107,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis2hh12", .data = LIS2HH12_ACCEL_DEV_NAME, }, + { + .compatible = "silan,sc7a20", + .data = SC7A20_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -142,6 +146,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS3DE_ACCEL_DEV_NAME }, { LIS2DE12_ACCEL_DEV_NAME }, { LIS2HH12_ACCEL_DEV_NAME }, + { SC7A20_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); @@ -194,3 +199,4 @@ module_i2c_driver(st_accel_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index b74a1c6d03de..108b63d0146c 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -164,3 +164,4 @@ module_spi_driver(st_accel_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index de0cdf8c1f94..a71dfff3ca4a 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -611,7 +611,6 @@ static int stk8312_remove(struct i2c_client *client) return stk8312_set_mode(data, STK8312_MODE_STANDBY); } -#ifdef CONFIG_PM_SLEEP static int stk8312_suspend(struct device *dev) { struct stk8312_data *data; @@ -630,12 +629,8 @@ static int stk8312_resume(struct device *dev) return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE); } -static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume); - -#define STK8312_PM_OPS (&stk8312_pm_ops) -#else -#define STK8312_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, + stk8312_resume); static const struct i2c_device_id stk8312_i2c_id[] = { /* Deprecated in favour of lowercase form */ @@ -648,7 +643,7 @@ MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id); static struct i2c_driver stk8312_driver = { .driver = { .name = STK8312_DRIVER_NAME, - .pm = STK8312_PM_OPS, + .pm = pm_sleep_ptr(&stk8312_pm_ops), }, .probe = stk8312_probe, .remove = stk8312_remove, diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 517c57ed9e94..0067ec5cbae8 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -504,7 +504,6 @@ static int stk8ba50_remove(struct i2c_client *client) return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND); } -#ifdef CONFIG_PM_SLEEP static int stk8ba50_suspend(struct device *dev) { struct stk8ba50_data *data; @@ -523,12 +522,8 @@ static int stk8ba50_resume(struct device *dev) return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL); } -static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume); - -#define STK8BA50_PM_OPS (&stk8ba50_pm_ops) -#else -#define STK8BA50_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, + stk8ba50_resume); static const struct i2c_device_id stk8ba50_i2c_id[] = { {"stk8ba50", 0}, @@ -546,7 +541,7 @@ MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id); static struct i2c_driver stk8ba50_driver = { .driver = { .name = "stk8ba50", - .pm = STK8BA50_PM_OPS, + .pm = pm_sleep_ptr(&stk8ba50_pm_ops), .acpi_match_table = ACPI_PTR(stk8ba50_acpi_id), }, .probe = stk8ba50_probe, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4fdc8bfbb407..71ab0a06aa82 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -64,6 +64,17 @@ config AD7266 To compile this driver as a module, choose M here: the module will be called ad7266. +config AD7280 + tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System" + depends on SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD7280A + Lithium Ion Battery Monitoring System. + + To compile this driver as a module, choose M here: the + module will be called ad7280a + config AD7291 tristate "Analog Devices AD7291 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 4a8f1833993b..39d806f6d457 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7280) += ad7280a.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7292) += ad7292.o obj-$(CONFIG_AD7298) += ad7298.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 4c46a201d4ef..930ce96e6ff5 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -942,7 +942,6 @@ static const struct iio_info ab8500_gpadc_info = { .read_raw = ab8500_gpadc_read_raw, }; -#ifdef CONFIG_PM static int ab8500_gpadc_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -965,7 +964,6 @@ static int ab8500_gpadc_runtime_resume(struct device *dev) return ret; } -#endif /** * ab8500_gpadc_parse_channel() - process devicetree channel configuration @@ -1199,20 +1197,16 @@ static int ab8500_gpadc_remove(struct platform_device *pdev) return 0; } -static const struct dev_pm_ops ab8500_gpadc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, - ab8500_gpadc_runtime_resume, - NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops, + ab8500_gpadc_runtime_suspend, + ab8500_gpadc_runtime_resume, NULL); static struct platform_driver ab8500_gpadc_driver = { .probe = ab8500_gpadc_probe, .remove = ab8500_gpadc_remove, .driver = { .name = "ab8500-gpadc", - .pm = &ab8500_gpadc_pm_ops, + .pm = pm_ptr(&ab8500_gpadc_pm_ops), }, }; builtin_platform_driver(ab8500_gpadc_driver); diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 63b4d6ea4566..8e252cde735b 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -260,7 +260,7 @@ int ad7091r_probe(struct device *dev, const char *name, return devm_iio_device_register(dev, iio_dev); } -EXPORT_SYMBOL_GPL(ad7091r_probe); +EXPORT_SYMBOL_NS_GPL(ad7091r_probe, IIO_AD7091R); static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) { @@ -290,7 +290,7 @@ const struct regmap_config ad7091r_regmap_config = { .writeable_reg = ad7091r_writeable_reg, .volatile_reg = ad7091r_volatile_reg, }; -EXPORT_SYMBOL_GPL(ad7091r_regmap_config); +EXPORT_SYMBOL_NS_GPL(ad7091r_regmap_config, IIO_AD7091R); MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters"); diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index 9665679c3ea6..47f5763023a4 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -111,3 +111,4 @@ module_i2c_driver(ad7091r5_driver); MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD7091R); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index b400bbe291aa..c47ead15f6e5 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -970,3 +970,4 @@ module_spi_driver(ad71124_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index cc990205f306..770b4e59238f 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -433,7 +433,7 @@ static ssize_t ad7192_show_ac_excitation(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7192_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX)); + return sysfs_emit(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX)); } static ssize_t ad7192_show_bridge_switch(struct device *dev, @@ -443,7 +443,7 @@ static ssize_t ad7192_show_bridge_switch(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7192_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW)); + return sysfs_emit(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW)); } static ssize_t ad7192_set(struct device *dev, @@ -1048,3 +1048,4 @@ module_spi_driver(ad7192_driver); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7193, AD7195 ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c new file mode 100644 index 000000000000..ef9d27759961 --- /dev/null +++ b/drivers/iio/adc/ad7280a.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7280A Lithium Ion Battery Monitoring System + * + * Copyright 2011 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> + +/* Registers */ + +#define AD7280A_CELL_VOLTAGE_1_REG 0x0 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_2_REG 0x1 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_3_REG 0x2 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_4_REG 0x3 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_5_REG 0x4 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_6_REG 0x5 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_1_REG 0x6 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_2_REG 0x7 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_3_REG 0x8 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_4_REG 0x9 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_5_REG 0xA /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_6_REG 0xB /* D11 to D0, Read only */ +#define AD7280A_SELF_TEST_REG 0xC /* D11 to D0, Read only */ + +#define AD7280A_CTRL_HB_REG 0xD /* D15 to D8, Read/write */ +#define AD7280A_CTRL_HB_CONV_INPUT_MSK GENMASK(7, 6) +#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0 +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_5 1 +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL 2 +#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST 3 +#define AD7280A_CTRL_HB_CONV_RREAD_MSK GENMASK(5, 4) +#define AD7280A_CTRL_HB_CONV_RREAD_ALL 0 +#define AD7280A_CTRL_HB_CONV_RREAD_6CELL_AUX1_3_5 1 +#define AD7280A_CTRL_HB_CONV_RREAD_6CELL 2 +#define AD7280A_CTRL_HB_CONV_RREAD_NO 3 +#define AD7280A_CTRL_HB_CONV_START_MSK BIT(3) +#define AD7280A_CTRL_HB_CONV_START_CNVST 0 +#define AD7280A_CTRL_HB_CONV_START_CS 1 +#define AD7280A_CTRL_HB_CONV_AVG_MSK GENMASK(2, 1) +#define AD7280A_CTRL_HB_CONV_AVG_DIS 0 +#define AD7280A_CTRL_HB_CONV_AVG_2 1 +#define AD7280A_CTRL_HB_CONV_AVG_4 2 +#define AD7280A_CTRL_HB_CONV_AVG_8 3 +#define AD7280A_CTRL_HB_PWRDN_SW BIT(0) + +#define AD7280A_CTRL_LB_REG 0xE /* D7 to D0, Read/write */ +#define AD7280A_CTRL_LB_SWRST_MSK BIT(7) +#define AD7280A_CTRL_LB_ACQ_TIME_MSK GENMASK(6, 5) +#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0 +#define AD7280A_CTRL_LB_ACQ_TIME_800ns 1 +#define AD7280A_CTRL_LB_ACQ_TIME_1200ns 2 +#define AD7280A_CTRL_LB_ACQ_TIME_1600ns 3 +#define AD7280A_CTRL_LB_MUST_SET BIT(4) +#define AD7280A_CTRL_LB_THERMISTOR_MSK BIT(3) +#define AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK BIT(2) +#define AD7280A_CTRL_LB_INC_DEV_ADDR_MSK BIT(1) +#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK BIT(0) + +#define AD7280A_CELL_OVERVOLTAGE_REG 0xF /* D7 to D0, Read/write */ +#define AD7280A_CELL_UNDERVOLTAGE_REG 0x10 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_OVERVOLTAGE_REG 0x11 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_UNDERVOLTAGE_REG 0x12 /* D7 to D0, Read/write */ + +#define AD7280A_ALERT_REG 0x13 /* D7 to D0, Read/write */ +#define AD7280A_ALERT_REMOVE_MSK GENMASK(3, 0) +#define AD7280A_ALERT_REMOVE_AUX5 BIT(0) +#define AD7280A_ALERT_REMOVE_AUX3_AUX5 BIT(1) +#define AD7280A_ALERT_REMOVE_VIN5 BIT(2) +#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3) +#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6) +#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6)) + +#define AD7280A_CELL_BALANCE_REG 0x14 /* D7 to D0, Read/write */ +#define AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK GENMASK(7, 2) +#define AD7280A_CB1_TIMER_REG 0x15 /* D7 to D0, Read/write */ +#define AD7280A_CB_TIMER_VAL_MSK GENMASK(7, 3) +#define AD7280A_CB2_TIMER_REG 0x16 /* D7 to D0, Read/write */ +#define AD7280A_CB3_TIMER_REG 0x17 /* D7 to D0, Read/write */ +#define AD7280A_CB4_TIMER_REG 0x18 /* D7 to D0, Read/write */ +#define AD7280A_CB5_TIMER_REG 0x19 /* D7 to D0, Read/write */ +#define AD7280A_CB6_TIMER_REG 0x1A /* D7 to D0, Read/write */ +#define AD7280A_PD_TIMER_REG 0x1B /* D7 to D0, Read/write */ +#define AD7280A_READ_REG 0x1C /* D7 to D0, Read/write */ +#define AD7280A_READ_ADDR_MSK GENMASK(7, 2) +#define AD7280A_CNVST_CTRL_REG 0x1D /* D7 to D0, Read/write */ + +/* Transfer fields */ +#define AD7280A_TRANS_WRITE_DEVADDR_MSK GENMASK(31, 27) +#define AD7280A_TRANS_WRITE_ADDR_MSK GENMASK(26, 21) +#define AD7280A_TRANS_WRITE_VAL_MSK GENMASK(20, 13) +#define AD7280A_TRANS_WRITE_ALL_MSK BIT(12) +#define AD7280A_TRANS_WRITE_CRC_MSK GENMASK(10, 3) +#define AD7280A_TRANS_WRITE_RES_PATTERN 0x2 + +/* Layouts differ for channel vs other registers */ +#define AD7280A_TRANS_READ_DEVADDR_MSK GENMASK(31, 27) +#define AD7280A_TRANS_READ_CONV_CHANADDR_MSK GENMASK(26, 23) +#define AD7280A_TRANS_READ_CONV_DATA_MSK GENMASK(22, 11) +#define AD7280A_TRANS_READ_REG_REGADDR_MSK GENMASK(26, 21) +#define AD7280A_TRANS_READ_REG_DATA_MSK GENMASK(20, 13) +#define AD7280A_TRANS_READ_WRITE_ACK_MSK BIT(10) +#define AD7280A_TRANS_READ_CRC_MSK GENMASK(9, 2) + +/* Magic value used to indicate this special case */ +#define AD7280A_ALL_CELLS (0xAD << 16) + +#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */ +#define AD7280A_MAX_CHAIN 8 +#define AD7280A_CELLS_PER_DEV 6 +#define AD7280A_BITS 12 +#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6_REG - \ + AD7280A_CELL_VOLTAGE_1_REG + 1) + +#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \ + (c)) +#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \ + (c) - AD7280A_CELLS_PER_DEV) + +#define AD7280A_DEVADDR_MASTER 0 +#define AD7280A_DEVADDR_ALL 0x1F + +static const unsigned short ad7280a_n_avg[4] = {1, 2, 4, 8}; +static const unsigned short ad7280a_t_acq_ns[4] = {470, 1030, 1510, 1945}; + +/* 5-bit device address is sent LSB first */ +static unsigned int ad7280a_devaddr(unsigned int addr) +{ + return ((addr & 0x1) << 4) | + ((addr & 0x2) << 2) | + (addr & 0x4) | + ((addr & 0x8) >> 2) | + ((addr & 0x10) >> 4); +} + +/* + * During a read a valid write is mandatory. + * So writing to the highest available address (Address 0x1F) and setting the + * address all parts bit to 0 is recommended. + * So the TXVAL is AD7280A_DEVADDR_ALL + CRC + */ +#define AD7280A_READ_TXVAL 0xF800030A + +/* + * AD7280 CRC + * + * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F + */ +#define POLYNOM 0x2F + +struct ad7280_state { + struct spi_device *spi; + struct iio_chan_spec *channels; + unsigned int chain_last_alert_ignore; + bool thermistor_term_en; + int slave_num; + int scan_cnt; + int readback_delay_us; + unsigned char crc_tab[CRC8_TABLE_SIZE]; + u8 oversampling_ratio; + u8 acquisition_time; + unsigned char ctrl_lb; + unsigned char cell_threshhigh; + unsigned char cell_threshlow; + unsigned char aux_threshhigh; + unsigned char aux_threshlow; + unsigned char cb_mask[AD7280A_MAX_CHAIN]; + struct mutex lock; /* protect sensor state */ + + __be32 tx ____cacheline_aligned; + __be32 rx; +}; + +static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val) +{ + unsigned char crc; + + crc = crc_tab[val >> 16 & 0xFF]; + crc = crc_tab[crc ^ (val >> 8 & 0xFF)]; + + return crc ^ (val & 0xFF); +} + +static int ad7280_check_crc(struct ad7280_state *st, unsigned int val) +{ + unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10); + + if (crc != ((val >> 2) & 0xFF)) + return -EIO; + + return 0; +} + +/* + * After initiating a conversion sequence we need to wait until the conversion + * is done. The delay is typically in the range of 15..30us however depending on + * the number of devices in the daisy chain, the number of averages taken, + * conversion delays and acquisition time options it may take up to 250us, in + * this case we better sleep instead of busy wait. + */ + +static void ad7280_delay(struct ad7280_state *st) +{ + if (st->readback_delay_us < 50) + udelay(st->readback_delay_us); + else + usleep_range(250, 500); +} + +static int __ad7280_read32(struct ad7280_state *st, unsigned int *val) +{ + int ret; + struct spi_transfer t = { + .tx_buf = &st->tx, + .rx_buf = &st->rx, + .len = sizeof(st->tx), + }; + + st->tx = cpu_to_be32(AD7280A_READ_TXVAL); + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + *val = be32_to_cpu(st->rx); + + return 0; +} + +static int ad7280_write(struct ad7280_state *st, unsigned int devaddr, + unsigned int addr, bool all, unsigned int val) +{ + unsigned int reg = FIELD_PREP(AD7280A_TRANS_WRITE_DEVADDR_MSK, devaddr) | + FIELD_PREP(AD7280A_TRANS_WRITE_ADDR_MSK, addr) | + FIELD_PREP(AD7280A_TRANS_WRITE_VAL_MSK, val) | + FIELD_PREP(AD7280A_TRANS_WRITE_ALL_MSK, all); + + reg |= FIELD_PREP(AD7280A_TRANS_WRITE_CRC_MSK, + ad7280_calc_crc8(st->crc_tab, reg >> 11)); + /* Reserved b010 pattern not included crc calc */ + reg |= AD7280A_TRANS_WRITE_RES_PATTERN; + + st->tx = cpu_to_be32(reg); + + return spi_write(st->spi, &st->tx, sizeof(st->tx)); +} + +static int ad7280_read_reg(struct ad7280_state *st, unsigned int devaddr, + unsigned int addr) +{ + int ret; + unsigned int tmp; + + /* turns off the read operation on all parts */ + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_NO) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + /* turns on the read operation on the addressed part */ + ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + /* Set register address on the part to be read from */ + ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0, + FIELD_PREP(AD7280A_READ_ADDR_MSK, addr)); + if (ret) + return ret; + + ret = __ad7280_read32(st, &tmp); + if (ret) + return ret; + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) || + (FIELD_GET(AD7280A_TRANS_READ_REG_REGADDR_MSK, tmp) != addr)) + return -EFAULT; + + return FIELD_GET(AD7280A_TRANS_READ_REG_DATA_MSK, tmp); +} + +static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr, + unsigned int addr) +{ + int ret; + unsigned int tmp; + + ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0, + FIELD_PREP(AD7280A_READ_ADDR_MSK, addr)); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_NO) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK, + AD7280A_CTRL_HB_CONV_START_CS) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + ad7280_delay(st); + + ret = __ad7280_read32(st, &tmp); + if (ret) + return ret; + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) || + (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) != addr)) + return -EFAULT; + + return FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp); +} + +static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt, + unsigned int *array) +{ + int i, ret; + unsigned int tmp, sum = 0; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1, + AD7280A_CELL_VOLTAGE_1_REG << 2); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK, + AD7280A_CTRL_HB_CONV_START_CS) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + ad7280_delay(st); + + for (i = 0; i < cnt; i++) { + ret = __ad7280_read32(st, &tmp); + if (ret) + return ret; + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if (array) + array[i] = tmp; + /* only sum cell voltages */ + if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) <= + AD7280A_CELL_VOLTAGE_6_REG) + sum += FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp); + } + + return sum; +} + +static void ad7280_sw_power_down(void *data) +{ + struct ad7280_state *st = data; + + ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + AD7280A_CTRL_HB_PWRDN_SW | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio)); +} + +static int ad7280_chain_setup(struct ad7280_state *st) +{ + unsigned int val, n; + int ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1, + FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) | + FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) | + AD7280A_CTRL_LB_MUST_SET | + FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 1) | + st->ctrl_lb); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1, + FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) | + FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) | + AD7280A_CTRL_LB_MUST_SET | + FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 0) | + st->ctrl_lb); + if (ret) + goto error_power_down; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1, + FIELD_PREP(AD7280A_READ_ADDR_MSK, AD7280A_CTRL_LB_REG)); + if (ret) + goto error_power_down; + + for (n = 0; n <= AD7280A_MAX_CHAIN; n++) { + ret = __ad7280_read32(st, &val); + if (ret) + goto error_power_down; + + if (val == 0) + return n - 1; + + if (ad7280_check_crc(st, val)) { + ret = -EIO; + goto error_power_down; + } + + if (n != ad7280a_devaddr(FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, val))) { + ret = -EIO; + goto error_power_down; + } + } + ret = -EFAULT; + +error_power_down: + ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + AD7280A_CTRL_HB_PWRDN_SW | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio)); + + return ret; +} + +static ssize_t ad7280_show_balance_sw(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad7280_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", + !!(st->cb_mask[chan->address >> 8] & + BIT(chan->address & 0xFF))); +} + +static ssize_t ad7280_store_balance_sw(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7280_state *st = iio_priv(indio_dev); + unsigned int devaddr, ch; + bool readin; + int ret; + + ret = strtobool(buf, &readin); + if (ret) + return ret; + + devaddr = chan->address >> 8; + ch = chan->address & 0xFF; + + mutex_lock(&st->lock); + if (readin) + st->cb_mask[devaddr] |= BIT(ch); + else + st->cb_mask[devaddr] &= ~BIT(ch); + + ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE_REG, 0, + FIELD_PREP(AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK, + st->cb_mask[devaddr])); + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +static ssize_t ad7280_show_balance_timer(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad7280_state *st = iio_priv(indio_dev); + unsigned int msecs; + int ret; + + mutex_lock(&st->lock); + ret = ad7280_read_reg(st, chan->address >> 8, + (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + msecs = FIELD_GET(AD7280A_CB_TIMER_VAL_MSK, ret) * 71500; + + return sysfs_emit(buf, "%u.%u\n", msecs / 1000, msecs % 1000); +} + +static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7280_state *st = iio_priv(indio_dev); + int val, val2; + int ret; + + ret = iio_str_to_fixpoint(buf, 1000, &val, &val2); + if (ret) + return ret; + + val = val * 1000 + val2; + val /= 71500; + + if (val > 31) + return -EINVAL; + + mutex_lock(&st->lock); + ret = ad7280_write(st, chan->address >> 8, + (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG, 0, + FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val)); + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec_ext_info ad7280_cell_ext_info[] = { + { + .name = "balance_switch_en", + .read = ad7280_show_balance_sw, + .write = ad7280_store_balance_sw, + .shared = IIO_SEPARATE, + }, { + .name = "balance_switch_timer", + .read = ad7280_show_balance_timer, + .write = ad7280_store_balance_timer, + .shared = IIO_SEPARATE, + }, + {} +}; + +static const struct iio_event_spec ad7280_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, +}; + +static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i, + bool irq_present) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = i; + chan->channel2 = chan->channel + 1; + if (irq_present) { + chan->event_spec = ad7280_events; + chan->num_event_specs = ARRAY_SIZE(ad7280_events); + } + chan->ext_info = ad7280_cell_ext_info; +} + +static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i, + bool irq_present) +{ + chan->type = IIO_TEMP; + chan->channel = i; + if (irq_present) { + chan->event_spec = ad7280_events; + chan->num_event_specs = ARRAY_SIZE(ad7280_events); + } +} + +static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr, + int cnt) +{ + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->address = addr; + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 32; +} + +static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan, + int cnt, int dev) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = 0; + chan->channel2 = dev * AD7280A_CELLS_PER_DEV; + chan->address = AD7280A_ALL_CELLS; + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 32; + chan->scan_type.storagebits = 32; +} + +static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt, + bool irq_present) +{ + int addr, ch, i; + struct iio_chan_spec *chan; + + for (ch = AD7280A_CELL_VOLTAGE_1_REG; ch <= AD7280A_AUX_ADC_6_REG; ch++) { + chan = &st->channels[*cnt]; + + if (ch < AD7280A_AUX_ADC_1_REG) { + i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch); + ad7280_voltage_channel_init(chan, i, irq_present); + } else { + i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch); + ad7280_temp_channel_init(chan, i, irq_present); + } + + addr = ad7280a_devaddr(dev) << 8 | ch; + ad7280_common_fields_init(chan, addr, *cnt); + + (*cnt)++; + } +} + +static int ad7280_channel_init(struct ad7280_state *st, bool irq_present) +{ + int dev, cnt = 0; + + st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 1, + sizeof(*st->channels), GFP_KERNEL); + if (!st->channels) + return -ENOMEM; + + for (dev = 0; dev <= st->slave_num; dev++) + ad7280_init_dev_channels(st, dev, &cnt, irq_present); + + ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev); + + return cnt + 1; +} + +static int ad7280a_read_thresh(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 ad7280_state *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_VOLTAGE: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = 1000 + (st->cell_threshhigh * 1568L) / 100; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = 1000 + (st->cell_threshlow * 1568L) / 100; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_TEMP: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = ((st->aux_threshhigh) * 196L) / 10; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = (st->aux_threshlow * 196L) / 10; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } +} + +static int ad7280a_write_thresh(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 ad7280_state *st = iio_priv(indio_dev); + unsigned int addr; + long value; + int ret; + + if (val2 != 0) + return -EINVAL; + + mutex_lock(&st->lock); + switch (chan->type) { + case IIO_VOLTAGE: + value = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */ + value = clamp(value, 0L, 0xFFL); + switch (dir) { + case IIO_EV_DIR_RISING: + addr = AD7280A_CELL_OVERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, val); + if (ret) + break; + st->cell_threshhigh = value; + break; + case IIO_EV_DIR_FALLING: + addr = AD7280A_CELL_UNDERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, val); + if (ret) + break; + st->cell_threshlow = value; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + break; + case IIO_TEMP: + value = (val * 10) / 196; /* LSB 19.6mV */ + value = clamp(value, 0L, 0xFFL); + switch (dir) { + case IIO_EV_DIR_RISING: + addr = AD7280A_AUX_ADC_OVERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, val); + if (ret) + break; + st->aux_threshhigh = val; + break; + case IIO_EV_DIR_FALLING: + addr = AD7280A_AUX_ADC_UNDERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, val); + if (ret) + break; + st->aux_threshlow = val; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + break; + default: + ret = -EINVAL; + goto err_unlock; + } + +err_unlock: + mutex_unlock(&st->lock); + + return ret; +} + +static irqreturn_t ad7280_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad7280_state *st = iio_priv(indio_dev); + unsigned int *channels; + int i, ret; + + channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL); + if (!channels) + return IRQ_HANDLED; + + ret = ad7280_read_all_channels(st, st->scan_cnt, channels); + if (ret < 0) + goto out; + + for (i = 0; i < st->scan_cnt; i++) { + unsigned int val; + + val = FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, channels[i]); + if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, channels[i]) <= + AD7280A_CELL_VOLTAGE_6_REG) { + if (val >= st->cell_threshhigh) { + u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0, + IIO_EV_DIR_RISING, + IIO_EV_TYPE_THRESH, + 0, 0, 0); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } else if (val <= st->cell_threshlow) { + u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0, + IIO_EV_DIR_FALLING, + IIO_EV_TYPE_THRESH, + 0, 0, 0); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } + } else { + if (val >= st->aux_threshhigh) { + u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } else if (val <= st->aux_threshlow) { + u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } + } + } + +out: + kfree(channels); + + return IRQ_HANDLED; +} + +static void ad7280_update_delay(struct ad7280_state *st) +{ + /* + * Total Conversion Time = ((tACQ + tCONV) * + * (Number of Conversions per Part)) − + * tACQ + ((N - 1) * tDELAY) + * + * Readback Delay = Total Conversion Time + tWAIT + */ + + st->readback_delay_us = + ((ad7280a_t_acq_ns[st->acquisition_time & 0x3] + 720) * + (AD7280A_NUM_CH * ad7280a_n_avg[st->oversampling_ratio & 0x3])) - + ad7280a_t_acq_ns[st->acquisition_time & 0x3] + st->slave_num * 250; + + /* Convert to usecs */ + st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000); + st->readback_delay_us += 5; /* Add tWAIT */ +} + +static int ad7280_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7280_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + if (chan->address == AD7280A_ALL_CELLS) + ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); + else + ret = ad7280_read_channel(st, chan->address >> 8, + chan->address & 0xFF); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6_REG) + *val = 4000; + else + *val = 5000; + + *val2 = AD7280A_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = ad7280a_n_avg[st->oversampling_ratio]; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int ad7280_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad7280_state *st = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val2 != 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(ad7280a_n_avg); i++) { + if (val == ad7280a_n_avg[i]) { + st->oversampling_ratio = i; + ad7280_update_delay(st); + return 0; + } + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static const struct iio_info ad7280_info = { + .read_raw = ad7280_read_raw, + .write_raw = ad7280_write_raw, + .read_event_value = &ad7280a_read_thresh, + .write_event_value = &ad7280a_write_thresh, +}; + +static const struct iio_info ad7280_info_no_irq = { + .read_raw = ad7280_read_raw, + .write_raw = ad7280_write_raw, +}; + +static int ad7280_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7280_state *st; + int ret; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + mutex_init(&st->lock); + + st->thermistor_term_en = + device_property_read_bool(dev, "adi,thermistor-termination"); + + if (device_property_present(dev, "adi,acquisition-time-ns")) { + u32 val; + + ret = device_property_read_u32(dev, "adi,acquisition-time-ns", &val); + if (ret) + return ret; + + switch (val) { + case 400: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns; + break; + case 800: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_800ns; + break; + case 1200: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1200ns; + break; + case 1600: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1600ns; + break; + default: + dev_err(dev, "Firmware provided acquisition time is invalid\n"); + return -EINVAL; + } + } else { + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns; + } + + /* Alert masks are intended for when particular inputs are not wired up */ + if (device_property_present(dev, "adi,voltage-alert-last-chan")) { + u32 val; + + ret = device_property_read_u32(dev, "adi,voltage-alert-last-chan", &val); + if (ret) + return ret; + + switch (val) { + case 3: + st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN4_VIN5; + break; + case 4: + st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN5; + break; + case 5: + break; + default: + dev_err(dev, + "Firmware provided last voltage alert channel invalid\n"); + break; + } + } + crc8_populate_msb(st->crc_tab, POLYNOM); + + st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ; + st->spi->mode = SPI_MODE_1; + spi_setup(st->spi); + + st->ctrl_lb = FIELD_PREP(AD7280A_CTRL_LB_ACQ_TIME_MSK, st->acquisition_time) | + FIELD_PREP(AD7280A_CTRL_LB_THERMISTOR_MSK, st->thermistor_term_en); + st->oversampling_ratio = 0; /* No oversampling */ + + ret = ad7280_chain_setup(st); + if (ret < 0) + return ret; + + st->slave_num = ret; + st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH; + st->cell_threshhigh = 0xFF; + st->aux_threshhigh = 0xFF; + + ret = devm_add_action_or_reset(dev, ad7280_sw_power_down, st); + if (ret) + return ret; + + ad7280_update_delay(st); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7280_channel_init(st, spi->irq > 0); + if (ret < 0) + return ret; + + indio_dev->num_channels = ret; + indio_dev->channels = st->channels; + if (spi->irq > 0) { + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, + AD7280A_ALERT_REG, 1, + AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN); + if (ret) + return ret; + + ret = ad7280_write(st, ad7280a_devaddr(st->slave_num), + AD7280A_ALERT_REG, 0, + AD7280A_ALERT_GEN_STATIC_HIGH | + FIELD_PREP(AD7280A_ALERT_REMOVE_MSK, + st->chain_last_alert_ignore)); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, spi->irq, + NULL, + ad7280_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + indio_dev->name, + indio_dev); + if (ret) + return ret; + + indio_dev->info = &ad7280_info; + } else { + indio_dev->info = &ad7280_info_no_irq; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad7280_id[] = { + {"ad7280a", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7280_id); + +static struct spi_driver ad7280_driver = { + .driver = { + .name = "ad7280", + }, + .probe = ad7280_probe, + .id_table = ad7280_id, +}; +module_spi_driver(ad7280_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7280A"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 0a60ecc69d38..3b193dc26438 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -693,7 +693,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(ad7606_probe); +EXPORT_SYMBOL_NS_GPL(ad7606_probe, IIO_AD7606); #ifdef CONFIG_PM_SLEEP @@ -725,7 +725,7 @@ static int ad7606_resume(struct device *dev) } SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume); -EXPORT_SYMBOL_GPL(ad7606_pm_ops); +EXPORT_SYMBOL_NS_GPL(ad7606_pm_ops, IIO_AD7606); #endif diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c index f732b3ac7878..8888e56b5e90 100644 --- a/drivers/iio/adc/ad7606_par.c +++ b/drivers/iio/adc/ad7606_par.c @@ -101,3 +101,4 @@ module_platform_driver(ad7606_driver); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD7606); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 29945ad07dca..263a778bcf25 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -362,3 +362,4 @@ module_spi_driver(ad7606_driver); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD7606); diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index b6e8c8abf6f4..a813fe04787c 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -375,3 +375,4 @@ module_spi_driver(ad7780_driver); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index cb579aa89f39..fee8d129a5f0 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -474,3 +474,4 @@ module_spi_driver(ad7791_driver); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 0e7ab3fb072a..5f8cb9aaac70 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -867,3 +867,4 @@ module_spi_driver(ad7793_driver); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index cd418bd8bd87..ebcd52526cac 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -42,7 +42,7 @@ void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm) * to select the channel */ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; } -EXPORT_SYMBOL_GPL(ad_sd_set_comm); +EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, IIO_AD_SIGMA_DELTA); /** * ad_sd_write_reg() - Write a register @@ -94,7 +94,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, return ret; } -EXPORT_SYMBOL_GPL(ad_sd_write_reg); +EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, IIO_AD_SIGMA_DELTA); static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, uint8_t *val) @@ -171,7 +171,7 @@ int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, out: return ret; } -EXPORT_SYMBOL_GPL(ad_sd_read_reg); +EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, IIO_AD_SIGMA_DELTA); /** * ad_sd_reset() - Reset the serial interface @@ -199,7 +199,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, return ret; } -EXPORT_SYMBOL_GPL(ad_sd_reset); +EXPORT_SYMBOL_NS_GPL(ad_sd_reset, IIO_AD_SIGMA_DELTA); int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) @@ -238,7 +238,7 @@ out: return ret; } -EXPORT_SYMBOL_GPL(ad_sd_calibrate); +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, IIO_AD_SIGMA_DELTA); /** * ad_sd_calibrate_all() - Performs channel calibration @@ -262,7 +262,7 @@ int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, return 0; } -EXPORT_SYMBOL_GPL(ad_sd_calibrate_all); +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, IIO_AD_SIGMA_DELTA); /** * ad_sigma_delta_single_conversion() - Performs a single data conversion @@ -337,7 +337,7 @@ out: return IIO_VAL_INT; } -EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion); +EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, IIO_AD_SIGMA_DELTA); static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) { @@ -465,7 +465,7 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) return 0; } -EXPORT_SYMBOL_GPL(ad_sd_validate_trigger); +EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA); static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) { @@ -524,7 +524,7 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi return devm_ad_sd_probe_trigger(dev, indio_dev); } -EXPORT_SYMBOL_GPL(devm_ad_sd_setup_buffer_and_trigger); +EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, IIO_AD_SIGMA_DELTA); /** * ad_sd_init() - Initializes a ad_sigma_delta struct @@ -545,7 +545,7 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL_GPL(ad_sd_init); +EXPORT_SYMBOL_NS_GPL(ad_sd_init, IIO_AD_SIGMA_DELTA); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index e939b84cbb56..0793d2474cdc 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -539,7 +539,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) data->clk_scaler = devm_clk_hw_register_divider( &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, - data->model_data->scaler_bit_width, 0, &data->clk_lock); + data->model_data->scaler_bit_width, + data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0, + &data->clk_lock); if (IS_ERR(data->clk_scaler)) return PTR_ERR(data->clk_scaler); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 5a7d3a3a5fa8..532daaa6f943 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1234,7 +1234,6 @@ static int at91_adc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int at91_adc_suspend(struct device *dev) { struct iio_dev *idev = dev_get_drvdata(dev); @@ -1256,9 +1255,9 @@ static int at91_adc_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, + at91_adc_resume); static const struct at91_adc_trigger at91sam9260_triggers[] = { { .name = "timer-counter-0", .value = 0x1 }, @@ -1386,7 +1385,7 @@ static struct platform_driver at91_adc_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = at91_adc_dt_ids, - .pm = &at91_adc_pm_ops, + .pm = pm_sleep_ptr(&at91_adc_pm_ops), }, }; diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index 40e59f4c95bc..b6c4ef70484e 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -474,7 +474,7 @@ static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata, for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { calibration_data[0] = 0; calibration_data[1] = 0; - cal_data_diff = 0; + cpcap_adc_setup_calibrate(ddata, channel); error = regmap_read(ddata->reg, calibration_register, &calibration_data[0]); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 3b3868aa2533..cff1ba57fb16 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -968,7 +968,6 @@ static int exynos_adc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int exynos_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -1001,11 +1000,9 @@ static int exynos_adc_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, - exynos_adc_suspend, - exynos_adc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend, + exynos_adc_resume); static struct platform_driver exynos_adc_driver = { .probe = exynos_adc_probe, @@ -1013,7 +1010,7 @@ static struct platform_driver exynos_adc_driver = { .driver = { .name = "exynos-adc", .of_match_table = exynos_adc_match, - .pm = &exynos_adc_pm_ops, + .pm = pm_sleep_ptr(&exynos_adc_pm_ops), }, }; diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index e665e14c6e54..8eb0140df133 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -529,7 +529,7 @@ static const struct of_device_id hi8435_dt_ids[] = { MODULE_DEVICE_TABLE(of, hi8435_dt_ids); static const struct spi_device_id hi8435_id[] = { - { "hi8435", 0}, + { "hi8435", 0 }, { } }; MODULE_DEVICE_TABLE(spi, hi8435_id); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 4f9992a51e64..8d902a32a0fd 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -539,7 +539,7 @@ static ssize_t ina2xx_allow_async_readout_show(struct device *dev, { struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); - return sprintf(buf, "%d\n", chip->allow_async_readout); + return sysfs_emit(buf, "%d\n", chip->allow_async_readout); } static ssize_t ina2xx_allow_async_readout_store(struct device *dev, diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 01a4275e9c46..f982f00303dc 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -429,7 +429,7 @@ static ssize_t max9611_shunt_resistor_show(struct device *dev, i = max9611->shunt_resistor_uohm / 1000000; r = max9611->shunt_resistor_uohm % 1000000; - return sprintf(buf, "%u.%06u\n", i, r); + return sysfs_emit(buf, "%u.%06u\n", i, r); } static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444, diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index d4fccd52ef08..e78c96a185db 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -46,6 +46,11 @@ struct mt6577_auxadc_device { const struct mtk_auxadc_compatible *dev_comp; }; +static const struct mtk_auxadc_compatible mt8186_compat = { + .sample_data_cali = false, + .check_global_idle = false, +}; + static const struct mtk_auxadc_compatible mt8173_compat = { .sample_data_cali = false, .check_global_idle = true, @@ -330,11 +335,12 @@ static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops, mt6577_auxadc_resume); static const struct of_device_id mt6577_auxadc_of_match[] = { - { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat}, - { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat}, - { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat}, - { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat}, - { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat}, + { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8186-auxadc", .data = &mt8186_compat }, + { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat }, { } }; MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match); diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index f9c8385c72d3..61e80bf3d05e 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -653,7 +653,6 @@ static int palmas_gpadc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) { int adc_period, conv; @@ -822,12 +821,9 @@ static int palmas_gpadc_resume(struct device *dev) return 0; }; -#endif -static const struct dev_pm_ops palmas_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(palmas_gpadc_suspend, - palmas_gpadc_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_gpadc_suspend, + palmas_gpadc_resume); static const struct of_device_id of_palmas_gpadc_match_tbl[] = { { .compatible = "ti,palmas-gpadc", }, @@ -840,7 +836,7 @@ static struct platform_driver palmas_gpadc_driver = { .remove = palmas_gpadc_remove, .driver = { .name = MOD_NAME, - .pm = &palmas_pm_ops, + .pm = pm_sleep_ptr(&palmas_pm_ops), .of_match_table = of_palmas_gpadc_match_tbl, }, }; diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 21d7eff645c3..5e9e56821075 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -175,7 +175,7 @@ struct xoadc_channel { const char *datasheet_name; u8 pre_scale_mux:2; u8 amux_channel:4; - const struct vadc_prescale_ratio prescale; + const struct u32_fract prescale; enum iio_chan_type type; enum vadc_scale_fn_type scale_fn_type; u8 amux_ip_rsv:3; @@ -218,7 +218,9 @@ struct xoadc_variant { .datasheet_name = __stringify(_dname), \ .pre_scale_mux = _presmux, \ .amux_channel = _amux, \ - .prescale = { .num = _prenum, .den = _preden }, \ + .prescale = { \ + .numerator = _prenum, .denominator = _preden, \ + }, \ .type = _type, \ .scale_fn_type = _scale, \ .amux_ip_rsv = _amip, \ @@ -809,12 +811,11 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, BIT(IIO_CHAN_INFO_PROCESSED); iio_chan->indexed = 1; - dev_dbg(dev, "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" " - "ref voltage: %d, decimation %d " - "prescale %d/%d, scale function %d\n", + dev_dbg(dev, + "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" ref voltage: %d, decimation %d prescale %d/%d, scale function %d\n", hwchan->pre_scale_mux, hwchan->amux_channel, ch->name, - ch->amux_ip_rsv, ch->decimation, hwchan->prescale.num, - hwchan->prescale.den, hwchan->scale_fn_type); + ch->amux_ip_rsv, ch->decimation, hwchan->prescale.numerator, + hwchan->prescale.denominator, hwchan->scale_fn_type); return 0; } diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c index 07b1a99381d9..34202ba52469 100644 --- a/drivers/iio/adc/qcom-spmi-vadc.c +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -122,15 +122,15 @@ struct vadc_priv { struct mutex lock; }; -static const struct vadc_prescale_ratio vadc_prescale_ratios[] = { - {.num = 1, .den = 1}, - {.num = 1, .den = 3}, - {.num = 1, .den = 4}, - {.num = 1, .den = 6}, - {.num = 1, .den = 20}, - {.num = 1, .den = 8}, - {.num = 10, .den = 81}, - {.num = 1, .den = 10} +static const struct u32_fract vadc_prescale_ratios[] = { + { .numerator = 1, .denominator = 1 }, + { .numerator = 1, .denominator = 3 }, + { .numerator = 1, .denominator = 4 }, + { .numerator = 1, .denominator = 6 }, + { .numerator = 1, .denominator = 20 }, + { .numerator = 1, .denominator = 8 }, + { .numerator = 10, .denominator = 81 }, + { .numerator = 1, .denominator = 10 }, }; static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data) @@ -404,13 +404,13 @@ err: return ret; } -static int vadc_prescaling_from_dt(u32 num, u32 den) +static int vadc_prescaling_from_dt(u32 numerator, u32 denominator) { unsigned int pre; for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++) - if (vadc_prescale_ratios[pre].num == num && - vadc_prescale_ratios[pre].den == den) + if (vadc_prescale_ratios[pre].numerator == numerator && + vadc_prescale_ratios[pre].denominator == denominator) break; if (pre == ARRAY_SIZE(vadc_prescale_ratios)) diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index 14723896aab2..6c6aec848f98 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -289,44 +289,44 @@ static const struct vadc_map_pt adcmap7_100k[] = { { 2420, 130048 } }; -static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { - {.num = 1, .den = 1}, - {.num = 1, .den = 3}, - {.num = 1, .den = 4}, - {.num = 1, .den = 6}, - {.num = 1, .den = 20}, - {.num = 1, .den = 8}, - {.num = 10, .den = 81}, - {.num = 1, .den = 10}, - {.num = 1, .den = 16} +static const struct u32_fract adc5_prescale_ratios[] = { + { .numerator = 1, .denominator = 1 }, + { .numerator = 1, .denominator = 3 }, + { .numerator = 1, .denominator = 4 }, + { .numerator = 1, .denominator = 6 }, + { .numerator = 1, .denominator = 20 }, + { .numerator = 1, .denominator = 8 }, + { .numerator = 10, .denominator = 81 }, + { .numerator = 1, .denominator = 10 }, + { .numerator = 1, .denominator = 16 }, }; static int qcom_vadc_scale_hw_calib_volt( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_uv); static int qcom_vadc_scale_hw_calib_therm( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec); static int qcom_vadc7_scale_hw_calib_therm( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec); static int qcom_vadc_scale_hw_smb_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec); static int qcom_vadc_scale_hw_chg5_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec); static int qcom_vadc_scale_hw_calib_die_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec); static int qcom_vadc7_scale_hw_calib_die_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec); @@ -406,7 +406,7 @@ static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph, } static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, bool absolute, u16 adc_code, int *result_uv) { @@ -414,15 +414,15 @@ static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph, qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); - voltage = voltage * prescale->den; - result = div64_s64(voltage, prescale->num); + voltage *= prescale->denominator; + result = div64_s64(voltage, prescale->numerator); *result_uv = result; return 0; } static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, bool absolute, u16 adc_code, int *result_mdec) { @@ -444,7 +444,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, } static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, bool absolute, u16 adc_code, int *result_mdec) { @@ -454,8 +454,8 @@ static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); if (voltage > 0) { - temp = voltage * prescale->den; - do_div(temp, prescale->num * 2); + temp = voltage * prescale->denominator; + do_div(temp, prescale->numerator * 2); voltage = temp; } else { voltage = 0; @@ -467,7 +467,7 @@ static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, } static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, bool absolute, u16 adc_code, int *result_mdec) { @@ -475,8 +475,8 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); - voltage = voltage * prescale->den; - voltage = div64_s64(voltage, prescale->num); + voltage *= prescale->denominator; + voltage = div64_s64(voltage, prescale->numerator); voltage = ((PMI_CHG_SCALE_1) * (voltage * 2)); voltage = (voltage + PMI_CHG_SCALE_2); result = div64_s64(voltage, 1000000); @@ -487,21 +487,21 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, /* convert voltage to ADC code, using 1.875V reference */ static u16 qcom_vadc_scale_voltage_code(s32 voltage, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const u32 full_scale_code_volt, unsigned int factor) { s64 volt = voltage; s64 adc_vdd_ref_mv = 1875; /* reference voltage */ - volt *= prescale->num * factor * full_scale_code_volt; - volt = div64_s64(volt, (s64)prescale->den * adc_vdd_ref_mv * 1000); + volt *= prescale->numerator * factor * full_scale_code_volt; + volt = div64_s64(volt, (s64)prescale->denominator * adc_vdd_ref_mv * 1000); return volt; } static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, unsigned int factor) { @@ -520,8 +520,8 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, voltage = (s64) adc_code * adc_vdd_ref_mv * 1000; voltage = div64_s64(voltage, data->full_scale_code_volt); if (voltage > 0) { - voltage *= prescale->den; - temp = prescale->num * factor; + voltage *= prescale->denominator; + temp = prescale->numerator * factor; voltage = div64_s64(voltage, temp); } else { voltage = 0; @@ -531,7 +531,7 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, } static int qcom_vadc7_scale_hw_calib_therm( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec) { @@ -557,7 +557,7 @@ static int qcom_vadc7_scale_hw_calib_therm( } static int qcom_vadc_scale_hw_calib_volt( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_uv) { @@ -568,7 +568,7 @@ static int qcom_vadc_scale_hw_calib_volt( } static int qcom_vadc_scale_hw_calib_therm( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec) { @@ -584,7 +584,7 @@ static int qcom_vadc_scale_hw_calib_therm( } static int qcom_vadc_scale_hw_calib_die_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec) { @@ -596,7 +596,7 @@ static int qcom_vadc_scale_hw_calib_die_temp( } static int qcom_vadc7_scale_hw_calib_die_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec) { @@ -611,7 +611,7 @@ static int qcom_vadc7_scale_hw_calib_die_temp( } static int qcom_vadc_scale_hw_smb_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec) { @@ -623,7 +623,7 @@ static int qcom_vadc_scale_hw_smb_temp( } static int qcom_vadc_scale_hw_chg5_temp( - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, const struct adc5_data *data, u16 adc_code, int *result_mdec) { @@ -636,7 +636,7 @@ static int qcom_vadc_scale_hw_chg5_temp( int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, const struct vadc_linear_graph *calib_graph, - const struct vadc_prescale_ratio *prescale, + const struct u32_fract *prescale, bool absolute, u16 adc_code, int *result) { @@ -667,7 +667,7 @@ EXPORT_SYMBOL(qcom_vadc_scale); u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio, u32 full_scale_code_volt, int temp) { - const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio]; + const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio]; s32 voltage; voltage = qcom_vadc_map_temp_voltage(adcmap_100k_104ef_104fb_1875_vref, @@ -682,7 +682,7 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, const struct adc5_data *data, u16 adc_code, int *result) { - const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio]; + const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio]; if (!(scaletype >= SCALE_HW_CALIB_DEFAULT && scaletype < SCALE_HW_CALIB_INVALID)) { @@ -695,13 +695,13 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, } EXPORT_SYMBOL(qcom_adc5_hw_scale); -int qcom_adc5_prescaling_from_dt(u32 num, u32 den) +int qcom_adc5_prescaling_from_dt(u32 numerator, u32 denominator) { unsigned int pre; for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++) - if (adc5_prescale_ratios[pre].num == num && - adc5_prescale_ratios[pre].den == den) + if (adc5_prescale_ratios[pre].numerator == numerator && + adc5_prescale_ratios[pre].denominator == denominator) break; if (pre == ARRAY_SIZE(adc5_prescale_ratios)) diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 727ea6c68049..27d9e147b4b7 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -577,7 +577,6 @@ static int rcar_gyroadc_remove(struct platform_device *pdev) return 0; } -#if defined(CONFIG_PM) static int rcar_gyroadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -597,10 +596,9 @@ static int rcar_gyroadc_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops rcar_gyroadc_pm_ops = { - SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL) + RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL) }; static struct platform_driver rcar_gyroadc_driver = { @@ -609,7 +607,7 @@ static struct platform_driver rcar_gyroadc_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = rcar_gyroadc_match, - .pm = &rcar_gyroadc_pm_ops, + .pm = pm_ptr(&rcar_gyroadc_pm_ops), }, }; diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c index 7d891b4ea461..6bf32907f01d 100644 --- a/drivers/iio/adc/rn5t618-adc.c +++ b/drivers/iio/adc/rn5t618-adc.c @@ -42,11 +42,6 @@ struct rn5t618_adc_data { int irq; }; -struct rn5t618_channel_ratios { - u16 numerator; - u16 denominator; -}; - enum rn5t618_channels { LIMMON = 0, VBAT, @@ -58,7 +53,7 @@ enum rn5t618_channels { AIN0 }; -static const struct rn5t618_channel_ratios rn5t618_ratios[8] = { +static const struct u16_fract rn5t618_ratios[8] = { [LIMMON] = {50, 32}, /* measured across 20mOhm, amplified by 32 */ [VBAT] = {2, 1}, [VADP] = {3, 1}, diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 14b8df4ca9c8..b87ea7148b58 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -481,7 +481,6 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return devm_iio_device_register(&pdev->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int rockchip_saradc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -514,17 +513,17 @@ static int rockchip_saradc_resume(struct device *dev) return ret; } -#endif -static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops, - rockchip_saradc_suspend, rockchip_saradc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops, + rockchip_saradc_suspend, + rockchip_saradc_resume); static struct platform_driver rockchip_saradc_driver = { .probe = rockchip_saradc_probe, .driver = { .name = "rockchip-saradc", .of_match_table = rockchip_saradc_match, - .pm = &rockchip_saradc_pm_ops, + .pm = pm_sleep_ptr(&rockchip_saradc_pm_ops), }, }; diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index 9d5be52bd948..7585144b9715 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -55,7 +55,7 @@ #define RZG2L_ADCR(n) (0x30 + ((n) * 0x4)) #define RZG2L_ADCR_AD_MASK GENMASK(11, 0) -#define RZG2L_ADSMP_DEFUALT_SAMPLING 0x578 +#define RZG2L_ADSMP_DEFAULT_SAMPLING 0x578 #define RZG2L_ADC_MAX_CHANNELS 8 #define RZG2L_ADC_CHN_MASK 0x7 @@ -395,7 +395,7 @@ static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) reg &= ~RZG2L_ADM3_ADIL_MASK; reg &= ~RZG2L_ADM3_ADCMP_MASK; reg &= ~RZG2L_ADM3_ADSMP_MASK; - reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFUALT_SAMPLING); + reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFAULT_SAMPLING); rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); exit_hw_init: diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index b6e18eb101f7..142656232157 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -763,7 +763,6 @@ static int stm32_adc_remove(struct platform_device *pdev) return 0; } -#if defined(CONFIG_PM) static int stm32_adc_core_runtime_suspend(struct device *dev) { stm32_adc_core_hw_stop(dev); @@ -782,15 +781,11 @@ static int stm32_adc_core_runtime_idle(struct device *dev) return 0; } -#endif - -static const struct dev_pm_ops stm32_adc_core_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(stm32_adc_core_runtime_suspend, - stm32_adc_core_runtime_resume, - stm32_adc_core_runtime_idle) -}; + +static DEFINE_RUNTIME_DEV_PM_OPS(stm32_adc_core_pm_ops, + stm32_adc_core_runtime_suspend, + stm32_adc_core_runtime_resume, + stm32_adc_core_runtime_idle); static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { .regs = &stm32f4_adc_common_regs, @@ -836,7 +831,7 @@ static struct platform_driver stm32_adc_driver = { .driver = { .name = "stm32-adc-core", .of_match_table = stm32_adc_of_match, - .pm = &stm32_adc_core_pm_ops, + .pm = pm_ptr(&stm32_adc_core_pm_ops), }, }; module_platform_driver(stm32_adc_driver); diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 897166d9e45c..a68ecbda6480 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2352,7 +2352,6 @@ static int stm32_adc_remove(struct platform_device *pdev) return 0; } -#if defined(CONFIG_PM_SLEEP) static int stm32_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -2382,9 +2381,7 @@ static int stm32_adc_resume(struct device *dev) return stm32_adc_buffer_postenable(indio_dev); } -#endif -#if defined(CONFIG_PM) static int stm32_adc_runtime_suspend(struct device *dev) { return stm32_adc_hw_stop(dev); @@ -2394,12 +2391,11 @@ static int stm32_adc_runtime_resume(struct device *dev) { return stm32_adc_hw_start(dev); } -#endif static const struct dev_pm_ops stm32_adc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume) - SET_RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume) + RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume, + NULL) }; static const struct stm32_adc_cfg stm32f4_adc_cfg = { @@ -2453,7 +2449,7 @@ static struct platform_driver stm32_adc_driver = { .driver = { .name = "stm32-adc", .of_match_table = stm32_adc_of_match, - .pm = &stm32_adc_pm_ops, + .pm = pm_ptr(&stm32_adc_pm_ops), }, }; module_platform_driver(stm32_adc_driver); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 1cfefb3b5e56..9704cf0b9753 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1632,7 +1632,7 @@ static int stm32_dfsdm_adc_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev) +static int stm32_dfsdm_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -1642,7 +1642,7 @@ static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev) +static int stm32_dfsdm_adc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); @@ -1665,14 +1665,15 @@ static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops, - stm32_dfsdm_adc_suspend, stm32_dfsdm_adc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops, + stm32_dfsdm_adc_suspend, + stm32_dfsdm_adc_resume); static struct platform_driver stm32_dfsdm_adc_driver = { .driver = { .name = "stm32-dfsdm-adc", .of_match_table = stm32_dfsdm_adc_match, - .pm = &stm32_dfsdm_adc_pm_ops, + .pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops), }, .probe = stm32_dfsdm_adc_probe, .remove = stm32_dfsdm_adc_remove, diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index a627af9a825e..a3d4de6ba4c2 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -381,7 +381,7 @@ static int stm32_dfsdm_core_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev) +static int stm32_dfsdm_core_suspend(struct device *dev) { struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); @@ -397,7 +397,7 @@ static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev) return pinctrl_pm_select_sleep_state(dev); } -static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev) +static int stm32_dfsdm_core_resume(struct device *dev) { struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); @@ -414,7 +414,7 @@ static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev) return pm_runtime_force_resume(dev); } -static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev) +static int stm32_dfsdm_core_runtime_suspend(struct device *dev) { struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); @@ -423,7 +423,7 @@ static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev) +static int stm32_dfsdm_core_runtime_resume(struct device *dev) { struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); @@ -431,11 +431,10 @@ static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev) } static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, - stm32_dfsdm_core_resume) - SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend, - stm32_dfsdm_core_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, stm32_dfsdm_core_resume) + RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend, + stm32_dfsdm_core_runtime_resume, + NULL) }; static struct platform_driver stm32_dfsdm_driver = { @@ -444,7 +443,7 @@ static struct platform_driver stm32_dfsdm_driver = { .driver = { .name = "stm32-dfsdm", .of_match_table = stm32_dfsdm_of_match, - .pm = &stm32_dfsdm_core_pm_ops, + .pm = pm_ptr(&stm32_dfsdm_core_pm_ops), }, }; diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index ce3f5a3814f9..c9b5d9aec3dc 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -248,7 +248,7 @@ static const struct of_device_id adc084s021_of_match[] = { MODULE_DEVICE_TABLE(of, adc084s021_of_match); static const struct spi_device_id adc084s021_id[] = { - { ADC084S021_DRIVER_NAME, 0}, + { ADC084S021_DRIVER_NAME, 0 }, {} }; MODULE_DEVICE_TABLE(spi, adc084s021_id); diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index e8fc4d01f30b..55b35570ad8b 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -82,6 +82,11 @@ #define TI_TSC2046_DATA_12BIT GENMASK(14, 3) #define TI_TSC2046_MAX_CHAN 8 +#define TI_TSC2046_MIN_POLL_CNT 3 +#define TI_TSC2046_EXT_POLL_CNT 3 +#define TI_TSC2046_POLL_CNT \ + (TI_TSC2046_MIN_POLL_CNT + TI_TSC2046_EXT_POLL_CNT) +#define TI_TSC2046_INT_VREF 2500 /* Represents a HW sample */ struct tsc2046_adc_atom { @@ -123,14 +128,23 @@ struct tsc2046_adc_ch_cfg { unsigned int oversampling_ratio; }; +enum tsc2046_state { + TSC2046_STATE_SHUTDOWN, + TSC2046_STATE_STANDBY, + TSC2046_STATE_POLL, + TSC2046_STATE_POLL_IRQ_DISABLE, + TSC2046_STATE_ENABLE_IRQ, +}; + struct tsc2046_adc_priv { struct spi_device *spi; const struct tsc2046_adc_dcfg *dcfg; struct iio_trigger *trig; struct hrtimer trig_timer; - spinlock_t trig_lock; - unsigned int trig_more_count; + enum tsc2046_state state; + int poll_cnt; + spinlock_t state_lock; struct spi_transfer xfer; struct spi_message msg; @@ -153,9 +167,6 @@ struct tsc2046_adc_priv { struct tsc2046_adc_atom *rx; struct tsc2046_adc_atom *tx; - struct tsc2046_adc_atom *rx_one; - struct tsc2046_adc_atom *tx_one; - unsigned int count; unsigned int groups; u32 effective_speed_hz; @@ -171,6 +182,8 @@ struct tsc2046_adc_priv { .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = "#name", \ .scan_index = index, \ .scan_type = { \ @@ -234,6 +247,14 @@ static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx, else pd = 0; + switch (ch_idx) { + case TI_TSC2046_ADDR_TEMP1: + case TI_TSC2046_ADDR_AUX: + case TI_TSC2046_ADDR_VBAT: + case TI_TSC2046_ADDR_TEMP0: + pd |= TI_TSC2046_SER | TI_TSC2046_PD1_VREF_ON; + } + return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd; } @@ -245,16 +266,50 @@ static u16 tsc2046_adc_get_value(struct tsc2046_adc_atom *buf) static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, u32 *effective_speed_hz) { + struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx]; + struct tsc2046_adc_atom *rx_buf, *tx_buf; + unsigned int val, val_normalized = 0; + int ret, i, count_skip = 0, max_count; struct spi_transfer xfer; struct spi_message msg; - int ret; + u8 cmd; + + if (!effective_speed_hz) { + count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us); + max_count = count_skip + ch->oversampling_ratio; + } else { + max_count = 1; + } + + if (sizeof(*tx_buf) * max_count > PAGE_SIZE) + return -ENOSPC; + + tx_buf = kcalloc(max_count, sizeof(*tx_buf), GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + rx_buf = kcalloc(max_count, sizeof(*rx_buf), GFP_KERNEL); + if (!rx_buf) { + ret = -ENOMEM; + goto free_tx; + } + + /* + * Do not enable automatic power down on working samples. Otherwise the + * plates will never be completely charged. + */ + cmd = tsc2046_adc_get_cmd(priv, ch_idx, true); + + for (i = 0; i < max_count - 1; i++) + tx_buf[i].cmd = cmd; + + /* automatically power down on last sample */ + tx_buf[i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); memset(&xfer, 0, sizeof(xfer)); - priv->tx_one->cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); - priv->tx_one->data = 0; - xfer.tx_buf = priv->tx_one; - xfer.rx_buf = priv->rx_one; - xfer.len = sizeof(*priv->tx_one); + xfer.tx_buf = tx_buf; + xfer.rx_buf = rx_buf; + xfer.len = sizeof(*tx_buf) * max_count; spi_message_init_with_transfers(&msg, &xfer, 1); /* @@ -265,13 +320,25 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, if (ret) { dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n", ERR_PTR(ret)); - return ret; + goto free_bufs; } if (effective_speed_hz) *effective_speed_hz = xfer.effective_speed_hz; - return tsc2046_adc_get_value(priv->rx_one); + for (i = 0; i < max_count - count_skip; i++) { + val = tsc2046_adc_get_value(&rx_buf[count_skip + i]); + val_normalized += val; + } + + ret = DIV_ROUND_UP(val_normalized, max_count - count_skip); + +free_bufs: + kfree(rx_buf); +free_tx: + kfree(tx_buf); + + return ret; } static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv, @@ -378,6 +445,37 @@ static irqreturn_t tsc2046_adc_trigger_handler(int irq, void *p) return IRQ_HANDLED; } +static int tsc2046_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = tsc2046_adc_read_one(priv, chan->channel, NULL); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * Note: the TSC2046 has internal voltage divider on the VBAT + * line. This divider can be influenced by external divider. + * So, it is better to use external voltage-divider driver + * instead, which is calculating complete chain. + */ + *val = TI_TSC2046_INT_VREF; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *active_scan_mask) { @@ -408,24 +506,67 @@ static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev, } static const struct iio_info tsc2046_adc_info = { + .read_raw = tsc2046_adc_read_raw, .update_scan_mode = tsc2046_adc_update_scan_mode, }; -static enum hrtimer_restart tsc2046_adc_trig_more(struct hrtimer *hrtimer) +static enum hrtimer_restart tsc2046_adc_timer(struct hrtimer *hrtimer) { struct tsc2046_adc_priv *priv = container_of(hrtimer, struct tsc2046_adc_priv, trig_timer); unsigned long flags; - spin_lock_irqsave(&priv->trig_lock, flags); - - disable_irq_nosync(priv->spi->irq); - - priv->trig_more_count++; - iio_trigger_poll(priv->trig); + /* + * This state machine should address following challenges : + * - the interrupt source is based on level shifter attached to the X + * channel of ADC. It will change the state every time we switch + * between channels. So, we need to disable IRQ if we do + * iio_trigger_poll(). + * - we should do iio_trigger_poll() at some reduced sample rate + * - we should still trigger for some amount of time after last + * interrupt with enabled IRQ was processed. + */ - spin_unlock_irqrestore(&priv->trig_lock, flags); + spin_lock_irqsave(&priv->state_lock, flags); + switch (priv->state) { + case TSC2046_STATE_ENABLE_IRQ: + if (priv->poll_cnt < TI_TSC2046_POLL_CNT) { + priv->poll_cnt++; + hrtimer_start(&priv->trig_timer, + ns_to_ktime(priv->scan_interval_us * + NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + + if (priv->poll_cnt >= TI_TSC2046_MIN_POLL_CNT) { + priv->state = TSC2046_STATE_POLL_IRQ_DISABLE; + enable_irq(priv->spi->irq); + } else { + priv->state = TSC2046_STATE_POLL; + } + } else { + priv->state = TSC2046_STATE_STANDBY; + enable_irq(priv->spi->irq); + } + break; + case TSC2046_STATE_POLL_IRQ_DISABLE: + disable_irq_nosync(priv->spi->irq); + fallthrough; + case TSC2046_STATE_POLL: + priv->state = TSC2046_STATE_ENABLE_IRQ; + /* iio_trigger_poll() starts hrtimer */ + iio_trigger_poll(priv->trig); + break; + case TSC2046_STATE_SHUTDOWN: + break; + case TSC2046_STATE_STANDBY: + fallthrough; + default: + dev_warn(&priv->spi->dev, "Got unexpected state: %i\n", + priv->state); + break; + } + spin_unlock_irqrestore(&priv->state_lock, flags); return HRTIMER_NORESTART; } @@ -434,16 +575,20 @@ static irqreturn_t tsc2046_adc_irq(int irq, void *dev_id) { struct iio_dev *indio_dev = dev_id; struct tsc2046_adc_priv *priv = iio_priv(indio_dev); - - spin_lock(&priv->trig_lock); + unsigned long flags; hrtimer_try_to_cancel(&priv->trig_timer); - priv->trig_more_count = 0; - disable_irq_nosync(priv->spi->irq); - iio_trigger_poll(priv->trig); + spin_lock_irqsave(&priv->state_lock, flags); + if (priv->state != TSC2046_STATE_SHUTDOWN) { + priv->state = TSC2046_STATE_ENABLE_IRQ; + priv->poll_cnt = 0; - spin_unlock(&priv->trig_lock); + /* iio_trigger_poll() starts hrtimer */ + disable_irq_nosync(priv->spi->irq); + iio_trigger_poll(priv->trig); + } + spin_unlock_irqrestore(&priv->state_lock, flags); return IRQ_HANDLED; } @@ -452,49 +597,42 @@ static void tsc2046_adc_reenable_trigger(struct iio_trigger *trig) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct tsc2046_adc_priv *priv = iio_priv(indio_dev); - unsigned long flags; - int delta; + ktime_t tim; /* * We can sample it as fast as we can, but usually we do not need so * many samples. Reduce the sample rate for default (touchscreen) use * case. - * Currently we do not need a highly precise sample rate. It is enough - * to have calculated numbers. - */ - delta = priv->scan_interval_us - priv->time_per_scan_us; - if (delta > 0) - fsleep(delta); - - spin_lock_irqsave(&priv->trig_lock, flags); - - /* - * We need to trigger at least one extra sample to detect state - * difference on ADC side. */ - if (!priv->trig_more_count) { - int timeout_ms = DIV_ROUND_UP(priv->scan_interval_us, - USEC_PER_MSEC); - - hrtimer_start(&priv->trig_timer, ms_to_ktime(timeout_ms), - HRTIMER_MODE_REL_SOFT); - } - - enable_irq(priv->spi->irq); - - spin_unlock_irqrestore(&priv->trig_lock, flags); + tim = ns_to_ktime((priv->scan_interval_us - priv->time_per_scan_us) * + NSEC_PER_USEC); + hrtimer_start(&priv->trig_timer, tim, HRTIMER_MODE_REL_SOFT); } static int tsc2046_adc_set_trigger_state(struct iio_trigger *trig, bool enable) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + unsigned long flags; if (enable) { - enable_irq(priv->spi->irq); + spin_lock_irqsave(&priv->state_lock, flags); + if (priv->state == TSC2046_STATE_SHUTDOWN) { + priv->state = TSC2046_STATE_STANDBY; + enable_irq(priv->spi->irq); + } + spin_unlock_irqrestore(&priv->state_lock, flags); } else { - disable_irq(priv->spi->irq); - hrtimer_try_to_cancel(&priv->trig_timer); + spin_lock_irqsave(&priv->state_lock, flags); + + if (priv->state == TSC2046_STATE_STANDBY || + priv->state == TSC2046_STATE_POLL_IRQ_DISABLE) + disable_irq_nosync(priv->spi->irq); + + priv->state = TSC2046_STATE_SHUTDOWN; + spin_unlock_irqrestore(&priv->state_lock, flags); + + hrtimer_cancel(&priv->trig_timer); } return 0; @@ -511,16 +649,6 @@ static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv) size_t size; int ret; - priv->tx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->tx_one), - GFP_KERNEL); - if (!priv->tx_one) - return -ENOMEM; - - priv->rx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->rx_one), - GFP_KERNEL); - if (!priv->rx_one) - return -ENOMEM; - /* * Make dummy read to set initial power state and get real SPI clock * freq. It seems to be not important which channel is used for this @@ -551,6 +679,12 @@ static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv) for (ch_idx = 0; ch_idx < ARRAY_SIZE(priv->l); ch_idx++) size += tsc2046_adc_group_set_layout(priv, ch_idx, ch_idx); + if (size > PAGE_SIZE) { + dev_err(&priv->spi->dev, + "Calculated scan buffer is too big. Try to reduce spi-max-frequency, settling-time-us or oversampling-ratio\n"); + return -ENOSPC; + } + priv->tx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL); if (!priv->tx) return -ENOMEM; @@ -668,10 +802,11 @@ static int tsc2046_adc_probe(struct spi_device *spi) iio_trigger_set_drvdata(trig, indio_dev); trig->ops = &tsc2046_adc_trigger_ops; - spin_lock_init(&priv->trig_lock); + spin_lock_init(&priv->state_lock); + priv->state = TSC2046_STATE_SHUTDOWN; hrtimer_init(&priv->trig_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - priv->trig_timer.function = tsc2046_adc_trig_more; + priv->trig_timer.function = tsc2046_adc_timer; ret = devm_iio_trigger_register(dev, trig); if (ret) { diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 6ce40cc4568a..f8f8aea15612 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -231,13 +231,7 @@ static const struct iio_chan_spec twl4030_madc_iio_channels[] = { static struct twl4030_madc_data *twl4030_madc; -struct twl4030_prescale_divider_ratios { - s16 numerator; - s16 denominator; -}; - -static const struct twl4030_prescale_divider_ratios -twl4030_divider_ratios[16] = { +static const struct s16_fract twl4030_divider_ratios[16] = { {1, 1}, /* CHANNEL 0 No Prescaler */ {1, 1}, /* CHANNEL 1 No Prescaler */ {6, 10}, /* CHANNEL 2 */ @@ -256,7 +250,6 @@ twl4030_divider_ratios[16] = { {5, 11}, /* CHANNEL 15 */ }; - /* Conversion table from -3 to 55 degrees Celcius */ static int twl4030_therm_tbl[] = { 30800, 29500, 28300, 27100, diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index afdb59e0b526..f53e8558b560 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -911,6 +911,8 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, irq, NULL, twl6030_gpadc_irq_handler, IRQF_ONESHOT, "twl6030_gpadc", indio_dev); + if (ret) + return ret; ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); if (ret < 0) { @@ -944,7 +946,6 @@ static int twl6030_gpadc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int twl6030_gpadc_suspend(struct device *pdev) { int ret; @@ -968,17 +969,16 @@ static int twl6030_gpadc_resume(struct device *pdev) return 0; }; -#endif -static SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, - twl6030_gpadc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, + twl6030_gpadc_resume); static struct platform_driver twl6030_gpadc_driver = { .probe = twl6030_gpadc_probe, .remove = twl6030_gpadc_remove, .driver = { .name = DRIVER_NAME, - .pm = &twl6030_gpadc_pm_ops, + .pm = pm_sleep_ptr(&twl6030_gpadc_pm_ops), .of_match_table = of_twl6030_match_tbl, }, }; diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index fd57fc43e8e5..c84293efc129 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -912,7 +912,6 @@ static int vf610_adc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int vf610_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -952,9 +951,9 @@ disable_reg: regulator_disable(info->vref); return ret; } -#endif -static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, vf610_adc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, + vf610_adc_resume); static struct platform_driver vf610_adc_driver = { .probe = vf610_adc_probe, @@ -962,7 +961,7 @@ static struct platform_driver vf610_adc_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = vf610_adc_match, - .pm = &vf610_adc_pm_ops, + .pm = pm_sleep_ptr(&vf610_adc_pm_ops), }, }; diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 8343c5f74121..a55396c1f8b2 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -12,6 +12,7 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> @@ -91,8 +92,8 @@ #define AMS_CONF1_SEQ_MASK GENMASK(15, 12) #define AMS_CONF1_SEQ_DEFAULT FIELD_PREP(AMS_CONF1_SEQ_MASK, 0) -#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 1) -#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 2) +#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 2) +#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 3) #define AMS_REG_SEQ0_MASK GENMASK(15, 0) #define AMS_REG_SEQ2_MASK GENMASK(21, 16) @@ -530,14 +531,18 @@ static int ams_enable_single_channel(struct ams *ams, unsigned int offset) return -EINVAL; } - /* set single channel, sequencer off mode */ + /* put sysmon in a soft reset to change the sequence */ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, - AMS_CONF1_SEQ_SINGLE_CHANNEL); + AMS_CONF1_SEQ_DEFAULT); /* write the channel number */ ams_ps_update_reg(ams, AMS_REG_CONFIG0, AMS_CONF0_CHANNEL_NUM_MASK, channel_num); + /* set single channel, sequencer off mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_SINGLE_CHANNEL); + return 0; } @@ -551,6 +556,8 @@ static int ams_read_vcc_reg(struct ams *ams, unsigned int offset, u32 *data) if (ret) return ret; + /* clear end-of-conversion flag, wait for next conversion to complete */ + writel(expect, ams->base + AMS_ISR_1); ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg, (reg & expect), AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); if (ret) @@ -1224,6 +1231,7 @@ static int ams_init_module(struct iio_dev *indio_dev, /* add PS channels to iio device channels */ memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels)); + num_channels = ARRAY_SIZE(ams_ps_channels); } else if (fwnode_property_match_string(fwnode, "compatible", "xlnx,zynqmp-ams-pl") == 0) { ams->pl_base = fwnode_iomap(fwnode, 0); @@ -1348,11 +1356,6 @@ static void ams_clk_disable_unprepare(void *data) clk_disable_unprepare(data); } -static void ams_cancel_delayed_work(void *data) -{ - cancel_delayed_work(data); -} - static int ams_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -1389,9 +1392,8 @@ static int ams_probe(struct platform_device *pdev) if (ret < 0) return ret; - INIT_DELAYED_WORK(&ams->ams_unmask_work, ams_unmask_worker); - ret = devm_add_action_or_reset(&pdev->dev, ams_cancel_delayed_work, - &ams->ams_unmask_work); + ret = devm_delayed_work_autocancel(&pdev->dev, &ams->ams_unmask_work, + ams_unmask_worker); if (ret < 0) return ret; diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 774eb3044edd..7e511293d6d1 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -3,43 +3,152 @@ * IIO rescale driver * * Copyright (C) 2018 Axentia Technologies AB + * Copyright (C) 2022 Liam Beguin <liambeguin@gmail.com> * * Author: Peter Rosin <peda@axentia.se> */ #include <linux/err.h> #include <linux/gcd.h> -#include <linux/iio/consumer.h> -#include <linux/iio/iio.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/property.h> -struct rescale; +#include <linux/iio/afe/rescale.h> +#include <linux/iio/consumer.h> +#include <linux/iio/iio.h> -struct rescale_cfg { - enum iio_chan_type type; - int (*props)(struct device *dev, struct rescale *rescale); -}; +int rescale_process_scale(struct rescale *rescale, int scale_type, + int *val, int *val2) +{ + s64 tmp; + int _val, _val2; + s32 rem, rem2; + u32 mult; + u32 neg; + + switch (scale_type) { + case IIO_VAL_INT: + *val *= rescale->numerator; + if (rescale->denominator == 1) + return scale_type; + *val2 = rescale->denominator; + return IIO_VAL_FRACTIONAL; + case IIO_VAL_FRACTIONAL: + /* + * When the product of both scales doesn't overflow, avoid + * potential accuracy loss (for in kernel consumers) by + * keeping a fractional representation. + */ + if (!check_mul_overflow(*val, rescale->numerator, &_val) && + !check_mul_overflow(*val2, rescale->denominator, &_val2)) { + *val = _val; + *val2 = _val2; + return IIO_VAL_FRACTIONAL; + } + fallthrough; + case IIO_VAL_FRACTIONAL_LOG2: + tmp = (s64)*val * 1000000000LL; + tmp = div_s64(tmp, rescale->denominator); + tmp *= rescale->numerator; -struct rescale { - const struct rescale_cfg *cfg; - struct iio_channel *source; - struct iio_chan_spec chan; - struct iio_chan_spec_ext_info *ext_info; - bool chan_processed; - s32 numerator; - s32 denominator; -}; + tmp = div_s64_rem(tmp, 1000000000LL, &rem); + *val = tmp; + + if (!rem) + return scale_type; + + if (scale_type == IIO_VAL_FRACTIONAL) + tmp = *val2; + else + tmp = ULL(1) << *val2; + + rem2 = *val % (int)tmp; + *val = *val / (int)tmp; + + *val2 = rem / (int)tmp; + if (rem2) + *val2 += div_s64((s64)rem2 * 1000000000LL, tmp); + + return IIO_VAL_INT_PLUS_NANO; + case IIO_VAL_INT_PLUS_NANO: + case IIO_VAL_INT_PLUS_MICRO: + mult = scale_type == IIO_VAL_INT_PLUS_NANO ? 1000000000L : 1000000L; + + /* + * For IIO_VAL_INT_PLUS_{MICRO,NANO} scale types if either *val + * OR *val2 is negative the schan scale is negative, i.e. + * *val = 1 and *val2 = -0.5 yields -1.5 not -0.5. + */ + neg = *val < 0 || *val2 < 0; + + tmp = (s64)abs(*val) * abs(rescale->numerator); + *val = div_s64_rem(tmp, abs(rescale->denominator), &rem); + + tmp = (s64)rem * mult + (s64)abs(*val2) * abs(rescale->numerator); + tmp = div_s64(tmp, abs(rescale->denominator)); + + *val += div_s64_rem(tmp, mult, val2); + + /* + * If only one of the rescaler elements or the schan scale is + * negative, the combined scale is negative. + */ + if (neg ^ ((rescale->numerator < 0) ^ (rescale->denominator < 0))) { + if (*val) + *val = -*val; + else + *val2 = -*val2; + } + + return scale_type; + default: + return -EOPNOTSUPP; + } +} + +int rescale_process_offset(struct rescale *rescale, int scale_type, + int scale, int scale2, int schan_off, + int *val, int *val2) +{ + s64 tmp, tmp2; + + switch (scale_type) { + case IIO_VAL_FRACTIONAL: + tmp = (s64)rescale->offset * scale2; + *val = div_s64(tmp, scale) + schan_off; + return IIO_VAL_INT; + case IIO_VAL_INT: + *val = div_s64(rescale->offset, scale) + schan_off; + return IIO_VAL_INT; + case IIO_VAL_FRACTIONAL_LOG2: + tmp = (s64)rescale->offset * (1 << scale2); + *val = div_s64(tmp, scale) + schan_off; + return IIO_VAL_INT; + case IIO_VAL_INT_PLUS_NANO: + tmp = (s64)rescale->offset * 1000000000LL; + tmp2 = ((s64)scale * 1000000000LL) + scale2; + *val = div64_s64(tmp, tmp2) + schan_off; + return IIO_VAL_INT; + case IIO_VAL_INT_PLUS_MICRO: + tmp = (s64)rescale->offset * 1000000LL; + tmp2 = ((s64)scale * 1000000LL) + scale2; + *val = div64_s64(tmp, tmp2) + schan_off; + return IIO_VAL_INT; + default: + return -EOPNOTSUPP; + } +} static int rescale_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct rescale *rescale = iio_priv(indio_dev); - unsigned long long tmp; + int scale, scale2; + int schan_off = 0; int ret; switch (mask) { @@ -65,27 +174,48 @@ static int rescale_read_raw(struct iio_dev *indio_dev, } else { ret = iio_read_channel_scale(rescale->source, val, val2); } - switch (ret) { - case IIO_VAL_FRACTIONAL: - *val *= rescale->numerator; - *val2 *= rescale->denominator; - return ret; - case IIO_VAL_INT: - *val *= rescale->numerator; - if (rescale->denominator == 1) - return ret; - *val2 = rescale->denominator; - return IIO_VAL_FRACTIONAL; - case IIO_VAL_FRACTIONAL_LOG2: - tmp = *val * 1000000000LL; - do_div(tmp, rescale->denominator); - tmp *= rescale->numerator; - do_div(tmp, 1000000000LL); - *val = tmp; - return ret; - default: - return -EOPNOTSUPP; + return rescale_process_scale(rescale, ret, val, val2); + case IIO_CHAN_INFO_OFFSET: + /* + * Processed channels are scaled 1-to-1 and source offset is + * already taken into account. + * + * In other cases, real world measurement are expressed as: + * + * schan_scale * (raw + schan_offset) + * + * Given that the rescaler parameters are applied recursively: + * + * rescaler_scale * (schan_scale * (raw + schan_offset) + + * rescaler_offset) + * + * Or, + * + * (rescaler_scale * schan_scale) * (raw + + * (schan_offset + rescaler_offset / schan_scale) + * + * Thus, reusing the original expression the parameters exposed + * to userspace are: + * + * scale = schan_scale * rescaler_scale + * offset = schan_offset + rescaler_offset / schan_scale + */ + if (rescale->chan_processed) { + *val = rescale->offset; + return IIO_VAL_INT; } + + if (iio_channel_has_info(rescale->source->channel, + IIO_CHAN_INFO_OFFSET)) { + ret = iio_read_channel_offset(rescale->source, + &schan_off, NULL); + if (ret != IIO_VAL_INT) + return ret < 0 ? ret : -EOPNOTSUPP; + } + + ret = iio_read_channel_scale(rescale->source, &scale, &scale2); + return rescale_process_offset(rescale, ret, scale, scale2, + schan_off, val, val2); default: return -EINVAL; } @@ -162,6 +292,9 @@ static int rescale_configure_channel(struct device *dev, chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); + if (rescale->offset) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET); + /* * Using .read_avail() is fringe to begin with and makes no sense * whatsoever for processed channels, so we make sure that this cannot @@ -261,10 +394,78 @@ static int rescale_voltage_divider_props(struct device *dev, return 0; } +static int rescale_temp_sense_rtd_props(struct device *dev, + struct rescale *rescale) +{ + u32 factor; + u32 alpha; + u32 iexc; + u32 tmp; + int ret; + u32 r0; + + ret = device_property_read_u32(dev, "excitation-current-microamp", + &iexc); + if (ret) { + dev_err(dev, "failed to read excitation-current-microamp: %d\n", + ret); + return ret; + } + + ret = device_property_read_u32(dev, "alpha-ppm-per-celsius", &alpha); + if (ret) { + dev_err(dev, "failed to read alpha-ppm-per-celsius: %d\n", + ret); + return ret; + } + + ret = device_property_read_u32(dev, "r-naught-ohms", &r0); + if (ret) { + dev_err(dev, "failed to read r-naught-ohms: %d\n", ret); + return ret; + } + + tmp = r0 * iexc * alpha / 1000000; + factor = gcd(tmp, 1000000); + rescale->numerator = 1000000 / factor; + rescale->denominator = tmp / factor; + + rescale->offset = -1 * ((r0 * iexc) / 1000); + + return 0; +} + +static int rescale_temp_transducer_props(struct device *dev, + struct rescale *rescale) +{ + s32 offset = 0; + s32 sense = 1; + s32 alpha; + int ret; + + device_property_read_u32(dev, "sense-offset-millicelsius", &offset); + device_property_read_u32(dev, "sense-resistor-ohms", &sense); + ret = device_property_read_u32(dev, "alpha-ppm-per-celsius", &alpha); + if (ret) { + dev_err(dev, "failed to read alpha-ppm-per-celsius: %d\n", ret); + return ret; + } + + rescale->numerator = 1000000; + rescale->denominator = alpha * sense; + + rescale->offset = div_s64((s64)offset * rescale->denominator, + rescale->numerator); + + return 0; +} + enum rescale_variant { CURRENT_SENSE_AMPLIFIER, CURRENT_SENSE_SHUNT, VOLTAGE_DIVIDER, + TEMP_SENSE_RTD, + TEMP_TRANSDUCER, }; static const struct rescale_cfg rescale_cfg[] = { @@ -280,6 +481,14 @@ static const struct rescale_cfg rescale_cfg[] = { .type = IIO_VOLTAGE, .props = rescale_voltage_divider_props, }, + [TEMP_SENSE_RTD] = { + .type = IIO_TEMP, + .props = rescale_temp_sense_rtd_props, + }, + [TEMP_TRANSDUCER] = { + .type = IIO_TEMP, + .props = rescale_temp_transducer_props, + }, }; static const struct of_device_id rescale_match[] = { @@ -289,6 +498,10 @@ static const struct of_device_id rescale_match[] = { .data = &rescale_cfg[CURRENT_SENSE_SHUNT], }, { .compatible = "voltage-divider", .data = &rescale_cfg[VOLTAGE_DIVIDER], }, + { .compatible = "temperature-sense-rtd", + .data = &rescale_cfg[TEMP_SENSE_RTD], }, + { .compatible = "temperature-transducer", + .data = &rescale_cfg[TEMP_TRANSDUCER], }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rescale_match); @@ -326,6 +539,7 @@ static int rescale_probe(struct platform_device *pdev) rescale->cfg = of_device_get_match_data(dev); rescale->numerator = 1; rescale->denominator = 1; + rescale->offset = 0; ret = rescale->cfg->props(dev, rescale); if (ret) diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index 5eb1357a9c78..f217a2a1e958 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -23,6 +23,17 @@ config AD8366 To compile this driver as a module, choose M here: the module will be called ad8366. +config ADA4250 + tristate "Analog Devices ADA4250 Instrumentation Amplifier" + depends on SPI + help + Say yes here to build support for Analog Devices ADA4250 + SPI Amplifier's support. The driver provides direct access via + sysfs. + + To compile this driver as a module, choose M here: the + module will be called ada4250. + config HMC425 tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers" depends on GPIOLIB diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile index cb551d82f56b..2126331129cf 100644 --- a/drivers/iio/amplifiers/Makefile +++ b/drivers/iio/amplifiers/Makefile @@ -5,4 +5,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD8366) += ad8366.o +obj-$(CONFIG_ADA4250) += ada4250.o obj-$(CONFIG_HMC425) += hmc425a.o diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c new file mode 100644 index 000000000000..4b32d350dc5d --- /dev/null +++ b/drivers/iio/amplifiers/ada4250.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADA4250 driver + * + * Copyright 2022 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <asm/unaligned.h> + +/* ADA4250 Register Map */ +#define ADA4250_REG_GAIN_MUX 0x00 +#define ADA4250_REG_REFBUF_EN 0x01 +#define ADA4250_REG_RESET 0x02 +#define ADA4250_REG_SNSR_CAL_VAL 0x04 +#define ADA4250_REG_SNSR_CAL_CNFG 0x05 +#define ADA4250_REG_DIE_REV 0x18 +#define ADA4250_REG_CHIP_ID 0x19 + +/* ADA4250_REG_GAIN_MUX Map */ +#define ADA4250_GAIN_MUX_MSK GENMASK(2, 0) + +/* ADA4250_REG_REFBUF Map */ +#define ADA4250_REFBUF_MSK BIT(0) + +/* ADA4250_REG_RESET Map */ +#define ADA4250_RESET_MSK BIT(0) + +/* ADA4250_REG_SNSR_CAL_VAL Map */ +#define ADA4250_CAL_CFG_BIAS_MSK GENMASK(7, 0) + +/* ADA4250_REG_SNSR_CAL_CNFG Bit Definition */ +#define ADA4250_BIAS_SET_MSK GENMASK(3, 2) +#define ADA4250_RANGE_SET_MSK GENMASK(1, 0) + +/* Miscellaneous definitions */ +#define ADA4250_CHIP_ID 0x4250 +#define ADA4250_RANGE1 0 +#define ADA4250_RANGE4 3 + +/* ADA4250 current bias set */ +enum ada4250_current_bias { + ADA4250_BIAS_DISABLED, + ADA4250_BIAS_BANDGAP, + ADA4250_BIAS_AVDD, +}; + +struct ada4250_state { + struct spi_device *spi; + struct regmap *regmap; + struct regulator *reg; + /* Protect against concurrent accesses to the device and data content */ + struct mutex lock; + u8 bias; + u8 gain; + int offset_uv; + bool refbuf_en; +}; + +/* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */ +static const int calibbias_table[] = {0, 1, 2}; + +/* ADA4250 Gain (V/V) values: 1, 2, 4, 8, 16, 32, 64, 128 */ +static const int hwgain_table[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +static const struct regmap_config ada4250_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = BIT(7), + .max_register = 0x1A, +}; + +static int ada4250_set_offset_uv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int offset_uv) +{ + struct ada4250_state *st = iio_priv(indio_dev); + + int i, ret, x[8], max_vos, min_vos, voltage_v, vlsb = 0; + u8 offset_raw, range = ADA4250_RANGE1; + u32 lsb_coeff[6] = {1333, 2301, 4283, 8289, 16311, 31599}; + + if (st->bias == 0 || st->bias == 3) + return -EINVAL; + + voltage_v = regulator_get_voltage(st->reg); + voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000); + + if (st->bias == ADA4250_BIAS_AVDD) + x[0] = voltage_v; + else + x[0] = 5; + + x[1] = 126 * (x[0] - 1); + + for (i = 0; i < 6; i++) + x[i + 2] = DIV_ROUND_CLOSEST(x[1] * 1000, lsb_coeff[i]); + + if (st->gain == 0) + return -EINVAL; + + /* + * Compute Range and Voltage per LSB for the Sensor Offset Calibration + * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD): + * Range 1 Range 2 + * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) | + * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 | + * 4 | X2*127 | X2=X1/1.3333 | X2*3*127 | X2*3 | + * 8 | X3*127 | X3=X1/2.301 | X3*3*127 | X3*3 | + * 16 | X4*127 | X4=X1/4.283 | X4*3*127 | X4*3 | + * 32 | X5*127 | X5=X1/8.289 | X5*3*127 | X5*3 | + * 64 | X6*127 | X6=X1/16.311 | X6*3*127 | X6*3 | + * 128 | X7*127 | X7=X1/31.599 | X7*3*127 | X7*3 | + */ + for (i = ADA4250_RANGE1; i <= ADA4250_RANGE4; i++) { + max_vos = x[st->gain] * 127 * ((1 << (i + 1)) - 1); + min_vos = -1 * max_vos; + if (offset_uv > min_vos && offset_uv < max_vos) { + range = i; + vlsb = x[st->gain] * ((1 << (i + 1)) - 1); + break; + } + } + + if (vlsb <= 0) + return -EINVAL; + + offset_raw = DIV_ROUND_CLOSEST(abs(offset_uv), vlsb); + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, + ADA4250_RANGE_SET_MSK, + FIELD_PREP(ADA4250_RANGE_SET_MSK, range)); + if (ret) + goto exit; + + st->offset_uv = offset_raw * vlsb; + + /* + * To set the offset calibration value, use bits [6:0] and bit 7 as the + * polarity bit (set to "0" for a negative offset and "1" for a positive + * offset). + */ + if (offset_uv < 0) { + offset_raw |= BIT(7); + st->offset_uv *= (-1); + } + + ret = regmap_write(st->regmap, ADA4250_REG_SNSR_CAL_VAL, offset_raw); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ada4250_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ada4250_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = regmap_read(st->regmap, ADA4250_REG_GAIN_MUX, val); + if (ret) + return ret; + + *val = BIT(*val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = st->offset_uv; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, val); + if (ret) + return ret; + + *val = FIELD_GET(ADA4250_BIAS_SET_MSK, *val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 1; + *val2 = 1000000; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int ada4250_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ada4250_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = regmap_write(st->regmap, ADA4250_REG_GAIN_MUX, + FIELD_PREP(ADA4250_GAIN_MUX_MSK, ilog2(val))); + if (ret) + return ret; + + st->gain = ilog2(val); + + return ret; + case IIO_CHAN_INFO_OFFSET: + return ada4250_set_offset_uv(indio_dev, chan, val); + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, + ADA4250_BIAS_SET_MSK, + FIELD_PREP(ADA4250_BIAS_SET_MSK, val)); + if (ret) + return ret; + + st->bias = val; + + return ret; + default: + return -EINVAL; + } +} + +static int ada4250_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + *vals = calibbias_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(calibbias_table); + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HARDWAREGAIN: + *vals = hwgain_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(hwgain_table); + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ada4250_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct ada4250_state *st = iio_priv(indio_dev); + + if (read_val) + return regmap_read(st->regmap, reg, read_val); + else + return regmap_write(st->regmap, reg, write_val); +} + +static const struct iio_info ada4250_info = { + .read_raw = ada4250_read_raw, + .write_raw = ada4250_write_raw, + .read_avail = &ada4250_read_avail, + .debugfs_reg_access = &ada4250_reg_access, +}; + +static const struct iio_chan_spec ada4250_channels[] = { + { + .type = IIO_VOLTAGE, + .output = 1, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + } +}; + +static void ada4250_reg_disable(void *data) +{ + regulator_disable(data); +} + +static int ada4250_init(struct ada4250_state *st) +{ + int ret; + u16 chip_id; + u8 data[2] __aligned(8) = {}; + struct spi_device *spi = st->spi; + + st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable"); + + st->reg = devm_regulator_get(&spi->dev, "avdd"); + if (IS_ERR(st->reg)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg), + "failed to get the AVDD voltage\n"); + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified AVDD supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADA4250_REG_RESET, + FIELD_PREP(ADA4250_RESET_MSK, 1)); + if (ret) + return ret; + + ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2); + if (ret) + return ret; + + chip_id = get_unaligned_le16(data); + + if (chip_id != ADA4250_CHIP_ID) { + dev_err(&spi->dev, "Invalid chip ID.\n"); + return -EINVAL; + } + + return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN, + FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en)); +} + +static int ada4250_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct regmap *regmap; + struct ada4250_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &ada4250_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st = iio_priv(indio_dev); + st->regmap = regmap; + st->spi = spi; + + indio_dev->info = &ada4250_info; + indio_dev->name = "ada4250"; + indio_dev->channels = ada4250_channels; + indio_dev->num_channels = ARRAY_SIZE(ada4250_channels); + + mutex_init(&st->lock); + + ret = ada4250_init(st); + if (ret) { + dev_err(&spi->dev, "ADA4250 init failed\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ada4250_id[] = { + { "ada4250", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ada4250_id); + +static const struct of_device_id ada4250_of_match[] = { + { .compatible = "adi,ada4250" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ada4250_of_match); + +static struct spi_driver ada4250_driver = { + .driver = { + .name = "ada4250", + .of_match_table = ada4250_of_match, + }, + .probe = ada4250_probe, + .id_table = ada4250_id, +}; +module_spi_driver(ada4250_driver); + +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com"); +MODULE_DESCRIPTION("Analog Devices ADA4250"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index 16c0a77f6a1c..ce80e0c916f4 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -11,10 +11,10 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> #include <linux/sysfs.h> @@ -192,7 +192,7 @@ static int hmc425a_probe(struct platform_device *pdev) return -ENOMEM; st = iio_priv(indio_dev); - st->type = (uintptr_t)of_device_get_match_data(&pdev->dev); + st->type = (uintptr_t)device_get_match_data(&pdev->dev); st->chip_info = &hmc425a_chip_info_tbl[st->type]; indio_dev->num_channels = st->chip_info->num_channels; diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index f8ce26a24c57..f744b62a636a 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -136,7 +136,7 @@ static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev, struct dmaengine_buffer *dmaengine_buffer = iio_buffer_to_dmaengine_buffer(buffer); - return sprintf(buf, "%zu\n", dmaengine_buffer->align); + return sysfs_emit(buf, "%zu\n", dmaengine_buffer->align); } static IIO_DEVICE_ATTR(length_align_bytes, 0444, diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 87d9aabd20c7..fb58f599a80b 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -52,7 +52,6 @@ static const struct iio_buffer_access_funcs iio_hw_buf_access = { static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( struct iio_hw_consumer *hwc, struct iio_dev *indio_dev) { - size_t mask_size = BITS_TO_LONGS(indio_dev->masklength) * sizeof(long); struct hw_consumer_buffer *buf; list_for_each_entry(buf, &hwc->buffers, head) { @@ -60,7 +59,8 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( return buf; } - buf = kzalloc(sizeof(*buf) + mask_size, GFP_KERNEL); + buf = kzalloc(struct_size(buf, scan_mask, BITS_TO_LONGS(indio_dev->masklength)), + GFP_KERNEL); if (!buf) return NULL; diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c index b1bacfe3c3ce..bbcf5a59c1f4 100644 --- a/drivers/iio/chemical/atlas-ezo-sensor.c +++ b/drivers/iio/chemical/atlas-ezo-sensor.c @@ -6,13 +6,15 @@ * Author: Matt Ranostay <matt.ranostay@konsulko.com> */ -#include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/mutex.h> +#include <linux/property.h> #include <linux/err.h> #include <linux/i2c.h> -#include <linux/of_device.h> + #include <linux/iio/iio.h> #define ATLAS_EZO_DRV_NAME "atlas-ezo-sensor" @@ -33,7 +35,7 @@ struct atlas_ezo_device { struct atlas_ezo_data { struct i2c_client *client; - struct atlas_ezo_device *chip; + const struct atlas_ezo_device *chip; /* lock to avoid multiple concurrent read calls */ struct mutex lock; @@ -184,17 +186,17 @@ static const struct iio_info atlas_info = { }; static const struct i2c_device_id atlas_ezo_id[] = { - { "atlas-co2-ezo", ATLAS_CO2_EZO }, - { "atlas-o2-ezo", ATLAS_O2_EZO }, - { "atlas-hum-ezo", ATLAS_HUM_EZO }, + { "atlas-co2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_CO2_EZO] }, + { "atlas-o2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_O2_EZO] }, + { "atlas-hum-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_HUM_EZO] }, {} }; MODULE_DEVICE_TABLE(i2c, atlas_ezo_id); static const struct of_device_id atlas_ezo_dt_ids[] = { - { .compatible = "atlas,co2-ezo", .data = (void *)ATLAS_CO2_EZO, }, - { .compatible = "atlas,o2-ezo", .data = (void *)ATLAS_O2_EZO, }, - { .compatible = "atlas,hum-ezo", .data = (void *)ATLAS_HUM_EZO, }, + { .compatible = "atlas,co2-ezo", .data = &atlas_ezo_devices[ATLAS_CO2_EZO], }, + { .compatible = "atlas,o2-ezo", .data = &atlas_ezo_devices[ATLAS_O2_EZO], }, + { .compatible = "atlas,hum-ezo", .data = &atlas_ezo_devices[ATLAS_HUM_EZO], }, {} }; MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids); @@ -202,20 +204,20 @@ MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids); static int atlas_ezo_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct atlas_ezo_device *chip; struct atlas_ezo_data *data; - struct atlas_ezo_device *chip; - const struct of_device_id *of_id; struct iio_dev *indio_dev; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; - of_id = of_match_device(atlas_ezo_dt_ids, &client->dev); - if (!of_id) - chip = &atlas_ezo_devices[id->driver_data]; + if (dev_fwnode(&client->dev)) + chip = device_get_match_data(&client->dev); else - chip = &atlas_ezo_devices[(unsigned long)of_id->data]; + chip = (const struct atlas_ezo_device *)id->driver_data; + if (!chip) + return -EINVAL; indio_dev->info = &atlas_info; indio_dev->name = ATLAS_EZO_DRV_NAME; diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 04b44a327614..56dea9734c8d 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -589,11 +589,11 @@ static const struct iio_info atlas_info = { }; static const struct i2c_device_id atlas_id[] = { - { "atlas-ph-sm", ATLAS_PH_SM}, - { "atlas-ec-sm", ATLAS_EC_SM}, - { "atlas-orp-sm", ATLAS_ORP_SM}, - { "atlas-do-sm", ATLAS_DO_SM}, - { "atlas-rtd-sm", ATLAS_RTD_SM}, + { "atlas-ph-sm", ATLAS_PH_SM }, + { "atlas-ec-sm", ATLAS_EC_SM }, + { "atlas-orp-sm", ATLAS_ORP_SM }, + { "atlas-do-sm", ATLAS_DO_SM }, + { "atlas-rtd-sm", ATLAS_RTD_SM }, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); @@ -737,7 +737,6 @@ static int atlas_remove(struct i2c_client *client) return atlas_set_powermode(data, 0); } -#ifdef CONFIG_PM static int atlas_runtime_suspend(struct device *dev) { struct atlas_data *data = @@ -753,18 +752,16 @@ static int atlas_runtime_resume(struct device *dev) return atlas_set_powermode(data, 1); } -#endif static const struct dev_pm_ops atlas_pm_ops = { - SET_RUNTIME_PM_OPS(atlas_runtime_suspend, - atlas_runtime_resume, NULL) + RUNTIME_PM_OPS(atlas_runtime_suspend, atlas_runtime_resume, NULL) }; static struct i2c_driver atlas_driver = { .driver = { .name = ATLAS_DRV_NAME, .of_match_table = atlas_dt_ids, - .pm = &atlas_pm_ops, + .pm = pm_ptr(&atlas_pm_ops), }, .probe = atlas_probe, .remove = atlas_remove, diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index bf23cc7eb99e..16ff7a98c9f0 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -81,7 +81,7 @@ const struct regmap_config bme680_regmap_config = { .volatile_table = &bme680_volatile_table, .cache_type = REGCACHE_RBTREE, }; -EXPORT_SYMBOL(bme680_regmap_config); +EXPORT_SYMBOL_NS(bme680_regmap_config, IIO_BME680); static const struct iio_chan_spec bme680_channels[] = { { @@ -957,7 +957,7 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(bme680_core_probe); +EXPORT_SYMBOL_NS_GPL(bme680_core_probe, IIO_BME680); MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>"); MODULE_DESCRIPTION("Bosch BME680 Driver"); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 74cf89c82c0a..20f2c20b6b02 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -60,3 +60,4 @@ module_i2c_driver(bme680_i2c_driver); MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>"); MODULE_DESCRIPTION("BME680 I2C driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_BME680); diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index cc579a7ac5ce..4404d42ae5ec 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -4,8 +4,8 @@ * * Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com> */ +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/regmap.h> #include <linux/spi/spi.h> @@ -163,3 +163,4 @@ module_spi_driver(bme680_spi_driver); MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>"); MODULE_DESCRIPTION("Bosch BME680 SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_BME680); diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index 267bc3c05338..20d4e7584e92 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -423,7 +423,7 @@ static ssize_t calibration_auto_enable_show(struct device *dev, val = (be16_to_cpu(bval) & SCD4X_READY_MASK) ? 1 : 0; - return sprintf(buf, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_auto_enable_store(struct device *dev, diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index d51314505115..abd67559e451 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -221,7 +221,7 @@ static ssize_t cleaning_period_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%d\n", be32_to_cpu(val)); + return sysfs_emit(buf, "%d\n", be32_to_cpu(val)); } static ssize_t cleaning_period_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c index 16ea697e945c..6633b35a94e6 100644 --- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c +++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c @@ -58,7 +58,7 @@ int ms_sensors_reset(void *cli, u8 cmd, unsigned int delay) return 0; } -EXPORT_SYMBOL(ms_sensors_reset); +EXPORT_SYMBOL_NS(ms_sensors_reset, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_read_prom_word() - PROM word read function @@ -84,7 +84,7 @@ int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word) return 0; } -EXPORT_SYMBOL(ms_sensors_read_prom_word); +EXPORT_SYMBOL_NS(ms_sensors_read_prom_word, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_convert_and_read() - ADC conversion & read function @@ -130,7 +130,7 @@ err: dev_err(&client->dev, "Unable to make sensor adc conversion\n"); return ret; } -EXPORT_SYMBOL(ms_sensors_convert_and_read); +EXPORT_SYMBOL_NS(ms_sensors_convert_and_read, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_crc_valid() - CRC check function @@ -248,7 +248,7 @@ int ms_sensors_read_serial(struct i2c_client *client, u64 *sn) return 0; } -EXPORT_SYMBOL(ms_sensors_read_serial); +EXPORT_SYMBOL_NS(ms_sensors_read_serial, IIO_MEAS_SPEC_SENSORS); static int ms_sensors_read_config_reg(struct i2c_client *client, u8 *config_reg) @@ -299,7 +299,7 @@ ssize_t ms_sensors_write_resolution(struct ms_ht_dev *dev_data, MS_SENSORS_CONFIG_REG_WRITE, config_reg); } -EXPORT_SYMBOL(ms_sensors_write_resolution); +EXPORT_SYMBOL_NS(ms_sensors_write_resolution, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_show_battery_low() - Show device battery low indicator @@ -324,9 +324,9 @@ ssize_t ms_sensors_show_battery_low(struct ms_ht_dev *dev_data, if (ret) return ret; - return sprintf(buf, "%d\n", (config_reg & 0x40) >> 6); + return sysfs_emit(buf, "%d\n", (config_reg & 0x40) >> 6); } -EXPORT_SYMBOL(ms_sensors_show_battery_low); +EXPORT_SYMBOL_NS(ms_sensors_show_battery_low, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_show_heater() - Show device heater @@ -351,9 +351,9 @@ ssize_t ms_sensors_show_heater(struct ms_ht_dev *dev_data, if (ret) return ret; - return sprintf(buf, "%d\n", (config_reg & 0x4) >> 2); + return sysfs_emit(buf, "%d\n", (config_reg & 0x4) >> 2); } -EXPORT_SYMBOL(ms_sensors_show_heater); +EXPORT_SYMBOL_NS(ms_sensors_show_heater, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_write_heater() - Write device heater @@ -401,7 +401,7 @@ ssize_t ms_sensors_write_heater(struct ms_ht_dev *dev_data, return len; } -EXPORT_SYMBOL(ms_sensors_write_heater); +EXPORT_SYMBOL_NS(ms_sensors_write_heater, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_ht_read_temperature() - Read temperature @@ -442,7 +442,7 @@ int ms_sensors_ht_read_temperature(struct ms_ht_dev *dev_data, return 0; } -EXPORT_SYMBOL(ms_sensors_ht_read_temperature); +EXPORT_SYMBOL_NS(ms_sensors_ht_read_temperature, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_ht_read_humidity() - Read humidity @@ -485,7 +485,7 @@ int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data, return 0; } -EXPORT_SYMBOL(ms_sensors_ht_read_humidity); +EXPORT_SYMBOL_NS(ms_sensors_ht_read_humidity, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_tp_crc4() - Calculate PROM CRC for @@ -602,7 +602,7 @@ int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data) return 0; } -EXPORT_SYMBOL(ms_sensors_tp_read_prom); +EXPORT_SYMBOL_NS(ms_sensors_tp_read_prom, IIO_MEAS_SPEC_SENSORS); /** * ms_sensors_read_temp_and_pressure() - read temp and pressure @@ -688,7 +688,7 @@ int ms_sensors_read_temp_and_pressure(struct ms_tp_dev *dev_data, return 0; } -EXPORT_SYMBOL(ms_sensors_read_temp_and_pressure); +EXPORT_SYMBOL_NS(ms_sensors_read_temp_and_pressure, IIO_MEAS_SPEC_SENSORS); MODULE_DESCRIPTION("Measurement-Specialties common i2c driver"); MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index eafaf4529df5..e64d242145e0 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -7,9 +7,10 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mfd/core.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/property.h> + #include "ssp.h" #define SSP_WDT_TIME 10000 @@ -204,7 +205,7 @@ u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type) { return data->delay_buf[type]; } -EXPORT_SYMBOL(ssp_get_sensor_delay); +EXPORT_SYMBOL_NS(ssp_get_sensor_delay, IIO_SSP_SENSORS); /** * ssp_enable_sensor() - enables data acquisition for sensor @@ -266,7 +267,7 @@ int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type, derror: return ret; } -EXPORT_SYMBOL(ssp_enable_sensor); +EXPORT_SYMBOL_NS(ssp_enable_sensor, IIO_SSP_SENSORS); /** * ssp_change_delay() - changes data acquisition for sensor @@ -297,7 +298,7 @@ int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type, return 0; } -EXPORT_SYMBOL(ssp_change_delay); +EXPORT_SYMBOL_NS(ssp_change_delay, IIO_SSP_SENSORS); /** * ssp_disable_sensor() - disables sensor @@ -334,7 +335,7 @@ int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type) return 0; } -EXPORT_SYMBOL(ssp_disable_sensor); +EXPORT_SYMBOL_NS(ssp_disable_sensor, IIO_SSP_SENSORS); static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id) { @@ -425,7 +426,6 @@ int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay) msecs_to_jiffies(delay)); } -#ifdef CONFIG_OF static const struct of_device_id ssp_of_match[] = { { .compatible = "samsung,sensorhub-rinato", @@ -441,8 +441,6 @@ MODULE_DEVICE_TABLE(of, ssp_of_match); static struct ssp_data *ssp_parse_dt(struct device *dev) { struct ssp_data *data; - struct device_node *node = dev->of_node; - const struct of_device_id *match; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -461,22 +459,12 @@ static struct ssp_data *ssp_parse_dt(struct device *dev) if (IS_ERR(data->mcu_reset_gpiod)) return NULL; - match = of_match_node(ssp_of_match, node); - if (!match) - return NULL; - - data->sensorhub_info = match->data; + data->sensorhub_info = device_get_match_data(dev); dev_set_drvdata(dev, data); return data; } -#else -static struct ssp_data *ssp_parse_dt(struct device *pdev) -{ - return NULL; -} -#endif /** * ssp_register_consumer() - registers iio consumer in ssp framework @@ -490,7 +478,7 @@ void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type) data->sensor_devs[type] = indio_dev; } -EXPORT_SYMBOL(ssp_register_consumer); +EXPORT_SYMBOL_NS(ssp_register_consumer, IIO_SSP_SENSORS); static int ssp_probe(struct spi_device *spi) { @@ -610,7 +598,6 @@ static void ssp_remove(struct spi_device *spi) mfd_remove_devices(&spi->dev); } -#ifdef CONFIG_PM_SLEEP static int ssp_suspend(struct device *dev) { int ret; @@ -659,18 +646,15 @@ static int ssp_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static const struct dev_pm_ops ssp_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(ssp_pm_ops, ssp_suspend, ssp_resume); static struct spi_driver ssp_driver = { .probe = ssp_probe, .remove = ssp_remove, .driver = { - .pm = &ssp_pm_ops, - .of_match_table = of_match_ptr(ssp_of_match), + .pm = pm_sleep_ptr(&ssp_pm_ops), + .of_match_table = ssp_of_match, .name = "sensorhub" }, }; diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c index 5336db81ba0a..88b8b56bfa51 100644 --- a/drivers/iio/common/ssp_sensors/ssp_iio.c +++ b/drivers/iio/common/ssp_sensors/ssp_iio.c @@ -32,7 +32,7 @@ int ssp_common_buffer_postenable(struct iio_dev *indio_dev) return ssp_enable_sensor(data, spd->type, ssp_get_sensor_delay(data, spd->type)); } -EXPORT_SYMBOL(ssp_common_buffer_postenable); +EXPORT_SYMBOL_NS(ssp_common_buffer_postenable, IIO_SSP_SENSORS); /** * ssp_common_buffer_postdisable() - generic postdisable callback for ssp buffer @@ -55,7 +55,7 @@ int ssp_common_buffer_postdisable(struct iio_dev *indio_dev) return ret; } -EXPORT_SYMBOL(ssp_common_buffer_postdisable); +EXPORT_SYMBOL_NS(ssp_common_buffer_postdisable, IIO_SSP_SENSORS); /** * ssp_common_process_data() - Common process data callback for ssp sensors @@ -91,8 +91,9 @@ int ssp_common_process_data(struct iio_dev *indio_dev, void *buf, return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer, calculated_time); } -EXPORT_SYMBOL(ssp_common_process_data); +EXPORT_SYMBOL_NS(ssp_common_process_data, IIO_SSP_SENSORS); MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>"); MODULE_DESCRIPTION("Samsung sensorhub commons"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_SSP_SENSORS); diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig index 9364ec7a811f..eda8f347fda5 100644 --- a/drivers/iio/common/st_sensors/Kconfig +++ b/drivers/iio/common/st_sensors/Kconfig @@ -13,5 +13,3 @@ config IIO_ST_SENSORS_SPI config IIO_ST_SENSORS_CORE tristate - select IIO_ST_SENSORS_I2C if I2C - select IIO_ST_SENSORS_SPI if SPI_MASTER diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index dccc471e79da..e2f108ca949c 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -8,7 +8,6 @@ */ #include <linux/kernel.h> -#include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/trigger.h> #include <linux/interrupt.h> @@ -77,8 +76,4 @@ st_sensors_get_buffer_element_error: return IRQ_HANDLED; } -EXPORT_SYMBOL(st_sensors_trigger_handler); - -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer"); -MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL_NS(st_sensors_trigger_handler, IIO_ST_SENSORS); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index eb452d0c423c..fa9bcdf0d190 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -46,7 +46,7 @@ int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL(st_sensors_debugfs_reg_access); +EXPORT_SYMBOL_NS(st_sensors_debugfs_reg_access, IIO_ST_SENSORS); static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings, unsigned int odr, struct st_sensor_odr_avl *odr_out) @@ -106,7 +106,7 @@ int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr) st_sensors_match_odr_error: return err; } -EXPORT_SYMBOL(st_sensors_set_odr); +EXPORT_SYMBOL_NS(st_sensors_set_odr, IIO_ST_SENSORS); static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings, unsigned int fs, int *index_fs_avl) @@ -199,7 +199,7 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable) set_enable_error: return err; } -EXPORT_SYMBOL(st_sensors_set_enable); +EXPORT_SYMBOL_NS(st_sensors_set_enable, IIO_ST_SENSORS); int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) { @@ -213,7 +213,7 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) axis_enable); return err; } -EXPORT_SYMBOL(st_sensors_set_axis_enable); +EXPORT_SYMBOL_NS(st_sensors_set_axis_enable, IIO_ST_SENSORS); static void st_reg_disable(void *reg) { @@ -257,7 +257,7 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) return devm_add_action_or_reset(parent, st_reg_disable, pdata->vdd_io); } -EXPORT_SYMBOL(st_sensors_power_enable); +EXPORT_SYMBOL_NS(st_sensors_power_enable, IIO_ST_SENSORS); static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) @@ -352,7 +352,7 @@ void st_sensors_dev_name_probe(struct device *dev, char *name, int len) /* The name from the match takes precedence if present */ strlcpy(name, match, len); } -EXPORT_SYMBOL(st_sensors_dev_name_probe); +EXPORT_SYMBOL_NS(st_sensors_dev_name_probe, IIO_ST_SENSORS); int st_sensors_init_sensor(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) @@ -437,7 +437,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, return err; } -EXPORT_SYMBOL(st_sensors_init_sensor); +EXPORT_SYMBOL_NS(st_sensors_init_sensor, IIO_ST_SENSORS); int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) { @@ -486,7 +486,7 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) st_accel_set_dataready_irq_error: return err; } -EXPORT_SYMBOL(st_sensors_set_dataready_irq); +EXPORT_SYMBOL_NS(st_sensors_set_dataready_irq, IIO_ST_SENSORS); int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale) { @@ -509,7 +509,7 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale) st_sensors_match_scale_error: return err; } -EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain); +EXPORT_SYMBOL_NS(st_sensors_set_fullscale_by_gain, IIO_ST_SENSORS); static int st_sensors_read_axis_data(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *data) @@ -572,7 +572,7 @@ out: return err; } -EXPORT_SYMBOL(st_sensors_read_info_raw); +EXPORT_SYMBOL_NS(st_sensors_read_info_raw, IIO_ST_SENSORS); /* * st_sensors_get_settings_index() - get index of the sensor settings for a @@ -599,7 +599,7 @@ int st_sensors_get_settings_index(const char *name, return -ENODEV; } -EXPORT_SYMBOL(st_sensors_get_settings_index); +EXPORT_SYMBOL_NS(st_sensors_get_settings_index, IIO_ST_SENSORS); /* * st_sensors_verify_id() - verify sensor ID (WhoAmI) is matching with the @@ -632,7 +632,7 @@ int st_sensors_verify_id(struct iio_dev *indio_dev) return 0; } -EXPORT_SYMBOL(st_sensors_verify_id); +EXPORT_SYMBOL_NS(st_sensors_verify_id, IIO_ST_SENSORS); ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -654,7 +654,7 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, return len; } -EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail); +EXPORT_SYMBOL_NS(st_sensors_sysfs_sampling_frequency_avail, IIO_ST_SENSORS); ssize_t st_sensors_sysfs_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -678,7 +678,7 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev, return len; } -EXPORT_SYMBOL(st_sensors_sysfs_scale_avail); +EXPORT_SYMBOL_NS(st_sensors_sysfs_scale_avail, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics ST-sensors core"); diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index 18bd3c3d99bc..ee95082c7410 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -61,7 +61,7 @@ int st_sensors_i2c_configure(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL(st_sensors_i2c_configure); +EXPORT_SYMBOL_NS(st_sensors_i2c_configure, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver"); diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c index 7c60050e90dc..63e302c3fbaa 100644 --- a/drivers/iio/common/st_sensors/st_sensors_spi.c +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -113,7 +113,7 @@ int st_sensors_spi_configure(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL(st_sensors_spi_configure); +EXPORT_SYMBOL_NS(st_sensors_spi_configure, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver"); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 392d74449886..899b640c0a70 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -8,7 +8,6 @@ */ #include <linux/kernel.h> -#include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/trigger.h> #include <linux/interrupt.h> @@ -228,7 +227,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL(st_sensors_allocate_trigger); +EXPORT_SYMBOL_NS(st_sensors_allocate_trigger, IIO_ST_SENSORS); int st_sensors_validate_device(struct iio_trigger *trig, struct iio_dev *indio_dev) @@ -240,8 +239,4 @@ int st_sensors_validate_device(struct iio_trigger *trig, return 0; } -EXPORT_SYMBOL(st_sensors_validate_device); - -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger"); -MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL_NS(st_sensors_validate_device, IIO_ST_SENSORS); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index bfcf7568de32..c0bf0d84197f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -131,6 +131,17 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config LTC2688 + tristate "Analog Devices LTC2688 DAC spi driver" + depends on SPI + select REGMAP + help + Say yes here to build support for Analog Devices + LTC2688 converters (DAC). + + To compile this driver as a module, choose M here: the + module will be called ltc2688. + config AD5686 tristate diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 01a50131572f..ec3e42713f00 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_DS4424) += ds4424.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_LTC1660) += ltc1660.o obj-$(CONFIG_LTC2632) += ltc2632.o +obj-$(CONFIG_LTC2688) += ltc2688.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX5821) += max5821.o diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 2fcc59728fd6..a424b7220b61 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -11,7 +11,6 @@ #include <linux/iio/iio.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> @@ -661,7 +660,7 @@ error_disable_reg: return ret; } -EXPORT_SYMBOL_GPL(ad5592r_probe); +EXPORT_SYMBOL_NS_GPL(ad5592r_probe, IIO_AD5592R); void ad5592r_remove(struct device *dev) { @@ -675,7 +674,7 @@ void ad5592r_remove(struct device *dev) if (st->reg) regulator_disable(st->reg); } -EXPORT_SYMBOL_GPL(ad5592r_remove); +EXPORT_SYMBOL_NS_GPL(ad5592r_remove, IIO_AD5592R); MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters"); diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c index 0f7abfa75bec..32d950bbb1ca 100644 --- a/drivers/iio/dac/ad5592r.c +++ b/drivers/iio/dac/ad5592r.c @@ -168,3 +168,4 @@ module_spi_driver(ad5592r_spi_driver); MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD5592R); diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index 64dd7a0bddf7..34e1319a9712 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -137,3 +137,4 @@ module_i2c_driver(ad5593r_driver); MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5593R multi-channel converters"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD5592R); diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index d26fb29b6b04..8ba2ea70451a 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -135,3 +135,4 @@ module_spi_driver(ad5686_spi_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD5686); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index e592a995f404..f78dd3f33199 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -536,7 +536,7 @@ error_disable_reg: regulator_disable(st->reg); return ret; } -EXPORT_SYMBOL_GPL(ad5686_probe); +EXPORT_SYMBOL_NS_GPL(ad5686_probe, IIO_AD5686); void ad5686_remove(struct device *dev) { @@ -547,7 +547,7 @@ void ad5686_remove(struct device *dev) if (!IS_ERR(st->reg)) regulator_disable(st->reg); } -EXPORT_SYMBOL_GPL(ad5686_remove); +EXPORT_SYMBOL_NS_GPL(ad5686_remove, IIO_AD5686); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 93f0e0e66c22..762503c1901b 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -125,3 +125,4 @@ module_i2c_driver(ad5686_i2c_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD5686); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c new file mode 100644 index 000000000000..e41861d29767 --- /dev/null +++ b/drivers/iio/dac/ltc2688.c @@ -0,0 +1,1071 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LTC2688 16 channel, 16 bit Voltage Output SoftSpan DAC driver + * + * Copyright 2022 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/limits.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#define LTC2688_DAC_CHANNELS 16 + +#define LTC2688_CMD_CH_CODE(x) (0x00 + (x)) +#define LTC2688_CMD_CH_SETTING(x) (0x10 + (x)) +#define LTC2688_CMD_CH_OFFSET(x) (0X20 + (x)) +#define LTC2688_CMD_CH_GAIN(x) (0x30 + (x)) +#define LTC2688_CMD_CH_CODE_UPDATE(x) (0x40 + (x)) + +#define LTC2688_CMD_CONFIG 0x70 +#define LTC2688_CMD_POWERDOWN 0x71 +#define LTC2688_CMD_A_B_SELECT 0x72 +#define LTC2688_CMD_SW_TOGGLE 0x73 +#define LTC2688_CMD_TOGGLE_DITHER_EN 0x74 +#define LTC2688_CMD_THERMAL_STAT 0x77 +#define LTC2688_CMD_UPDATE_ALL 0x7C +#define LTC2688_CMD_NOOP 0xFF + +#define LTC2688_READ_OPERATION 0x80 + +/* Channel Settings */ +#define LTC2688_CH_SPAN_MSK GENMASK(2, 0) +#define LTC2688_CH_OVERRANGE_MSK BIT(3) +#define LTC2688_CH_TD_SEL_MSK GENMASK(5, 4) +#define LTC2688_CH_TGP_MAX 3 +#define LTC2688_CH_DIT_PER_MSK GENMASK(8, 6) +#define LTC2688_CH_DIT_PH_MSK GENMASK(10, 9) +#define LTC2688_CH_MODE_MSK BIT(11) + +#define LTC2688_DITHER_RAW_MASK GENMASK(15, 2) +#define LTC2688_CH_CALIBBIAS_MASK GENMASK(15, 2) +#define LTC2688_DITHER_RAW_MAX_VAL (BIT(14) - 1) +#define LTC2688_CH_CALIBBIAS_MAX_VAL (BIT(14) - 1) + +/* Configuration register */ +#define LTC2688_CONFIG_RST BIT(15) +#define LTC2688_CONFIG_EXT_REF BIT(1) + +#define LTC2688_DITHER_FREQ_AVAIL_N 5 + +enum { + LTC2688_SPAN_RANGE_0V_5V, + LTC2688_SPAN_RANGE_0V_10V, + LTC2688_SPAN_RANGE_M5V_5V, + LTC2688_SPAN_RANGE_M10V_10V, + LTC2688_SPAN_RANGE_M15V_15V, + LTC2688_SPAN_RANGE_MAX +}; + +enum { + LTC2688_MODE_DEFAULT, + LTC2688_MODE_DITHER_TOGGLE, +}; + +struct ltc2688_chan { + long dither_frequency[LTC2688_DITHER_FREQ_AVAIL_N]; + bool overrange; + bool toggle_chan; + u8 mode; +}; + +struct ltc2688_state { + struct spi_device *spi; + struct regmap *regmap; + struct regulator_bulk_data regulators[2]; + struct ltc2688_chan channels[LTC2688_DAC_CHANNELS]; + struct iio_chan_spec *iio_chan; + /* lock to protect against multiple access to the device and shared data */ + struct mutex lock; + int vref; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 tx_data[6] ____cacheline_aligned; + u8 rx_data[3]; +}; + +static int ltc2688_spi_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct ltc2688_state *st = context; + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx_data, + .bits_per_word = 8, + .len = reg_size + val_size, + .cs_change = 1, + }, { + .tx_buf = st->tx_data + 3, + .rx_buf = st->rx_data, + .bits_per_word = 8, + .len = reg_size + val_size, + }, + }; + int ret; + + memcpy(st->tx_data, reg, reg_size); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) + return ret; + + memcpy(val, &st->rx_data[1], val_size); + + return 0; +} + +static int ltc2688_spi_write(void *context, const void *data, size_t count) +{ + struct ltc2688_state *st = context; + + return spi_write(st->spi, data, count); +} + +static int ltc2688_span_get(const struct ltc2688_state *st, int c) +{ + int ret, reg, span; + + ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(c), ®); + if (ret) + return ret; + + span = FIELD_GET(LTC2688_CH_SPAN_MSK, reg); + /* sanity check to make sure we don't get any weird value from the HW */ + if (span >= LTC2688_SPAN_RANGE_MAX) + return -EIO; + + return span; +} + +static const int ltc2688_span_helper[LTC2688_SPAN_RANGE_MAX][2] = { + {0, 5000}, {0, 10000}, {-5000, 5000}, {-10000, 10000}, {-15000, 15000}, +}; + +static int ltc2688_scale_get(const struct ltc2688_state *st, int c, int *val) +{ + const struct ltc2688_chan *chan = &st->channels[c]; + int span, fs; + + span = ltc2688_span_get(st, c); + if (span < 0) + return span; + + fs = ltc2688_span_helper[span][1] - ltc2688_span_helper[span][0]; + if (chan->overrange) + fs = mult_frac(fs, 105, 100); + + *val = DIV_ROUND_CLOSEST(fs * st->vref, 4096); + + return 0; +} + +static int ltc2688_offset_get(const struct ltc2688_state *st, int c, int *val) +{ + int span; + + span = ltc2688_span_get(st, c); + if (span < 0) + return span; + + if (ltc2688_span_helper[span][0] < 0) + *val = -32768; + else + *val = 0; + + return 0; +} + +enum { + LTC2688_INPUT_A, + LTC2688_INPUT_B, + LTC2688_INPUT_B_AVAIL, + LTC2688_DITHER_OFF, + LTC2688_DITHER_FREQ_AVAIL, +}; + +static int ltc2688_dac_code_write(struct ltc2688_state *st, u32 chan, u32 input, + u16 code) +{ + struct ltc2688_chan *c = &st->channels[chan]; + int ret, reg; + + /* 2 LSBs set to 0 if writing dither amplitude */ + if (!c->toggle_chan && input == LTC2688_INPUT_B) { + if (code > LTC2688_DITHER_RAW_MAX_VAL) + return -EINVAL; + + code = FIELD_PREP(LTC2688_DITHER_RAW_MASK, code); + } + + mutex_lock(&st->lock); + /* select the correct input register to read from */ + ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan), + input << chan); + if (ret) + goto out_unlock; + + /* + * If in dither/toggle mode the dac should be updated by an + * external signal (or sw toggle) and not here. + */ + if (c->mode == LTC2688_MODE_DEFAULT) + reg = LTC2688_CMD_CH_CODE_UPDATE(chan); + else + reg = LTC2688_CMD_CH_CODE(chan); + + ret = regmap_write(st->regmap, reg, code); +out_unlock: + mutex_unlock(&st->lock); + return ret; +} + +static int ltc2688_dac_code_read(struct ltc2688_state *st, u32 chan, u32 input, + u32 *code) +{ + struct ltc2688_chan *c = &st->channels[chan]; + int ret; + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan), + input << chan); + if (ret) + goto out_unlock; + + ret = regmap_read(st->regmap, LTC2688_CMD_CH_CODE(chan), code); +out_unlock: + mutex_unlock(&st->lock); + + if (!c->toggle_chan && input == LTC2688_INPUT_B) + *code = FIELD_GET(LTC2688_DITHER_RAW_MASK, *code); + + return ret; +} + +static const int ltc2688_raw_range[] = {0, 1, U16_MAX}; + +static int ltc2688_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + *vals = ltc2688_raw_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int ltc2688_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ltc2688_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ltc2688_dac_code_read(st, chan->channel, LTC2688_INPUT_A, + val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + ret = ltc2688_offset_get(st, chan->channel, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = ltc2688_scale_get(st, chan->channel, val); + if (ret) + return ret; + + *val = 16; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(st->regmap, + LTC2688_CMD_CH_OFFSET(chan->channel), val); + if (ret) + return ret; + + *val = FIELD_GET(LTC2688_CH_CALIBBIAS_MASK, *val); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = regmap_read(st->regmap, + LTC2688_CMD_CH_GAIN(chan->channel), val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ltc2688_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ltc2688_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val > U16_MAX || val < 0) + return -EINVAL; + + return ltc2688_dac_code_write(st, chan->channel, + LTC2688_INPUT_A, val); + case IIO_CHAN_INFO_CALIBBIAS: + if (val > LTC2688_CH_CALIBBIAS_MAX_VAL) + return -EINVAL; + + return regmap_write(st->regmap, + LTC2688_CMD_CH_OFFSET(chan->channel), + FIELD_PREP(LTC2688_CH_CALIBBIAS_MASK, val)); + case IIO_CHAN_INFO_CALIBSCALE: + return regmap_write(st->regmap, + LTC2688_CMD_CH_GAIN(chan->channel), val); + default: + return -EINVAL; + } +} + +static ssize_t ltc2688_dither_toggle_set(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ltc2688_state *st = iio_priv(indio_dev); + struct ltc2688_chan *c = &st->channels[chan->channel]; + int ret; + bool en; + + ret = kstrtobool(buf, &en); + if (ret) + return ret; + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, LTC2688_CMD_TOGGLE_DITHER_EN, + BIT(chan->channel), en << chan->channel); + if (ret) + goto out_unlock; + + c->mode = en ? LTC2688_MODE_DITHER_TOGGLE : LTC2688_MODE_DEFAULT; +out_unlock: + mutex_unlock(&st->lock); + + return ret ?: len; +} + +static ssize_t ltc2688_reg_bool_get(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + const struct ltc2688_state *st = iio_priv(indio_dev); + int ret; + u32 val; + + ret = regmap_read(st->regmap, private, &val); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", !!(val & BIT(chan->channel))); +} + +static ssize_t ltc2688_reg_bool_set(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + const struct ltc2688_state *st = iio_priv(indio_dev); + int ret; + bool en; + + ret = kstrtobool(buf, &en); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, private, BIT(chan->channel), + en << chan->channel); + if (ret) + return ret; + + return len; +} + +static ssize_t ltc2688_dither_freq_avail(const struct ltc2688_state *st, + const struct ltc2688_chan *chan, + char *buf) +{ + int sz = 0; + u32 f; + + for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++) + sz += sysfs_emit_at(buf, sz, "%ld ", chan->dither_frequency[f]); + + buf[sz - 1] = '\n'; + + return sz; +} + +static ssize_t ltc2688_dither_freq_get(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + const struct ltc2688_state *st = iio_priv(indio_dev); + const struct ltc2688_chan *c = &st->channels[chan->channel]; + u32 reg, freq; + int ret; + + if (private == LTC2688_DITHER_FREQ_AVAIL) + return ltc2688_dither_freq_avail(st, c, buf); + + ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel), + ®); + if (ret) + return ret; + + freq = FIELD_GET(LTC2688_CH_DIT_PER_MSK, reg); + if (freq >= ARRAY_SIZE(c->dither_frequency)) + return -EIO; + + return sysfs_emit(buf, "%ld\n", c->dither_frequency[freq]); +} + +static ssize_t ltc2688_dither_freq_set(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + const struct ltc2688_state *st = iio_priv(indio_dev); + const struct ltc2688_chan *c = &st->channels[chan->channel]; + long val; + u32 freq; + int ret; + + if (private == LTC2688_DITHER_FREQ_AVAIL) + return -EINVAL; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + for (freq = 0; freq < ARRAY_SIZE(c->dither_frequency); freq++) { + if (val == c->dither_frequency[freq]) + break; + } + + if (freq == ARRAY_SIZE(c->dither_frequency)) + return -EINVAL; + + ret = regmap_update_bits(st->regmap, + LTC2688_CMD_CH_SETTING(chan->channel), + LTC2688_CH_DIT_PER_MSK, + FIELD_PREP(LTC2688_CH_DIT_PER_MSK, freq)); + if (ret) + return ret; + + return len; +} + +static ssize_t ltc2688_dac_input_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltc2688_state *st = iio_priv(indio_dev); + int ret; + u32 val; + + if (private == LTC2688_INPUT_B_AVAIL) + return sysfs_emit(buf, "[%u %u %u]\n", ltc2688_raw_range[0], + ltc2688_raw_range[1], + ltc2688_raw_range[2] / 4); + + if (private == LTC2688_DITHER_OFF) + return sysfs_emit(buf, "0\n"); + + ret = ltc2688_dac_code_read(st, chan->channel, private, &val); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t ltc2688_dac_input_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ltc2688_state *st = iio_priv(indio_dev); + int ret; + u16 val; + + if (private == LTC2688_INPUT_B_AVAIL || private == LTC2688_DITHER_OFF) + return -EINVAL; + + ret = kstrtou16(buf, 10, &val); + if (ret) + return ret; + + ret = ltc2688_dac_code_write(st, chan->channel, private, val); + if (ret) + return ret; + + return len; +} + +static int ltc2688_get_dither_phase(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ltc2688_state *st = iio_priv(dev); + int ret, regval; + + ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel), + ®val); + if (ret) + return ret; + + return FIELD_GET(LTC2688_CH_DIT_PH_MSK, regval); +} + +static int ltc2688_set_dither_phase(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int phase) +{ + struct ltc2688_state *st = iio_priv(dev); + + return regmap_update_bits(st->regmap, + LTC2688_CMD_CH_SETTING(chan->channel), + LTC2688_CH_DIT_PH_MSK, + FIELD_PREP(LTC2688_CH_DIT_PH_MSK, phase)); +} + +static int ltc2688_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ltc2688_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static const char * const ltc2688_dither_phase[] = { + "0", "1.5708", "3.14159", "4.71239", +}; + +static const struct iio_enum ltc2688_dither_phase_enum = { + .items = ltc2688_dither_phase, + .num_items = ARRAY_SIZE(ltc2688_dither_phase), + .set = ltc2688_set_dither_phase, + .get = ltc2688_get_dither_phase, +}; + +#define LTC2688_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { \ + .name = _name, \ + .read = (_read), \ + .write = (_write), \ + .private = (_what), \ + .shared = (_shared), \ +} + +/* + * For toggle mode we only expose the symbol attr (sw_toggle) in case a TGPx is + * not provided in dts. + */ +static const struct iio_chan_spec_ext_info ltc2688_toggle_sym_ext_info[] = { + LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE, + ltc2688_dac_input_read, ltc2688_dac_input_write), + LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE, + ltc2688_dac_input_read, ltc2688_dac_input_write), + LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN, + IIO_SEPARATE, ltc2688_reg_bool_get, + ltc2688_dither_toggle_set), + LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, + ltc2688_reg_bool_get, ltc2688_reg_bool_set), + LTC2688_CHAN_EXT_INFO("symbol", LTC2688_CMD_SW_TOGGLE, IIO_SEPARATE, + ltc2688_reg_bool_get, ltc2688_reg_bool_set), + {} +}; + +static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = { + LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE, + ltc2688_dac_input_read, ltc2688_dac_input_write), + LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE, + ltc2688_dac_input_read, ltc2688_dac_input_write), + LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN, + IIO_SEPARATE, ltc2688_reg_bool_get, + ltc2688_dither_toggle_set), + LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, + ltc2688_reg_bool_get, ltc2688_reg_bool_set), + {} +}; + +static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = { + LTC2688_CHAN_EXT_INFO("dither_raw", LTC2688_INPUT_B, IIO_SEPARATE, + ltc2688_dac_input_read, ltc2688_dac_input_write), + LTC2688_CHAN_EXT_INFO("dither_raw_available", LTC2688_INPUT_B_AVAIL, + IIO_SEPARATE, ltc2688_dac_input_read, + ltc2688_dac_input_write), + LTC2688_CHAN_EXT_INFO("dither_offset", LTC2688_DITHER_OFF, IIO_SEPARATE, + ltc2688_dac_input_read, ltc2688_dac_input_write), + /* + * Not IIO_ENUM because the available freq needs to be computed at + * probe. We could still use it, but it didn't felt much right. + */ + LTC2688_CHAN_EXT_INFO("dither_frequency", 0, IIO_SEPARATE, + ltc2688_dither_freq_get, ltc2688_dither_freq_set), + LTC2688_CHAN_EXT_INFO("dither_frequency_available", + LTC2688_DITHER_FREQ_AVAIL, IIO_SEPARATE, + ltc2688_dither_freq_get, ltc2688_dither_freq_set), + IIO_ENUM("dither_phase", IIO_SEPARATE, <c2688_dither_phase_enum), + IIO_ENUM_AVAILABLE("dither_phase", IIO_SEPARATE, + <c2688_dither_phase_enum), + LTC2688_CHAN_EXT_INFO("dither_en", LTC2688_CMD_TOGGLE_DITHER_EN, + IIO_SEPARATE, ltc2688_reg_bool_get, + ltc2688_dither_toggle_set), + LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, + ltc2688_reg_bool_get, ltc2688_reg_bool_set), + {} +}; + +static const struct iio_chan_spec_ext_info ltc2688_ext_info[] = { + LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, + ltc2688_reg_bool_get, ltc2688_reg_bool_set), + {} +}; + +#define LTC2688_CHANNEL(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \ + .ext_info = ltc2688_ext_info, \ +} + +static const struct iio_chan_spec ltc2688_channels[] = { + LTC2688_CHANNEL(0), + LTC2688_CHANNEL(1), + LTC2688_CHANNEL(2), + LTC2688_CHANNEL(3), + LTC2688_CHANNEL(4), + LTC2688_CHANNEL(5), + LTC2688_CHANNEL(6), + LTC2688_CHANNEL(7), + LTC2688_CHANNEL(8), + LTC2688_CHANNEL(9), + LTC2688_CHANNEL(10), + LTC2688_CHANNEL(11), + LTC2688_CHANNEL(12), + LTC2688_CHANNEL(13), + LTC2688_CHANNEL(14), + LTC2688_CHANNEL(15), +}; + +static void ltc2688_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + +static const int ltc2688_period[LTC2688_DITHER_FREQ_AVAIL_N] = { + 4, 8, 16, 32, 64, +}; + +static int ltc2688_tgp_clk_setup(struct ltc2688_state *st, + struct ltc2688_chan *chan, + struct fwnode_handle *node, int tgp) +{ + unsigned long rate; + struct clk *clk; + int ret, f; + + clk = devm_get_clk_from_child(&st->spi->dev, to_of_node(node), NULL); + if (IS_ERR(clk)) + return dev_err_probe(&st->spi->dev, PTR_ERR(clk), + "failed to get tgp clk.\n"); + + ret = clk_prepare_enable(clk); + if (ret) + return dev_err_probe(&st->spi->dev, ret, + "failed to enable tgp clk.\n"); + + ret = devm_add_action_or_reset(&st->spi->dev, ltc2688_clk_disable, clk); + if (ret) + return ret; + + if (chan->toggle_chan) + return 0; + + /* calculate available dither frequencies */ + rate = clk_get_rate(clk); + for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++) + chan->dither_frequency[f] = DIV_ROUND_CLOSEST(rate, ltc2688_period[f]); + + return 0; +} + +static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max) +{ + u32 span; + + for (span = 0; span < ARRAY_SIZE(ltc2688_span_helper); span++) { + if (min == ltc2688_span_helper[span][0] && + max == ltc2688_span_helper[span][1]) + return span; + } + + return -EINVAL; +} + +static int ltc2688_channel_config(struct ltc2688_state *st) +{ + struct device *dev = &st->spi->dev; + struct fwnode_handle *child; + u32 reg, clk_input, val, tmp[2]; + int ret, span; + + device_for_each_child_node(dev, child) { + struct ltc2688_chan *chan; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) { + fwnode_handle_put(child); + return dev_err_probe(dev, ret, + "Failed to get reg property\n"); + } + + if (reg >= LTC2688_DAC_CHANNELS) { + fwnode_handle_put(child); + return dev_err_probe(dev, -EINVAL, + "reg bigger than: %d\n", + LTC2688_DAC_CHANNELS); + } + + val = 0; + chan = &st->channels[reg]; + if (fwnode_property_read_bool(child, "adi,toggle-mode")) { + chan->toggle_chan = true; + /* assume sw toggle ABI */ + st->iio_chan[reg].ext_info = ltc2688_toggle_sym_ext_info; + /* + * Clear IIO_CHAN_INFO_RAW bit as toggle channels expose + * out_voltage_raw{0|1} files. + */ + __clear_bit(IIO_CHAN_INFO_RAW, + &st->iio_chan[reg].info_mask_separate); + } + + ret = fwnode_property_read_u32_array(child, "adi,output-range-microvolt", + tmp, ARRAY_SIZE(tmp)); + if (!ret) { + span = ltc2688_span_lookup(st, (int)tmp[0] / 1000, + tmp[1] / 1000); + if (span < 0) { + fwnode_handle_put(child); + return dev_err_probe(dev, -EINVAL, + "output range not valid:[%d %d]\n", + tmp[0], tmp[1]); + } + + val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span); + } + + ret = fwnode_property_read_u32(child, "adi,toggle-dither-input", + &clk_input); + if (!ret) { + if (clk_input >= LTC2688_CH_TGP_MAX) { + fwnode_handle_put(child); + return dev_err_probe(dev, -EINVAL, + "toggle-dither-input inv value(%d)\n", + clk_input); + } + + ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input); + if (ret) { + fwnode_handle_put(child); + return ret; + } + + /* + * 0 means software toggle which is the default mode. + * Hence the +1. + */ + val |= FIELD_PREP(LTC2688_CH_TD_SEL_MSK, clk_input + 1); + + /* + * If a TGPx is given, we automatically assume a dither + * capable channel (unless toggle is already enabled). + * On top of this we just set here the dither bit in the + * channel settings. It won't have any effect until the + * global toggle/dither bit is enabled. + */ + if (!chan->toggle_chan) { + val |= FIELD_PREP(LTC2688_CH_MODE_MSK, 1); + st->iio_chan[reg].ext_info = ltc2688_dither_ext_info; + } else { + /* wait, no sw toggle after all */ + st->iio_chan[reg].ext_info = ltc2688_toggle_ext_info; + } + } + + if (fwnode_property_read_bool(child, "adi,overrange")) { + chan->overrange = true; + val |= LTC2688_CH_OVERRANGE_MSK; + } + + if (!val) + continue; + + ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg), + val); + if (ret) { + fwnode_handle_put(child); + return dev_err_probe(dev, -EINVAL, + "failed to set chan settings\n"); + } + } + + return 0; +} + +static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref) +{ + struct gpio_desc *gpio; + int ret; + + /* + * If we have a reset pin, use that to reset the board, If not, use + * the reset bit. + */ + gpio = devm_gpiod_get_optional(&st->spi->dev, "clr", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return dev_err_probe(&st->spi->dev, PTR_ERR(gpio), + "Failed to get reset gpio"); + if (gpio) { + usleep_range(1000, 1200); + /* bring device out of reset */ + gpiod_set_value_cansleep(gpio, 0); + } else { + ret = regmap_update_bits(st->regmap, LTC2688_CMD_CONFIG, + LTC2688_CONFIG_RST, + LTC2688_CONFIG_RST); + if (ret) + return ret; + } + + usleep_range(10000, 12000); + + /* + * Duplicate the default channel configuration as it can change during + * @ltc2688_channel_config() + */ + st->iio_chan = devm_kmemdup(&st->spi->dev, ltc2688_channels, + sizeof(ltc2688_channels), GFP_KERNEL); + if (!st->iio_chan) + return -ENOMEM; + + ret = ltc2688_channel_config(st); + if (ret) + return ret; + + if (!vref) + return 0; + + return regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG, + LTC2688_CONFIG_EXT_REF); +} + +static void ltc2688_disable_regulators(void *data) +{ + struct ltc2688_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); +} + +static void ltc2688_disable_regulator(void *regulator) +{ + regulator_disable(regulator); +} + +static bool ltc2688_reg_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC2688_CMD_CH_CODE(0) ... LTC2688_CMD_CH_GAIN(15): + return true; + case LTC2688_CMD_CONFIG ... LTC2688_CMD_THERMAL_STAT: + return true; + default: + return false; + } +} + +static bool ltc2688_reg_writable(struct device *dev, unsigned int reg) +{ + /* + * There's a jump from 0x76 to 0x78 in the write codes and the thermal + * status code is 0x77 (which is read only) so that we need to check + * that special condition. + */ + if (reg <= LTC2688_CMD_UPDATE_ALL && reg != LTC2688_CMD_THERMAL_STAT) + return true; + + return false; +} + +static struct regmap_bus ltc2688_regmap_bus = { + .read = ltc2688_spi_read, + .write = ltc2688_spi_write, + .read_flag_mask = LTC2688_READ_OPERATION, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_config ltc2688_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .readable_reg = ltc2688_reg_readable, + .writeable_reg = ltc2688_reg_writable, + /* ignoring the no op command */ + .max_register = LTC2688_CMD_UPDATE_ALL, +}; + +static const struct iio_info ltc2688_info = { + .write_raw = ltc2688_write_raw, + .read_raw = ltc2688_read_raw, + .read_avail = ltc2688_read_avail, + .debugfs_reg_access = ltc2688_reg_access, +}; + +static int ltc2688_probe(struct spi_device *spi) +{ + struct ltc2688_state *st; + struct iio_dev *indio_dev; + struct regulator *vref_reg; + struct device *dev = &spi->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + /* Just write this once. No need to do it in every regmap read. */ + st->tx_data[3] = LTC2688_CMD_NOOP; + mutex_init(&st->lock); + + st->regmap = devm_regmap_init(dev, <c2688_regmap_bus, st, + <c2688_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to init regmap"); + + st->regulators[0].supply = "vcc"; + st->regulators[1].supply = "iovcc"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators), + st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, ltc2688_disable_regulators, st); + if (ret) + return ret; + + vref_reg = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(vref_reg)) { + if (PTR_ERR(vref_reg) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vref_reg), + "Failed to get vref regulator"); + + vref_reg = NULL; + /* internal reference */ + st->vref = 4096; + } else { + ret = regulator_enable(vref_reg); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable vref regulators\n"); + + ret = devm_add_action_or_reset(dev, ltc2688_disable_regulator, + vref_reg); + if (ret) + return ret; + + ret = regulator_get_voltage(vref_reg); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get vref\n"); + + st->vref = ret / 1000; + } + + ret = ltc2688_setup(st, vref_reg); + if (ret) + return ret; + + indio_dev->name = "ltc2688"; + indio_dev->info = <c2688_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->iio_chan; + indio_dev->num_channels = ARRAY_SIZE(ltc2688_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ltc2688_of_id[] = { + { .compatible = "adi,ltc2688" }, + {} +}; +MODULE_DEVICE_TABLE(of, ltc2688_of_id); + +static const struct spi_device_id ltc2688_id[] = { + { "ltc2688" }, + {} +}; +MODULE_DEVICE_TABLE(spi, ltc2688_id); + +static struct spi_driver ltc2688_driver = { + .driver = { + .name = "ltc2688", + .of_match_table = ltc2688_of_id, + }, + .probe = ltc2688_probe, + .id_table = ltc2688_id, +}; +module_spi_driver(ltc2688_driver); + +MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices LTC2688 DAC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index 225b1a374dc1..22b02f50fe41 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -25,9 +25,7 @@ struct m62332_data { struct regulator *vcc; struct mutex mutex; u8 raw[M62332_CHANNELS]; -#ifdef CONFIG_PM_SLEEP u8 save[M62332_CHANNELS]; -#endif }; static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) @@ -124,7 +122,6 @@ static int m62332_write_raw(struct iio_dev *indio_dev, return -EINVAL; } -#ifdef CONFIG_PM_SLEEP static int m62332_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -156,11 +153,7 @@ static int m62332_resume(struct device *dev) return m62332_set_value(indio_dev, data->save[1], 1); } -static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume); -#define M62332_PM_OPS (&m62332_pm_ops) -#else -#define M62332_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume); static const struct iio_info m62332_info = { .read_raw = m62332_read_raw, @@ -246,7 +239,7 @@ MODULE_DEVICE_TABLE(i2c, m62332_id); static struct i2c_driver m62332_driver = { .driver = { .name = "m62332", - .pm = M62332_PM_OPS, + .pm = pm_sleep_ptr(&m62332_pm_ops), }, .probe = m62332_probe, .remove = m62332_remove, diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index bd7a3b20e645..83bf184e3adc 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -195,7 +195,7 @@ static int stm32_dac_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_dac_core_resume(struct device *dev) +static int stm32_dac_core_resume(struct device *dev) { struct stm32_dac_common *common = dev_get_drvdata(dev); struct stm32_dac_priv *priv = to_stm32_dac_priv(common); @@ -213,23 +213,23 @@ static int __maybe_unused stm32_dac_core_resume(struct device *dev) return pm_runtime_force_resume(dev); } -static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev) +static int stm32_dac_core_runtime_suspend(struct device *dev) { stm32_dac_core_hw_stop(dev); return 0; } -static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev) +static int stm32_dac_core_runtime_resume(struct device *dev) { return stm32_dac_core_hw_start(dev); } static const struct dev_pm_ops stm32_dac_core_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume) - SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend, - stm32_dac_core_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume) + RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend, + stm32_dac_core_runtime_resume, + NULL) }; static const struct stm32_dac_cfg stm32h7_dac_cfg = { @@ -253,7 +253,7 @@ static struct platform_driver stm32_dac_driver = { .driver = { .name = "stm32-dac-core", .of_match_table = stm32_dac_of_match, - .pm = &stm32_dac_core_pm_ops, + .pm = pm_ptr(&stm32_dac_core_pm_ops), }, }; module_platform_driver(stm32_dac_driver); diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index cd71cc4553a7..b20192a071cb 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -372,7 +372,7 @@ static int stm32_dac_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_dac_suspend(struct device *dev) +static int stm32_dac_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); int channel = indio_dev->channels[0].channel; @@ -386,9 +386,8 @@ static int __maybe_unused stm32_dac_suspend(struct device *dev) return pm_runtime_force_suspend(dev); } -static const struct dev_pm_ops stm32_dac_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dac_pm_ops, stm32_dac_suspend, + pm_runtime_force_resume); static const struct of_device_id stm32_dac_of_match[] = { { .compatible = "st,stm32-dac", }, @@ -402,7 +401,7 @@ static struct platform_driver stm32_dac_driver = { .driver = { .name = "stm32-dac", .of_match_table = stm32_dac_of_match, - .pm = &stm32_dac_pm_ops, + .pm = pm_sleep_ptr(&stm32_dac_pm_ops), }, }; module_platform_driver(stm32_dac_driver); diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index 636b4009f763..92429c0d2685 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -242,7 +242,6 @@ static int vf610_dac_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int vf610_dac_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -268,9 +267,9 @@ static int vf610_dac_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, + vf610_dac_resume); static struct platform_driver vf610_dac_driver = { .probe = vf610_dac_probe, @@ -278,7 +277,7 @@ static struct platform_driver vf610_dac_driver = { .driver = { .name = "vf610-dac", .of_match_table = vf610_dac_match, - .pm = &vf610_dac_pm_ops, + .pm = pm_sleep_ptr(&vf610_dac_pm_ops), }, }; module_platform_driver(vf610_dac_driver); diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index b44036f843af..f3702f36436c 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -60,6 +60,26 @@ config ADMV1013 To compile this driver as a module, choose M here: the module will be called admv1013. +config ADMV1014 + tristate "Analog Devices ADMV1014 Microwave Downconverter" + depends on SPI && COMMON_CLK && 64BIT + help + Say yes here to build support for Analog Devices ADMV1014 + 24 GHz to 44 GHz, Wideband, Microwave Downconverter. + + To compile this driver as a module, choose M here: the + module will be called admv1014. + +config ADMV4420 + tristate "Analog Devices ADMV4420 K Band Downconverter" + depends on SPI + help + Say yes here to build support for Analog Devices K Band + Downconverter with integrated Fractional-N PLL and VCO. + + To compile this driver as a module, choose M here: the + module will be called admv4420. + config ADRF6780 tristate "Analog Devices ADRF6780 Microwave Upconverter" depends on SPI diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index ae6899856c99..48add732f1d3 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -8,4 +8,6 @@ obj-$(CONFIG_AD9523) += ad9523.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o obj-$(CONFIG_ADMV1013) += admv1013.o +obj-$(CONFIG_ADMV1014) += admv1014.o +obj-$(CONFIG_ADMV4420) += admv4420.o obj-$(CONFIG_ADRF6780) += adrf6780.o diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index bdb0bc3b12dd..a0f92c336fc4 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -551,7 +551,7 @@ static ssize_t ad9523_show(struct device *dev, mutex_lock(&st->lock); ret = ad9523_read(indio_dev, AD9523_READBACK_0); if (ret >= 0) { - ret = sprintf(buf, "%d\n", !!(ret & (1 << + ret = sysfs_emit(buf, "%d\n", !!(ret & (1 << (u32)this_attr->address))); } mutex_unlock(&st->lock); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index f3521330f6fb..be1218d86291 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -7,17 +7,18 @@ #include <linux/device.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/spi/spi.h> #include <linux/regulator/consumer.h> #include <linux/err.h> -#include <linux/module.h> #include <linux/gcd.h> #include <linux/gpio/consumer.h> #include <asm/div64.h> #include <linux/clk.h> -#include <linux/of.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -381,10 +382,8 @@ static const struct iio_info adf4350_info = { .debugfs_reg_access = &adf4350_reg_access, }; -#ifdef CONFIG_OF static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) { - struct device_node *np = dev->of_node; struct adf4350_platform_data *pdata; unsigned int tmp; @@ -392,101 +391,83 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) if (!pdata) return NULL; - snprintf(&pdata->name[0], SPI_NAME_SIZE - 1, "%pOFn", np); + snprintf(pdata->name, sizeof(pdata->name), "%pfw", dev_fwnode(dev)); tmp = 10000; - of_property_read_u32(np, "adi,channel-spacing", &tmp); + device_property_read_u32(dev, "adi,channel-spacing", &tmp); pdata->channel_spacing = tmp; tmp = 0; - of_property_read_u32(np, "adi,power-up-frequency", &tmp); + device_property_read_u32(dev, "adi,power-up-frequency", &tmp); pdata->power_up_frequency = tmp; tmp = 0; - of_property_read_u32(np, "adi,reference-div-factor", &tmp); + device_property_read_u32(dev, "adi,reference-div-factor", &tmp); pdata->ref_div_factor = tmp; - pdata->ref_doubler_en = of_property_read_bool(np, - "adi,reference-doubler-enable"); - pdata->ref_div2_en = of_property_read_bool(np, - "adi,reference-div2-enable"); + pdata->ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable"); + pdata->ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable"); /* r2_user_settings */ - pdata->r2_user_settings = of_property_read_bool(np, - "adi,phase-detector-polarity-positive-enable") ? - ADF4350_REG2_PD_POLARITY_POS : 0; - pdata->r2_user_settings |= of_property_read_bool(np, - "adi,lock-detect-precision-6ns-enable") ? - ADF4350_REG2_LDP_6ns : 0; - pdata->r2_user_settings |= of_property_read_bool(np, - "adi,lock-detect-function-integer-n-enable") ? - ADF4350_REG2_LDF_INT_N : 0; + pdata->r2_user_settings = 0; + if (device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable")) + pdata->r2_user_settings |= ADF4350_REG2_PD_POLARITY_POS; + if (device_property_read_bool(dev, "adi,lock-detect-precision-6ns-enable")) + pdata->r2_user_settings |= ADF4350_REG2_LDP_6ns; + if (device_property_read_bool(dev, "adi,lock-detect-function-integer-n-enable")) + pdata->r2_user_settings |= ADF4350_REG2_LDF_INT_N; tmp = 2500; - of_property_read_u32(np, "adi,charge-pump-current", &tmp); + device_property_read_u32(dev, "adi,charge-pump-current", &tmp); pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp); tmp = 0; - of_property_read_u32(np, "adi,muxout-select", &tmp); + device_property_read_u32(dev, "adi,muxout-select", &tmp); pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp); - pdata->r2_user_settings |= of_property_read_bool(np, - "adi,low-spur-mode-enable") ? - ADF4350_REG2_NOISE_MODE(0x3) : 0; + if (device_property_read_bool(dev, "adi,low-spur-mode-enable")) + pdata->r2_user_settings |= ADF4350_REG2_NOISE_MODE(0x3); /* r3_user_settings */ - pdata->r3_user_settings = of_property_read_bool(np, - "adi,cycle-slip-reduction-enable") ? - ADF4350_REG3_12BIT_CSR_EN : 0; - pdata->r3_user_settings |= of_property_read_bool(np, - "adi,charge-cancellation-enable") ? - ADF4351_REG3_CHARGE_CANCELLATION_EN : 0; - - pdata->r3_user_settings |= of_property_read_bool(np, - "adi,anti-backlash-3ns-enable") ? - ADF4351_REG3_ANTI_BACKLASH_3ns_EN : 0; - pdata->r3_user_settings |= of_property_read_bool(np, - "adi,band-select-clock-mode-high-enable") ? - ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH : 0; + pdata->r3_user_settings = 0; + if (device_property_read_bool(dev, "adi,cycle-slip-reduction-enable")) + pdata->r3_user_settings |= ADF4350_REG3_12BIT_CSR_EN; + if (device_property_read_bool(dev, "adi,charge-cancellation-enable")) + pdata->r3_user_settings |= ADF4351_REG3_CHARGE_CANCELLATION_EN; + if (device_property_read_bool(dev, "adi,anti-backlash-3ns-enable")) + pdata->r3_user_settings |= ADF4351_REG3_ANTI_BACKLASH_3ns_EN; + if (device_property_read_bool(dev, "adi,band-select-clock-mode-high-enable")) + pdata->r3_user_settings |= ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH; tmp = 0; - of_property_read_u32(np, "adi,12bit-clk-divider", &tmp); + device_property_read_u32(dev, "adi,12bit-clk-divider", &tmp); pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp); tmp = 0; - of_property_read_u32(np, "adi,clk-divider-mode", &tmp); + device_property_read_u32(dev, "adi,clk-divider-mode", &tmp); pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp); /* r4_user_settings */ - pdata->r4_user_settings = of_property_read_bool(np, - "adi,aux-output-enable") ? - ADF4350_REG4_AUX_OUTPUT_EN : 0; - pdata->r4_user_settings |= of_property_read_bool(np, - "adi,aux-output-fundamental-enable") ? - ADF4350_REG4_AUX_OUTPUT_FUND : 0; - pdata->r4_user_settings |= of_property_read_bool(np, - "adi,mute-till-lock-enable") ? - ADF4350_REG4_MUTE_TILL_LOCK_EN : 0; + pdata->r4_user_settings = 0; + if (device_property_read_bool(dev, "adi,aux-output-enable")) + pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_EN; + if (device_property_read_bool(dev, "adi,aux-output-fundamental-enable")) + pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_FUND; + if (device_property_read_bool(dev, "adi,mute-till-lock-enable")) + pdata->r4_user_settings |= ADF4350_REG4_MUTE_TILL_LOCK_EN; tmp = 0; - of_property_read_u32(np, "adi,output-power", &tmp); + device_property_read_u32(dev, "adi,output-power", &tmp); pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp); tmp = 0; - of_property_read_u32(np, "adi,aux-output-power", &tmp); + device_property_read_u32(dev, "adi,aux-output-power", &tmp); pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp); return pdata; } -#else -static -struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) -{ - return NULL; -} -#endif static int adf4350_probe(struct spi_device *spi) { @@ -496,7 +477,7 @@ static int adf4350_probe(struct spi_device *spi) struct clk *clk = NULL; int ret; - if (spi->dev.of_node) { + if (dev_fwnode(&spi->dev)) { pdata = adf4350_parse_dt(&spi->dev); if (pdata == NULL) return -EINVAL; @@ -623,7 +604,7 @@ MODULE_DEVICE_TABLE(spi, adf4350_id); static struct spi_driver adf4350_driver = { .driver = { .name = "adf4350", - .of_match_table = of_match_ptr(adf4350_of_match), + .of_match_table = adf4350_of_match, }, .probe = adf4350_probe, .remove = adf4350_remove, diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index 3f3c478e9baa..b0e1f6571afb 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -630,7 +630,7 @@ static int admv1013_probe(struct spi_device *spi) } static const struct spi_device_id admv1013_id[] = { - { "admv1013", 0}, + { "admv1013", 0 }, {} }; MODULE_DEVICE_TABLE(spi, admv1013_id); diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c new file mode 100644 index 000000000000..a7994f8e6b9b --- /dev/null +++ b/drivers/iio/frequency/admv1014.c @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADMV1014 driver + * + * Copyright 2022 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/device.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/notifier.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/units.h> + +#include <asm/unaligned.h> + +/* ADMV1014 Register Map */ +#define ADMV1014_REG_SPI_CONTROL 0x00 +#define ADMV1014_REG_ALARM 0x01 +#define ADMV1014_REG_ALARM_MASKS 0x02 +#define ADMV1014_REG_ENABLE 0x03 +#define ADMV1014_REG_QUAD 0x04 +#define ADMV1014_REG_LO_AMP_PHASE_ADJUST1 0x05 +#define ADMV1014_REG_MIXER 0x07 +#define ADMV1014_REG_IF_AMP 0x08 +#define ADMV1014_REG_IF_AMP_BB_AMP 0x09 +#define ADMV1014_REG_BB_AMP_AGC 0x0A +#define ADMV1014_REG_VVA_TEMP_COMP 0x0B + +/* ADMV1014_REG_SPI_CONTROL Map */ +#define ADMV1014_PARITY_EN_MSK BIT(15) +#define ADMV1014_SPI_SOFT_RESET_MSK BIT(14) +#define ADMV1014_CHIP_ID_MSK GENMASK(11, 4) +#define ADMV1014_CHIP_ID 0x9 +#define ADMV1014_REVISION_ID_MSK GENMASK(3, 0) + +/* ADMV1014_REG_ALARM Map */ +#define ADMV1014_PARITY_ERROR_MSK BIT(15) +#define ADMV1014_TOO_FEW_ERRORS_MSK BIT(14) +#define ADMV1014_TOO_MANY_ERRORS_MSK BIT(13) +#define ADMV1014_ADDRESS_RANGE_ERROR_MSK BIT(12) + +/* ADMV1014_REG_ENABLE Map */ +#define ADMV1014_IBIAS_PD_MSK BIT(14) +#define ADMV1014_P1DB_COMPENSATION_MSK GENMASK(13, 12) +#define ADMV1014_IF_AMP_PD_MSK BIT(11) +#define ADMV1014_QUAD_BG_PD_MSK BIT(9) +#define ADMV1014_BB_AMP_PD_MSK BIT(8) +#define ADMV1014_QUAD_IBIAS_PD_MSK BIT(7) +#define ADMV1014_DET_EN_MSK BIT(6) +#define ADMV1014_BG_PD_MSK BIT(5) + +/* ADMV1014_REG_QUAD Map */ +#define ADMV1014_QUAD_SE_MODE_MSK GENMASK(9, 6) +#define ADMV1014_QUAD_FILTERS_MSK GENMASK(3, 0) + +/* ADMV1014_REG_LO_AMP_PHASE_ADJUST1 Map */ +#define ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK GENMASK(15, 9) +#define ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK GENMASK(8, 2) + +/* ADMV1014_REG_MIXER Map */ +#define ADMV1014_MIXER_VGATE_MSK GENMASK(15, 9) +#define ADMV1014_DET_PROG_MSK GENMASK(6, 0) + +/* ADMV1014_REG_IF_AMP Map */ +#define ADMV1014_IF_AMP_COARSE_GAIN_I_MSK GENMASK(11, 8) +#define ADMV1014_IF_AMP_FINE_GAIN_Q_MSK GENMASK(7, 4) +#define ADMV1014_IF_AMP_FINE_GAIN_I_MSK GENMASK(3, 0) + +/* ADMV1014_REG_IF_AMP_BB_AMP Map */ +#define ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK GENMASK(15, 12) +#define ADMV1014_BB_AMP_OFFSET_Q_MSK GENMASK(9, 5) +#define ADMV1014_BB_AMP_OFFSET_I_MSK GENMASK(4, 0) + +/* ADMV1014_REG_BB_AMP_AGC Map */ +#define ADMV1014_BB_AMP_REF_GEN_MSK GENMASK(6, 3) +#define ADMV1014_BB_AMP_GAIN_CTRL_MSK GENMASK(2, 1) +#define ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK BIT(0) + +/* ADMV1014_REG_VVA_TEMP_COMP Map */ +#define ADMV1014_VVA_TEMP_COMP_MSK GENMASK(15, 0) + +/* ADMV1014 Miscellaneous Defines */ +#define ADMV1014_READ BIT(7) +#define ADMV1014_REG_ADDR_READ_MSK GENMASK(6, 1) +#define ADMV1014_REG_ADDR_WRITE_MSK GENMASK(22, 17) +#define ADMV1014_REG_DATA_MSK GENMASK(16, 1) +#define ADMV1014_NUM_REGULATORS 9 + +enum { + ADMV1014_IQ_MODE, + ADMV1014_IF_MODE, +}; + +enum { + ADMV1014_SE_MODE_POS = 6, + ADMV1014_SE_MODE_NEG = 9, + ADMV1014_SE_MODE_DIFF = 12, +}; + +enum { + ADMV1014_CALIBSCALE_COARSE, + ADMV1014_CALIBSCALE_FINE, +}; + +static const int detector_table[] = {0, 1, 2, 4, 8, 16, 32, 64}; + +static const char * const input_mode_names[] = { "iq", "if" }; + +static const char * const quad_se_mode_names[] = { "se-pos", "se-neg", "diff" }; + +struct admv1014_state { + struct spi_device *spi; + struct clk *clkin; + struct notifier_block nb; + /* Protect against concurrent accesses to the device and to data*/ + struct mutex lock; + struct regulator_bulk_data regulators[ADMV1014_NUM_REGULATORS]; + unsigned int input_mode; + unsigned int quad_se_mode; + unsigned int p1db_comp; + bool det_en; + u8 data[3] ____cacheline_aligned; +}; + +static const int mixer_vgate_table[] = {106, 107, 108, 110, 111, 112, 113, 114, + 117, 118, 119, 120, 122, 123, 44, 45}; + +static int __admv1014_spi_read(struct admv1014_state *st, unsigned int reg, + unsigned int *val) +{ + struct spi_transfer t = {}; + int ret; + + st->data[0] = ADMV1014_READ | FIELD_PREP(ADMV1014_REG_ADDR_READ_MSK, reg); + st->data[1] = 0; + st->data[2] = 0; + + t.rx_buf = &st->data[0]; + t.tx_buf = &st->data[0]; + t.len = sizeof(st->data); + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + *val = FIELD_GET(ADMV1014_REG_DATA_MSK, get_unaligned_be24(&st->data[0])); + + return ret; +} + +static int admv1014_spi_read(struct admv1014_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv1014_spi_read(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv1014_spi_write(struct admv1014_state *st, + unsigned int reg, + unsigned int val) +{ + put_unaligned_be24(FIELD_PREP(ADMV1014_REG_DATA_MSK, val) | + FIELD_PREP(ADMV1014_REG_ADDR_WRITE_MSK, reg), &st->data[0]); + + return spi_write(st->spi, &st->data[0], 3); +} + +static int admv1014_spi_write(struct admv1014_state *st, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv1014_spi_write(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv1014_spi_update_bits(struct admv1014_state *st, unsigned int reg, + unsigned int mask, unsigned int val) +{ + unsigned int data, temp; + int ret; + + ret = __admv1014_spi_read(st, reg, &data); + if (ret) + return ret; + + temp = (data & ~mask) | (val & mask); + + return __admv1014_spi_write(st, reg, temp); +} + +static int admv1014_spi_update_bits(struct admv1014_state *st, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv1014_spi_update_bits(st, reg, mask, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int admv1014_update_quad_filters(struct admv1014_state *st) +{ + unsigned int filt_raw; + u64 rate = clk_get_rate(st->clkin); + + if (rate >= (5400 * HZ_PER_MHZ) && rate <= (7000 * HZ_PER_MHZ)) + filt_raw = 15; + else if (rate > (7000 * HZ_PER_MHZ) && rate <= (8000 * HZ_PER_MHZ)) + filt_raw = 10; + else if (rate > (8000 * HZ_PER_MHZ) && rate <= (9200 * HZ_PER_MHZ)) + filt_raw = 5; + else + filt_raw = 0; + + return __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD, + ADMV1014_QUAD_FILTERS_MSK, + FIELD_PREP(ADMV1014_QUAD_FILTERS_MSK, filt_raw)); +} + +static int admv1014_update_vcm_settings(struct admv1014_state *st) +{ + unsigned int i, vcm_mv, vcm_comp, bb_sw_hl_cm; + int ret; + + vcm_mv = regulator_get_voltage(st->regulators[0].consumer) / 1000; + for (i = 0; i < ARRAY_SIZE(mixer_vgate_table); i++) { + vcm_comp = 1050 + mult_frac(i, 450, 8); + if (vcm_mv != vcm_comp) + continue; + + ret = __admv1014_spi_update_bits(st, ADMV1014_REG_MIXER, + ADMV1014_MIXER_VGATE_MSK, + FIELD_PREP(ADMV1014_MIXER_VGATE_MSK, + mixer_vgate_table[i])); + if (ret) + return ret; + + bb_sw_hl_cm = ~(i / 8); + bb_sw_hl_cm = FIELD_PREP(ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK, bb_sw_hl_cm); + + return __admv1014_spi_update_bits(st, ADMV1014_REG_BB_AMP_AGC, + ADMV1014_BB_AMP_REF_GEN_MSK | + ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK, + FIELD_PREP(ADMV1014_BB_AMP_REF_GEN_MSK, i) | + bb_sw_hl_cm); + } + + return -EINVAL; +} + +static int admv1014_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct admv1014_state *st = iio_priv(indio_dev); + unsigned int data; + int ret; + + switch (info) { + case IIO_CHAN_INFO_OFFSET: + ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP_BB_AMP, &data); + if (ret) + return ret; + + if (chan->channel2 == IIO_MOD_I) + *val = FIELD_GET(ADMV1014_BB_AMP_OFFSET_I_MSK, data); + else + *val = FIELD_GET(ADMV1014_BB_AMP_OFFSET_Q_MSK, data); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_PHASE: + ret = admv1014_spi_read(st, ADMV1014_REG_LO_AMP_PHASE_ADJUST1, &data); + if (ret) + return ret; + + if (chan->channel2 == IIO_MOD_I) + *val = FIELD_GET(ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK, data); + else + *val = FIELD_GET(ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK, data); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = admv1014_spi_read(st, ADMV1014_REG_MIXER, &data); + if (ret) + return ret; + + *val = FIELD_GET(ADMV1014_DET_PROG_MSK, data); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = admv1014_spi_read(st, ADMV1014_REG_BB_AMP_AGC, &data); + if (ret) + return ret; + + *val = FIELD_GET(ADMV1014_BB_AMP_GAIN_CTRL_MSK, data); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int admv1014_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + int data; + unsigned int msk; + struct admv1014_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_OFFSET: + if (chan->channel2 == IIO_MOD_I) { + msk = ADMV1014_BB_AMP_OFFSET_I_MSK; + data = FIELD_PREP(ADMV1014_BB_AMP_OFFSET_I_MSK, val); + } else { + msk = ADMV1014_BB_AMP_OFFSET_Q_MSK; + data = FIELD_PREP(ADMV1014_BB_AMP_OFFSET_Q_MSK, val); + } + + return admv1014_spi_update_bits(st, ADMV1014_REG_IF_AMP_BB_AMP, msk, data); + case IIO_CHAN_INFO_PHASE: + if (chan->channel2 == IIO_MOD_I) { + msk = ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK; + data = FIELD_PREP(ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK, val); + } else { + msk = ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK; + data = FIELD_PREP(ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK, val); + } + + return admv1014_spi_update_bits(st, ADMV1014_REG_LO_AMP_PHASE_ADJUST1, msk, data); + case IIO_CHAN_INFO_SCALE: + return admv1014_spi_update_bits(st, ADMV1014_REG_MIXER, + ADMV1014_DET_PROG_MSK, + FIELD_PREP(ADMV1014_DET_PROG_MSK, val)); + case IIO_CHAN_INFO_CALIBSCALE: + return admv1014_spi_update_bits(st, ADMV1014_REG_BB_AMP_AGC, + ADMV1014_BB_AMP_GAIN_CTRL_MSK, + FIELD_PREP(ADMV1014_BB_AMP_GAIN_CTRL_MSK, val)); + default: + return -EINVAL; + } +} + +static ssize_t admv1014_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct admv1014_state *st = iio_priv(indio_dev); + unsigned int data; + int ret; + + switch (private) { + case ADMV1014_CALIBSCALE_COARSE: + if (chan->channel2 == IIO_MOD_I) { + ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP, &data); + if (ret) + return ret; + + data = FIELD_GET(ADMV1014_IF_AMP_COARSE_GAIN_I_MSK, data); + } else { + ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP_BB_AMP, &data); + if (ret) + return ret; + + data = FIELD_GET(ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK, data); + } + break; + case ADMV1014_CALIBSCALE_FINE: + ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP, &data); + if (ret) + return ret; + + if (chan->channel2 == IIO_MOD_I) + data = FIELD_GET(ADMV1014_IF_AMP_FINE_GAIN_I_MSK, data); + else + data = FIELD_GET(ADMV1014_IF_AMP_FINE_GAIN_Q_MSK, data); + break; + default: + return -EINVAL; + } + + return sysfs_emit(buf, "%u\n", data); +} + +static ssize_t admv1014_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct admv1014_state *st = iio_priv(indio_dev); + unsigned int data, addr, msk; + int ret; + + ret = kstrtouint(buf, 10, &data); + if (ret) + return ret; + + switch (private) { + case ADMV1014_CALIBSCALE_COARSE: + if (chan->channel2 == IIO_MOD_I) { + addr = ADMV1014_REG_IF_AMP; + msk = ADMV1014_IF_AMP_COARSE_GAIN_I_MSK; + data = FIELD_PREP(ADMV1014_IF_AMP_COARSE_GAIN_I_MSK, data); + } else { + addr = ADMV1014_REG_IF_AMP_BB_AMP; + msk = ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK; + data = FIELD_PREP(ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK, data); + } + break; + case ADMV1014_CALIBSCALE_FINE: + addr = ADMV1014_REG_IF_AMP; + + if (chan->channel2 == IIO_MOD_I) { + msk = ADMV1014_IF_AMP_FINE_GAIN_I_MSK; + data = FIELD_PREP(ADMV1014_IF_AMP_FINE_GAIN_I_MSK, data); + } else { + msk = ADMV1014_IF_AMP_FINE_GAIN_Q_MSK; + data = FIELD_PREP(ADMV1014_IF_AMP_FINE_GAIN_Q_MSK, data); + } + break; + default: + return -EINVAL; + } + + ret = admv1014_spi_update_bits(st, addr, msk, data); + + return ret ? ret : len; +} + +static int admv1014_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = detector_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(detector_table); + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int admv1014_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct admv1014_state *st = iio_priv(indio_dev); + + if (read_val) + return admv1014_spi_read(st, reg, read_val); + else + return admv1014_spi_write(st, reg, write_val); +} + +static const struct iio_info admv1014_info = { + .read_raw = admv1014_read_raw, + .write_raw = admv1014_write_raw, + .read_avail = &admv1014_read_avail, + .debugfs_reg_access = &admv1014_reg_access, +}; + +static const char * const admv1014_reg_name[] = { + "vcm", "vcc-if-bb", "vcc-vga", "vcc-vva", "vcc-lna-3p3", + "vcc-lna-1p5", "vcc-bg", "vcc-quad", "vcc-mixer" +}; + +static int admv1014_freq_change(struct notifier_block *nb, unsigned long action, void *data) +{ + struct admv1014_state *st = container_of(nb, struct admv1014_state, nb); + int ret; + + if (action == POST_RATE_CHANGE) { + mutex_lock(&st->lock); + ret = notifier_from_errno(admv1014_update_quad_filters(st)); + mutex_unlock(&st->lock); + return ret; + } + + return NOTIFY_OK; +} + +#define _ADMV1014_EXT_INFO(_name, _shared, _ident) { \ + .name = _name, \ + .read = admv1014_read, \ + .write = admv1014_write, \ + .private = _ident, \ + .shared = _shared, \ +} + +static const struct iio_chan_spec_ext_info admv1014_ext_info[] = { + _ADMV1014_EXT_INFO("calibscale_coarse", IIO_SEPARATE, ADMV1014_CALIBSCALE_COARSE), + _ADMV1014_EXT_INFO("calibscale_fine", IIO_SEPARATE, ADMV1014_CALIBSCALE_FINE), + { } +}; + +#define ADMV1014_CHAN_IQ(_channel, rf_comp) { \ + .type = IIO_ALTVOLTAGE, \ + .modified = 1, \ + .output = 0, \ + .indexed = 1, \ + .channel2 = IIO_MOD_##rf_comp, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE), \ + } + +#define ADMV1014_CHAN_IF(_channel, rf_comp) { \ + .type = IIO_ALTVOLTAGE, \ + .modified = 1, \ + .output = 0, \ + .indexed = 1, \ + .channel2 = IIO_MOD_##rf_comp, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + } + +#define ADMV1014_CHAN_POWER(_channel) { \ + .type = IIO_POWER, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + } + +#define ADMV1014_CHAN_CALIBSCALE(_channel, rf_comp, _admv1014_ext_info) { \ + .type = IIO_ALTVOLTAGE, \ + .modified = 1, \ + .output = 0, \ + .indexed = 1, \ + .channel2 = IIO_MOD_##rf_comp, \ + .channel = _channel, \ + .ext_info = _admv1014_ext_info, \ + } + +static const struct iio_chan_spec admv1014_channels_iq[] = { + ADMV1014_CHAN_IQ(0, I), + ADMV1014_CHAN_IQ(0, Q), + ADMV1014_CHAN_POWER(0), +}; + +static const struct iio_chan_spec admv1014_channels_if[] = { + ADMV1014_CHAN_IF(0, I), + ADMV1014_CHAN_IF(0, Q), + ADMV1014_CHAN_CALIBSCALE(0, I, admv1014_ext_info), + ADMV1014_CHAN_CALIBSCALE(0, Q, admv1014_ext_info), + ADMV1014_CHAN_POWER(0), +}; + +static void admv1014_clk_disable(void *data) +{ + clk_disable_unprepare(data); +} + +static void admv1014_reg_disable(void *data) +{ + regulator_bulk_disable(ADMV1014_NUM_REGULATORS, data); +} + +static void admv1014_powerdown(void *data) +{ + unsigned int enable_reg, enable_reg_msk; + + /* Disable all components in the Enable Register */ + enable_reg_msk = ADMV1014_IBIAS_PD_MSK | + ADMV1014_IF_AMP_PD_MSK | + ADMV1014_QUAD_BG_PD_MSK | + ADMV1014_BB_AMP_PD_MSK | + ADMV1014_QUAD_IBIAS_PD_MSK | + ADMV1014_BG_PD_MSK; + + enable_reg = FIELD_PREP(ADMV1014_IBIAS_PD_MSK, 1) | + FIELD_PREP(ADMV1014_IF_AMP_PD_MSK, 1) | + FIELD_PREP(ADMV1014_QUAD_BG_PD_MSK, 1) | + FIELD_PREP(ADMV1014_BB_AMP_PD_MSK, 1) | + FIELD_PREP(ADMV1014_QUAD_IBIAS_PD_MSK, 1) | + FIELD_PREP(ADMV1014_BG_PD_MSK, 1); + + admv1014_spi_update_bits(data, ADMV1014_REG_ENABLE, + enable_reg_msk, enable_reg); +} + +static int admv1014_init(struct admv1014_state *st) +{ + unsigned int chip_id, enable_reg, enable_reg_msk; + struct spi_device *spi = st->spi; + int ret; + + ret = regulator_bulk_enable(ADMV1014_NUM_REGULATORS, st->regulators); + if (ret) { + dev_err(&spi->dev, "Failed to enable regulators"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, admv1014_reg_disable, st->regulators); + if (ret) + return ret; + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, admv1014_clk_disable, st->clkin); + if (ret) + return ret; + + st->nb.notifier_call = admv1014_freq_change; + ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, admv1014_powerdown, st); + if (ret) + return ret; + + /* Perform a software reset */ + ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL, + ADMV1014_SPI_SOFT_RESET_MSK, + FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV1014 SPI software reset failed.\n"); + return ret; + } + + ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL, + ADMV1014_SPI_SOFT_RESET_MSK, + FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 0)); + if (ret) { + dev_err(&spi->dev, "ADMV1014 SPI software reset disable failed.\n"); + return ret; + } + + ret = __admv1014_spi_write(st, ADMV1014_REG_VVA_TEMP_COMP, 0x727C); + if (ret) { + dev_err(&spi->dev, "Writing default Temperature Compensation value failed.\n"); + return ret; + } + + ret = __admv1014_spi_read(st, ADMV1014_REG_SPI_CONTROL, &chip_id); + if (ret) + return ret; + + chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id); + if (chip_id != ADMV1014_CHIP_ID) { + dev_err(&spi->dev, "Invalid Chip ID.\n"); + ret = -EINVAL; + return ret; + } + + ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD, + ADMV1014_QUAD_SE_MODE_MSK, + FIELD_PREP(ADMV1014_QUAD_SE_MODE_MSK, + st->quad_se_mode)); + if (ret) { + dev_err(&spi->dev, "Writing Quad SE Mode failed.\n"); + return ret; + } + + ret = admv1014_update_quad_filters(st); + if (ret) { + dev_err(&spi->dev, "Update Quad Filters failed.\n"); + return ret; + } + + ret = admv1014_update_vcm_settings(st); + if (ret) { + dev_err(&spi->dev, "Update VCM Settings failed.\n"); + return ret; + } + + enable_reg_msk = ADMV1014_P1DB_COMPENSATION_MSK | + ADMV1014_IF_AMP_PD_MSK | + ADMV1014_BB_AMP_PD_MSK | + ADMV1014_DET_EN_MSK; + + enable_reg = FIELD_PREP(ADMV1014_P1DB_COMPENSATION_MSK, st->p1db_comp ? 3 : 0) | + FIELD_PREP(ADMV1014_IF_AMP_PD_MSK, !(st->input_mode)) | + FIELD_PREP(ADMV1014_BB_AMP_PD_MSK, st->input_mode) | + FIELD_PREP(ADMV1014_DET_EN_MSK, st->det_en); + + return __admv1014_spi_update_bits(st, ADMV1014_REG_ENABLE, enable_reg_msk, enable_reg); +} + +static int admv1014_properties_parse(struct admv1014_state *st) +{ + const char *str; + unsigned int i; + struct spi_device *spi = st->spi; + int ret; + + st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable"); + + st->p1db_comp = device_property_read_bool(&spi->dev, "adi,p1db-compensation-enable"); + + ret = device_property_read_string(&spi->dev, "adi,input-mode", &str); + if (ret) { + st->input_mode = ADMV1014_IQ_MODE; + } else { + ret = match_string(input_mode_names, ARRAY_SIZE(input_mode_names), str); + if (ret < 0) + return ret; + + st->input_mode = ret; + } + + ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str); + if (ret) { + st->quad_se_mode = ADMV1014_SE_MODE_POS; + } else { + ret = match_string(quad_se_mode_names, ARRAY_SIZE(quad_se_mode_names), str); + if (ret < 0) + return ret; + + st->quad_se_mode = ADMV1014_SE_MODE_POS + (ret * 3); + } + + for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i) + st->regulators[i].supply = admv1014_reg_name[i]; + + ret = devm_regulator_bulk_get(&st->spi->dev, ADMV1014_NUM_REGULATORS, + st->regulators); + if (ret) { + dev_err(&spi->dev, "Failed to request regulators"); + return ret; + } + + 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; +} + +static int admv1014_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct admv1014_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = admv1014_properties_parse(st); + if (ret) + return ret; + + indio_dev->info = &admv1014_info; + indio_dev->name = "admv1014"; + + if (st->input_mode == ADMV1014_IQ_MODE) { + indio_dev->channels = admv1014_channels_iq; + indio_dev->num_channels = ARRAY_SIZE(admv1014_channels_iq); + } else { + indio_dev->channels = admv1014_channels_if; + indio_dev->num_channels = ARRAY_SIZE(admv1014_channels_if); + } + + st->spi = spi; + + mutex_init(&st->lock); + + ret = admv1014_init(st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id admv1014_id[] = { + { "admv1014", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, admv1014_id); + +static const struct of_device_id admv1014_of_match[] = { + { .compatible = "adi,admv1014" }, + {} +}; +MODULE_DEVICE_TABLE(of, admv1014_of_match); + +static struct spi_driver admv1014_driver = { + .driver = { + .name = "admv1014", + .of_match_table = admv1014_of_match, + }, + .probe = admv1014_probe, + .id_table = admv1014_id, +}; +module_spi_driver(admv1014_driver); + +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com"); +MODULE_DESCRIPTION("Analog Devices ADMV1014"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/frequency/admv4420.c b/drivers/iio/frequency/admv4420.c new file mode 100644 index 000000000000..51134aee8510 --- /dev/null +++ b/drivers/iio/frequency/admv4420.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * ADMV4420 + * + * Copyright 2021 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/units.h> + +#include <asm/unaligned.h> + +/* ADMV4420 Register Map */ +#define ADMV4420_SPI_CONFIG_1 0x00 +#define ADMV4420_SPI_CONFIG_2 0x01 +#define ADMV4420_CHIPTYPE 0x03 +#define ADMV4420_PRODUCT_ID_L 0x04 +#define ADMV4420_PRODUCT_ID_H 0x05 +#define ADMV4420_SCRATCHPAD 0x0A +#define ADMV4420_SPI_REV 0x0B +#define ADMV4420_ENABLES 0x103 +#define ADMV4420_SDO_LEVEL 0x108 +#define ADMV4420_INT_L 0x200 +#define ADMV4420_INT_H 0x201 +#define ADMV4420_FRAC_L 0x202 +#define ADMV4420_FRAC_M 0x203 +#define ADMV4420_FRAC_H 0x204 +#define ADMV4420_MOD_L 0x208 +#define ADMV4420_MOD_M 0x209 +#define ADMV4420_MOD_H 0x20A +#define ADMV4420_R_DIV_L 0x20C +#define ADMV4420_R_DIV_H 0x20D +#define ADMV4420_REFERENCE 0x20E +#define ADMV4420_VCO_DATA_READBACK1 0x211 +#define ADMV4420_VCO_DATA_READBACK2 0x212 +#define ADMV4420_PLL_MUX_SEL 0x213 +#define ADMV4420_LOCK_DETECT 0x214 +#define ADMV4420_BAND_SELECT 0x215 +#define ADMV4420_VCO_ALC_TIMEOUT 0x216 +#define ADMV4420_VCO_MANUAL 0x217 +#define ADMV4420_ALC 0x219 +#define ADMV4420_VCO_TIMEOUT1 0x21C +#define ADMV4420_VCO_TIMEOUT2 0x21D +#define ADMV4420_VCO_BAND_DIV 0x21E +#define ADMV4420_VCO_READBACK_SEL 0x21F +#define ADMV4420_AUTOCAL 0x226 +#define ADMV4420_CP_STATE 0x22C +#define ADMV4420_CP_BLEED_EN 0x22D +#define ADMV4420_CP_CURRENT 0x22E +#define ADMV4420_CP_BLEED 0x22F + +#define ADMV4420_SPI_CONFIG_1_SDOACTIVE (BIT(4) | BIT(3)) +#define ADMV4420_SPI_CONFIG_1_ENDIAN (BIT(5) | BIT(2)) +#define ADMV4420_SPI_CONFIG_1_SOFTRESET (BIT(7) | BIT(1)) + +#define ADMV4420_REFERENCE_DIVIDE_BY_2_MASK BIT(0) +#define ADMV4420_REFERENCE_MODE_MASK BIT(1) +#define ADMV4420_REFERENCE_DOUBLER_MASK BIT(2) + +#define ADMV4420_REF_DIVIDER_MAX_VAL GENMASK(9, 0) +#define ADMV4420_N_COUNTER_INT_MAX GENMASK(15, 0) +#define ADMV4420_N_COUNTER_FRAC_MAX GENMASK(23, 0) +#define ADMV4420_N_COUNTER_MOD_MAX GENMASK(23, 0) + +#define ENABLE_PLL BIT(6) +#define ENABLE_LO BIT(5) +#define ENABLE_VCO BIT(3) +#define ENABLE_IFAMP BIT(2) +#define ENABLE_MIXER BIT(1) +#define ENABLE_LNA BIT(0) + +#define ADMV4420_SCRATCH_PAD_VAL_1 0xAD +#define ADMV4420_SCRATCH_PAD_VAL_2 0xEA + +#define ADMV4420_REF_FREQ_HZ 50000000 +#define MAX_N_COUNTER 655360UL +#define MAX_R_DIVIDER 1024 +#define ADMV4420_DEFAULT_LO_FREQ_HZ 16750000000ULL + +enum admv4420_mux_sel { + ADMV4420_LOW = 0, + ADMV4420_LOCK_DTCT = 1, + ADMV4420_R_COUNTER_PER_2 = 4, + ADMV4420_N_CONUTER_PER_2 = 5, + ADMV4420_HIGH = 8, +}; + +struct admv4420_reference_block { + bool doubler_en; + bool divide_by_2_en; + bool ref_single_ended; + u32 divider; +}; + +struct admv4420_n_counter { + u32 int_val; + u32 frac_val; + u32 mod_val; + u32 n_counter; +}; + +struct admv4420_state { + struct spi_device *spi; + struct regmap *regmap; + u64 vco_freq_hz; + u64 lo_freq_hz; + struct admv4420_reference_block ref_block; + struct admv4420_n_counter n_counter; + enum admv4420_mux_sel mux_sel; + struct mutex lock; + u8 transf_buf[4] ____cacheline_aligned; +}; + +static const struct regmap_config admv4420_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), +}; + +static int admv4420_reg_access(struct iio_dev *indio_dev, + u32 reg, u32 writeval, + u32 *readval) +{ + struct admv4420_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static int admv4420_set_n_counter(struct admv4420_state *st, u32 int_val, + u32 frac_val, u32 mod_val) +{ + int ret; + + put_unaligned_le32(frac_val, st->transf_buf); + ret = regmap_bulk_write(st->regmap, ADMV4420_FRAC_L, st->transf_buf, 3); + if (ret) + return ret; + + put_unaligned_le32(mod_val, st->transf_buf); + ret = regmap_bulk_write(st->regmap, ADMV4420_MOD_L, st->transf_buf, 3); + if (ret) + return ret; + + put_unaligned_le32(int_val, st->transf_buf); + return regmap_bulk_write(st->regmap, ADMV4420_INT_L, st->transf_buf, 2); +} + +static int admv4420_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct admv4420_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + + *val = div_u64_rem(st->lo_freq_hz, MICRO, val2); + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info admv4420_info = { + .read_raw = admv4420_read_raw, + .debugfs_reg_access = &admv4420_reg_access, +}; + +static const struct iio_chan_spec admv4420_channels[] = { + { + .type = IIO_ALTVOLTAGE, + .output = 0, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY), + }, +}; + +static void admv4420_fw_parse(struct admv4420_state *st) +{ + struct device *dev = &st->spi->dev; + u32 tmp; + int ret; + + ret = device_property_read_u32(dev, "adi,lo-freq-khz", &tmp); + if (!ret) + st->lo_freq_hz = (u64)tmp * KILO; + + st->ref_block.ref_single_ended = device_property_read_bool(dev, + "adi,ref-ext-single-ended-en"); +} + +static inline uint64_t admv4420_calc_pfd_vco(struct admv4420_state *st) +{ + return div_u64(st->vco_freq_hz * 10, st->n_counter.n_counter); +} + +static inline uint32_t admv4420_calc_pfd_ref(struct admv4420_state *st) +{ + uint32_t tmp; + u8 doubler, divide_by_2; + + doubler = st->ref_block.doubler_en ? 2 : 1; + divide_by_2 = st->ref_block.divide_by_2_en ? 2 : 1; + tmp = ADMV4420_REF_FREQ_HZ * doubler; + + return (tmp / (st->ref_block.divider * divide_by_2)); +} + +static int admv4420_calc_parameters(struct admv4420_state *st) +{ + u64 pfd_ref, pfd_vco; + bool sol_found = false; + + st->ref_block.doubler_en = false; + st->ref_block.divide_by_2_en = false; + st->vco_freq_hz = div_u64(st->lo_freq_hz, 2); + + for (st->ref_block.divider = 1; st->ref_block.divider < MAX_R_DIVIDER; + st->ref_block.divider++) { + pfd_ref = admv4420_calc_pfd_ref(st); + for (st->n_counter.n_counter = 1; st->n_counter.n_counter < MAX_N_COUNTER; + st->n_counter.n_counter++) { + pfd_vco = admv4420_calc_pfd_vco(st); + if (pfd_ref == pfd_vco) { + sol_found = true; + break; + } + } + + if (sol_found) + break; + + st->n_counter.n_counter = 1; + } + if (!sol_found) + return -1; + + st->n_counter.int_val = div_u64_rem(st->n_counter.n_counter, 10, &st->n_counter.frac_val); + st->n_counter.mod_val = 10; + + return 0; +} + +static int admv4420_setup(struct iio_dev *indio_dev) +{ + struct admv4420_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + u32 val; + int ret; + + ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1, + ADMV4420_SPI_CONFIG_1_SOFTRESET); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1, + ADMV4420_SPI_CONFIG_1_SDOACTIVE | + ADMV4420_SPI_CONFIG_1_ENDIAN); + if (ret) + return ret; + + ret = regmap_write(st->regmap, + ADMV4420_SCRATCHPAD, + ADMV4420_SCRATCH_PAD_VAL_1); + if (ret) + return ret; + + ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val); + if (ret) + return ret; + + if (val != ADMV4420_SCRATCH_PAD_VAL_1) { + dev_err(dev, "Failed ADMV4420 to read/write scratchpad %x ", val); + return -EIO; + } + + ret = regmap_write(st->regmap, + ADMV4420_SCRATCHPAD, + ADMV4420_SCRATCH_PAD_VAL_2); + if (ret) + return ret; + + ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val); + if (ret) + return ret; + + if (val != ADMV4420_SCRATCH_PAD_VAL_2) { + dev_err(dev, "Failed to read/write scratchpad %x ", val); + return -EIO; + } + + st->mux_sel = ADMV4420_LOCK_DTCT; + st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ; + + admv4420_fw_parse(st); + + ret = admv4420_calc_parameters(st); + if (ret) { + dev_err(dev, "Failed calc parameters for %lld ", st->vco_freq_hz); + return ret; + } + + ret = regmap_write(st->regmap, ADMV4420_R_DIV_L, + FIELD_GET(0xFF, st->ref_block.divider)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADMV4420_R_DIV_H, + FIELD_GET(0xFF00, st->ref_block.divider)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADMV4420_REFERENCE, + st->ref_block.divide_by_2_en | + FIELD_PREP(ADMV4420_REFERENCE_MODE_MASK, st->ref_block.ref_single_ended) | + FIELD_PREP(ADMV4420_REFERENCE_DOUBLER_MASK, st->ref_block.doubler_en)); + if (ret) + return ret; + + ret = admv4420_set_n_counter(st, st->n_counter.int_val, + st->n_counter.frac_val, + st->n_counter.mod_val); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADMV4420_PLL_MUX_SEL, st->mux_sel); + if (ret) + return ret; + + return regmap_write(st->regmap, ADMV4420_ENABLES, + ENABLE_PLL | ENABLE_LO | ENABLE_VCO | + ENABLE_IFAMP | ENABLE_MIXER | ENABLE_LNA); +} + +static int admv4420_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct admv4420_state *st; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to initializing spi regmap\n"); + + st = iio_priv(indio_dev); + st->spi = spi; + st->regmap = regmap; + + indio_dev->name = "admv4420"; + indio_dev->info = &admv4420_info; + indio_dev->channels = admv4420_channels; + indio_dev->num_channels = ARRAY_SIZE(admv4420_channels); + + ret = admv4420_setup(indio_dev); + if (ret) { + dev_err(&spi->dev, "Setup ADMV4420 failed (%d)\n", ret); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id admv4420_of_match[] = { + { .compatible = "adi,admv4420" }, + { } +}; + +MODULE_DEVICE_TABLE(of, admv4420_of_match); + +static struct spi_driver admv4420_driver = { + .driver = { + .name = "admv4420", + .of_match_table = admv4420_of_match, + }, + .probe = admv4420_probe, +}; + +module_spi_driver(admv4420_driver); + +MODULE_AUTHOR("Cristian Pop <cristian.pop@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADMV44200 K Band Downconverter"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index a672f7d12bbb..97b86c4a53a6 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -139,30 +139,37 @@ config IIO_ST_GYRO_3AXIS tristate "STMicroelectronics gyroscopes 3-Axis Driver" depends on (I2C || SPI_MASTER) && SYSFS select IIO_ST_SENSORS_CORE - select IIO_ST_GYRO_I2C_3AXIS if (I2C) - select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics gyroscopes: L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330, LSM9DS0. - This driver can also be built as a module. If so, these modules - will be created: - - st_gyro (core functions for the driver [it is mandatory]); - - st_gyro_i2c (necessary for the I2C devices [optional*]); - - st_gyro_spi (necessary for the SPI devices [optional*]); - - (*) one of these is necessary to do something. + Also need to enable at least one of I2C and SPI interface drivers + below. config IIO_ST_GYRO_I2C_3AXIS - tristate - depends on IIO_ST_GYRO_3AXIS - depends on IIO_ST_SENSORS_I2C + tristate "STMicroelectronics gyroscopes 3-Axis I2C Interface" + depends on I2C && IIO_ST_GYRO_3AXIS + default I2C && IIO_ST_GYRO_3AXIS + select IIO_ST_SENSORS_I2C + help + Build support for STMicroelectronics gyroscopes I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_gyro_i2c. + config IIO_ST_GYRO_SPI_3AXIS - tristate - depends on IIO_ST_GYRO_3AXIS - depends on IIO_ST_SENSORS_SPI + tristate "STMicroelectronics gyroscopes 3-Axis SPI Interface" + depends on SPI_MASTER && IIO_ST_GYRO_3AXIS + default SPI_MASTER && IIO_ST_GYRO_3AXIS + select IIO_ST_SENSORS_SPI + help + Build support for STMicroelectronics gyroscopes SPI interface. + + To compile this driver as a module, choose M here. The module + will be called st_gyro_spi. + config ITG3200 tristate "InvenSense ITG3200 Digital 3-Axis Gyroscope I2C driver" diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 36879f01e28c..71295709f2b9 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -591,3 +591,4 @@ module_spi_driver(adis16136_driver); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 66b6b7bd5e1b..eaf57bd339ed 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -433,3 +433,4 @@ module_spi_driver(adis16260_driver); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c index 46ed12771d2f..5fd1bf9902ea 100644 --- a/drivers/iio/gyro/ssp_gyro_sensor.c +++ b/drivers/iio/gyro/ssp_gyro_sensor.c @@ -142,3 +142,4 @@ module_platform_driver(ssp_gyro_driver); MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>"); MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_SSP_SENSORS); diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c index 4ae33ef25b9c..1ebfe7aa6c96 100644 --- a/drivers/iio/gyro/st_gyro_buffer.c +++ b/drivers/iio/gyro/st_gyro_buffer.c @@ -7,7 +7,6 @@ * Denis Ciocca <denis.ciocca@st.com> */ -#include <linux/module.h> #include <linux/kernel.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> @@ -65,6 +64,3 @@ int st_gyro_allocate_ring(struct iio_dev *indio_dev) NULL, &st_sensors_trigger_handler, &st_gyro_buffer_setup_ops); } -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics gyroscopes buffer"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 201050b76fe5..62172e18d0d8 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -472,7 +472,7 @@ const struct st_sensor_settings *st_gyro_get_settings(const char *name) return &st_gyro_sensors_settings[index]; } -EXPORT_SYMBOL(st_gyro_get_settings); +EXPORT_SYMBOL_NS(st_gyro_get_settings, IIO_ST_SENSORS); int st_gyro_common_probe(struct iio_dev *indio_dev) { @@ -518,8 +518,9 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) return devm_iio_device_register(parent, indio_dev); } -EXPORT_SYMBOL(st_gyro_common_probe); +EXPORT_SYMBOL_NS(st_gyro_common_probe, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 163c7ba300c1..8c7af42b6558 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -120,3 +120,4 @@ module_i2c_driver(st_gyro_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index b0023f9b9771..22aaabe48e4a 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -124,3 +124,4 @@ module_spi_driver(st_gyro_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index 9a7819817488..c97e25448772 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -11,10 +11,9 @@ #include <linux/kernel.h> #include <linux/printk.h> #include <linux/slab.h> -#include <linux/of.h> -#include <linux/of_device.h> #include <linux/sysfs.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/wait.h> diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index 9e0fce917ce4..47f8e8ef56d6 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -417,10 +417,17 @@ static const struct of_device_id hdc100x_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, hdc100x_dt_ids); +static const struct acpi_device_id hdc100x_acpi_match[] = { + { "TXNW1010" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hdc100x_acpi_match); + static struct i2c_driver hdc100x_driver = { .driver = { .name = "hdc100x", .of_match_table = hdc100x_dt_ids, + .acpi_match_table = hdc100x_acpi_match, }, .probe = hdc100x_probe, .id_table = hdc100x_id, diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index 36df2a102ca4..fd9e2565f8a2 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -258,3 +258,4 @@ MODULE_DESCRIPTION("Measurement-Specialties htu21 temperature and humidity drive MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS); diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index cb0d66bf6561..f7fcfd04f659 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -30,8 +30,8 @@ * @value: The value to write to device (up to 4 bytes) * @size: The size of the @value (in bytes) */ -int __adis_write_reg(struct adis *adis, unsigned int reg, - unsigned int value, unsigned int size) +int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, + unsigned int size) { unsigned int page = reg / ADIS_PAGE_SIZE; int ret, i; @@ -114,14 +114,14 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, ret = spi_sync(adis->spi, &msg); if (ret) { dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n", - reg, ret); + reg, ret); } else { adis->current_page = page; } return ret; } -EXPORT_SYMBOL_GPL(__adis_write_reg); +EXPORT_SYMBOL_NS_GPL(__adis_write_reg, IIO_ADISLIB); /** * __adis_read_reg() - read N bytes from register (unlocked version) @@ -130,8 +130,8 @@ EXPORT_SYMBOL_GPL(__adis_write_reg); * @val: The value read back from the device * @size: The size of the @val buffer */ -int __adis_read_reg(struct adis *adis, unsigned int reg, - unsigned int *val, unsigned int size) +int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, + unsigned int size) { unsigned int page = reg / ADIS_PAGE_SIZE; struct spi_message msg; @@ -201,12 +201,12 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, ret = spi_sync(adis->spi, &msg); if (ret) { dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n", - reg, ret); + reg, ret); return ret; - } else { - adis->current_page = page; } + adis->current_page = page; + switch (size) { case 4: *val = get_unaligned_be32(adis->rx); @@ -218,7 +218,7 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, return ret; } -EXPORT_SYMBOL_GPL(__adis_read_reg); +EXPORT_SYMBOL_NS_GPL(__adis_read_reg, IIO_ADISLIB); /** * __adis_update_bits_base() - ADIS Update bits function - Unlocked version * @adis: The adis device @@ -243,17 +243,17 @@ int __adis_update_bits_base(struct adis *adis, unsigned int reg, const u32 mask, return __adis_write_reg(adis, reg, __val, size); } -EXPORT_SYMBOL_GPL(__adis_update_bits_base); +EXPORT_SYMBOL_NS_GPL(__adis_update_bits_base, IIO_ADISLIB); #ifdef CONFIG_DEBUG_FS -int adis_debugfs_reg_access(struct iio_dev *indio_dev, - unsigned int reg, unsigned int writeval, unsigned int *readval) +int adis_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) { struct adis *adis = iio_device_get_drvdata(indio_dev); if (readval) { - uint16_t val16; + u16 val16; int ret; ret = adis_read_reg_16(adis, reg, &val16); @@ -261,11 +261,11 @@ int adis_debugfs_reg_access(struct iio_dev *indio_dev, *readval = val16; return ret; - } else { - return adis_write_reg_16(adis, reg, writeval); } + + return adis_write_reg_16(adis, reg, writeval); } -EXPORT_SYMBOL(adis_debugfs_reg_access); +EXPORT_SYMBOL_NS(adis_debugfs_reg_access, IIO_ADISLIB); #endif @@ -279,14 +279,16 @@ EXPORT_SYMBOL(adis_debugfs_reg_access); int adis_enable_irq(struct adis *adis, bool enable) { int ret = 0; - uint16_t msc; + u16 msc; mutex_lock(&adis->state_lock); if (adis->data->enable_irq) { ret = adis->data->enable_irq(adis, enable); goto out_unlock; - } else if (adis->data->unmasked_drdy) { + } + + if (adis->data->unmasked_drdy) { if (enable) enable_irq(adis->spi->irq); else @@ -312,7 +314,7 @@ out_unlock: mutex_unlock(&adis->state_lock); return ret; } -EXPORT_SYMBOL(adis_enable_irq); +EXPORT_SYMBOL_NS(adis_enable_irq, IIO_ADISLIB); /** * __adis_check_status() - Check the device for error conditions (unlocked) @@ -322,7 +324,7 @@ EXPORT_SYMBOL(adis_enable_irq); */ int __adis_check_status(struct adis *adis) { - uint16_t status; + u16 status; int ret; int i; @@ -344,7 +346,7 @@ int __adis_check_status(struct adis *adis) return -EIO; } -EXPORT_SYMBOL_GPL(__adis_check_status); +EXPORT_SYMBOL_NS_GPL(__adis_check_status, IIO_ADISLIB); /** * __adis_reset() - Reset the device (unlocked version) @@ -358,7 +360,7 @@ int __adis_reset(struct adis *adis) const struct adis_timeout *timeouts = adis->data->timeouts; ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg, - ADIS_GLOB_CMD_SW_RESET); + ADIS_GLOB_CMD_SW_RESET); if (ret) { dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret); return ret; @@ -368,7 +370,7 @@ int __adis_reset(struct adis *adis) return 0; } -EXPORT_SYMBOL_GPL(__adis_reset); +EXPORT_SYMBOL_NS_GPL(__adis_reset, IIO_ADIS_LIB); static int adis_self_test(struct adis *adis) { @@ -414,7 +416,7 @@ int __adis_initial_startup(struct adis *adis) { const struct adis_timeout *timeouts = adis->data->timeouts; struct gpio_desc *gpio; - uint16_t prod_id; + u16 prod_id; int ret; /* check if the device has rst pin low */ @@ -423,7 +425,7 @@ int __adis_initial_startup(struct adis *adis) return PTR_ERR(gpio); if (gpio) { - msleep(10); + usleep_range(10, 12); /* bring device out of reset */ gpiod_set_value_cansleep(gpio, 0); msleep(timeouts->reset_ms); @@ -459,7 +461,7 @@ int __adis_initial_startup(struct adis *adis) return 0; } -EXPORT_SYMBOL_GPL(__adis_initial_startup); +EXPORT_SYMBOL_NS_GPL(__adis_initial_startup, IIO_ADISLIB); /** * adis_single_conversion() - Performs a single sample conversion @@ -477,7 +479,8 @@ EXPORT_SYMBOL_GPL(__adis_initial_startup); * a error bit in the channels raw value set error_mask to 0. */ int adis_single_conversion(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int error_mask, int *val) + const struct iio_chan_spec *chan, + unsigned int error_mask, int *val) { struct adis *adis = iio_device_get_drvdata(indio_dev); unsigned int uval; @@ -486,7 +489,7 @@ int adis_single_conversion(struct iio_dev *indio_dev, mutex_lock(&adis->state_lock); ret = __adis_read_reg(adis, chan->address, &uval, - chan->scan_type.storagebits / 8); + chan->scan_type.storagebits / 8); if (ret) goto err_unlock; @@ -506,7 +509,7 @@ err_unlock: mutex_unlock(&adis->state_lock); return ret; } -EXPORT_SYMBOL_GPL(adis_single_conversion); +EXPORT_SYMBOL_NS_GPL(adis_single_conversion, IIO_ADISLIB); /** * adis_init() - Initialize adis device structure @@ -521,7 +524,7 @@ EXPORT_SYMBOL_GPL(adis_single_conversion); * called. */ int adis_init(struct adis *adis, struct iio_dev *indio_dev, - struct spi_device *spi, const struct adis_data *data) + struct spi_device *spi, const struct adis_data *data) { if (!data || !data->timeouts) { dev_err(&spi->dev, "No config data or timeouts not defined!\n"); @@ -543,7 +546,7 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL_GPL(adis_init); +EXPORT_SYMBOL_NS_GPL(adis_init, IIO_ADISLIB); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 9fd30e62d6e8..17bb0c40a149 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -1240,3 +1240,4 @@ module_spi_driver(adis16400_driver); MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>"); MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index b01988170118..69facd72bd7d 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -428,3 +428,4 @@ module_spi_driver(adis16460_driver); MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index ea91d127077d..ff2b0fab840a 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -1365,3 +1365,4 @@ module_spi_driver(adis16475_driver); MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16475 IMU driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index f9b4540db1f4..44bbe3d19907 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -1538,3 +1538,4 @@ module_spi_driver(adis16480_driver); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 351c303c8a8c..928933027ae3 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -20,7 +20,7 @@ #include <linux/iio/imu/adis.h> static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, - const unsigned long *scan_mask) + const unsigned long *scan_mask) { struct adis *adis = iio_device_get_drvdata(indio_dev); unsigned int burst_length, burst_max_length; @@ -67,7 +67,7 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, } int adis_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask) + const unsigned long *scan_mask) { struct adis *adis = iio_device_get_drvdata(indio_dev); const struct iio_chan_spec *chan; @@ -124,7 +124,7 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL_GPL(adis_update_scan_mode); +EXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, IIO_ADISLIB); static irqreturn_t adis_trigger_handler(int irq, void *p) { @@ -158,7 +158,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, - pf->timestamp); + pf->timestamp); irq_done: iio_trigger_notify_done(indio_dev->trig); @@ -212,5 +212,5 @@ devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup, adis); } -EXPORT_SYMBOL_GPL(devm_adis_setup_buffer_and_trigger); +EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger, IIO_ADISLIB); diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index c461bd1e8e69..f890bf842db8 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -15,8 +15,7 @@ #include <linux/iio/trigger.h> #include <linux/iio/imu/adis.h> -static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) +static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state) { struct adis *adis = iio_trigger_get_drvdata(trig); @@ -88,5 +87,5 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) return devm_iio_trigger_register(&adis->spi->dev, adis->trig); } -EXPORT_SYMBOL_GPL(devm_adis_probe_trigger); +EXPORT_SYMBOL_NS_GPL(devm_adis_probe_trigger, IIO_ADISLIB); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index f8f0cf716bc6..9b4298095d3f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -127,15 +127,14 @@ static int inv_mpu_process_acpi_config(struct i2c_client *client, int inv_mpu_acpi_create_mux_client(struct i2c_client *client) { struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); + struct acpi_device *adev = ACPI_COMPANION(&client->dev); st->mux_client = NULL; - if (ACPI_HANDLE(&client->dev)) { + if (adev) { struct i2c_board_info info; struct i2c_client *mux_client; - struct acpi_device *adev; int ret = -1; - adev = ACPI_COMPANION(&client->dev); memset(&info, 0, sizeof(info)); dmi_check_system(inv_mpu_dev_list); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index fe03707ec2d3..55cffb5fa115 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -3,14 +3,14 @@ * Copyright (C) 2012 Invensense, Inc. */ -#include <linux/acpi.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/iio/iio.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> #include <linux/property.h> + #include "inv_mpu_iio.h" static const struct regmap_config inv_mpu_regmap_config = { @@ -51,7 +51,7 @@ static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); struct device *dev = indio_dev->dev.parent; - struct device_node *mux_node; + struct fwnode_handle *mux_node; int ret; /* @@ -65,12 +65,12 @@ static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev) case INV_MPU9150: case INV_MPU9250: case INV_MPU9255: - mux_node = of_get_child_by_name(dev->of_node, "i2c-gate"); + mux_node = device_get_named_child_node(dev, "i2c-gate"); if (mux_node != NULL) { st->magn_disabled = true; dev_warn(dev, "disable internal use of magnetometer\n"); } - of_node_put(mux_node); + fwnode_handle_put(mux_node); break; default: break; @@ -249,11 +249,10 @@ static const struct of_device_id inv_of_match[] = { }; MODULE_DEVICE_TABLE(of, inv_of_match); -static const struct acpi_device_id __maybe_unused inv_acpi_match[] = { +static const struct acpi_device_id inv_acpi_match[] = { {"INVN6500", INV_MPU6500}, { }, }; - MODULE_DEVICE_TABLE(acpi, inv_acpi_match); static struct i2c_driver inv_mpu_driver = { @@ -262,7 +261,7 @@ static struct i2c_driver inv_mpu_driver = { .id_table = inv_mpu_id, .driver = { .of_match_table = inv_of_match, - .acpi_match_table = ACPI_PTR(inv_acpi_match), + .acpi_match_table = inv_acpi_match, .name = "inv-mpu6050-i2c", .pm = &inv_mpu_pmops, }, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 6800356b25fb..26a7c2521dc4 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -2,9 +2,8 @@ /* * Copyright (C) 2015 Intel Corporation Inc. */ +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/acpi.h> -#include <linux/of.h> #include <linux/property.h> #include <linux/spi/spi.h> #include <linux/regmap.h> @@ -148,7 +147,7 @@ static struct spi_driver inv_mpu_driver = { .id_table = inv_mpu_id, .driver = { .of_match_table = inv_of_match, - .acpi_match_table = ACPI_PTR(inv_acpi_match), + .acpi_match_table = inv_acpi_match, .name = "inv-mpu6000-spi", .pm = &inv_mpu_pmops, }, diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index f89724481df9..ec23b1ee472b 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1443,7 +1443,6 @@ static int kmx61_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int kmx61_suspend(struct device *dev) { int ret; @@ -1469,9 +1468,7 @@ static int kmx61_resume(struct device *dev) return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true); } -#endif -#ifdef CONFIG_PM static int kmx61_runtime_suspend(struct device *dev) { struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); @@ -1496,11 +1493,10 @@ static int kmx61_runtime_resume(struct device *dev) return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true); } -#endif static const struct dev_pm_ops kmx61_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume) - SET_RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume) + RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL) }; static const struct acpi_device_id kmx61_acpi_match[] = { @@ -1521,7 +1517,7 @@ static struct i2c_driver kmx61_driver = { .driver = { .name = KMX61_DRV_NAME, .acpi_match_table = ACPI_PTR(kmx61_acpi_match), - .pm = &kmx61_pm_ops, + .pm = pm_ptr(&kmx61_pm_ops), }, .probe = kmx61_probe, .remove = kmx61_remove, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 93f0c6bce502..b1d8d5a66f01 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1633,7 +1633,7 @@ st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); const struct st_lsm6dsx_odr_table_entry *odr_table; int i, len = 0; @@ -1651,7 +1651,7 @@ static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); const struct st_lsm6dsx_fs_table_entry *fs_table; struct st_lsm6dsx_hw *hw = sensor->hw; int i, len = 0; diff --git a/drivers/iio/imu/st_lsm9ds0/Kconfig b/drivers/iio/imu/st_lsm9ds0/Kconfig index 53b7017014f8..d29558edee60 100644 --- a/drivers/iio/imu/st_lsm9ds0/Kconfig +++ b/drivers/iio/imu/st_lsm9ds0/Kconfig @@ -5,8 +5,6 @@ config IIO_ST_LSM9DS0 depends on (I2C || SPI_MASTER) && SYSFS depends on !SENSORS_LIS3_I2C depends on !SENSORS_LIS3_SPI - select IIO_ST_LSM9DS0_I2C if I2C - select IIO_ST_LSM9DS0_SPI if SPI_MASTER select IIO_ST_ACCEL_3AXIS select IIO_ST_MAGN_3AXIS @@ -17,12 +15,30 @@ config IIO_ST_LSM9DS0 To compile this driver as a module, choose M here: the module will be called st_lsm9ds0. + Also need to enable at least one of I2C and SPI interface drivers + config IIO_ST_LSM9DS0_I2C - tristate - depends on IIO_ST_LSM9DS0 + tristate "STMicroelectronics LSM9DS0 IMU I2C interface" + depends on I2C && IIO_ST_LSM9DS0 + default I2C && IIO_ST_LSM9DS0 + select IIO_ST_ACCEL_I2C_3AXIS + select IIO_ST_MAGN_I2C_3AXIS select REGMAP_I2C + help + Build support for STMicroelectronics LSM9DS0 IMU I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_lsm9ds0_i2c. config IIO_ST_LSM9DS0_SPI - tristate - depends on IIO_ST_LSM9DS0 + tristate "STMicroelectronics LSM9DS0 IMU SPI interface" + depends on SPI_MASTER && IIO_ST_LSM9DS0 + default SPI_MASTER && IIO_ST_LSM9DS0 + select IIO_ST_ACCEL_SPI_3AXIS + select IIO_ST_MAGN_SPI_3AXIS select REGMAP_SPI + help + Build support for STMicroelectronics LSM9DS0 IMU I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_lsm9ds0_spi. diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c index 9fb06b7cde3c..ae7bc815382f 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -142,8 +142,9 @@ int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) /* Setup magnetometer device */ return st_lsm9ds0_probe_magn(lsm9ds0, regmap); } -EXPORT_SYMBOL_GPL(st_lsm9ds0_probe); +EXPORT_SYMBOL_NS_GPL(st_lsm9ds0_probe, IIO_ST_SENSORS); MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU core driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c index 8f205c477e6f..a90138d8b06a 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -77,3 +77,4 @@ module_i2c_driver(st_lsm9ds0_driver); MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU I2C driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 0ddfa53166af..b743bf3546a7 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -76,3 +76,4 @@ module_spi_driver(st_lsm9ds0_driver); MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 208b5193c621..b078eb2f3c9d 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1383,9 +1383,9 @@ static ssize_t direction_show(struct device *dev, switch (buffer->direction) { case IIO_BUFFER_DIRECTION_IN: - return sprintf(buf, "in\n"); + return sysfs_emit(buf, "in\n"); case IIO_BUFFER_DIRECTION_OUT: - return sprintf(buf, "out\n"); + return sysfs_emit(buf, "out\n"); default: return -EINVAL; } diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 409c278a4c2c..e1ed44dec2ab 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -747,7 +747,7 @@ static ssize_t iio_read_channel_label(struct device *dev, return indio_dev->info->read_label(indio_dev, this_attr->c, buf); if (this_attr->c->extend_name) - return sprintf(buf, "%s\n", this_attr->c->extend_name); + return sysfs_emit(buf, "%s\n", this_attr->c->extend_name); return -EINVAL; } diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index d0732eac0f0a..ce8b102ce52f 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -230,6 +230,7 @@ static const char * const iio_ev_type_text[] = { [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive", [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", [IIO_EV_TYPE_CHANGE] = "change", + [IIO_EV_TYPE_MAG_REFERENCED] = "mag_referenced", }; static const char * const iio_ev_dir_text[] = { diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 0222885b334c..df74765d33dc 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -595,28 +595,50 @@ EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, int raw, int *processed, unsigned int scale) { - int scale_type, scale_val, scale_val2, offset; + int scale_type, scale_val, scale_val2; + int offset_type, offset_val, offset_val2; s64 raw64 = raw; - int ret; - ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_OFFSET); - if (ret >= 0) - raw64 += offset; + offset_type = iio_channel_read(chan, &offset_val, &offset_val2, + IIO_CHAN_INFO_OFFSET); + if (offset_type >= 0) { + switch (offset_type) { + case IIO_VAL_INT: + break; + case IIO_VAL_INT_PLUS_MICRO: + case IIO_VAL_INT_PLUS_NANO: + /* + * Both IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO + * implicitely truncate the offset to it's integer form. + */ + break; + case IIO_VAL_FRACTIONAL: + offset_val /= offset_val2; + break; + case IIO_VAL_FRACTIONAL_LOG2: + offset_val >>= offset_val2; + break; + default: + return -EINVAL; + } + + raw64 += offset_val; + } scale_type = iio_channel_read(chan, &scale_val, &scale_val2, IIO_CHAN_INFO_SCALE); if (scale_type < 0) { /* - * Just pass raw values as processed if no scaling is - * available. + * If no channel scaling is available apply consumer scale to + * raw value and return. */ - *processed = raw; + *processed = raw * scale; return 0; } switch (scale_type) { case IIO_VAL_INT: - *processed = raw64 * scale_val; + *processed = raw64 * scale_val * scale; break; case IIO_VAL_INT_PLUS_MICRO: if (scale_val2 < 0) diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index baaf202dce05..0f9d77598997 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -466,7 +466,6 @@ static int apds9300_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int apds9300_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -493,11 +492,8 @@ static int apds9300_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend, apds9300_resume); -#define APDS9300_PM_OPS (&apds9300_pm_ops) -#else -#define APDS9300_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend, + apds9300_resume); static const struct i2c_device_id apds9300_id[] = { { APDS9300_DRV_NAME, 0 }, @@ -509,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, apds9300_id); static struct i2c_driver apds9300_driver = { .driver = { .name = APDS9300_DRV_NAME, - .pm = APDS9300_PM_OPS, + .pm = pm_sleep_ptr(&apds9300_pm_ops), }, .probe = apds9300_probe, .remove = apds9300_remove, diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c index abbf2e662e7d..790d3d613979 100644 --- a/drivers/iio/light/bh1780.c +++ b/drivers/iio/light/bh1780.c @@ -221,7 +221,6 @@ static int bh1780_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM static int bh1780_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -256,14 +255,9 @@ static int bh1780_runtime_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ -static const struct dev_pm_ops bh1780_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(bh1780_runtime_suspend, - bh1780_runtime_resume, NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(bh1780_dev_pm_ops, bh1780_runtime_suspend, + bh1780_runtime_resume, NULL); static const struct i2c_device_id bh1780_id[] = { { "bh1780", 0 }, @@ -284,7 +278,7 @@ static struct i2c_driver bh1780_driver = { .id_table = bh1780_id, .driver = { .name = "bh1780", - .pm = &bh1780_dev_pm_ops, + .pm = pm_ptr(&bh1780_dev_pm_ops), .of_match_table = of_bh1780_match, }, }; diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index 18a410340dc5..2c80a0535d2c 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -374,7 +374,6 @@ static const struct i2c_device_id cm3232_id[] = { {} }; -#ifdef CONFIG_PM_SLEEP static int cm3232_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -403,9 +402,7 @@ static int cm3232_resume(struct device *dev) return ret; } -static const struct dev_pm_ops cm3232_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)}; -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(cm3232_pm_ops, cm3232_suspend, cm3232_resume); MODULE_DEVICE_TABLE(i2c, cm3232_id); @@ -419,9 +416,7 @@ static struct i2c_driver cm3232_driver = { .driver = { .name = "cm3232", .of_match_table = cm3232_of_match, -#ifdef CONFIG_PM_SLEEP - .pm = &cm3232_pm_ops, -#endif + .pm = pm_sleep_ptr(&cm3232_pm_ops), }, .id_table = cm3232_id, .probe = cm3232_probe, diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index 2689867467a8..b36f8b7ca68e 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -784,7 +784,6 @@ static int isl29018_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int isl29018_suspend(struct device *dev) { struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev)); @@ -830,11 +829,8 @@ static int isl29018_resume(struct device *dev) return err; } -static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume); -#define ISL29018_PM_OPS (&isl29018_pm_ops) -#else -#define ISL29018_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, + isl29018_resume); #ifdef CONFIG_ACPI static const struct acpi_device_id isl29018_acpi_match[] = { @@ -866,7 +862,7 @@ static struct i2c_driver isl29018_driver = { .driver = { .name = "isl29018", .acpi_match_table = ACPI_PTR(isl29018_acpi_match), - .pm = ISL29018_PM_OPS, + .pm = pm_sleep_ptr(&isl29018_pm_ops), .of_match_table = isl29018_of_match, }, .probe = isl29018_probe, diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index ba53b50d711a..eb68a52aab82 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -311,7 +311,6 @@ static int isl29125_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int isl29125_suspend(struct device *dev) { struct isl29125_data *data = iio_priv(i2c_get_clientdata( @@ -326,9 +325,9 @@ static int isl29125_resume(struct device *dev) return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1, data->conf1); } -#endif -static SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend, isl29125_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend, + isl29125_resume); static const struct i2c_device_id isl29125_id[] = { { "isl29125", 0 }, @@ -339,7 +338,7 @@ MODULE_DEVICE_TABLE(i2c, isl29125_id); static struct i2c_driver isl29125_driver = { .driver = { .name = ISL29125_DRV_NAME, - .pm = &isl29125_pm_ops, + .pm = pm_sleep_ptr(&isl29125_pm_ops), }, .probe = isl29125_probe, .remove = isl29125_remove, diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index 724a0ec9f35c..a55194263d23 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -383,7 +383,6 @@ static int jsa1212_remove(struct i2c_client *client) return jsa1212_power_off(data); } -#ifdef CONFIG_PM_SLEEP static int jsa1212_suspend(struct device *dev) { struct jsa1212_data *data; @@ -421,12 +420,8 @@ unlock_and_ret: return ret; } -static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume); - -#define JSA1212_PM_OPS (&jsa1212_pm_ops) -#else -#define JSA1212_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, + jsa1212_resume); static const struct acpi_device_id jsa1212_acpi_match[] = { {"JSA1212", 0}, @@ -443,7 +438,7 @@ MODULE_DEVICE_TABLE(i2c, jsa1212_id); static struct i2c_driver jsa1212_driver = { .driver = { .name = JSA1212_DRIVER_NAME, - .pm = JSA1212_PM_OPS, + .pm = pm_sleep_ptr(&jsa1212_pm_ops), .acpi_match_table = ACPI_PTR(jsa1212_acpi_match), }, .probe = jsa1212_probe, diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 8a621244dd01..827bc25269e9 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -417,7 +417,7 @@ static ssize_t show_thresh_either_en(struct device *dev, enable = 0; } - return scnprintf(buf, PAGE_SIZE, "%u\n", enable); + return sysfs_emit(buf, "%u\n", enable); } static ssize_t store_thresh_either_en(struct device *dev, @@ -474,7 +474,7 @@ static ssize_t show_zone(struct device *dev, if (ret) return ret; - return scnprintf(buf, PAGE_SIZE, "%u\n", zone); + return sysfs_emit(buf, "%u\n", zone); } enum lm3533_als_attribute_type { @@ -530,7 +530,7 @@ static ssize_t show_als_attr(struct device *dev, if (ret) return ret; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t store_als_attr(struct device *dev, diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 47d61ec2bb50..679a1e1086ae 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1611,7 +1611,6 @@ static int ltr501_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int ltr501_suspend(struct device *dev) { struct ltr501_data *data = iio_priv(i2c_get_clientdata( @@ -1627,23 +1626,22 @@ static int ltr501_resume(struct device *dev) return ltr501_write_contr(data, data->als_contr, data->ps_contr); } -#endif -static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); static const struct acpi_device_id ltr_acpi_match[] = { - {"LTER0501", ltr501}, - {"LTER0559", ltr559}, - {"LTER0301", ltr301}, + { "LTER0501", ltr501 }, + { "LTER0559", ltr559 }, + { "LTER0301", ltr301 }, { }, }; MODULE_DEVICE_TABLE(acpi, ltr_acpi_match); static const struct i2c_device_id ltr501_id[] = { - { "ltr501", ltr501}, - { "ltr559", ltr559}, - { "ltr301", ltr301}, - { "ltr303", ltr303}, + { "ltr501", ltr501 }, + { "ltr559", ltr559 }, + { "ltr301", ltr301 }, + { "ltr303", ltr303 }, { } }; MODULE_DEVICE_TABLE(i2c, ltr501_id); @@ -1661,7 +1659,7 @@ static struct i2c_driver ltr501_driver = { .driver = { .name = LTR501_DRV_NAME, .of_match_table = ltr501_of_match, - .pm = <r501_pm_ops, + .pm = pm_sleep_ptr(<r501_pm_ops), .acpi_match_table = ACPI_PTR(ltr_acpi_match), }, .probe = ltr501_probe, diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index a52b2c788540..528fa5dd2b13 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -452,14 +452,14 @@ static const struct dev_pm_ops pa12203001_pm_ops = { }; static const struct acpi_device_id pa12203001_acpi_match[] = { - { "TXCPA122", 0}, + { "TXCPA122", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match); static const struct i2c_device_id pa12203001_id[] = { - {"txcpa122", 0}, + { "txcpa122", 0 }, {} }; diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index c2dd8a3d4217..dabdd05f0e2c 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -1055,7 +1055,6 @@ static int rpr0521_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM static int rpr0521_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1101,11 +1100,9 @@ static int rpr0521_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops rpr0521_pm_ops = { - SET_RUNTIME_PM_OPS(rpr0521_runtime_suspend, - rpr0521_runtime_resume, NULL) + RUNTIME_PM_OPS(rpr0521_runtime_suspend, rpr0521_runtime_resume, NULL) }; static const struct acpi_device_id rpr0521_acpi_match[] = { @@ -1124,7 +1121,7 @@ MODULE_DEVICE_TABLE(i2c, rpr0521_id); static struct i2c_driver rpr0521_driver = { .driver = { .name = RPR0521_DRV_NAME, - .pm = &rpr0521_pm_ops, + .pm = pm_ptr(&rpr0521_pm_ops), .acpi_match_table = ACPI_PTR(rpr0521_acpi_match), }, .probe = rpr0521_probe, diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 41a2ce5a2d53..3d4cc1180b6a 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -323,7 +323,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap) return devm_iio_device_register(dev, iio_dev); } -EXPORT_SYMBOL(st_uvis25_probe); +EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25); static int __maybe_unused st_uvis25_suspend(struct device *dev) { @@ -349,7 +349,7 @@ static int __maybe_unused st_uvis25_resume(struct device *dev) const struct dev_pm_ops st_uvis25_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume) }; -EXPORT_SYMBOL(st_uvis25_pm_ops); +EXPORT_SYMBOL_NS(st_uvis25_pm_ops, IIO_UVIS25); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver"); diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c index 98cd49eefe45..b06d09af28a3 100644 --- a/drivers/iio/light/st_uvis25_i2c.c +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -66,3 +66,4 @@ module_i2c_driver(st_uvis25_driver); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); MODULE_DESCRIPTION("STMicroelectronics uvis25 i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_UVIS25); diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index af9d94d12787..3a4dc6d7180c 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -66,3 +66,4 @@ module_spi_driver(st_uvis25_driver); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); MODULE_DESCRIPTION("STMicroelectronics uvis25 spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_UVIS25); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index fc63856ed54d..1d02dfbc29d1 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -632,7 +632,6 @@ static int stk3310_remove(struct i2c_client *client) return stk3310_set_state(iio_priv(indio_dev), STK3310_STATE_STANDBY); } -#ifdef CONFIG_PM_SLEEP static int stk3310_suspend(struct device *dev) { struct stk3310_data *data; @@ -656,12 +655,8 @@ static int stk3310_resume(struct device *dev) return stk3310_set_state(data, state); } -static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume); - -#define STK3310_PM_OPS (&stk3310_pm_ops) -#else -#define STK3310_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, + stk3310_resume); static const struct i2c_device_id stk3310_i2c_id[] = { {"STK3310", 0}, @@ -692,7 +687,7 @@ static struct i2c_driver stk3310_driver = { .driver = { .name = "stk3310", .of_match_table = stk3310_of_match, - .pm = STK3310_PM_OPS, + .pm = pm_sleep_ptr(&stk3310_pm_ops), .acpi_match_table = ACPI_PTR(stk3310_acpi_id), }, .probe = stk3310_probe, diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index b87222141429..3951536022b3 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -345,7 +345,6 @@ static int tcs3414_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int tcs3414_suspend(struct device *dev) { struct tcs3414_data *data = iio_priv(i2c_get_clientdata( @@ -360,9 +359,9 @@ static int tcs3414_resume(struct device *dev) return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, data->control); } -#endif -static SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend, tcs3414_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend, + tcs3414_resume); static const struct i2c_device_id tcs3414_id[] = { { "tcs3414", 0 }, @@ -373,7 +372,7 @@ MODULE_DEVICE_TABLE(i2c, tcs3414_id); static struct i2c_driver tcs3414_driver = { .driver = { .name = TCS3414_DRV_NAME, - .pm = &tcs3414_pm_ops, + .pm = pm_sleep_ptr(&tcs3414_pm_ops), }, .probe = tcs3414_probe, .id_table = tcs3414_id, diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 371c6a39a165..823435f59bb6 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -572,7 +572,6 @@ static int tcs3472_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int tcs3472_suspend(struct device *dev) { struct tcs3472_data *data = iio_priv(i2c_get_clientdata( @@ -598,9 +597,9 @@ static int tcs3472_resume(struct device *dev) return ret; } -#endif -static SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, tcs3472_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, + tcs3472_resume); static const struct i2c_device_id tcs3472_id[] = { { "tcs3472", 0 }, @@ -611,7 +610,7 @@ MODULE_DEVICE_TABLE(i2c, tcs3472_id); static struct i2c_driver tcs3472_driver = { .driver = { .name = TCS3472_DRV_NAME, - .pm = &tcs3472_pm_ops, + .pm = pm_sleep_ptr(&tcs3472_pm_ops), }, .probe = tcs3472_probe, .remove = tcs3472_remove, diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 5bf2bfbc5379..0a278eea36ca 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -814,7 +814,6 @@ static int tsl2563_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int tsl2563_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -857,11 +856,8 @@ out: return ret; } -static SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume); -#define TSL2563_PM_OPS (&tsl2563_pm_ops) -#else -#define TSL2563_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, + tsl2563_resume); static const struct i2c_device_id tsl2563_id[] = { { "tsl2560", 0 }, @@ -885,7 +881,7 @@ static struct i2c_driver tsl2563_i2c_driver = { .driver = { .name = "tsl2563", .of_match_table = tsl2563_of_match, - .pm = TSL2563_PM_OPS, + .pm = pm_sleep_ptr(&tsl2563_pm_ops), }, .probe = tsl2563_probe, .remove = tsl2563_remove, diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index d79205361dfa..729f14d9f2a4 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1902,7 +1902,7 @@ static const struct i2c_device_id tsl2772_idtable[] = { { "tmd2672", tmd2672 }, { "tsl2772", tsl2772 }, { "tmd2772", tmd2772 }, - { "apds9930", apds9930}, + { "apds9930", apds9930 }, {} }; diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c index 70505ba6d858..6ae1b27e50b6 100644 --- a/drivers/iio/light/tsl4531.c +++ b/drivers/iio/light/tsl4531.c @@ -215,7 +215,6 @@ static int tsl4531_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int tsl4531_suspend(struct device *dev) { return tsl4531_powerdown(to_i2c_client(dev)); @@ -227,11 +226,8 @@ static int tsl4531_resume(struct device *dev) TSL4531_MODE_NORMAL); } -static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume); -#define TSL4531_PM_OPS (&tsl4531_pm_ops) -#else -#define TSL4531_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, + tsl4531_resume); static const struct i2c_device_id tsl4531_id[] = { { "tsl4531", 0 }, @@ -242,7 +238,7 @@ MODULE_DEVICE_TABLE(i2c, tsl4531_id); static struct i2c_driver tsl4531_driver = { .driver = { .name = TSL4531_DRV_NAME, - .pm = TSL4531_PM_OPS, + .pm = pm_sleep_ptr(&tsl4531_pm_ops), }, .probe = tsl4531_probe, .remove = tsl4531_remove, diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index 96e4a66ddf28..1492aaf8d84c 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -947,15 +947,15 @@ static const struct dev_pm_ops us5182d_pm_ops = { }; static const struct acpi_device_id us5182d_acpi_match[] = { - { "USD5182", 0}, + { "USD5182", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match); static const struct i2c_device_id us5182d_id[] = { - {"usd5182", 0}, - {} + { "usd5182", 0 }, + {} }; MODULE_DEVICE_TABLE(i2c, us5182d_id); diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 0db306ee910e..da2bf622a67b 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -651,7 +651,7 @@ static const struct dev_pm_ops vcnl4035_pm_ops = { }; static const struct i2c_device_id vcnl4035_id[] = { - { "vcnl4035", 0}, + { "vcnl4035", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, vcnl4035_id); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 565ee41ccb3a..54445365c4bc 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -117,30 +117,35 @@ config IIO_ST_MAGN_3AXIS tristate "STMicroelectronics magnetometers 3-Axis Driver" depends on (I2C || SPI_MASTER) && SYSFS select IIO_ST_SENSORS_CORE - select IIO_ST_MAGN_I2C_3AXIS if (I2C) - select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics magnetometers: LSM303DLHC, LSM303DLM, LIS3MDL. - This driver can also be built as a module. If so, these modules - will be created: - - st_magn (core functions for the driver [it is mandatory]); - - st_magn_i2c (necessary for the I2C devices [optional*]); - - st_magn_spi (necessary for the SPI devices [optional*]); - - (*) one of these is necessary to do something. + Also need to enable at least one of I2C and SPI interface drivers + below. config IIO_ST_MAGN_I2C_3AXIS - tristate - depends on IIO_ST_MAGN_3AXIS - depends on IIO_ST_SENSORS_I2C + tristate "STMicroelectronics magnetometers 3-Axis I2C Interface" + depends on I2C && IIO_ST_MAGN_3AXIS + default I2C && IIO_ST_MAGN_3AXIS + select IIO_ST_SENSORS_I2C + help + Build support for STMicroelectronics magnetometers I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_magn_i2c. config IIO_ST_MAGN_SPI_3AXIS - tristate - depends on IIO_ST_MAGN_3AXIS - depends on IIO_ST_SENSORS_SPI + tristate "STMicroelectronics magnetometers 3-Axis SPI Interface" + depends on SPI_MASTER && IIO_ST_MAGN_3AXIS + default SPI_MASTER && IIO_ST_MAGN_3AXIS + select IIO_ST_SENSORS_SPI + help + Build support for STMicroelectronics magnetometers SPI interface. + + To compile this driver as a module, choose M here. The module + will be called st_magn_spi. config SENSORS_HMC5843 tristate diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 55879a20ae52..088f748b683e 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -1033,7 +1033,6 @@ static int ak8975_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM static int ak8975_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1074,14 +1073,9 @@ static int ak8975_runtime_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ -static const struct dev_pm_ops ak8975_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(ak8975_runtime_suspend, - ak8975_runtime_resume, NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ak8975_dev_pm_ops, ak8975_runtime_suspend, + ak8975_runtime_resume, NULL); static const struct i2c_device_id ak8975_id[] = { {"ak8975", AK8975}, @@ -1113,7 +1107,7 @@ MODULE_DEVICE_TABLE(of, ak8975_of_match); static struct i2c_driver ak8975_driver = { .driver = { .name = "ak8975", - .pm = &ak8975_dev_pm_ops, + .pm = pm_ptr(&ak8975_dev_pm_ops), .of_match_table = ak8975_of_match, .acpi_match_table = ak_acpi_match, }, diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 3d4d21f979fa..64e8b04e654b 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -226,7 +226,7 @@ const struct regmap_config bmc150_magn_regmap_config = { .writeable_reg = bmc150_magn_is_writeable_reg, .volatile_reg = bmc150_magn_is_volatile_reg, }; -EXPORT_SYMBOL(bmc150_magn_regmap_config); +EXPORT_SYMBOL_NS(bmc150_magn_regmap_config, IIO_BMC150_MAGN); static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, enum bmc150_magn_power_modes mode, @@ -983,7 +983,7 @@ err_poweroff: bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true); return ret; } -EXPORT_SYMBOL(bmc150_magn_probe); +EXPORT_SYMBOL_NS(bmc150_magn_probe, IIO_BMC150_MAGN); int bmc150_magn_remove(struct device *dev) { @@ -1010,7 +1010,7 @@ int bmc150_magn_remove(struct device *dev) regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); return 0; } -EXPORT_SYMBOL(bmc150_magn_remove); +EXPORT_SYMBOL_NS(bmc150_magn_remove, IIO_BMC150_MAGN); #ifdef CONFIG_PM static int bmc150_magn_runtime_suspend(struct device *dev) @@ -1078,7 +1078,7 @@ const struct dev_pm_ops bmc150_magn_pm_ops = { SET_RUNTIME_PM_OPS(bmc150_magn_runtime_suspend, bmc150_magn_runtime_resume, NULL) }; -EXPORT_SYMBOL(bmc150_magn_pm_ops); +EXPORT_SYMBOL_NS(bmc150_magn_pm_ops, IIO_BMC150_MAGN); MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c index 876e96005e33..e39b89661ad1 100644 --- a/drivers/iio/magnetometer/bmc150_magn_i2c.c +++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c @@ -80,3 +80,4 @@ module_i2c_driver(bmc150_magn_driver); MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("BMC150 I2C magnetometer driver"); +MODULE_IMPORT_NS(IIO_BMC150_MAGN); diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index 4c570412d65c..882987721071 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -64,3 +64,4 @@ module_spi_driver(bmc150_magn_spi_driver); MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com"); MODULE_DESCRIPTION("BMC150 magnetometer SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_BMC150_MAGN); diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 5a730d9bdbb0..92eb2d156ddb 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -608,14 +608,14 @@ int hmc5843_common_suspend(struct device *dev) return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)), HMC5843_MODE_SLEEP); } -EXPORT_SYMBOL(hmc5843_common_suspend); +EXPORT_SYMBOL_NS(hmc5843_common_suspend, IIO_HMC5843); int hmc5843_common_resume(struct device *dev) { return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)), HMC5843_MODE_CONVERSION_CONTINUOUS); } -EXPORT_SYMBOL(hmc5843_common_resume); +EXPORT_SYMBOL_NS(hmc5843_common_resume, IIO_HMC5843); int hmc5843_common_probe(struct device *dev, struct regmap *regmap, enum hmc5843_ids id, const char *name) @@ -669,7 +669,7 @@ buffer_setup_err: hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP); return ret; } -EXPORT_SYMBOL(hmc5843_common_probe); +EXPORT_SYMBOL_NS(hmc5843_common_probe, IIO_HMC5843); void hmc5843_common_remove(struct device *dev) { @@ -681,7 +681,7 @@ void hmc5843_common_remove(struct device *dev) /* sleep mode to save power */ hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP); } -EXPORT_SYMBOL(hmc5843_common_remove); +EXPORT_SYMBOL_NS(hmc5843_common_remove, IIO_HMC5843); MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com>"); MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 core driver"); diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c index bc6e12f1d521..8d2ff8fc204d 100644 --- a/drivers/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -105,3 +105,4 @@ module_i2c_driver(hmc5843_driver); MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>"); MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 i2c driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HMC5843); diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index a99dd9b33e95..8403f09aba39 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -100,3 +100,4 @@ module_spi_driver(hmc5843_driver); MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>"); MODULE_DESCRIPTION("HMC5983 SPI driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HMC5843); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index 17c62d806218..226439d0bfb5 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -573,7 +573,6 @@ static int mag3110_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int mag3110_suspend(struct device *dev) { struct mag3110_data *data = iio_priv(i2c_get_clientdata( @@ -623,11 +622,8 @@ static int mag3110_resume(struct device *dev) data->ctrl_reg1); } -static SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume); -#define MAG3110_PM_OPS (&mag3110_pm_ops) -#else -#define MAG3110_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, + mag3110_resume); static const struct i2c_device_id mag3110_id[] = { { "mag3110", 0 }, @@ -645,7 +641,7 @@ static struct i2c_driver mag3110_driver = { .driver = { .name = "mag3110", .of_match_table = mag3110_of_match, - .pm = MAG3110_PM_OPS, + .pm = pm_sleep_ptr(&mag3110_pm_ops), }, .probe = mag3110_probe, .remove = mag3110_remove, diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 65f3d1ed0d59..186edfcda0b7 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -521,7 +521,6 @@ static int mmc35240_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int mmc35240_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -548,11 +547,9 @@ static int mmc35240_resume(struct device *dev) return 0; } -#endif -static const struct dev_pm_ops mmc35240_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(mmc35240_pm_ops, mmc35240_suspend, + mmc35240_resume); static const struct of_device_id mmc35240_of_match[] = { { .compatible = "memsic,mmc35240", }, @@ -576,7 +573,7 @@ static struct i2c_driver mmc35240_driver = { .driver = { .name = MMC35240_DRV_NAME, .of_match_table = mmc35240_of_match, - .pm = &mmc35240_pm_ops, + .pm = pm_sleep_ptr(&mmc35240_pm_ops), .acpi_match_table = ACPI_PTR(mmc35240_acpi_match), }, .probe = mmc35240_probe, diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index 13914273c999..26195733ea3e 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -100,7 +100,7 @@ const struct regmap_access_table rm3100_readable_table = { .yes_ranges = rm3100_readable_ranges, .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges), }; -EXPORT_SYMBOL_GPL(rm3100_readable_table); +EXPORT_SYMBOL_NS_GPL(rm3100_readable_table, IIO_RM3100); static const struct regmap_range rm3100_writable_ranges[] = { regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END), @@ -110,7 +110,7 @@ const struct regmap_access_table rm3100_writable_table = { .yes_ranges = rm3100_writable_ranges, .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges), }; -EXPORT_SYMBOL_GPL(rm3100_writable_table); +EXPORT_SYMBOL_NS_GPL(rm3100_writable_table, IIO_RM3100); static const struct regmap_range rm3100_volatile_ranges[] = { regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END), @@ -120,7 +120,7 @@ const struct regmap_access_table rm3100_volatile_table = { .yes_ranges = rm3100_volatile_ranges, .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges), }; -EXPORT_SYMBOL_GPL(rm3100_volatile_table); +EXPORT_SYMBOL_NS_GPL(rm3100_volatile_table, IIO_RM3100); static irqreturn_t rm3100_thread_fn(int irq, void *d) { @@ -607,7 +607,7 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(rm3100_common_probe); +EXPORT_SYMBOL_NS_GPL(rm3100_common_probe, IIO_RM3100); MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver"); diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c index 1ac622c6d6c9..ba669ab7113d 100644 --- a/drivers/iio/magnetometer/rm3100-i2c.c +++ b/drivers/iio/magnetometer/rm3100-i2c.c @@ -52,3 +52,4 @@ module_i2c_driver(rm3100_driver); MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_RM3100); diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c index 65d5eb9e4f5e..76dc9b66cd3c 100644 --- a/drivers/iio/magnetometer/rm3100-spi.c +++ b/drivers/iio/magnetometer/rm3100-spi.c @@ -62,3 +62,4 @@ module_spi_driver(rm3100_driver); MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_RM3100); diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c index cb43ccda808d..79987f42e8d9 100644 --- a/drivers/iio/magnetometer/st_magn_buffer.c +++ b/drivers/iio/magnetometer/st_magn_buffer.c @@ -7,7 +7,6 @@ * Denis Ciocca <denis.ciocca@st.com> */ -#include <linux/module.h> #include <linux/kernel.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> @@ -45,6 +44,3 @@ int st_magn_allocate_ring(struct iio_dev *indio_dev) NULL, &st_sensors_trigger_handler, &st_magn_buffer_setup_ops); } -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics magnetometers buffer"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 0806a1e65ce4..74435f4a427d 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -606,7 +606,7 @@ const struct st_sensor_settings *st_magn_get_settings(const char *name) return &st_magn_sensors_settings[index]; } -EXPORT_SYMBOL(st_magn_get_settings); +EXPORT_SYMBOL_NS(st_magn_get_settings, IIO_ST_SENSORS); int st_magn_common_probe(struct iio_dev *indio_dev) { @@ -653,8 +653,9 @@ int st_magn_common_probe(struct iio_dev *indio_dev) return devm_iio_device_register(parent, indio_dev); } -EXPORT_SYMBOL(st_magn_common_probe); +EXPORT_SYMBOL_NS(st_magn_common_probe, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics magnetometers driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 7237711fc09b..c5d8c303db4e 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -115,3 +115,4 @@ module_i2c_driver(st_magn_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 489d4462862f..6ddc4318564a 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -106,3 +106,4 @@ module_spi_driver(st_magn_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index 832df8da2bc6..01dd3f858d99 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -27,11 +27,11 @@ config AD5272 module will be called ad5272. config DS1803 - tristate "Maxim Integrated DS1803 Digital Potentiometer driver" + tristate "Maxim Integrated DS1803 and similar Digital Potentiometer driver" depends on I2C help - Say yes here to build support for the Maxim Integrated DS1803 - digital potentiometer chip. + Say yes here to build support for the Maxim Integrated DS1803 and + DS3502 digital potentiometer chip. To compile this driver as a module, choose M here: the module will be called ds1803. diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c index 20b45407eaac..5c212ed7a931 100644 --- a/drivers/iio/potentiometer/ds1803.c +++ b/drivers/iio/potentiometer/ds1803.c @@ -1,12 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Maxim Integrated DS1803 digital potentiometer driver + * Maxim Integrated DS1803 and similar digital potentiometer driver * Copyright (c) 2016 Slawomir Stepien + * Copyright (c) 2022 Jagath Jog J * * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS1803.pdf + * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3502.pdf * * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address * ds1803 2 256 10, 50, 100 0101xxx + * ds3502 1 128 10 01010xx */ #include <linux/err.h> @@ -15,24 +18,27 @@ #include <linux/iio/iio.h> #include <linux/module.h> #include <linux/mod_devicetable.h> +#include <linux/property.h> -#define DS1803_MAX_POS 255 -#define DS1803_WRITE(chan) (0xa8 | ((chan) + 1)) +#define DS1803_WIPER_0 0xA9 +#define DS1803_WIPER_1 0xAA +#define DS3502_WR_IVR 0x00 enum ds1803_type { DS1803_010, DS1803_050, DS1803_100, + DS3502, }; struct ds1803_cfg { + int wipers; + int avail[3]; int kohms; -}; - -static const struct ds1803_cfg ds1803_cfg[] = { - [DS1803_010] = { .kohms = 10, }, - [DS1803_050] = { .kohms = 50, }, - [DS1803_100] = { .kohms = 100, }, + const struct iio_chan_spec *channels; + u8 num_channels; + int (*read)(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val); }; struct ds1803_data { @@ -40,42 +46,110 @@ struct ds1803_data { const struct ds1803_cfg *cfg; }; -#define DS1803_CHANNEL(ch) { \ - .type = IIO_RESISTANCE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (ch), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +#define DS1803_CHANNEL(ch, addr) { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (ch), \ + .address = (addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \ } static const struct iio_chan_spec ds1803_channels[] = { - DS1803_CHANNEL(0), - DS1803_CHANNEL(1), + DS1803_CHANNEL(0, DS1803_WIPER_0), + DS1803_CHANNEL(1, DS1803_WIPER_1), }; -static int ds1803_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static const struct iio_chan_spec ds3502_channels[] = { + DS1803_CHANNEL(0, DS3502_WR_IVR), +}; + +static int ds1803_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) { struct ds1803_data *data = iio_priv(indio_dev); - int pot = chan->channel; int ret; u8 result[ARRAY_SIZE(ds1803_channels)]; + ret = i2c_master_recv(data->client, result, indio_dev->num_channels); + if (ret < 0) + return ret; + + *val = result[chan->channel]; + return ret; +} + +static int ds3502_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ds1803_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(data->client, chan->address); + if (ret < 0) + return ret; + + *val = ret; + return ret; +} + +static const struct ds1803_cfg ds1803_cfg[] = { + [DS1803_010] = { + .wipers = 2, + .avail = { 0, 1, 255 }, + .kohms = 10, + .channels = ds1803_channels, + .num_channels = ARRAY_SIZE(ds1803_channels), + .read = ds1803_read, + }, + [DS1803_050] = { + .wipers = 2, + .avail = { 0, 1, 255 }, + .kohms = 50, + .channels = ds1803_channels, + .num_channels = ARRAY_SIZE(ds1803_channels), + .read = ds1803_read, + }, + [DS1803_100] = { + .wipers = 2, + .avail = { 0, 1, 255 }, + .kohms = 100, + .channels = ds1803_channels, + .num_channels = ARRAY_SIZE(ds1803_channels), + .read = ds1803_read, + }, + [DS3502] = { + .wipers = 1, + .avail = { 0, 1, 127 }, + .kohms = 10, + .channels = ds3502_channels, + .num_channels = ARRAY_SIZE(ds3502_channels), + .read = ds3502_read, + }, +}; + +static int ds1803_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ds1803_data *data = iio_priv(indio_dev); + int ret; + switch (mask) { case IIO_CHAN_INFO_RAW: - ret = i2c_master_recv(data->client, result, - indio_dev->num_channels); + ret = data->cfg->read(indio_dev, chan, val); if (ret < 0) return ret; - *val = result[pot]; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 1000 * data->cfg->kohms; - *val2 = DS1803_MAX_POS; + *val2 = data->cfg->avail[2]; /* Max wiper position */ return IIO_VAL_FRACTIONAL; } @@ -83,34 +157,52 @@ static int ds1803_read_raw(struct iio_dev *indio_dev, } static int ds1803_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct ds1803_data *data = iio_priv(indio_dev); - int pot = chan->channel; + u8 addr = chan->address; + int max_pos = data->cfg->avail[2]; if (val2 != 0) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > DS1803_MAX_POS || val < 0) + if (val > max_pos || val < 0) return -EINVAL; break; default: return -EINVAL; } - return i2c_smbus_write_byte_data(data->client, DS1803_WRITE(pot), val); + return i2c_smbus_write_byte_data(data->client, addr, val); +} + +static int ds1803_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + struct ds1803_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *vals = data->cfg->avail; + *length = ARRAY_SIZE(data->cfg->avail); + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + } + return -EINVAL; } static const struct iio_info ds1803_info = { .read_raw = ds1803_read_raw, .write_raw = ds1803_write_raw, + .read_avail = ds1803_read_avail, }; -static int ds1803_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds1803_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct ds1803_data *data; @@ -124,11 +216,13 @@ static int ds1803_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; - data->cfg = &ds1803_cfg[id->driver_data]; + data->cfg = device_get_match_data(dev); + if (!data->cfg) + data->cfg = &ds1803_cfg[id->driver_data]; indio_dev->info = &ds1803_info; - indio_dev->channels = ds1803_channels; - indio_dev->num_channels = ARRAY_SIZE(ds1803_channels); + indio_dev->channels = data->cfg->channels; + indio_dev->num_channels = data->cfg->num_channels; indio_dev->name = client->name; return devm_iio_device_register(dev, indio_dev); @@ -138,6 +232,7 @@ static const struct of_device_id ds1803_dt_ids[] = { { .compatible = "maxim,ds1803-010", .data = &ds1803_cfg[DS1803_010] }, { .compatible = "maxim,ds1803-050", .data = &ds1803_cfg[DS1803_050] }, { .compatible = "maxim,ds1803-100", .data = &ds1803_cfg[DS1803_100] }, + { .compatible = "maxim,ds3502", .data = &ds1803_cfg[DS3502] }, {} }; MODULE_DEVICE_TABLE(of, ds1803_dt_ids); @@ -146,6 +241,7 @@ static const struct i2c_device_id ds1803_id[] = { { "ds1803-010", DS1803_010 }, { "ds1803-050", DS1803_050 }, { "ds1803-100", DS1803_100 }, + { "ds3502", DS3502 }, {} }; MODULE_DEVICE_TABLE(i2c, ds1803_id); @@ -162,5 +258,6 @@ static struct i2c_driver ds1803_driver = { module_i2c_driver(ds1803_driver); MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>"); +MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); MODULE_DESCRIPTION("DS1803 digital potentiometer"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index fc0d3cfca418..0ff756cea63a 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -194,30 +194,35 @@ config IIO_ST_PRESS tristate "STMicroelectronics pressure sensor Driver" depends on (I2C || SPI_MASTER) && SYSFS select IIO_ST_SENSORS_CORE - select IIO_ST_PRESS_I2C if (I2C) - select IIO_ST_PRESS_SPI if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics pressure sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH. - This driver can also be built as a module. If so, these modules - will be created: - - st_pressure (core functions for the driver [it is mandatory]); - - st_pressure_i2c (necessary for the I2C devices [optional*]); - - st_pressure_spi (necessary for the SPI devices [optional*]); - - (*) one of these is necessary to do something. + Also need to enable at least one of I2C and SPI interface drivers + below. config IIO_ST_PRESS_I2C - tristate - depends on IIO_ST_PRESS - depends on IIO_ST_SENSORS_I2C + tristate "STMicroelectronics pressure sensor I2C Interface" + depends on I2C && IIO_ST_PRESS + default I2C && IIO_ST_PRESS + select IIO_ST_SENSORS_I2C + help + Build support for STMicroelectronics pressure sensor I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_pressure_i2c. config IIO_ST_PRESS_SPI - tristate - depends on IIO_ST_PRESS - depends on IIO_ST_SENSORS_SPI + tristate "STMicroelectronics pressure sensor SPI Interface" + depends on SPI_MASTER && IIO_ST_PRESS + default SPI_MASTER && IIO_ST_PRESS + select IIO_ST_SENSORS_SPI + help + Build support for STMicroelectronics pressure sensor SPI interface. + + To compile this driver as a module, choose M here. The module + will be called st_pressure_spi. config T5403 tristate "EPCOS T5403 digital barometric pressure sensor driver" diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 0730380ceb69..36fb7ae0d0a9 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -812,9 +812,16 @@ static const struct i2c_device_id dps310_id[] = { }; MODULE_DEVICE_TABLE(i2c, dps310_id); +static const struct acpi_device_id dps310_acpi_match[] = { + { "IFX3100" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, dps310_acpi_match); + static struct i2c_driver dps310_driver = { .driver = { .name = DPS310_DEV_NAME, + .acpi_match_table = dps310_acpi_match, }, .probe = dps310_probe, .id_table = dps310_id, diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 81f288312a28..5bf5b9abe6f1 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -187,7 +187,7 @@ int mpl115_probe(struct device *dev, const char *name, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(mpl115_probe); +EXPORT_SYMBOL_NS_GPL(mpl115_probe, IIO_MPL115); MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c index ac1f12bcb65e..099ab1c6832c 100644 --- a/drivers/iio/pressure/mpl115_i2c.c +++ b/drivers/iio/pressure/mpl115_i2c.c @@ -62,3 +62,4 @@ module_i2c_driver(mpl115_i2c_driver); MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_MPL115); diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c index 4d064f98f56a..7feec87e2704 100644 --- a/drivers/iio/pressure/mpl115_spi.c +++ b/drivers/iio/pressure/mpl115_spi.c @@ -101,3 +101,4 @@ module_spi_driver(mpl115_spi_driver); MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_MPL115); diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index e95b9a5475b4..d4f89e4babed 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -301,7 +301,6 @@ static int mpl3115_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int mpl3115_suspend(struct device *dev) { return mpl3115_standby(iio_priv(i2c_get_clientdata( @@ -317,11 +316,8 @@ static int mpl3115_resume(struct device *dev) data->ctrl_reg1); } -static SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume); -#define MPL3115_PM_OPS (&mpl3115_pm_ops) -#else -#define MPL3115_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, + mpl3115_resume); static const struct i2c_device_id mpl3115_id[] = { { "mpl3115", 0 }, @@ -339,7 +335,7 @@ static struct i2c_driver mpl3115_driver = { .driver = { .name = "mpl3115", .of_match_table = mpl3115_of_match, - .pm = MPL3115_PM_OPS, + .pm = pm_sleep_ptr(&mpl3115_pm_ops), }, .probe = mpl3115_probe, .remove = mpl3115_remove, diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index a4d0b54cde9b..717521de66c4 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -471,7 +471,7 @@ err_fini: ms5611_fini(indio_dev); return ret; } -EXPORT_SYMBOL(ms5611_probe); +EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611); void ms5611_remove(struct iio_dev *indio_dev) { @@ -479,7 +479,7 @@ void ms5611_remove(struct iio_dev *indio_dev) iio_triggered_buffer_cleanup(indio_dev); ms5611_fini(indio_dev); } -EXPORT_SYMBOL(ms5611_remove); +EXPORT_SYMBOL_NS(ms5611_remove, IIO_MS5611); MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("MS5611 core driver"); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 1047a85527a9..3b1de71e0d15 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -140,3 +140,4 @@ module_i2c_driver(ms5611_driver); MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("MS5611 i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MS5611); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 7ccd960ced5d..432e912096f4 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -140,3 +140,4 @@ module_spi_driver(ms5611_driver); MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("MS5611 spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MS5611); diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index 81f683321b23..70c70019142a 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -252,3 +252,4 @@ MODULE_DESCRIPTION("Measurement-Specialties ms5637 temperature & pressure driver MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS); diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c index 25dbd5476b26..0dbf357c2c22 100644 --- a/drivers/iio/pressure/st_pressure_buffer.c +++ b/drivers/iio/pressure/st_pressure_buffer.c @@ -7,7 +7,6 @@ * Denis Ciocca <denis.ciocca@st.com> */ -#include <linux/module.h> #include <linux/kernel.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> @@ -44,7 +43,3 @@ int st_press_allocate_ring(struct iio_dev *indio_dev) return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, NULL, &st_sensors_trigger_handler, &st_press_buffer_setup_ops); } - -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics pressures buffer"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 26a1ee43d56e..5b93933a2e27 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -672,7 +672,7 @@ const struct st_sensor_settings *st_press_get_settings(const char *name) return &st_press_sensors_settings[index]; } -EXPORT_SYMBOL(st_press_get_settings); +EXPORT_SYMBOL_NS(st_press_get_settings, IIO_ST_SENSORS); int st_press_common_probe(struct iio_dev *indio_dev) { @@ -724,8 +724,9 @@ int st_press_common_probe(struct iio_dev *indio_dev) return devm_iio_device_register(parent, indio_dev); } -EXPORT_SYMBOL(st_press_common_probe); +EXPORT_SYMBOL_NS(st_press_common_probe, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics pressures driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 1939e999a427..7035777fd988 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -120,3 +120,4 @@ module_i2c_driver(st_press_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics pressures i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index d6fc954e28f8..bfab8e7fb061 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -118,3 +118,4 @@ module_spi_driver(st_press_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics pressures spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 89295c90f801..67119a9b95fc 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -162,7 +162,7 @@ bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg) return false; } } -EXPORT_SYMBOL_GPL(zpa2326_isreg_writeable); +EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_writeable, IIO_ZPA2326); bool zpa2326_isreg_readable(struct device *dev, unsigned int reg) { @@ -191,7 +191,7 @@ bool zpa2326_isreg_readable(struct device *dev, unsigned int reg) return false; } } -EXPORT_SYMBOL_GPL(zpa2326_isreg_readable); +EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_readable, IIO_ZPA2326); bool zpa2326_isreg_precious(struct device *dev, unsigned int reg) { @@ -204,7 +204,7 @@ bool zpa2326_isreg_precious(struct device *dev, unsigned int reg) return false; } } -EXPORT_SYMBOL_GPL(zpa2326_isreg_precious); +EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_precious, IIO_ZPA2326); /** * zpa2326_enable_device() - Enable device, i.e. get out of low power mode. @@ -649,7 +649,7 @@ const struct dev_pm_ops zpa2326_pm_ops = { SET_RUNTIME_PM_OPS(zpa2326_runtime_suspend, zpa2326_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(zpa2326_pm_ops); +EXPORT_SYMBOL_NS_GPL(zpa2326_pm_ops, IIO_ZPA2326); /** * zpa2326_resume() - Request the PM layer to power supply the device. @@ -1698,7 +1698,7 @@ poweroff: return err; } -EXPORT_SYMBOL_GPL(zpa2326_probe); +EXPORT_SYMBOL_NS_GPL(zpa2326_probe, IIO_ZPA2326); void zpa2326_remove(const struct device *parent) { @@ -1709,7 +1709,7 @@ void zpa2326_remove(const struct device *parent) zpa2326_sleep(indio_dev); zpa2326_power_off(indio_dev, iio_priv(indio_dev)); } -EXPORT_SYMBOL_GPL(zpa2326_remove); +EXPORT_SYMBOL_NS_GPL(zpa2326_remove, IIO_ZPA2326); MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>"); MODULE_DESCRIPTION("Core driver for Murata ZPA2326 pressure sensor"); diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c index 95d9739444c4..0db0860d386b 100644 --- a/drivers/iio/pressure/zpa2326_i2c.c +++ b/drivers/iio/pressure/zpa2326_i2c.c @@ -87,3 +87,4 @@ module_i2c_driver(zpa2326_i2c_driver); MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>"); MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ZPA2326); diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c index ee8ed77536ca..9c1bcb82d360 100644 --- a/drivers/iio/pressure/zpa2326_spi.c +++ b/drivers/iio/pressure/zpa2326_spi.c @@ -89,3 +89,4 @@ module_spi_driver(zpa2326_spi_driver); MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>"); MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ZPA2326); diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 7c7203ca3ac6..0e5c17530b8b 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -112,11 +112,17 @@ config SRF04 To compile this driver as a module, choose M here: the module will be called srf04. +config SX_COMMON + tristate + help + Common Semtech proximity sensor code. + config SX9310 tristate "SX9310/SX9311 Semtech proximity sensor" select IIO_BUFFER select IIO_TRIGGERED_BUFFER select REGMAP_I2C + select SX_COMMON depends on I2C help Say Y here to build a driver for Semtech's SX9310/SX9311 capacitive @@ -125,6 +131,34 @@ config SX9310 To compile this driver as a module, choose M here: the module will be called sx9310. +config SX9324 + tristate "SX9324 Semtech proximity sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + select SX_COMMON + depends on I2C + help + Say Y here to build a driver for Semtech's SX9324 + proximity/button sensor. + + To compile this driver as a module, choose M here: the + module will be called sx9324. + +config SX9360 + tristate "SX9360 Semtech proximity sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + select SX_COMMON + depends on I2C + help + Say Y here to build a driver for Semtech's SX9360 + proximity/button sensor, a simplified SX9324. + + To compile this driver as a module, choose M here: the + module will be called sx9360. + config SX9500 tristate "SX9500 Semtech proximity sensor" select IIO_BUFFER diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index cbdac09433eb..cc838bb5408a 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -14,6 +14,9 @@ obj-$(CONFIG_RFD77402) += rfd77402.o obj-$(CONFIG_SRF04) += srf04.o obj-$(CONFIG_SRF08) += srf08.o obj-$(CONFIG_SX9310) += sx9310.o +obj-$(CONFIG_SX9324) += sx9324.o +obj-$(CONFIG_SX9360) += sx9360.o +obj-$(CONFIG_SX_COMMON) += sx_common.o obj-$(CONFIG_SX9500) += sx9500.o obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 51f4f92ae84a..67891ce2bd09 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/workqueue.h> +#include <linux/devm-helpers.h> #include <linux/mutex.h> #include <linux/err.h> #include <linux/irq.h> @@ -122,7 +123,7 @@ static ssize_t as3935_sensor_sensitivity_show(struct device *dev, return ret; val = (val & AS3935_AFE_MASK) >> 1; - return sprintf(buf, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t as3935_sensor_sensitivity_store(struct device *dev, @@ -153,7 +154,7 @@ static ssize_t as3935_noise_level_tripped_show(struct device *dev, int ret; mutex_lock(&st->lock); - ret = sprintf(buf, "%d\n", !time_after(jiffies, st->noise_tripped + HZ)); + ret = sysfs_emit(buf, "%d\n", !time_after(jiffies, st->noise_tripped + HZ)); mutex_unlock(&st->lock); return ret; @@ -295,7 +296,6 @@ static void calibrate_as3935(struct as3935_state *st) as3935_write(st, AS3935_NFLWDTH, st->nflwdth_reg); } -#ifdef CONFIG_PM_SLEEP static int as3935_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -337,20 +337,7 @@ err_resume: return ret; } -static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume); -#define AS3935_PM_OPS (&as3935_pm_ops) - -#else -#define AS3935_PM_OPS NULL -#endif - -static void as3935_stop_work(void *data) -{ - struct iio_dev *indio_dev = data; - struct as3935_state *st = iio_priv(indio_dev); - - cancel_delayed_work_sync(&st->work); -} +static DEFINE_SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume); static int as3935_probe(struct spi_device *spi) { @@ -432,8 +419,7 @@ static int as3935_probe(struct spi_device *spi) calibrate_as3935(st); - INIT_DELAYED_WORK(&st->work, as3935_event_work); - ret = devm_add_action(dev, as3935_stop_work, indio_dev); + ret = devm_delayed_work_autocancel(dev, &st->work, as3935_event_work); if (ret) return ret; @@ -472,7 +458,7 @@ static struct spi_driver as3935_driver = { .driver = { .name = "as3935", .of_match_table = as3935_of_match, - .pm = AS3935_PM_OPS, + .pm = pm_sleep_ptr(&as3935_pm_ops), }, .probe = as3935_probe, .id_table = as3935_id, diff --git a/drivers/iio/proximity/ping.c b/drivers/iio/proximity/ping.c index 1283ac1c2e03..24a97d41e115 100644 --- a/drivers/iio/proximity/ping.c +++ b/drivers/iio/proximity/ping.c @@ -267,8 +267,8 @@ static const struct iio_chan_spec ping_chan_spec[] = { }; static const struct of_device_id of_ping_match[] = { - { .compatible = "parallax,ping", .data = &pa_ping_cfg}, - { .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg}, + { .compatible = "parallax,ping", .data = &pa_ping_cfg }, + { .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg }, {}, }; diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 27026c060ab9..648ae576d6fa 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -338,7 +338,6 @@ static const struct of_device_id lidar_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, lidar_dt_ids); -#ifdef CONFIG_PM static int lidar_pm_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -358,18 +357,16 @@ static int lidar_pm_runtime_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops lidar_pm_ops = { - SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend, - lidar_pm_runtime_resume, NULL) + RUNTIME_PM_OPS(lidar_pm_runtime_suspend, lidar_pm_runtime_resume, NULL) }; static struct i2c_driver lidar_driver = { .driver = { .name = LIDAR_DRV_NAME, .of_match_table = lidar_dt_ids, - .pm = &lidar_pm_ops, + .pm = pm_ptr(&lidar_pm_ops), }, .probe = lidar_probe, .remove = lidar_remove, diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c index 8c06d02139b6..cb80b3c9d073 100644 --- a/drivers/iio/proximity/rfd77402.c +++ b/drivers/iio/proximity/rfd77402.c @@ -295,7 +295,6 @@ static int rfd77402_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int rfd77402_suspend(struct device *dev) { return rfd77402_powerdown(to_i2c_client(dev)); @@ -305,12 +304,12 @@ static int rfd77402_resume(struct device *dev) { return rfd77402_init(to_i2c_client(dev)); } -#endif -static SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, + rfd77402_resume); static const struct i2c_device_id rfd77402_id[] = { - { "rfd77402", 0}, + { "rfd77402", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, rfd77402_id); @@ -318,7 +317,7 @@ MODULE_DEVICE_TABLE(i2c, rfd77402_id); static struct i2c_driver rfd77402_driver = { .driver = { .name = RFD77402_DRV_NAME, - .pm = &rfd77402_pm_ops, + .pm = pm_sleep_ptr(&rfd77402_pm_ops), }, .probe = rfd77402_probe, .id_table = rfd77402_id, diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index fe88b2bb60bc..4e6286765f01 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -235,12 +235,12 @@ static const struct iio_chan_spec srf04_chan_spec[] = { }; static const struct of_device_id of_srf04_match[] = { - { .compatible = "devantech,srf04", .data = &srf04_cfg}, - { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg}, - { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg}, - { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg}, - { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg}, - { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg}, + { .compatible = "devantech,srf04", .data = &srf04_cfg }, + { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg }, + { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg }, + { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg }, + { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg }, + { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg }, {}, }; diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index 9b0886760f76..ac1ab7e89d4e 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -528,9 +528,9 @@ static int srf08_probe(struct i2c_client *client, } static const struct of_device_id of_srf08_match[] = { - { .compatible = "devantech,srf02", (void *)SRF02}, - { .compatible = "devantech,srf08", (void *)SRF08}, - { .compatible = "devantech,srf10", (void *)SRF10}, + { .compatible = "devantech,srf02", (void *)SRF02 }, + { .compatible = "devantech,srf08", (void *)SRF08 }, + { .compatible = "devantech,srf10", (void *)SRF10 }, {}, }; diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index a3fdb59b06d2..ea7318b508ea 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -10,11 +10,10 @@ * and in January 2020 by Daniel Campello <campello@chromium.org>. */ -#include <linux/acpi.h> #include <linux/bitfield.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/irq.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/log2.h> #include <linux/mod_devicetable.h> @@ -22,19 +21,12 @@ #include <linux/pm.h> #include <linux/property.h> #include <linux/regmap.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> - -#include <linux/iio/buffer.h> -#include <linux/iio/events.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> -#include <linux/iio/trigger.h> -#include <linux/iio/triggered_buffer.h> -#include <linux/iio/trigger_consumer.h> + +#include "sx_common.h" /* Register definitions. */ -#define SX9310_REG_IRQ_SRC 0x00 +#define SX9310_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC #define SX9310_REG_STAT0 0x01 #define SX9310_REG_STAT1 0x02 #define SX9310_REG_STAT1_COMPSTAT_MASK GENMASK(3, 0) @@ -135,81 +127,36 @@ #define SX9310_WHOAMI_VALUE 0x01 #define SX9311_WHOAMI_VALUE 0x02 #define SX9310_REG_RESET 0x7f -#define SX9310_SOFT_RESET 0xde /* 4 hardware channels, as defined in STAT0: COMB, CS2, CS1 and CS0. */ #define SX9310_NUM_CHANNELS 4 -static_assert(SX9310_NUM_CHANNELS < BITS_PER_LONG); - -struct sx9310_data { - /* Serialize access to registers and channel configuration */ - struct mutex mutex; - struct i2c_client *client; - struct iio_trigger *trig; - struct regmap *regmap; - struct regulator_bulk_data supplies[2]; - /* - * Last reading of the proximity status for each channel. - * We only send an event to user space when this changes. - */ - unsigned long chan_prox_stat; - bool trigger_enabled; - /* Ensure correct alignment of timestamp when present. */ - struct { - __be16 channels[SX9310_NUM_CHANNELS]; - s64 ts __aligned(8); - } buffer; - /* Remember enabled channels and sample rate during suspend. */ - unsigned int suspend_ctrl0; - struct completion completion; - unsigned long chan_read; - unsigned long chan_event; - unsigned int whoami; -}; - -static const struct iio_event_spec sx9310_events[] = { - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), - }, - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), - }, - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_EITHER, - .mask_separate = BIT(IIO_EV_INFO_ENABLE) | - BIT(IIO_EV_INFO_HYSTERESIS) | - BIT(IIO_EV_INFO_VALUE), - }, -}; - -#define SX9310_NAMED_CHANNEL(idx, name) \ - { \ - .type = IIO_PROXIMITY, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .info_mask_separate_available = \ - BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ - .indexed = 1, \ - .channel = idx, \ - .extend_name = name, \ - .address = SX9310_REG_DIFF_MSB, \ - .event_spec = sx9310_events, \ - .num_event_specs = ARRAY_SIZE(sx9310_events), \ - .scan_index = idx, \ - .scan_type = { \ - .sign = 's', \ - .realbits = 12, \ - .storagebits = 16, \ - .endianness = IIO_BE, \ - }, \ - } +static_assert(SX9310_NUM_CHANNELS <= SX_COMMON_MAX_NUM_CHANNELS); + +#define SX9310_NAMED_CHANNEL(idx, name) \ +{ \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = 1, \ + .channel = idx, \ + .extend_name = name, \ + .address = SX9310_REG_DIFF_MSB, \ + .event_spec = sx_common_events, \ + .num_event_specs = ARRAY_SIZE(sx_common_events), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} #define SX9310_CHANNEL(idx) SX9310_NAMED_CHANNEL(idx, NULL) static const struct iio_chan_spec sx9310_channels[] = { @@ -251,22 +198,6 @@ static const unsigned int sx9310_scan_period_table[] = { 400, 600, 800, 1000, 2000, 3000, 4000, 5000, }; -static ssize_t sx9310_show_samp_freq_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - size_t len = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(sx9310_samp_freq_table); i++) - len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%d ", - sx9310_samp_freq_table[i].val, - sx9310_samp_freq_table[i].val2); - buf[len - 1] = '\n'; - return len; -} -static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sx9310_show_samp_freq_avail); - static const struct regmap_range sx9310_writable_reg_ranges[] = { regmap_reg_range(SX9310_REG_IRQ_MSK, SX9310_REG_IRQ_FUNC), regmap_reg_range(SX9310_REG_PROX_CTRL0, SX9310_REG_PROX_CTRL19), @@ -320,64 +251,7 @@ static const struct regmap_config sx9310_regmap_config = { .volatile_table = &sx9310_volatile_regs, }; -static int sx9310_update_chan_en(struct sx9310_data *data, - unsigned long chan_read, - unsigned long chan_event) -{ - int ret; - unsigned long channels = chan_read | chan_event; - - if ((data->chan_read | data->chan_event) != channels) { - ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL0, - SX9310_REG_PROX_CTRL0_SENSOREN_MASK, - channels); - if (ret) - return ret; - } - data->chan_read = chan_read; - data->chan_event = chan_event; - return 0; -} - -static int sx9310_get_read_channel(struct sx9310_data *data, int channel) -{ - return sx9310_update_chan_en(data, data->chan_read | BIT(channel), - data->chan_event); -} - -static int sx9310_put_read_channel(struct sx9310_data *data, int channel) -{ - return sx9310_update_chan_en(data, data->chan_read & ~BIT(channel), - data->chan_event); -} - -static int sx9310_get_event_channel(struct sx9310_data *data, int channel) -{ - return sx9310_update_chan_en(data, data->chan_read, - data->chan_event | BIT(channel)); -} - -static int sx9310_put_event_channel(struct sx9310_data *data, int channel) -{ - return sx9310_update_chan_en(data, data->chan_read, - data->chan_event & ~BIT(channel)); -} - -static int sx9310_enable_irq(struct sx9310_data *data, unsigned int irq) -{ - if (!data->client->irq) - return 0; - return regmap_update_bits(data->regmap, SX9310_REG_IRQ_MSK, irq, irq); -} - -static int sx9310_disable_irq(struct sx9310_data *data, unsigned int irq) -{ - if (!data->client->irq) - return 0; - return regmap_update_bits(data->regmap, SX9310_REG_IRQ_MSK, irq, 0); -} - -static int sx9310_read_prox_data(struct sx9310_data *data, +static int sx9310_read_prox_data(struct sx_common_data *data, const struct iio_chan_spec *chan, __be16 *val) { int ret; @@ -393,7 +267,7 @@ static int sx9310_read_prox_data(struct sx9310_data *data, * If we have no interrupt support, we have to wait for a scan period * after enabling a channel to get a result. */ -static int sx9310_wait_for_sample(struct sx9310_data *data) +static int sx9310_wait_for_sample(struct sx_common_data *data) { int ret; unsigned int val; @@ -409,66 +283,7 @@ static int sx9310_wait_for_sample(struct sx9310_data *data) return 0; } -static int sx9310_read_proximity(struct sx9310_data *data, - const struct iio_chan_spec *chan, int *val) -{ - int ret; - __be16 rawval; - - mutex_lock(&data->mutex); - - ret = sx9310_get_read_channel(data, chan->channel); - if (ret) - goto out; - - ret = sx9310_enable_irq(data, SX9310_CONVDONE_IRQ); - if (ret) - goto out_put_channel; - - mutex_unlock(&data->mutex); - - if (data->client->irq) { - ret = wait_for_completion_interruptible(&data->completion); - reinit_completion(&data->completion); - } else { - ret = sx9310_wait_for_sample(data); - } - - mutex_lock(&data->mutex); - - if (ret) - goto out_disable_irq; - - ret = sx9310_read_prox_data(data, chan, &rawval); - if (ret) - goto out_disable_irq; - - *val = sign_extend32(be16_to_cpu(rawval), - chan->address == SX9310_REG_DIFF_MSB ? 11 : 15); - - ret = sx9310_disable_irq(data, SX9310_CONVDONE_IRQ); - if (ret) - goto out_put_channel; - - ret = sx9310_put_read_channel(data, chan->channel); - if (ret) - goto out; - - mutex_unlock(&data->mutex); - - return IIO_VAL_INT; - -out_disable_irq: - sx9310_disable_irq(data, SX9310_CONVDONE_IRQ); -out_put_channel: - sx9310_put_read_channel(data, chan->channel); -out: - mutex_unlock(&data->mutex); - - return ret; -} - -static int sx9310_read_gain(struct sx9310_data *data, +static int sx9310_read_gain(struct sx_common_data *data, const struct iio_chan_spec *chan, int *val) { unsigned int regval, gain; @@ -496,7 +311,7 @@ static int sx9310_read_gain(struct sx9310_data *data, return IIO_VAL_INT; } -static int sx9310_read_samp_freq(struct sx9310_data *data, int *val, int *val2) +static int sx9310_read_samp_freq(struct sx_common_data *data, int *val, int *val2) { unsigned int regval; int ret; @@ -516,7 +331,7 @@ static int sx9310_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long mask) { - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(indio_dev); int ret; if (chan->type != IIO_PROXIMITY) @@ -528,7 +343,7 @@ static int sx9310_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - ret = sx9310_read_proximity(data, chan, val); + ret = sx_common_read_proximity(data, chan, val); iio_device_release_direct_mode(indio_dev); return ret; case IIO_CHAN_INFO_HARDWAREGAIN: @@ -562,9 +377,14 @@ static int sx9310_read_avail(struct iio_dev *indio_dev, *length = ARRAY_SIZE(sx9310_gain_vals); *vals = sx9310_gain_vals; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(sx9310_samp_freq_table) * 2; + *vals = (int *)sx9310_samp_freq_table; + return IIO_AVAIL_LIST; + default: + return -EINVAL; } - - return -EINVAL; } static const unsigned int sx9310_pthresh_codes[] = { @@ -581,12 +401,12 @@ static int sx9310_get_thresh_reg(unsigned int channel) case 1: case 2: return SX9310_REG_PROX_CTRL9; + default: + return -EINVAL; } - - return -EINVAL; } -static int sx9310_read_thresh(struct sx9310_data *data, +static int sx9310_read_thresh(struct sx_common_data *data, const struct iio_chan_spec *chan, int *val) { unsigned int reg; @@ -609,7 +429,7 @@ static int sx9310_read_thresh(struct sx9310_data *data, return IIO_VAL_INT; } -static int sx9310_read_hysteresis(struct sx9310_data *data, +static int sx9310_read_hysteresis(struct sx_common_data *data, const struct iio_chan_spec *chan, int *val) { unsigned int regval, pthresh; @@ -633,7 +453,7 @@ static int sx9310_read_hysteresis(struct sx9310_data *data, return IIO_VAL_INT; } -static int sx9310_read_far_debounce(struct sx9310_data *data, int *val) +static int sx9310_read_far_debounce(struct sx_common_data *data, int *val) { unsigned int regval; int ret; @@ -651,7 +471,7 @@ static int sx9310_read_far_debounce(struct sx9310_data *data, int *val) return IIO_VAL_INT; } -static int sx9310_read_close_debounce(struct sx9310_data *data, int *val) +static int sx9310_read_close_debounce(struct sx_common_data *data, int *val) { unsigned int regval; int ret; @@ -675,7 +495,7 @@ static int sx9310_read_event_val(struct iio_dev *indio_dev, enum iio_event_direction dir, enum iio_event_info info, int *val, int *val2) { - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(indio_dev); if (chan->type != IIO_PROXIMITY) return -EINVAL; @@ -699,7 +519,7 @@ static int sx9310_read_event_val(struct iio_dev *indio_dev, } } -static int sx9310_write_thresh(struct sx9310_data *data, +static int sx9310_write_thresh(struct sx_common_data *data, const struct iio_chan_spec *chan, int val) { unsigned int reg; @@ -729,7 +549,7 @@ static int sx9310_write_thresh(struct sx9310_data *data, return ret; } -static int sx9310_write_hysteresis(struct sx9310_data *data, +static int sx9310_write_hysteresis(struct sx_common_data *data, const struct iio_chan_spec *chan, int _val) { unsigned int hyst, val = _val; @@ -759,7 +579,7 @@ static int sx9310_write_hysteresis(struct sx9310_data *data, return ret; } -static int sx9310_write_far_debounce(struct sx9310_data *data, int val) +static int sx9310_write_far_debounce(struct sx_common_data *data, int val) { int ret; unsigned int regval; @@ -780,7 +600,7 @@ static int sx9310_write_far_debounce(struct sx9310_data *data, int val) return ret; } -static int sx9310_write_close_debounce(struct sx9310_data *data, int val) +static int sx9310_write_close_debounce(struct sx_common_data *data, int val) { int ret; unsigned int regval; @@ -807,7 +627,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev, enum iio_event_direction dir, enum iio_event_info info, int val, int val2) { - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(indio_dev); if (chan->type != IIO_PROXIMITY) return -EINVAL; @@ -831,7 +651,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev, } } -static int sx9310_set_samp_freq(struct sx9310_data *data, int val, int val2) +static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2) { int i, ret; @@ -855,8 +675,8 @@ static int sx9310_set_samp_freq(struct sx9310_data *data, int val, int val2) return ret; } -static int sx9310_write_gain(struct sx9310_data *data, - const struct iio_chan_spec *chan, int val) +static int sx9310_write_gain(struct sx_common_data *data, + const struct iio_chan_spec *chan, int val) { unsigned int gain, mask; int ret; @@ -890,7 +710,7 @@ static int sx9310_write_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int val, int val2, long mask) { - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(indio_dev); if (chan->type != IIO_PROXIMITY) return -EINVAL; @@ -900,253 +720,12 @@ static int sx9310_write_raw(struct iio_dev *indio_dev, return sx9310_set_samp_freq(data, val, val2); case IIO_CHAN_INFO_HARDWAREGAIN: return sx9310_write_gain(data, chan, val); + default: + return -EINVAL; } - - return -EINVAL; -} - -static irqreturn_t sx9310_irq_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct sx9310_data *data = iio_priv(indio_dev); - - if (data->trigger_enabled) - iio_trigger_poll(data->trig); - - /* - * Even if no event is enabled, we need to wake the thread to clear the - * interrupt state by reading SX9310_REG_IRQ_SRC. - * It is not possible to do that here because regmap_read takes a mutex. - */ - return IRQ_WAKE_THREAD; -} - -static void sx9310_push_events(struct iio_dev *indio_dev) -{ - int ret; - unsigned int val, chan; - struct sx9310_data *data = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(indio_dev); - unsigned long prox_changed; - - /* Read proximity state on all channels */ - ret = regmap_read(data->regmap, SX9310_REG_STAT0, &val); - if (ret) { - dev_err(&data->client->dev, "i2c transfer error in irq\n"); - return; - } - - /* - * Only iterate over channels with changes on proximity status that have - * events enabled. - */ - prox_changed = (data->chan_prox_stat ^ val) & data->chan_event; - - for_each_set_bit(chan, &prox_changed, SX9310_NUM_CHANNELS) { - int dir; - u64 ev; - - dir = (val & BIT(chan)) ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; - ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan, - IIO_EV_TYPE_THRESH, dir); - - iio_push_event(indio_dev, ev, timestamp); - } - data->chan_prox_stat = val; -} - -static irqreturn_t sx9310_irq_thread_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct sx9310_data *data = iio_priv(indio_dev); - int ret; - unsigned int val; - - mutex_lock(&data->mutex); - - ret = regmap_read(data->regmap, SX9310_REG_IRQ_SRC, &val); - if (ret) { - dev_err(&data->client->dev, "i2c transfer error in irq\n"); - goto out; - } - - if (val & (SX9310_FAR_IRQ | SX9310_CLOSE_IRQ)) - sx9310_push_events(indio_dev); - - if (val & SX9310_CONVDONE_IRQ) - complete(&data->completion); - -out: - mutex_unlock(&data->mutex); - - return IRQ_HANDLED; -} - -static int sx9310_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 sx9310_data *data = iio_priv(indio_dev); - - return !!(data->chan_event & BIT(chan->channel)); -} - -static int sx9310_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 sx9310_data *data = iio_priv(indio_dev); - unsigned int eventirq = SX9310_FAR_IRQ | SX9310_CLOSE_IRQ; - int ret; - - /* If the state hasn't changed, there's nothing to do. */ - if (!!(data->chan_event & BIT(chan->channel)) == state) - return 0; - - mutex_lock(&data->mutex); - if (state) { - ret = sx9310_get_event_channel(data, chan->channel); - if (ret) - goto out_unlock; - if (!(data->chan_event & ~BIT(chan->channel))) { - ret = sx9310_enable_irq(data, eventirq); - if (ret) - sx9310_put_event_channel(data, chan->channel); - } - } else { - ret = sx9310_put_event_channel(data, chan->channel); - if (ret) - goto out_unlock; - if (!data->chan_event) { - ret = sx9310_disable_irq(data, eventirq); - if (ret) - sx9310_get_event_channel(data, chan->channel); - } - } - -out_unlock: - mutex_unlock(&data->mutex); - return ret; -} - -static struct attribute *sx9310_attributes[] = { - &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - NULL -}; - -static const struct attribute_group sx9310_attribute_group = { - .attrs = sx9310_attributes, -}; - -static const struct iio_info sx9310_info = { - .attrs = &sx9310_attribute_group, - .read_raw = sx9310_read_raw, - .read_avail = sx9310_read_avail, - .read_event_value = sx9310_read_event_val, - .write_event_value = sx9310_write_event_val, - .write_raw = sx9310_write_raw, - .read_event_config = sx9310_read_event_config, - .write_event_config = sx9310_write_event_config, -}; - -static int sx9310_set_trigger_state(struct iio_trigger *trig, bool state) -{ - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); - struct sx9310_data *data = iio_priv(indio_dev); - int ret = 0; - - mutex_lock(&data->mutex); - - if (state) - ret = sx9310_enable_irq(data, SX9310_CONVDONE_IRQ); - else if (!data->chan_read) - ret = sx9310_disable_irq(data, SX9310_CONVDONE_IRQ); - if (ret) - goto out; - - data->trigger_enabled = state; - -out: - mutex_unlock(&data->mutex); - - return ret; -} - -static const struct iio_trigger_ops sx9310_trigger_ops = { - .set_trigger_state = sx9310_set_trigger_state, -}; - -static irqreturn_t sx9310_trigger_handler(int irq, void *private) -{ - struct iio_poll_func *pf = private; - struct iio_dev *indio_dev = pf->indio_dev; - struct sx9310_data *data = iio_priv(indio_dev); - __be16 val; - int bit, ret, i = 0; - - mutex_lock(&data->mutex); - - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { - ret = sx9310_read_prox_data(data, &indio_dev->channels[bit], - &val); - if (ret) - goto out; - - data->buffer.channels[i++] = val; - } - - iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, - pf->timestamp); - -out: - mutex_unlock(&data->mutex); - - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static int sx9310_buffer_preenable(struct iio_dev *indio_dev) -{ - struct sx9310_data *data = iio_priv(indio_dev); - unsigned long channels = 0; - int bit, ret; - - mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) - __set_bit(indio_dev->channels[bit].channel, &channels); - - ret = sx9310_update_chan_en(data, channels, data->chan_event); - mutex_unlock(&data->mutex); - return ret; } -static int sx9310_buffer_postdisable(struct iio_dev *indio_dev) -{ - struct sx9310_data *data = iio_priv(indio_dev); - int ret; - - mutex_lock(&data->mutex); - ret = sx9310_update_chan_en(data, 0, data->chan_event); - mutex_unlock(&data->mutex); - return ret; -} - -static const struct iio_buffer_setup_ops sx9310_buffer_setup_ops = { - .preenable = sx9310_buffer_preenable, - .postdisable = sx9310_buffer_postdisable, -}; - -struct sx9310_reg_default { - u8 reg; - u8 def; -}; - -static const struct sx9310_reg_default sx9310_default_regs[] = { +static const struct sx_common_reg_default sx9310_default_regs[] = { { SX9310_REG_IRQ_MSK, 0x00 }, { SX9310_REG_IRQ_FUNC, 0x00 }, /* @@ -1191,7 +770,7 @@ static const struct sx9310_reg_default sx9310_default_regs[] = { /* Activate all channels and perform an initial compensation. */ static int sx9310_init_compensation(struct iio_dev *indio_dev) { - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(indio_dev); int ret; unsigned int val; unsigned int ctrl0; @@ -1209,21 +788,16 @@ static int sx9310_init_compensation(struct iio_dev *indio_dev) ret = regmap_read_poll_timeout(data->regmap, SX9310_REG_STAT1, val, !(val & SX9310_REG_STAT1_COMPSTAT_MASK), 20000, 2000000); - if (ret) { - if (ret == -ETIMEDOUT) - dev_err(&data->client->dev, - "initial compensation timed out: 0x%02x\n", - val); + if (ret) return ret; - } regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0); return ret; } -static const struct sx9310_reg_default * +static const struct sx_common_reg_default * sx9310_get_default_reg(struct device *dev, int idx, - struct sx9310_reg_default *reg_def) + struct sx_common_reg_default *reg_def) { u32 combined[SX9310_NUM_CHANNELS]; u32 start = 0, raw = 0, pos = 0; @@ -1324,47 +898,21 @@ sx9310_get_default_reg(struct device *dev, int idx, return reg_def; } -static int sx9310_init_device(struct iio_dev *indio_dev) +static int sx9310_check_whoami(struct device *dev, + struct iio_dev *indio_dev) { - struct sx9310_data *data = iio_priv(indio_dev); - struct sx9310_reg_default tmp; - const struct sx9310_reg_default *initval; + struct sx_common_data *data = iio_priv(indio_dev); + unsigned int long ddata; + unsigned int whoami; int ret; - unsigned int i, val; - - ret = regmap_write(data->regmap, SX9310_REG_RESET, SX9310_SOFT_RESET); - if (ret) - return ret; - - usleep_range(1000, 2000); /* power-up time is ~1ms. */ - /* Clear reset interrupt state by reading SX9310_REG_IRQ_SRC. */ - ret = regmap_read(data->regmap, SX9310_REG_IRQ_SRC, &val); + ret = regmap_read(data->regmap, SX9310_REG_WHOAMI, &whoami); if (ret) return ret; - /* Program some sane defaults. */ - for (i = 0; i < ARRAY_SIZE(sx9310_default_regs); i++) { - initval = sx9310_get_default_reg(&indio_dev->dev, i, &tmp); - ret = regmap_write(data->regmap, initval->reg, initval->def); - if (ret) - return ret; - } - - return sx9310_init_compensation(indio_dev); -} - -static int sx9310_set_indio_dev_name(struct device *dev, - struct iio_dev *indio_dev, - unsigned int whoami) -{ - unsigned int long ddata; - ddata = (uintptr_t)device_get_match_data(dev); - if (ddata != whoami) { - dev_err(dev, "WHOAMI does not match device data: %u\n", whoami); - return -ENODEV; - } + if (ddata != whoami) + return -EINVAL; switch (whoami) { case SX9310_WHOAMI_VALUE: @@ -1374,115 +922,52 @@ static int sx9310_set_indio_dev_name(struct device *dev, indio_dev->name = "sx9311"; break; default: - dev_err(dev, "unexpected WHOAMI response: %u\n", whoami); return -ENODEV; } return 0; } -static void sx9310_regulator_disable(void *_data) -{ - struct sx9310_data *data = _data; +static const struct sx_common_chip_info sx9310_chip_info = { + .reg_stat = SX9310_REG_STAT0, + .reg_irq_msk = SX9310_REG_IRQ_MSK, + .reg_enable_chan = SX9310_REG_PROX_CTRL0, + .reg_reset = SX9310_REG_RESET, + + .mask_enable_chan = SX9310_REG_STAT1_COMPSTAT_MASK, + .irq_msk_offset = 3, + .num_channels = SX9310_NUM_CHANNELS, + .num_default_regs = ARRAY_SIZE(sx9310_default_regs), + + .ops = { + .read_prox_data = sx9310_read_prox_data, + .check_whoami = sx9310_check_whoami, + .init_compensation = sx9310_init_compensation, + .wait_for_sample = sx9310_wait_for_sample, + .get_default_reg = sx9310_get_default_reg, + }, - regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies); -} + .iio_channels = sx9310_channels, + .num_iio_channels = ARRAY_SIZE(sx9310_channels), + .iio_info = { + .read_raw = sx9310_read_raw, + .read_avail = sx9310_read_avail, + .read_event_value = sx9310_read_event_val, + .write_event_value = sx9310_write_event_val, + .write_raw = sx9310_write_raw, + .read_event_config = sx_common_read_event_config, + .write_event_config = sx_common_write_event_config, + }, +}; static int sx9310_probe(struct i2c_client *client) { - int ret; - struct device *dev = &client->dev; - struct iio_dev *indio_dev; - struct sx9310_data *data; - - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); - if (!indio_dev) - return -ENOMEM; - - data = iio_priv(indio_dev); - data->client = client; - data->supplies[0].supply = "vdd"; - data->supplies[1].supply = "svdd"; - mutex_init(&data->mutex); - init_completion(&data->completion); - - data->regmap = devm_regmap_init_i2c(client, &sx9310_regmap_config); - if (IS_ERR(data->regmap)) - return PTR_ERR(data->regmap); - - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), - data->supplies); - if (ret) - return ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies); - if (ret) - return ret; - /* Must wait for Tpor time after initial power up */ - usleep_range(1000, 1100); - - ret = devm_add_action_or_reset(dev, sx9310_regulator_disable, data); - if (ret) - return ret; - - ret = regmap_read(data->regmap, SX9310_REG_WHOAMI, &data->whoami); - if (ret) { - dev_err(dev, "error in reading WHOAMI register: %d", ret); - return ret; - } - - ret = sx9310_set_indio_dev_name(dev, indio_dev, data->whoami); - if (ret) - return ret; - - ACPI_COMPANION_SET(&indio_dev->dev, ACPI_COMPANION(dev)); - indio_dev->channels = sx9310_channels; - indio_dev->num_channels = ARRAY_SIZE(sx9310_channels); - indio_dev->info = &sx9310_info; - indio_dev->modes = INDIO_DIRECT_MODE; - i2c_set_clientdata(client, indio_dev); - - ret = sx9310_init_device(indio_dev); - if (ret) - return ret; - - if (client->irq) { - ret = devm_request_threaded_irq(dev, client->irq, - sx9310_irq_handler, - sx9310_irq_thread_handler, - IRQF_ONESHOT, - "sx9310_event", indio_dev); - if (ret) - return ret; - - data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); - if (!data->trig) - return -ENOMEM; - - data->trig->ops = &sx9310_trigger_ops; - iio_trigger_set_drvdata(data->trig, indio_dev); - - ret = devm_iio_trigger_register(dev, data->trig); - if (ret) - return ret; - } - - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - sx9310_trigger_handler, - &sx9310_buffer_setup_ops); - if (ret) - return ret; - - return devm_iio_device_register(dev, indio_dev); + return sx_common_probe(client, &sx9310_chip_info, &sx9310_regmap_config); } static int __maybe_unused sx9310_suspend(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); u8 ctrl0; int ret; @@ -1490,11 +975,11 @@ static int __maybe_unused sx9310_suspend(struct device *dev) mutex_lock(&data->mutex); ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0, - &data->suspend_ctrl0); + &data->suspend_ctrl); if (ret) goto out; - ctrl0 = data->suspend_ctrl0 & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK; + ctrl0 = data->suspend_ctrl & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK; ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0); if (ret) goto out; @@ -1508,8 +993,7 @@ out: static int __maybe_unused sx9310_resume(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); - struct sx9310_data *data = iio_priv(indio_dev); + struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; mutex_lock(&data->mutex); @@ -1518,7 +1002,7 @@ static int __maybe_unused sx9310_resume(struct device *dev) goto out; ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, - data->suspend_ctrl0); + data->suspend_ctrl); out: mutex_unlock(&data->mutex); @@ -1529,9 +1013,7 @@ out: return 0; } -static const struct dev_pm_ops sx9310_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sx9310_suspend, sx9310_resume) -}; +static SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume); static const struct acpi_device_id sx9310_acpi_match[] = { { "STH9310", SX9310_WHOAMI_VALUE }, @@ -1577,3 +1059,4 @@ MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); MODULE_AUTHOR("Daniel Campello <campello@chromium.org>"); MODULE_DESCRIPTION("Driver for Semtech SX9310/SX9311 proximity sensor"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(SEMTECH_PROX); diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c new file mode 100644 index 000000000000..0d9bbbb50cb4 --- /dev/null +++ b/drivers/iio/proximity/sx9324.c @@ -0,0 +1,1068 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 Google LLC. + * + * Driver for Semtech's SX9324 capacitive proximity/button solution. + * Based on SX9324 driver and copy of datasheet at: + * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf + */ + +#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> + +#include "sx_common.h" + +/* Register definitions. */ +#define SX9324_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC +#define SX9324_REG_STAT0 0x01 +#define SX9324_REG_STAT1 0x02 +#define SX9324_REG_STAT2 0x03 +#define SX9324_REG_STAT2_COMPSTAT_MASK GENMASK(3, 0) +#define SX9324_REG_STAT3 0x04 +#define SX9324_REG_IRQ_MSK 0x05 +#define SX9324_CONVDONE_IRQ BIT(3) +#define SX9324_FAR_IRQ BIT(5) +#define SX9324_CLOSE_IRQ BIT(6) +#define SX9324_REG_IRQ_CFG0 0x06 +#define SX9324_REG_IRQ_CFG1 0x07 +#define SX9324_REG_IRQ_CFG1_FAILCOND 0x80 +#define SX9324_REG_IRQ_CFG2 0x08 + +#define SX9324_REG_GNRL_CTRL0 0x10 +#define SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK GENMASK(4, 0) +#define SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS 0x16 +#define SX9324_REG_GNRL_CTRL1 0x11 +#define SX9324_REG_GNRL_CTRL1_PHEN_MASK GENMASK(3, 0) +#define SX9324_REG_GNRL_CTRL1_PAUSECTRL 0x20 + +#define SX9324_REG_I2C_ADDR 0x14 +#define SX9324_REG_CLK_SPRD 0x15 + +#define SX9324_REG_AFE_CTRL0 0x20 +#define SX9324_REG_AFE_CTRL1 0x21 +#define SX9324_REG_AFE_CTRL2 0x22 +#define SX9324_REG_AFE_CTRL3 0x23 +#define SX9324_REG_AFE_CTRL4 0x24 +#define SX9324_REG_AFE_CTRL4_FREQ_83_33HZ 0x40 +#define SX9324_REG_AFE_CTRL4_RESOLUTION_MASK GENMASK(2, 0) +#define SX9324_REG_AFE_CTRL4_RES_100 0x04 +#define SX9324_REG_AFE_CTRL5 0x25 +#define SX9324_REG_AFE_CTRL6 0x26 +#define SX9324_REG_AFE_CTRL7 0x27 +#define SX9324_REG_AFE_PH0 0x28 +#define SX9324_REG_AFE_PH0_PIN_MASK(_pin) \ + GENMASK(2 * (_pin) + 1, 2 * (_pin)) + +#define SX9324_REG_AFE_PH1 0x29 +#define SX9324_REG_AFE_PH2 0x2a +#define SX9324_REG_AFE_PH3 0x2b +#define SX9324_REG_AFE_CTRL8 0x2c +#define SX9324_REG_AFE_CTRL8_RESFILTN_4KOHM 0x02 +#define SX9324_REG_AFE_CTRL9 0x2d +#define SX9324_REG_AFE_CTRL9_AGAIN_1 0x08 + +#define SX9324_REG_PROX_CTRL0 0x30 +#define SX9324_REG_PROX_CTRL0_GAIN_MASK GENMASK(5, 3) +#define SX9324_REG_PROX_CTRL0_GAIN_1 0x80 +#define SX9324_REG_PROX_CTRL0_RAWFILT_MASK GENMASK(2, 0) +#define SX9324_REG_PROX_CTRL0_RAWFILT_1P50 0x01 +#define SX9324_REG_PROX_CTRL1 0x31 +#define SX9324_REG_PROX_CTRL2 0x32 +#define SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K 0x20 +#define SX9324_REG_PROX_CTRL3 0x33 +#define SX9324_REG_PROX_CTRL3_AVGDEB_2SAMPLES 0x40 +#define SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K 0x20 +#define SX9324_REG_PROX_CTRL4 0x34 +#define SX9324_REG_PROX_CTRL4_AVGNEGFILT_MASK GENMASK(5, 3) +#define SX9324_REG_PROX_CTRL4_AVGNEG_FILT_2 0x08 +#define SX9324_REG_PROX_CTRL4_AVGPOSFILT_MASK GENMASK(2, 0) +#define SX9324_REG_PROX_CTRL3_AVGPOS_FILT_256 0x04 +#define SX9324_REG_PROX_CTRL5 0x35 +#define SX9324_REG_PROX_CTRL5_HYST_MASK GENMASK(5, 4) +#define SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK GENMASK(3, 2) +#define SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK GENMASK(1, 0) +#define SX9324_REG_PROX_CTRL6 0x36 +#define SX9324_REG_PROX_CTRL6_PROXTHRESH_32 0x08 +#define SX9324_REG_PROX_CTRL7 0x37 + +#define SX9324_REG_ADV_CTRL0 0x40 +#define SX9324_REG_ADV_CTRL1 0x41 +#define SX9324_REG_ADV_CTRL2 0x42 +#define SX9324_REG_ADV_CTRL3 0x43 +#define SX9324_REG_ADV_CTRL4 0x44 +#define SX9324_REG_ADV_CTRL5 0x45 +#define SX9324_REG_ADV_CTRL5_STARTUPSENS_MASK GENMASK(3, 2) +#define SX9324_REG_ADV_CTRL5_STARTUP_SENSOR_1 0x04 +#define SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1 0x01 +#define SX9324_REG_ADV_CTRL6 0x46 +#define SX9324_REG_ADV_CTRL7 0x47 +#define SX9324_REG_ADV_CTRL8 0x48 +#define SX9324_REG_ADV_CTRL9 0x49 +#define SX9324_REG_ADV_CTRL10 0x4a +#define SX9324_REG_ADV_CTRL11 0x4b +#define SX9324_REG_ADV_CTRL12 0x4c +#define SX9324_REG_ADV_CTRL13 0x4d +#define SX9324_REG_ADV_CTRL14 0x4e +#define SX9324_REG_ADV_CTRL15 0x4f +#define SX9324_REG_ADV_CTRL16 0x50 +#define SX9324_REG_ADV_CTRL17 0x51 +#define SX9324_REG_ADV_CTRL18 0x52 +#define SX9324_REG_ADV_CTRL19 0x53 +#define SX9324_REG_ADV_CTRL20 0x54 +#define SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION 0xf0 + +#define SX9324_REG_PHASE_SEL 0x60 + +#define SX9324_REG_USEFUL_MSB 0x61 +#define SX9324_REG_USEFUL_LSB 0x62 + +#define SX9324_REG_AVG_MSB 0x63 +#define SX9324_REG_AVG_LSB 0x64 + +#define SX9324_REG_DIFF_MSB 0x65 +#define SX9324_REG_DIFF_LSB 0x66 + +#define SX9324_REG_OFFSET_MSB 0x67 +#define SX9324_REG_OFFSET_LSB 0x68 + +#define SX9324_REG_SAR_MSB 0x69 +#define SX9324_REG_SAR_LSB 0x6a + +#define SX9324_REG_RESET 0x9f +/* Write this to REG_RESET to do a soft reset. */ +#define SX9324_SOFT_RESET 0xde + +#define SX9324_REG_WHOAMI 0xfa +#define SX9324_WHOAMI_VALUE 0x23 + +#define SX9324_REG_REVISION 0xfe + +/* 4 channels, as defined in STAT0: PH0, PH1, PH2 and PH3. */ +#define SX9324_NUM_CHANNELS 4 +/* 3 CS pins: CS0, CS1, CS2. */ +#define SX9324_NUM_PINS 3 + +static const char * const sx9324_cs_pin_usage[] = { "HZ", "MI", "DS", "GD" }; + +static ssize_t sx9324_phase_configuration_show(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct sx_common_data *data = iio_priv(indio_dev); + unsigned int val; + int i, ret, pin_idx; + size_t len = 0; + + ret = regmap_read(data->regmap, SX9324_REG_AFE_PH0 + chan->channel, &val); + if (ret < 0) + return ret; + + for (i = 0; i < SX9324_NUM_PINS; i++) { + pin_idx = (val & SX9324_REG_AFE_PH0_PIN_MASK(i)) >> (2 * i); + len += sysfs_emit_at(buf, len, "%s,", + sx9324_cs_pin_usage[pin_idx]); + } + buf[len - 1] = '\n'; + return len; +} + +static const struct iio_chan_spec_ext_info sx9324_channel_ext_info[] = { + { + .name = "setup", + .shared = IIO_SEPARATE, + .read = sx9324_phase_configuration_show, + }, + {} +}; + +#define SX9324_CHANNEL(idx) \ +{ \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = 1, \ + .channel = idx, \ + .address = SX9324_REG_DIFF_MSB, \ + .event_spec = sx_common_events, \ + .num_event_specs = ARRAY_SIZE(sx_common_events), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = sx9324_channel_ext_info, \ +} + +static const struct iio_chan_spec sx9324_channels[] = { + SX9324_CHANNEL(0), /* Phase 0 */ + SX9324_CHANNEL(1), /* Phase 1 */ + SX9324_CHANNEL(2), /* Phase 2 */ + SX9324_CHANNEL(3), /* Phase 3 */ + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +/* + * Each entry contains the integer part (val) and the fractional part, in micro + * seconds. It conforms to the IIO output IIO_VAL_INT_PLUS_MICRO. + */ +static const struct { + int val; + int val2; +} sx9324_samp_freq_table[] = { + { 1000, 0 }, /* 00000: Min (no idle time) */ + { 500, 0 }, /* 00001: 2 ms */ + { 250, 0 }, /* 00010: 4 ms */ + { 166, 666666 }, /* 00011: 6 ms */ + { 125, 0 }, /* 00100: 8 ms */ + { 100, 0 }, /* 00101: 10 ms */ + { 71, 428571 }, /* 00110: 14 ms */ + { 55, 555556 }, /* 00111: 18 ms */ + { 45, 454545 }, /* 01000: 22 ms */ + { 38, 461538 }, /* 01001: 26 ms */ + { 33, 333333 }, /* 01010: 30 ms */ + { 29, 411765 }, /* 01011: 34 ms */ + { 26, 315789 }, /* 01100: 38 ms */ + { 23, 809524 }, /* 01101: 42 ms */ + { 21, 739130 }, /* 01110: 46 ms */ + { 20, 0 }, /* 01111: 50 ms */ + { 17, 857143 }, /* 10000: 56 ms */ + { 16, 129032 }, /* 10001: 62 ms */ + { 14, 705882 }, /* 10010: 68 ms */ + { 13, 513514 }, /* 10011: 74 ms */ + { 12, 500000 }, /* 10100: 80 ms */ + { 11, 111111 }, /* 10101: 90 ms */ + { 10, 0 }, /* 10110: 100 ms (Typ.) */ + { 5, 0 }, /* 10111: 200 ms */ + { 3, 333333 }, /* 11000: 300 ms */ + { 2, 500000 }, /* 11001: 400 ms */ + { 1, 666667 }, /* 11010: 600 ms */ + { 1, 250000 }, /* 11011: 800 ms */ + { 1, 0 }, /* 11100: 1 s */ + { 0, 500000 }, /* 11101: 2 s */ + { 0, 333333 }, /* 11110: 3 s */ + { 0, 250000 }, /* 11111: 4 s */ +}; + +static const unsigned int sx9324_scan_period_table[] = { + 2, 15, 30, 45, 60, 90, 120, 200, + 400, 600, 800, 1000, 2000, 3000, 4000, 5000, +}; + +static const struct regmap_range sx9324_writable_reg_ranges[] = { + /* + * To set COMPSTAT for compensation, even if datasheet says register is + * RO. + */ + regmap_reg_range(SX9324_REG_STAT2, SX9324_REG_STAT2), + regmap_reg_range(SX9324_REG_IRQ_MSK, SX9324_REG_IRQ_CFG2), + regmap_reg_range(SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL1), + /* Leave i2c and clock spreading as unavailable */ + regmap_reg_range(SX9324_REG_AFE_CTRL0, SX9324_REG_AFE_CTRL9), + regmap_reg_range(SX9324_REG_PROX_CTRL0, SX9324_REG_PROX_CTRL7), + regmap_reg_range(SX9324_REG_ADV_CTRL0, SX9324_REG_ADV_CTRL20), + regmap_reg_range(SX9324_REG_PHASE_SEL, SX9324_REG_PHASE_SEL), + regmap_reg_range(SX9324_REG_OFFSET_MSB, SX9324_REG_OFFSET_LSB), + regmap_reg_range(SX9324_REG_RESET, SX9324_REG_RESET), +}; + +static const struct regmap_access_table sx9324_writeable_regs = { + .yes_ranges = sx9324_writable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(sx9324_writable_reg_ranges), +}; + +/* + * All allocated registers are readable, so we just list unallocated + * ones. + */ +static const struct regmap_range sx9324_non_readable_reg_ranges[] = { + regmap_reg_range(SX9324_REG_IRQ_CFG2 + 1, SX9324_REG_GNRL_CTRL0 - 1), + regmap_reg_range(SX9324_REG_GNRL_CTRL1 + 1, SX9324_REG_AFE_CTRL0 - 1), + regmap_reg_range(SX9324_REG_AFE_CTRL9 + 1, SX9324_REG_PROX_CTRL0 - 1), + regmap_reg_range(SX9324_REG_PROX_CTRL7 + 1, SX9324_REG_ADV_CTRL0 - 1), + regmap_reg_range(SX9324_REG_ADV_CTRL20 + 1, SX9324_REG_PHASE_SEL - 1), + regmap_reg_range(SX9324_REG_SAR_LSB + 1, SX9324_REG_RESET - 1), + regmap_reg_range(SX9324_REG_RESET + 1, SX9324_REG_WHOAMI - 1), + regmap_reg_range(SX9324_REG_WHOAMI + 1, SX9324_REG_REVISION - 1), +}; + +static const struct regmap_access_table sx9324_readable_regs = { + .no_ranges = sx9324_non_readable_reg_ranges, + .n_no_ranges = ARRAY_SIZE(sx9324_non_readable_reg_ranges), +}; + +static const struct regmap_range sx9324_volatile_reg_ranges[] = { + regmap_reg_range(SX9324_REG_IRQ_SRC, SX9324_REG_STAT3), + regmap_reg_range(SX9324_REG_USEFUL_MSB, SX9324_REG_DIFF_LSB), + regmap_reg_range(SX9324_REG_SAR_MSB, SX9324_REG_SAR_LSB), + regmap_reg_range(SX9324_REG_WHOAMI, SX9324_REG_WHOAMI), + regmap_reg_range(SX9324_REG_REVISION, SX9324_REG_REVISION), +}; + +static const struct regmap_access_table sx9324_volatile_regs = { + .yes_ranges = sx9324_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(sx9324_volatile_reg_ranges), +}; + +static const struct regmap_config sx9324_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SX9324_REG_REVISION, + .cache_type = REGCACHE_RBTREE, + + .wr_table = &sx9324_writeable_regs, + .rd_table = &sx9324_readable_regs, + .volatile_table = &sx9324_volatile_regs, +}; + +static int sx9324_read_prox_data(struct sx_common_data *data, + const struct iio_chan_spec *chan, + __be16 *val) +{ + int ret; + + ret = regmap_write(data->regmap, SX9324_REG_PHASE_SEL, chan->channel); + if (ret < 0) + return ret; + + return regmap_bulk_read(data->regmap, chan->address, val, sizeof(*val)); +} + +/* + * If we have no interrupt support, we have to wait for a scan period + * after enabling a channel to get a result. + */ +static int sx9324_wait_for_sample(struct sx_common_data *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL0, &val); + if (ret < 0) + return ret; + val = FIELD_GET(SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, val); + + msleep(sx9324_scan_period_table[val]); + + return 0; +} + +static int sx9324_read_gain(struct sx_common_data *data, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int reg, regval; + int ret; + + reg = SX9324_REG_PROX_CTRL0 + chan->channel / 2; + ret = regmap_read(data->regmap, reg, ®val); + if (ret) + return ret; + + *val = 1 << FIELD_GET(SX9324_REG_PROX_CTRL0_GAIN_MASK, regval); + + return IIO_VAL_INT; +} + +static int sx9324_read_samp_freq(struct sx_common_data *data, + int *val, int *val2) +{ + int ret; + unsigned int regval; + + ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL0, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, regval); + *val = sx9324_samp_freq_table[regval].val; + *val2 = sx9324_samp_freq_table[regval].val2; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int sx9324_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct sx_common_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = sx_common_read_proximity(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = sx9324_read_gain(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return sx9324_read_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static const int sx9324_gain_vals[] = { 1, 2, 4, 8 }; + +static int sx9324_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sx9324_gain_vals); + *vals = sx9324_gain_vals; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(sx9324_samp_freq_table) * 2; + *vals = (int *)sx9324_samp_freq_table; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int sx9324_set_samp_freq(struct sx_common_data *data, + int val, int val2) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sx9324_samp_freq_table); i++) + if (val == sx9324_samp_freq_table[i].val && + val2 == sx9324_samp_freq_table[i].val2) + break; + + if (i == ARRAY_SIZE(sx9324_samp_freq_table)) + return -EINVAL; + + mutex_lock(&data->mutex); + + ret = regmap_update_bits(data->regmap, + SX9324_REG_GNRL_CTRL0, + SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, i); + + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9324_read_thresh(struct sx_common_data *data, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int regval; + unsigned int reg; + int ret; + + /* + * TODO(gwendal): Depending on the phase function + * (proximity/table/body), retrieve the right threshold. + * For now, return the proximity threshold. + */ + reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2; + ret = regmap_read(data->regmap, reg, ®val); + if (ret) + return ret; + + if (regval <= 1) + *val = regval; + else + *val = (regval * regval) / 2; + + return IIO_VAL_INT; +} + +static int sx9324_read_hysteresis(struct sx_common_data *data, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int regval, pthresh; + int ret; + + ret = sx9324_read_thresh(data, chan, &pthresh); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, SX9324_REG_PROX_CTRL5, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9324_REG_PROX_CTRL5_HYST_MASK, regval); + if (!regval) + *val = 0; + else + *val = pthresh >> (5 - regval); + + return IIO_VAL_INT; +} + +static int sx9324_read_far_debounce(struct sx_common_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, SX9324_REG_PROX_CTRL5, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, regval); + if (regval) + *val = 1 << regval; + else + *val = 0; + + return IIO_VAL_INT; +} + +static int sx9324_read_close_debounce(struct sx_common_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, SX9324_REG_PROX_CTRL5, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, regval); + if (regval) + *val = 1 << regval; + else + *val = 0; + + return IIO_VAL_INT; +} + +static int sx9324_read_event_val(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 sx_common_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return sx9324_read_thresh(data, chan, val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return sx9324_read_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return sx9324_read_close_debounce(data, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return sx9324_read_hysteresis(data, chan, val); + default: + return -EINVAL; + } +} + +static int sx9324_write_thresh(struct sx_common_data *data, + const struct iio_chan_spec *chan, int _val) +{ + unsigned int reg, val = _val; + int ret; + + reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2; + + if (val >= 1) + val = int_sqrt(2 * val); + + if (val > 0xff) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = regmap_write(data->regmap, reg, val); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9324_write_hysteresis(struct sx_common_data *data, + const struct iio_chan_spec *chan, int _val) +{ + unsigned int hyst, val = _val; + int ret, pthresh; + + ret = sx9324_read_thresh(data, chan, &pthresh); + if (ret < 0) + return ret; + + if (val == 0) + hyst = 0; + else if (val >= pthresh >> 2) + hyst = 3; + else if (val >= pthresh >> 3) + hyst = 2; + else if (val >= pthresh >> 4) + hyst = 1; + else + return -EINVAL; + + hyst = FIELD_PREP(SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, + SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9324_write_far_debounce(struct sx_common_data *data, int _val) +{ + unsigned int regval, val = _val; + int ret; + + if (val > 0) + val = ilog2(val); + if (!FIELD_FIT(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, val)) + return -EINVAL; + + regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, val); + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, + SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, + regval); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9324_write_close_debounce(struct sx_common_data *data, int _val) +{ + unsigned int regval, val = _val; + int ret; + + if (val > 0) + val = ilog2(val); + if (!FIELD_FIT(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val)) + return -EINVAL; + + regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val); + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, + SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, + regval); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9324_write_event_val(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 sx_common_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return sx9324_write_thresh(data, chan, val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return sx9324_write_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return sx9324_write_close_debounce(data, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return sx9324_write_hysteresis(data, chan, val); + default: + return -EINVAL; + } +} + +static int sx9324_write_gain(struct sx_common_data *data, + const struct iio_chan_spec *chan, int val) +{ + unsigned int gain, reg; + int ret; + + gain = ilog2(val); + reg = SX9324_REG_PROX_CTRL0 + chan->channel / 2; + gain = FIELD_PREP(SX9324_REG_PROX_CTRL0_GAIN_MASK, gain); + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, reg, + SX9324_REG_PROX_CTRL0_GAIN_MASK, + gain); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9324_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2, + long mask) +{ + struct sx_common_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return sx9324_set_samp_freq(data, val, val2); + case IIO_CHAN_INFO_HARDWAREGAIN: + return sx9324_write_gain(data, chan, val); + default: + return -EINVAL; + } +} + +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 }, + /* + * 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_AFE_CTRL0, 0x00 }, + { SX9324_REG_AFE_CTRL3, 0x00 }, + { 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_CTRL7, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ | + SX9324_REG_AFE_CTRL4_RES_100 }, + + /* 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_CTRL8, SX9324_REG_AFE_CTRL8_RESFILTN_4KOHM }, + { SX9324_REG_AFE_CTRL9, SX9324_REG_AFE_CTRL9_AGAIN_1 }, + + { SX9324_REG_PROX_CTRL0, SX9324_REG_PROX_CTRL0_GAIN_1 | + SX9324_REG_PROX_CTRL0_RAWFILT_1P50 }, + { SX9324_REG_PROX_CTRL1, SX9324_REG_PROX_CTRL0_GAIN_1 | + SX9324_REG_PROX_CTRL0_RAWFILT_1P50 }, + { SX9324_REG_PROX_CTRL2, SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K }, + { SX9324_REG_PROX_CTRL3, SX9324_REG_PROX_CTRL3_AVGDEB_2SAMPLES | + SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K }, + { SX9324_REG_PROX_CTRL4, SX9324_REG_PROX_CTRL4_AVGNEG_FILT_2 | + SX9324_REG_PROX_CTRL3_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_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 }, + /* Body/Table threshold */ + { SX9324_REG_ADV_CTRL10, 0x00 }, + { SX9324_REG_ADV_CTRL11, 0x00 }, + { SX9324_REG_ADV_CTRL12, 0x00 }, + /* 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 }, +}; + +/* Activate all channels and perform an initial compensation. */ +static int sx9324_init_compensation(struct iio_dev *indio_dev) +{ + struct sx_common_data *data = iio_priv(indio_dev); + unsigned int val; + int ret; + + /* run the compensation phase on all channels */ + ret = regmap_update_bits(data->regmap, SX9324_REG_STAT2, + SX9324_REG_STAT2_COMPSTAT_MASK, + SX9324_REG_STAT2_COMPSTAT_MASK); + if (ret) + return ret; + + return regmap_read_poll_timeout(data->regmap, SX9324_REG_STAT2, val, + !(val & SX9324_REG_STAT2_COMPSTAT_MASK), + 20000, 2000000); +} + +static const struct sx_common_reg_default * +sx9324_get_default_reg(struct device *dev, int idx, + struct sx_common_reg_default *reg_def) +{ +#define SX9324_PIN_DEF "semtech,ph0-pin" +#define SX9324_RESOLUTION_DEF "semtech,ph01-resolution" +#define SX9324_PROXRAW_DEF "semtech,ph01-proxraw-strength" + unsigned int pin_defs[SX9324_NUM_PINS]; + char prop[] = SX9324_PROXRAW_DEF; + u32 start = 0, raw = 0, pos = 0; + int ret, count, ph, pin; + + memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*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); + + count = device_property_count_u32(dev, prop); + if (count != ARRAY_SIZE(pin_defs)) + break; + ret = device_property_read_u32_array(dev, prop, pin_defs, + ARRAY_SIZE(pin_defs)); + for (pin = 0; pin < SX9324_NUM_PINS; pin++) + raw |= (pin_defs[pin] << (2 * pin)) & + SX9324_REG_AFE_PH0_PIN_MASK(pin); + reg_def->def = raw; + break; + case SX9324_REG_AFE_CTRL4: + case SX9324_REG_AFE_CTRL7: + if (reg_def->reg == SX9324_REG_AFE_CTRL4) + strncpy(prop, "semtech,ph01-resolution", + ARRAY_SIZE(prop)); + else + strncpy(prop, "semtech,ph23-resolution", + ARRAY_SIZE(prop)); + + ret = device_property_read_u32(dev, prop, &raw); + if (ret) + break; + + raw = ilog2(raw) - 3; + + reg_def->def &= ~SX9324_REG_AFE_CTRL4_RESOLUTION_MASK; + reg_def->def |= FIELD_PREP(SX9324_REG_AFE_CTRL4_RESOLUTION_MASK, + raw); + break; + case SX9324_REG_ADV_CTRL5: + ret = device_property_read_u32(dev, "semtech,startup-sensor", + &start); + if (ret) + break; + + reg_def->def &= ~SX9324_REG_ADV_CTRL5_STARTUPSENS_MASK; + reg_def->def |= FIELD_PREP(SX9324_REG_ADV_CTRL5_STARTUPSENS_MASK, + start); + break; + case SX9324_REG_PROX_CTRL4: + ret = device_property_read_u32(dev, "semtech,avg-pos-strength", + &pos); + if (ret) + break; + + /* Powers of 2, except for a gap between 16 and 64 */ + raw = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3); + + reg_def->def &= ~SX9324_REG_PROX_CTRL4_AVGPOSFILT_MASK; + reg_def->def |= FIELD_PREP(SX9324_REG_PROX_CTRL4_AVGPOSFILT_MASK, + raw); + break; + case SX9324_REG_PROX_CTRL0: + case SX9324_REG_PROX_CTRL1: + if (reg_def->reg == SX9324_REG_PROX_CTRL0) + strncpy(prop, "semtech,ph01-proxraw-strength", + ARRAY_SIZE(prop)); + else + strncpy(prop, "semtech,ph23-proxraw-strength", + ARRAY_SIZE(prop)); + ret = device_property_read_u32(dev, prop, &raw); + if (ret) + break; + + reg_def->def &= ~SX9324_REG_PROX_CTRL0_RAWFILT_MASK; + reg_def->def |= FIELD_PREP(SX9324_REG_PROX_CTRL0_RAWFILT_MASK, + raw); + break; + } + return reg_def; +} + +static int sx9324_check_whoami(struct device *dev, + struct iio_dev *indio_dev) +{ + /* + * Only one sensor for this driver. Assuming the device tree + * is correct, just set the sensor name. + */ + indio_dev->name = "sx9324"; + return 0; +} + +static const struct sx_common_chip_info sx9324_chip_info = { + .reg_stat = SX9324_REG_STAT0, + .reg_irq_msk = SX9324_REG_IRQ_MSK, + .reg_enable_chan = SX9324_REG_GNRL_CTRL1, + .reg_reset = SX9324_REG_RESET, + + .mask_enable_chan = SX9324_REG_GNRL_CTRL1_PHEN_MASK, + .irq_msk_offset = 3, + .num_channels = SX9324_NUM_CHANNELS, + .num_default_regs = ARRAY_SIZE(sx9324_default_regs), + + .ops = { + .read_prox_data = sx9324_read_prox_data, + .check_whoami = sx9324_check_whoami, + .init_compensation = sx9324_init_compensation, + .wait_for_sample = sx9324_wait_for_sample, + .get_default_reg = sx9324_get_default_reg, + }, + + .iio_channels = sx9324_channels, + .num_iio_channels = ARRAY_SIZE(sx9324_channels), + .iio_info = { + .read_raw = sx9324_read_raw, + .read_avail = sx9324_read_avail, + .read_event_value = sx9324_read_event_val, + .write_event_value = sx9324_write_event_val, + .write_raw = sx9324_write_raw, + .read_event_config = sx_common_read_event_config, + .write_event_config = sx_common_write_event_config, + }, +}; + +static int sx9324_probe(struct i2c_client *client) +{ + return sx_common_probe(client, &sx9324_chip_info, &sx9324_regmap_config); +} + +static int __maybe_unused sx9324_suspend(struct device *dev) +{ + struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); + unsigned int regval; + int ret; + + disable_irq_nosync(data->client->irq); + + mutex_lock(&data->mutex); + ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL1, ®val); + + data->suspend_ctrl = + FIELD_GET(SX9324_REG_GNRL_CTRL1_PHEN_MASK, regval); + + if (ret < 0) + goto out; + + /* Disable all phases, send the device to sleep. */ + ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0); + +out: + mutex_unlock(&data->mutex); + return ret; +} + +static int __maybe_unused sx9324_resume(struct device *dev) +{ + struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + mutex_lock(&data->mutex); + ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, + data->suspend_ctrl | SX9324_REG_GNRL_CTRL1_PAUSECTRL); + mutex_unlock(&data->mutex); + if (ret) + return ret; + + enable_irq(data->client->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume); + +static const struct acpi_device_id sx9324_acpi_match[] = { + { "STH9324", SX9324_WHOAMI_VALUE }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sx9324_acpi_match); + +static const struct of_device_id sx9324_of_match[] = { + { .compatible = "semtech,sx9324", (void *)SX9324_WHOAMI_VALUE }, + { } +}; +MODULE_DEVICE_TABLE(of, sx9324_of_match); + +static const struct i2c_device_id sx9324_id[] = { + { "sx9324", SX9324_WHOAMI_VALUE }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx9324_id); + +static struct i2c_driver sx9324_driver = { + .driver = { + .name = "sx9324", + .acpi_match_table = sx9324_acpi_match, + .of_match_table = sx9324_of_match, + .pm = &sx9324_pm_ops, + + /* + * Lots of i2c transfers in probe + over 200 ms waiting in + * sx9324_init_compensation() mean a slow probe; prefer async + * so we don't delay boot if we're builtin to the kernel. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe_new = sx9324_probe, + .id_table = sx9324_id, +}; +module_i2c_driver(sx9324_driver); + +MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); +MODULE_DESCRIPTION("Driver for Semtech SX9324 proximity sensor"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(SEMTECH_PROX); diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c new file mode 100644 index 000000000000..3ebb30c8a4f6 --- /dev/null +++ b/drivers/iio/proximity/sx9360.c @@ -0,0 +1,893 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 Google LLC. + * + * Driver for Semtech's SX9360 capacitive proximity/button solution. + * Based on SX9360 driver and copy of datasheet at: + * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf + */ + +#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> + +#include "sx_common.h" + +/* Nominal Oscillator Frequency. */ +#define SX9360_FOSC_MHZ 4 +#define SX9360_FOSC_HZ (SX9360_FOSC_MHZ * 1000000) + +/* Register definitions. */ +#define SX9360_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC +#define SX9360_REG_STAT 0x01 +#define SX9360_REG_STAT_COMPSTAT_MASK GENMASK(2, 1) +#define SX9360_REG_IRQ_MSK 0x02 +#define SX9360_CONVDONE_IRQ BIT(0) +#define SX9360_FAR_IRQ BIT(2) +#define SX9360_CLOSE_IRQ BIT(3) +#define SX9360_REG_IRQ_CFG 0x03 + +#define SX9360_REG_GNRL_CTRL0 0x10 +#define SX9360_REG_GNRL_CTRL0_PHEN_MASK GENMASK(1, 0) +#define SX9360_REG_GNRL_CTRL1 0x11 +#define SX9360_REG_GNRL_CTRL1_SCANPERIOD_MASK GENMASK(2, 0) +#define SX9360_REG_GNRL_CTRL2 0x12 +#define SX9360_REG_GNRL_CTRL2_PERIOD_102MS 0x32 +#define SX9360_REG_GNRL_REG_2_PERIOD_MS(_r) \ + (((_r) * 8192) / (SX9360_FOSC_HZ / 1000)) +#define SX9360_REG_GNRL_FREQ_2_REG(_f) (((_f) * 8192) / SX9360_FOSC_HZ) +#define SX9360_REG_GNRL_REG_2_FREQ(_r) (SX9360_FOSC_HZ / ((_r) * 8192)) + +#define SX9360_REG_AFE_CTRL1 0x21 +#define SX9360_REG_AFE_PARAM0_PHR 0x22 +#define SX9360_REG_AFE_PARAM1_PHR 0x23 +#define SX9360_REG_AFE_PARAM0_PHM 0x24 +#define SX9360_REG_AFE_PARAM0_RSVD 0x08 +#define SX9360_REG_AFE_PARAM0_RESOLUTION_MASK GENMASK(2, 0) +#define SX9360_REG_AFE_PARAM0_RESOLUTION_128 0x02 +#define SX9360_REG_AFE_PARAM1_PHM 0x25 +#define SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF 0x40 +#define SX9360_REG_AFE_PARAM1_FREQ_83_33HZ 0x06 + +#define SX9360_REG_PROX_CTRL0_PHR 0x40 +#define SX9360_REG_PROX_CTRL0_PHM 0x41 +#define SX9360_REG_PROX_CTRL0_GAIN_MASK GENMASK(5, 3) +#define SX9360_REG_PROX_CTRL0_GAIN_1 0x80 +#define SX9360_REG_PROX_CTRL0_RAWFILT_MASK GENMASK(2, 0) +#define SX9360_REG_PROX_CTRL0_RAWFILT_1P50 0x01 +#define SX9360_REG_PROX_CTRL1 0x42 +#define SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_MASK GENMASK(5, 3) +#define SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K 0x20 +#define SX9360_REG_PROX_CTRL2 0x43 +#define SX9360_REG_PROX_CTRL2_AVGDEB_MASK GENMASK(7, 6) +#define SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES 0x40 +#define SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K 0x20 +#define SX9360_REG_PROX_CTRL3 0x44 +#define SX9360_REG_PROX_CTRL3_AVGNEG_FILT_MASK GENMASK(5, 3) +#define SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 0x08 +#define SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK GENMASK(2, 0) +#define SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256 0x04 +#define SX9360_REG_PROX_CTRL4 0x45 +#define SX9360_REG_PROX_CTRL4_HYST_MASK GENMASK(5, 4) +#define SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK GENMASK(3, 2) +#define SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK GENMASK(1, 0) +#define SX9360_REG_PROX_CTRL5 0x46 +#define SX9360_REG_PROX_CTRL5_PROXTHRESH_32 0x08 + +#define SX9360_REG_REF_CORR0 0x60 +#define SX9360_REG_REF_CORR1 0x61 + +#define SX9360_REG_USEFUL_PHR_MSB 0x90 +#define SX9360_REG_USEFUL_PHR_LSB 0x91 + +#define SX9360_REG_OFFSET_PMR_MSB 0x92 +#define SX9360_REG_OFFSET_PMR_LSB 0x93 + +#define SX9360_REG_USEFUL_PHM_MSB 0x94 +#define SX9360_REG_USEFUL_PHM_LSB 0x95 + +#define SX9360_REG_AVG_PHM_MSB 0x96 +#define SX9360_REG_AVG_PHM_LSB 0x97 + +#define SX9360_REG_DIFF_PHM_MSB 0x98 +#define SX9360_REG_DIFF_PHM_LSB 0x99 + +#define SX9360_REG_OFFSET_PHM_MSB 0x9a +#define SX9360_REG_OFFSET_PHM_LSB 0x9b + +#define SX9360_REG_USE_FILTER_MSB 0x9a +#define SX9360_REG_USE_FILTER_LSB 0x9b + +#define SX9360_REG_RESET 0xcf +/* Write this to REG_RESET to do a soft reset. */ +#define SX9360_SOFT_RESET 0xde + +#define SX9360_REG_WHOAMI 0xfa +#define SX9360_WHOAMI_VALUE 0x60 + +#define SX9360_REG_REVISION 0xfe + +/* 2 channels, Phase Reference and Measurement. */ +#define SX9360_NUM_CHANNELS 2 + +static const struct iio_chan_spec sx9360_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .indexed = 1, + .address = SX9360_REG_USEFUL_PHR_MSB, + .channel = 0, + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .indexed = 1, + .address = SX9360_REG_USEFUL_PHM_MSB, + .event_spec = sx_common_events, + .num_event_specs = ARRAY_SIZE(sx_common_events), + .channel = 1, + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +/* + * Each entry contains the integer part (val) and the fractional part, in micro + * seconds. It conforms to the IIO output IIO_VAL_INT_PLUS_MICRO. + * + * The frequency control register holds the period, with a ~2ms increment. + * Therefore the smallest frequency is 4MHz / (2047 * 8192), + * The fastest is 4MHz / 8192. + * The interval is not linear, but given there is 2047 possible value, + * Returns the fake increment of (Max-Min)/2047 + */ +static const struct { + int val; + int val2; +} sx9360_samp_freq_interval[] = { + { 0, 281250 }, /* 4MHz / (8192 * 2047) */ + { 0, 281250 }, + { 448, 281250 }, /* 4MHz / 8192 */ +}; + +static const struct regmap_range sx9360_writable_reg_ranges[] = { + /* + * To set COMPSTAT for compensation, even if datasheet says register is + * RO. + */ + regmap_reg_range(SX9360_REG_STAT, SX9360_REG_IRQ_CFG), + regmap_reg_range(SX9360_REG_GNRL_CTRL0, SX9360_REG_GNRL_CTRL2), + regmap_reg_range(SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_PARAM1_PHM), + regmap_reg_range(SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL5), + regmap_reg_range(SX9360_REG_REF_CORR0, SX9360_REG_REF_CORR1), + regmap_reg_range(SX9360_REG_OFFSET_PMR_MSB, SX9360_REG_OFFSET_PMR_LSB), + regmap_reg_range(SX9360_REG_RESET, SX9360_REG_RESET), +}; + +static const struct regmap_access_table sx9360_writeable_regs = { + .yes_ranges = sx9360_writable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(sx9360_writable_reg_ranges), +}; + +/* + * All allocated registers are readable, so we just list unallocated + * ones. + */ +static const struct regmap_range sx9360_non_readable_reg_ranges[] = { + regmap_reg_range(SX9360_REG_IRQ_CFG + 1, SX9360_REG_GNRL_CTRL0 - 1), + regmap_reg_range(SX9360_REG_GNRL_CTRL2 + 1, SX9360_REG_AFE_CTRL1 - 1), + regmap_reg_range(SX9360_REG_AFE_PARAM1_PHM + 1, + SX9360_REG_PROX_CTRL0_PHR - 1), + regmap_reg_range(SX9360_REG_PROX_CTRL5 + 1, SX9360_REG_REF_CORR0 - 1), + regmap_reg_range(SX9360_REG_REF_CORR1 + 1, + SX9360_REG_USEFUL_PHR_MSB - 1), + regmap_reg_range(SX9360_REG_USE_FILTER_LSB + 1, SX9360_REG_RESET - 1), + regmap_reg_range(SX9360_REG_RESET + 1, SX9360_REG_WHOAMI - 1), + regmap_reg_range(SX9360_REG_WHOAMI + 1, SX9360_REG_REVISION - 1), +}; + +static const struct regmap_access_table sx9360_readable_regs = { + .no_ranges = sx9360_non_readable_reg_ranges, + .n_no_ranges = ARRAY_SIZE(sx9360_non_readable_reg_ranges), +}; + +static const struct regmap_range sx9360_volatile_reg_ranges[] = { + regmap_reg_range(SX9360_REG_IRQ_SRC, SX9360_REG_STAT), + regmap_reg_range(SX9360_REG_USEFUL_PHR_MSB, SX9360_REG_USE_FILTER_LSB), + regmap_reg_range(SX9360_REG_WHOAMI, SX9360_REG_WHOAMI), + regmap_reg_range(SX9360_REG_REVISION, SX9360_REG_REVISION), +}; + +static const struct regmap_access_table sx9360_volatile_regs = { + .yes_ranges = sx9360_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(sx9360_volatile_reg_ranges), +}; + +static const struct regmap_config sx9360_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SX9360_REG_REVISION, + .cache_type = REGCACHE_RBTREE, + + .wr_table = &sx9360_writeable_regs, + .rd_table = &sx9360_readable_regs, + .volatile_table = &sx9360_volatile_regs, +}; + +static int sx9360_read_prox_data(struct sx_common_data *data, + const struct iio_chan_spec *chan, + __be16 *val) +{ + return regmap_bulk_read(data->regmap, chan->address, val, sizeof(*val)); +} + +/* + * If we have no interrupt support, we have to wait for a scan period + * after enabling a channel to get a result. + */ +static int sx9360_wait_for_sample(struct sx_common_data *data) +{ + int ret; + __be16 buf; + + ret = regmap_bulk_read(data->regmap, SX9360_REG_GNRL_CTRL1, + &buf, sizeof(buf)); + if (ret < 0) + return ret; + msleep(SX9360_REG_GNRL_REG_2_PERIOD_MS(be16_to_cpu(buf))); + + return 0; +} + +static int sx9360_read_gain(struct sx_common_data *data, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int reg, regval; + int ret; + + reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel; + ret = regmap_read(data->regmap, reg, ®val); + if (ret) + return ret; + + *val = 1 << FIELD_GET(SX9360_REG_PROX_CTRL0_GAIN_MASK, regval); + + return IIO_VAL_INT; +} + +static int sx9360_read_samp_freq(struct sx_common_data *data, + int *val, int *val2) +{ + int ret, divisor; + __be16 buf; + + ret = regmap_bulk_read(data->regmap, SX9360_REG_GNRL_CTRL1, + &buf, sizeof(buf)); + if (ret < 0) + return ret; + divisor = be16_to_cpu(buf); + if (divisor == 0) { + *val = 0; + return IIO_VAL_INT; + } + + *val = SX9360_FOSC_HZ; + *val2 = divisor * 8192; + + return IIO_VAL_FRACTIONAL; +} + +static int sx9360_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct sx_common_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = sx_common_read_proximity(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = sx9360_read_gain(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return sx9360_read_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static const char *sx9360_channel_labels[SX9360_NUM_CHANNELS] = { + "reference", "main", +}; + +static int sx9360_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", sx9360_channel_labels[chan->channel]); +} + +static const int sx9360_gain_vals[] = { 1, 2, 4, 8 }; + +static int sx9360_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sx9360_gain_vals); + *vals = sx9360_gain_vals; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(sx9360_samp_freq_interval) * 2; + *vals = (int *)sx9360_samp_freq_interval; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int sx9360_set_samp_freq(struct sx_common_data *data, + int val, int val2) +{ + int ret, reg; + __be16 buf; + + reg = val * 8192 / SX9360_FOSC_HZ + val2 * 8192 / (SX9360_FOSC_MHZ); + buf = cpu_to_be16(reg); + mutex_lock(&data->mutex); + + ret = regmap_bulk_write(data->regmap, SX9360_REG_GNRL_CTRL1, &buf, + sizeof(buf)); + + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9360_read_thresh(struct sx_common_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL5, ®val); + if (ret) + return ret; + + if (regval <= 1) + *val = regval; + else + *val = (regval * regval) / 2; + + return IIO_VAL_INT; +} + +static int sx9360_read_hysteresis(struct sx_common_data *data, int *val) +{ + unsigned int regval, pthresh; + int ret; + + ret = sx9360_read_thresh(data, &pthresh); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL4, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9360_REG_PROX_CTRL4_HYST_MASK, regval); + if (!regval) + *val = 0; + else + *val = pthresh >> (5 - regval); + + return IIO_VAL_INT; +} + +static int sx9360_read_far_debounce(struct sx_common_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL4, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, regval); + if (regval) + *val = 1 << regval; + else + *val = 0; + + return IIO_VAL_INT; +} + +static int sx9360_read_close_debounce(struct sx_common_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL4, ®val); + if (ret) + return ret; + + regval = FIELD_GET(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, regval); + if (regval) + *val = 1 << regval; + else + *val = 0; + + return IIO_VAL_INT; +} + +static int sx9360_read_event_val(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 sx_common_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return sx9360_read_thresh(data, val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return sx9360_read_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return sx9360_read_close_debounce(data, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return sx9360_read_hysteresis(data, val); + default: + return -EINVAL; + } +} + +static int sx9360_write_thresh(struct sx_common_data *data, int _val) +{ + unsigned int val = _val; + int ret; + + if (val >= 1) + val = int_sqrt(2 * val); + + if (val > 0xff) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = regmap_write(data->regmap, SX9360_REG_PROX_CTRL5, val); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9360_write_hysteresis(struct sx_common_data *data, int _val) +{ + unsigned int hyst, val = _val; + int ret, pthresh; + + ret = sx9360_read_thresh(data, &pthresh); + if (ret < 0) + return ret; + + if (val == 0) + hyst = 0; + else if (val >= pthresh >> 2) + hyst = 3; + else if (val >= pthresh >> 3) + hyst = 2; + else if (val >= pthresh >> 4) + hyst = 1; + else + return -EINVAL; + + hyst = FIELD_PREP(SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, + SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9360_write_far_debounce(struct sx_common_data *data, int _val) +{ + unsigned int regval, val = _val; + int ret; + + if (val > 0) + val = ilog2(val); + if (!FIELD_FIT(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val)) + return -EINVAL; + + regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val); + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, + SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, + regval); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9360_write_close_debounce(struct sx_common_data *data, int _val) +{ + unsigned int regval, val = _val; + int ret; + + if (val > 0) + val = ilog2(val); + if (!FIELD_FIT(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val)) + return -EINVAL; + + regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val); + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, + SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, + regval); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9360_write_event_val(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 sx_common_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return sx9360_write_thresh(data, val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return sx9360_write_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return sx9360_write_close_debounce(data, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return sx9360_write_hysteresis(data, val); + default: + return -EINVAL; + } +} + +static int sx9360_write_gain(struct sx_common_data *data, + const struct iio_chan_spec *chan, int val) +{ + unsigned int gain, reg; + int ret; + + gain = ilog2(val); + reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel; + gain = FIELD_PREP(SX9360_REG_PROX_CTRL0_GAIN_MASK, gain); + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, reg, + SX9360_REG_PROX_CTRL0_GAIN_MASK, + gain); + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9360_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2, + long mask) +{ + struct sx_common_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return sx9360_set_samp_freq(data, val, val2); + case IIO_CHAN_INFO_HARDWAREGAIN: + return sx9360_write_gain(data, chan, val); + default: + return -EINVAL; + } +} + +static const struct sx_common_reg_default sx9360_default_regs[] = { + { SX9360_REG_IRQ_MSK, 0x00 }, + { SX9360_REG_IRQ_CFG, 0x00 }, + /* + * 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_AFE_CTRL1, 0x00 }, + { SX9360_REG_AFE_PARAM0_PHR, SX9360_REG_AFE_PARAM0_RSVD | + SX9360_REG_AFE_PARAM0_RESOLUTION_128 }, + { SX9360_REG_AFE_PARAM1_PHR, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | + SX9360_REG_AFE_PARAM1_FREQ_83_33HZ }, + { SX9360_REG_AFE_PARAM0_PHM, SX9360_REG_AFE_PARAM0_RSVD | + SX9360_REG_AFE_PARAM0_RESOLUTION_128 }, + { SX9360_REG_AFE_PARAM1_PHM, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | + SX9360_REG_AFE_PARAM1_FREQ_83_33HZ }, + + { SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL0_GAIN_1 | + SX9360_REG_PROX_CTRL0_RAWFILT_1P50 }, + { 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_CTRL2, SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES | + SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K }, + { 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 }, +}; + +/* Activate all channels and perform an initial compensation. */ +static int sx9360_init_compensation(struct iio_dev *indio_dev) +{ + struct sx_common_data *data = iio_priv(indio_dev); + unsigned int val; + int ret; + + /* run the compensation phase on all channels */ + ret = regmap_update_bits(data->regmap, SX9360_REG_STAT, + SX9360_REG_STAT_COMPSTAT_MASK, + SX9360_REG_STAT_COMPSTAT_MASK); + if (ret) + return ret; + + return regmap_read_poll_timeout(data->regmap, SX9360_REG_STAT, val, + !(val & SX9360_REG_STAT_COMPSTAT_MASK), + 20000, 2000000); +} + +static const struct sx_common_reg_default * +sx9360_get_default_reg(struct device *dev, int idx, + struct sx_common_reg_default *reg_def) +{ + u32 raw = 0, pos = 0; + int ret; + + memcpy(reg_def, &sx9360_default_regs[idx], sizeof(*reg_def)); + switch (reg_def->reg) { + case SX9360_REG_AFE_PARAM0_PHR: + case SX9360_REG_AFE_PARAM0_PHM: + ret = device_property_read_u32(dev, "semtech,resolution", &raw); + if (ret) + break; + + raw = ilog2(raw) - 3; + + reg_def->def &= ~SX9360_REG_AFE_PARAM0_RESOLUTION_MASK; + reg_def->def |= FIELD_PREP(SX9360_REG_AFE_PARAM0_RESOLUTION_MASK, raw); + break; + case SX9360_REG_PROX_CTRL0_PHR: + case SX9360_REG_PROX_CTRL0_PHM: + ret = device_property_read_u32(dev, "semtech,proxraw-strength", &raw); + if (ret) + break; + + reg_def->def &= ~SX9360_REG_PROX_CTRL0_RAWFILT_MASK; + reg_def->def |= FIELD_PREP(SX9360_REG_PROX_CTRL0_RAWFILT_MASK, raw); + break; + case SX9360_REG_PROX_CTRL3: + ret = device_property_read_u32(dev, "semtech,avg-pos-strength", + &pos); + if (ret) + break; + + /* Powers of 2, except for a gap between 16 and 64 */ + raw = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3); + reg_def->def &= ~SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK; + reg_def->def |= FIELD_PREP(SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK, raw); + break; + } + + return reg_def; +} + +static int sx9360_check_whoami(struct device *dev, struct iio_dev *indio_dev) +{ + /* + * Only one sensor for this driver. Assuming the device tree + * is correct, just set the sensor name. + */ + indio_dev->name = "sx9360"; + return 0; +} + +static const struct sx_common_chip_info sx9360_chip_info = { + .reg_stat = SX9360_REG_STAT, + .reg_irq_msk = SX9360_REG_IRQ_MSK, + .reg_enable_chan = SX9360_REG_GNRL_CTRL0, + .reg_reset = SX9360_REG_RESET, + + .mask_enable_chan = SX9360_REG_GNRL_CTRL0_PHEN_MASK, + .stat_offset = 2, + .num_channels = SX9360_NUM_CHANNELS, + .num_default_regs = ARRAY_SIZE(sx9360_default_regs), + + .ops = { + .read_prox_data = sx9360_read_prox_data, + .check_whoami = sx9360_check_whoami, + .init_compensation = sx9360_init_compensation, + .wait_for_sample = sx9360_wait_for_sample, + .get_default_reg = sx9360_get_default_reg, + }, + + .iio_channels = sx9360_channels, + .num_iio_channels = ARRAY_SIZE(sx9360_channels), + .iio_info = { + .read_raw = sx9360_read_raw, + .read_avail = sx9360_read_avail, + .read_label = sx9360_read_label, + .read_event_value = sx9360_read_event_val, + .write_event_value = sx9360_write_event_val, + .write_raw = sx9360_write_raw, + .read_event_config = sx_common_read_event_config, + .write_event_config = sx_common_write_event_config, + }, +}; + +static int sx9360_probe(struct i2c_client *client) +{ + return sx_common_probe(client, &sx9360_chip_info, &sx9360_regmap_config); +} + +static int __maybe_unused sx9360_suspend(struct device *dev) +{ + struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); + unsigned int regval; + int ret; + + disable_irq_nosync(data->client->irq); + + mutex_lock(&data->mutex); + ret = regmap_read(data->regmap, SX9360_REG_GNRL_CTRL0, ®val); + + data->suspend_ctrl = + FIELD_GET(SX9360_REG_GNRL_CTRL0_PHEN_MASK, regval); + + if (ret < 0) + goto out; + + /* Disable all phases, send the device to sleep. */ + ret = regmap_write(data->regmap, SX9360_REG_GNRL_CTRL0, 0); + +out: + mutex_unlock(&data->mutex); + return ret; +} + +static int __maybe_unused sx9360_resume(struct device *dev) +{ + struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + mutex_lock(&data->mutex); + ret = regmap_update_bits(data->regmap, SX9360_REG_GNRL_CTRL0, + SX9360_REG_GNRL_CTRL0_PHEN_MASK, + data->suspend_ctrl); + mutex_unlock(&data->mutex); + if (ret) + return ret; + + enable_irq(data->client->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); + +static const struct acpi_device_id sx9360_acpi_match[] = { + { "STH9360", SX9360_WHOAMI_VALUE }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sx9360_acpi_match); + +static const struct of_device_id sx9360_of_match[] = { + { .compatible = "semtech,sx9360", (void *)SX9360_WHOAMI_VALUE }, + { } +}; +MODULE_DEVICE_TABLE(of, sx9360_of_match); + +static const struct i2c_device_id sx9360_id[] = { + {"sx9360", SX9360_WHOAMI_VALUE }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx9360_id); + +static struct i2c_driver sx9360_driver = { + .driver = { + .name = "sx9360", + .acpi_match_table = sx9360_acpi_match, + .of_match_table = sx9360_of_match, + .pm = &sx9360_pm_ops, + + /* + * Lots of i2c transfers in probe + over 200 ms waiting in + * sx9360_init_compensation() mean a slow probe; prefer async + * so we don't delay boot if we're builtin to the kernel. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe_new = sx9360_probe, + .id_table = sx9360_id, +}; +module_i2c_driver(sx9360_driver); + +MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); +MODULE_DESCRIPTION("Driver for Semtech SX9360 proximity sensor"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(SEMTECH_PROX); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 3e4ddb2e8c2b..42589d6200ad 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -993,7 +993,6 @@ static int sx9500_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int sx9500_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1030,11 +1029,8 @@ static int sx9500_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM_SLEEP */ -static const struct dev_pm_ops sx9500_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(sx9500_pm_ops, sx9500_suspend, sx9500_resume); static const struct acpi_device_id sx9500_acpi_match[] = { {"SSX9500", 0}, @@ -1060,7 +1056,7 @@ static struct i2c_driver sx9500_driver = { .name = SX9500_DRIVER_NAME, .acpi_match_table = ACPI_PTR(sx9500_acpi_match), .of_match_table = of_match_ptr(sx9500_of_match), - .pm = &sx9500_pm_ops, + .pm = pm_sleep_ptr(&sx9500_pm_ops), }, .probe = sx9500_probe, .remove = sx9500_remove, diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c new file mode 100644 index 000000000000..a7c07316a0a9 --- /dev/null +++ b/drivers/iio/proximity/sx_common.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 Google LLC. + * + * Common part of most Semtech SAR sensor. + */ + +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/byteorder/generic.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <vdso/bits.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#include "sx_common.h" + +/* All Semtech SAR sensors have IRQ bit in the same order. */ +#define SX_COMMON_CONVDONE_IRQ BIT(0) +#define SX_COMMON_FAR_IRQ BIT(2) +#define SX_COMMON_CLOSE_IRQ BIT(3) + +const struct iio_event_spec sx_common_events[3] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_VALUE), + }, +}; +EXPORT_SYMBOL_NS_GPL(sx_common_events, SEMTECH_PROX); + +static irqreturn_t sx_common_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct sx_common_data *data = iio_priv(indio_dev); + + if (data->trigger_enabled) + iio_trigger_poll(data->trig); + + /* + * Even if no event is enabled, we need to wake the thread to clear the + * interrupt state by reading SX_COMMON_REG_IRQ_SRC. + * It is not possible to do that here because regmap_read takes a mutex. + */ + return IRQ_WAKE_THREAD; +} + +static void sx_common_push_events(struct iio_dev *indio_dev) +{ + int ret; + unsigned int val, chan; + struct sx_common_data *data = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned long prox_changed; + + /* Read proximity state on all channels */ + ret = regmap_read(data->regmap, data->chip_info->reg_stat, &val); + if (ret) { + dev_err(&data->client->dev, "i2c transfer error in irq\n"); + return; + } + + val >>= data->chip_info->stat_offset; + + /* + * Only iterate over channels with changes on proximity status that have + * events enabled. + */ + prox_changed = (data->chan_prox_stat ^ val) & data->chan_event; + + for_each_set_bit(chan, &prox_changed, data->chip_info->num_channels) { + int dir; + u64 ev; + + dir = (val & BIT(chan)) ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan, + IIO_EV_TYPE_THRESH, dir); + + iio_push_event(indio_dev, ev, timestamp); + } + data->chan_prox_stat = val; +} + +static int sx_common_enable_irq(struct sx_common_data *data, unsigned int irq) +{ + if (!data->client->irq) + return 0; + return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk, + irq << data->chip_info->irq_msk_offset, + irq << data->chip_info->irq_msk_offset); +} + +static int sx_common_disable_irq(struct sx_common_data *data, unsigned int irq) +{ + if (!data->client->irq) + return 0; + return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk, + irq << data->chip_info->irq_msk_offset, 0); +} + +static int sx_common_update_chan_en(struct sx_common_data *data, + unsigned long chan_read, + unsigned long chan_event) +{ + int ret; + unsigned long channels = chan_read | chan_event; + + if ((data->chan_read | data->chan_event) != channels) { + ret = regmap_update_bits(data->regmap, + data->chip_info->reg_enable_chan, + data->chip_info->mask_enable_chan, + channels); + if (ret) + return ret; + } + data->chan_read = chan_read; + data->chan_event = chan_event; + return 0; +} + +static int sx_common_get_read_channel(struct sx_common_data *data, int channel) +{ + return sx_common_update_chan_en(data, data->chan_read | BIT(channel), + data->chan_event); +} + +static int sx_common_put_read_channel(struct sx_common_data *data, int channel) +{ + return sx_common_update_chan_en(data, data->chan_read & ~BIT(channel), + data->chan_event); +} + +static int sx_common_get_event_channel(struct sx_common_data *data, int channel) +{ + return sx_common_update_chan_en(data, data->chan_read, + data->chan_event | BIT(channel)); +} + +static int sx_common_put_event_channel(struct sx_common_data *data, int channel) +{ + return sx_common_update_chan_en(data, data->chan_read, + data->chan_event & ~BIT(channel)); +} + +/** + * sx_common_read_proximity() - Read raw proximity value. + * @data: Internal data + * @chan: Channel to read + * @val: pointer to return read value. + * + * Request a conversion, wait for the sensor to be ready and + * return the raw proximity value. + */ +int sx_common_read_proximity(struct sx_common_data *data, + const struct iio_chan_spec *chan, int *val) +{ + int ret; + __be16 rawval; + + mutex_lock(&data->mutex); + + ret = sx_common_get_read_channel(data, chan->channel); + if (ret) + goto out; + + ret = sx_common_enable_irq(data, SX_COMMON_CONVDONE_IRQ); + if (ret) + goto out_put_channel; + + mutex_unlock(&data->mutex); + + if (data->client->irq) { + ret = wait_for_completion_interruptible(&data->completion); + reinit_completion(&data->completion); + } else { + ret = data->chip_info->ops.wait_for_sample(data); + } + + mutex_lock(&data->mutex); + + if (ret) + goto out_disable_irq; + + ret = data->chip_info->ops.read_prox_data(data, chan, &rawval); + if (ret) + goto out_disable_irq; + + *val = sign_extend32(be16_to_cpu(rawval), chan->scan_type.realbits - 1); + + ret = sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ); + if (ret) + goto out_put_channel; + + ret = sx_common_put_read_channel(data, chan->channel); + if (ret) + goto out; + + mutex_unlock(&data->mutex); + + return IIO_VAL_INT; + +out_disable_irq: + sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ); +out_put_channel: + sx_common_put_read_channel(data, chan->channel); +out: + mutex_unlock(&data->mutex); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(sx_common_read_proximity, SEMTECH_PROX); + +/** + * sx_common_read_event_config() - Configure event setting. + * @indio_dev: iio device object + * @chan: Channel to read + * @type: Type of event (unused) + * @dir: Direction of event (unused) + * + * return if the given channel is used for event gathering. + */ +int sx_common_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 sx_common_data *data = iio_priv(indio_dev); + + return !!(data->chan_event & BIT(chan->channel)); +} +EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, SEMTECH_PROX); + +/** + * sx_common_write_event_config() - Configure event setting. + * @indio_dev: iio device object + * @chan: Channel to enable + * @type: Type of event (unused) + * @dir: Direction of event (unused) + * @state: State of the event. + * + * Enable/Disable event on a given channel. + */ +int sx_common_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 sx_common_data *data = iio_priv(indio_dev); + unsigned int eventirq = SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ; + int ret; + + /* If the state hasn't changed, there's nothing to do. */ + if (!!(data->chan_event & BIT(chan->channel)) == state) + return 0; + + mutex_lock(&data->mutex); + if (state) { + ret = sx_common_get_event_channel(data, chan->channel); + if (ret) + goto out_unlock; + if (!(data->chan_event & ~BIT(chan->channel))) { + ret = sx_common_enable_irq(data, eventirq); + if (ret) + sx_common_put_event_channel(data, chan->channel); + } + } else { + ret = sx_common_put_event_channel(data, chan->channel); + if (ret) + goto out_unlock; + if (!data->chan_event) { + ret = sx_common_disable_irq(data, eventirq); + if (ret) + sx_common_get_event_channel(data, chan->channel); + } + } + +out_unlock: + mutex_unlock(&data->mutex); + return ret; +} +EXPORT_SYMBOL_NS_GPL(sx_common_write_event_config, SEMTECH_PROX); + +static int sx_common_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct sx_common_data *data = iio_priv(indio_dev); + int ret = 0; + + mutex_lock(&data->mutex); + + if (state) + ret = sx_common_enable_irq(data, SX_COMMON_CONVDONE_IRQ); + else if (!data->chan_read) + ret = sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ); + if (ret) + goto out; + + data->trigger_enabled = state; + +out: + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_trigger_ops sx_common_trigger_ops = { + .set_trigger_state = sx_common_set_trigger_state, +}; + +static irqreturn_t sx_common_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct sx_common_data *data = iio_priv(indio_dev); + int ret; + unsigned int val; + + mutex_lock(&data->mutex); + + ret = regmap_read(data->regmap, SX_COMMON_REG_IRQ_SRC, &val); + if (ret) { + dev_err(&data->client->dev, "i2c transfer error in irq\n"); + goto out; + } + + if (val & ((SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ) << data->chip_info->irq_msk_offset)) + sx_common_push_events(indio_dev); + + if (val & (SX_COMMON_CONVDONE_IRQ << data->chip_info->irq_msk_offset)) + complete(&data->completion); + +out: + mutex_unlock(&data->mutex); + + return IRQ_HANDLED; +} + +static irqreturn_t sx_common_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct sx_common_data *data = iio_priv(indio_dev); + __be16 val; + int bit, ret, i = 0; + + mutex_lock(&data->mutex); + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = data->chip_info->ops.read_prox_data(data, + &indio_dev->channels[bit], + &val); + if (ret) + goto out; + + data->buffer.channels[i++] = val; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + pf->timestamp); + +out: + mutex_unlock(&data->mutex); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int sx_common_buffer_preenable(struct iio_dev *indio_dev) +{ + struct sx_common_data *data = iio_priv(indio_dev); + unsigned long channels = 0; + int bit, ret; + + mutex_lock(&data->mutex); + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) + __set_bit(indio_dev->channels[bit].channel, &channels); + + ret = sx_common_update_chan_en(data, channels, data->chan_event); + mutex_unlock(&data->mutex); + return ret; +} + +static int sx_common_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct sx_common_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + ret = sx_common_update_chan_en(data, 0, data->chan_event); + mutex_unlock(&data->mutex); + return ret; +} + +static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = { + .preenable = sx_common_buffer_preenable, + .postdisable = sx_common_buffer_postdisable, +}; + +static void sx_common_regulator_disable(void *_data) +{ + struct sx_common_data *data = _data; + + regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies); +} + +#define SX_COMMON_SOFT_RESET 0xde + +static int sx_common_init_device(struct iio_dev *indio_dev) +{ + struct sx_common_data *data = iio_priv(indio_dev); + struct sx_common_reg_default tmp; + const struct sx_common_reg_default *initval; + int ret; + unsigned int i, val; + + ret = regmap_write(data->regmap, data->chip_info->reg_reset, + SX_COMMON_SOFT_RESET); + if (ret) + return ret; + + usleep_range(1000, 2000); /* power-up time is ~1ms. */ + + /* Clear reset interrupt state by reading SX_COMMON_REG_IRQ_SRC. */ + ret = regmap_read(data->regmap, SX_COMMON_REG_IRQ_SRC, &val); + if (ret) + return ret; + + /* Program defaults from constant or BIOS. */ + for (i = 0; i < data->chip_info->num_default_regs; i++) { + initval = data->chip_info->ops.get_default_reg(&indio_dev->dev, + i, &tmp); + ret = regmap_write(data->regmap, initval->reg, initval->def); + if (ret) + return ret; + } + + return data->chip_info->ops.init_compensation(indio_dev); +} + +/** + * sx_common_probe() - Common setup for Semtech SAR sensor + * @client: I2C client object + * @chip_info: Semtech sensor chip information. + * @regmap_config: Sensor registers map configuration. + */ +int sx_common_probe(struct i2c_client *client, + const struct sx_common_chip_info *chip_info, + const struct regmap_config *regmap_config) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct sx_common_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + + data->chip_info = chip_info; + data->client = client; + data->supplies[0].supply = "vdd"; + data->supplies[1].supply = "svdd"; + mutex_init(&data->mutex); + init_completion(&data->completion); + + data->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Could init register map\n"); + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), + data->supplies); + if (ret) + return dev_err_probe(dev, ret, "Unable to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies); + if (ret) + return dev_err_probe(dev, ret, "Unable to enable regulators\n"); + + /* Must wait for Tpor time after initial power up */ + usleep_range(1000, 1100); + + ret = devm_add_action_or_reset(dev, sx_common_regulator_disable, data); + if (ret) + return dev_err_probe(dev, ret, + "Unable to register regulators deleter\n"); + + ret = data->chip_info->ops.check_whoami(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "error reading WHOAMI\n"); + + ACPI_COMPANION_SET(&indio_dev->dev, ACPI_COMPANION(dev)); + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = data->chip_info->iio_channels; + indio_dev->num_channels = data->chip_info->num_iio_channels; + indio_dev->info = &data->chip_info->iio_info; + + i2c_set_clientdata(client, indio_dev); + + ret = sx_common_init_device(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Unable to initialize sensor\n"); + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, + sx_common_irq_handler, + sx_common_irq_thread_handler, + IRQF_ONESHOT, + "sx_event", indio_dev); + if (ret) + return dev_err_probe(dev, ret, "No IRQ\n"); + + data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &sx_common_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + + ret = devm_iio_trigger_register(dev, data->trig); + if (ret) + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + sx_common_trigger_handler, + &sx_common_buffer_setup_ops); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(sx_common_probe, SEMTECH_PROX); + +MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); +MODULE_DESCRIPTION("Common functions and structures for Semtech sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h new file mode 100644 index 000000000000..5d3edeb75f4e --- /dev/null +++ b/drivers/iio/proximity/sx_common.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2021 Google LLC. + * + * Code shared between most Semtech SAR sensor driver. + */ + +#ifndef IIO_SX_COMMON_H +#define IIO_SX_COMMON_H + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +struct device; +struct i2c_client; +struct regmap_config; +struct sx_common_data; + +#define SX_COMMON_REG_IRQ_SRC 0x00 + +#define SX_COMMON_MAX_NUM_CHANNELS 4 +static_assert(SX_COMMON_MAX_NUM_CHANNELS < BITS_PER_LONG); + +struct sx_common_reg_default { + u8 reg; + u8 def; +}; + +/** + * struct sx_common_ops: function pointers needed by common code + * + * List functions needed by common code to gather information or configure + * the sensor. + * + * @read_prox_data: Function to read raw proximity data. + * @check_whoami: Set device name based on whoami register. + * @init_compensation: Function to set initial compensation. + * @wait_for_sample: When there are no physical IRQ, function to wait for a + * sample to be ready. + * @get_default_reg: Populate the initial value for a given register. + */ +struct sx_common_ops { + int (*read_prox_data)(struct sx_common_data *data, + const struct iio_chan_spec *chan, __be16 *val); + int (*check_whoami)(struct device *dev, struct iio_dev *indio_dev); + int (*init_compensation)(struct iio_dev *indio_dev); + int (*wait_for_sample)(struct sx_common_data *data); + const struct sx_common_reg_default * + (*get_default_reg)(struct device *dev, int idx, + struct sx_common_reg_default *reg_def); +}; + +/** + * struct sx_common_chip_info: Semtech Sensor private chip information + * + * @reg_stat: Main status register address. + * @reg_irq_msk: IRQ mask register address. + * @reg_enable_chan: Address to enable/disable channels. + * Each phase presented by the sensor is an IIO channel.. + * @reg_reset: Reset register address. + * @mask_enable_chan: Mask over the channels bits in the enable channel + * register. + * @stat_offset: Offset to check phase status. + * @irq_msk_offset: Offset to enable interrupt in the IRQ mask + * register. + * @num_channels: Number of channels. + * @num_default_regs: Number of internal registers that can be configured. + * + * @ops: Private functions pointers. + * @iio_channels: Description of exposed iio channels. + * @num_iio_channels: Number of iio_channels. + * @iio_info: iio_info structure for this driver. + */ +struct sx_common_chip_info { + unsigned int reg_stat; + unsigned int reg_irq_msk; + unsigned int reg_enable_chan; + unsigned int reg_reset; + + unsigned int mask_enable_chan; + unsigned int stat_offset; + unsigned int irq_msk_offset; + unsigned int num_channels; + int num_default_regs; + + struct sx_common_ops ops; + + const struct iio_chan_spec *iio_channels; + int num_iio_channels; + struct iio_info iio_info; +}; + +/** + * struct sx_common_data: Semtech Sensor private data structure. + * + * @chip_info: Structure defining sensor internals. + * @mutex: Serialize access to registers and channel configuration. + * @completion: completion object to wait for data acquisition. + * @client: I2C client structure. + * @trig: IIO trigger object. + * @regmap: Register map. + * @num_default_regs: Number of default registers to set at init. + * @supplies: Power supplies object. + * @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. + * @buffer: Buffer to store raw samples. + * @suspend_ctrl: Remember enabled channels and sample rate during suspend. + * @chan_read: Bit field for each raw channel enabled. + * @chan_event: Bit field for each event enabled. + */ +struct sx_common_data { + const struct sx_common_chip_info *chip_info; + + struct mutex mutex; + struct completion completion; + struct i2c_client *client; + struct iio_trigger *trig; + struct regmap *regmap; + + struct regulator_bulk_data supplies[2]; + unsigned long chan_prox_stat; + bool trigger_enabled; + + /* Ensure correct alignment of timestamp when present. */ + struct { + __be16 channels[SX_COMMON_MAX_NUM_CHANNELS]; + s64 ts __aligned(8); + } buffer; + + unsigned int suspend_ctrl; + unsigned long chan_read; + unsigned long chan_event; +}; + +int sx_common_read_proximity(struct sx_common_data *data, + const struct iio_chan_spec *chan, int *val); + +int sx_common_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir); +int sx_common_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); + +int sx_common_probe(struct i2c_client *client, + const struct sx_common_chip_info *chip_info, + const struct regmap_config *regmap_config); + +/* 3 is the number of events defined by a single phase. */ +extern const struct iio_event_spec sx_common_events[3]; + +#endif /* IIO_SX_COMMON_H */ diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index cf38144b6f95..661a79ea200d 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -226,7 +226,7 @@ static int vl53l0x_probe(struct i2c_client *client) } static const struct i2c_device_id vl53l0x_id[] = { - { "vl53l0x", 0}, + { "vl53l0x", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, vl53l0x_id); diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 1954322e43be..54840881259a 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -320,7 +320,7 @@ static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) fault = reg_val & faultbit; - return sprintf(buf, "%d\n", fault); + return sysfs_emit(buf, "%d\n", fault); } static ssize_t show_fault_ovuv(struct device *dev, @@ -344,7 +344,7 @@ static ssize_t show_filter(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct max31856_data *data = iio_priv(indio_dev); - return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); + return sysfs_emit(buf, "%d\n", data->filter_50hz ? 50 : 60); } static ssize_t set_filter(struct device *dev, diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c index 4c8d6e6cf677..86c3f3509a26 100644 --- a/drivers/iio/temperature/max31865.c +++ b/drivers/iio/temperature/max31865.c @@ -208,7 +208,7 @@ static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) fault = data->buf[0] & faultbit; - return sprintf(buf, "%d\n", fault); + return sysfs_emit(buf, "%d\n", fault); } static ssize_t show_fault_ovuv(struct device *dev, @@ -225,7 +225,7 @@ static ssize_t show_filter(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct max31865_data *data = iio_priv(indio_dev); - return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); + return sysfs_emit(buf, "%d\n", data->filter_50hz ? 50 : 60); } static ssize_t set_filter(struct device *dev, diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index 0297e215b61a..98c41cddc6f0 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -6,12 +6,11 @@ * Author: <matt.ranostay@konsulko.com> */ -#include <linux/module.h> #include <linux/init.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/mutex.h> #include <linux/err.h> -#include <linux/of.h> -#include <linux/of_device.h> #include <linux/spi/spi.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index afcb10ea7c44..c253a5315988 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -600,7 +600,6 @@ static const struct of_device_id mlx90614_of_match[] = { }; MODULE_DEVICE_TABLE(of, mlx90614_of_match); -#ifdef CONFIG_PM_SLEEP static int mlx90614_pm_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -630,9 +629,7 @@ static int mlx90614_pm_resume(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM static int mlx90614_pm_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -648,19 +645,18 @@ static int mlx90614_pm_runtime_resume(struct device *dev) return mlx90614_wakeup(data); } -#endif static const struct dev_pm_ops mlx90614_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume) - SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend, - mlx90614_pm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume) + RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend, + mlx90614_pm_runtime_resume, NULL) }; static struct i2c_driver mlx90614_driver = { .driver = { .name = "mlx90614", .of_match_table = mlx90614_of_match, - .pm = &mlx90614_pm_ops, + .pm = pm_ptr(&mlx90614_pm_ops), }, .probe = mlx90614_probe, .remove = mlx90614_remove, diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c index 608ccb1d8bc8..7ee7ff8047a4 100644 --- a/drivers/iio/temperature/mlx90632.c +++ b/drivers/iio/temperature/mlx90632.c @@ -13,9 +13,9 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/limits.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/math64.h> -#include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index e4943a0bc9aa..706a760f30b4 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -261,7 +261,6 @@ static int tmp006_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int tmp006_suspend(struct device *dev) { return tmp006_power(dev, false); @@ -271,9 +270,8 @@ static int tmp006_resume(struct device *dev) { return tmp006_power(dev, true); } -#endif -static SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume); static const struct i2c_device_id tmp006_id[] = { { "tmp006", 0 }, @@ -284,7 +282,7 @@ MODULE_DEVICE_TABLE(i2c, tmp006_id); static struct i2c_driver tmp006_driver = { .driver = { .name = "tmp006", - .pm = &tmp006_pm_ops, + .pm = pm_sleep_ptr(&tmp006_pm_ops), }, .probe = tmp006_probe, .id_table = tmp006_id, diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index b422371a4674..f3420d8a0e35 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -537,7 +537,6 @@ static int tmp007_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int tmp007_suspend(struct device *dev) { struct tmp007_data *data = iio_priv(i2c_get_clientdata( @@ -554,9 +553,8 @@ static int tmp007_resume(struct device *dev) return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, data->config | TMP007_CONFIG_CONV_EN); } -#endif -static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume); static const struct of_device_id tmp007_of_match[] = { { .compatible = "ti,tmp007", }, @@ -574,7 +572,7 @@ static struct i2c_driver tmp007_driver = { .driver = { .name = "tmp007", .of_match_table = tmp007_of_match, - .pm = &tmp007_pm_ops, + .pm = pm_sleep_ptr(&tmp007_pm_ops), }, .probe = tmp007_probe, .id_table = tmp007_id, diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index bbfbad9a8767..60d58ec5b063 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -233,3 +233,4 @@ MODULE_DESCRIPTION("Measurement-Specialties tsys01 temperature driver"); MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS); diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index fc96e5f9d3fc..49c275e4f510 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -187,3 +187,4 @@ MODULE_DESCRIPTION("Measurement-Specialties tsys02d temperature driver"); MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS); diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig index 679a7794af20..56ca0ad7e77a 100644 --- a/drivers/iio/test/Kconfig +++ b/drivers/iio/test/Kconfig @@ -4,6 +4,16 @@ # # Keep in alphabetical order +config IIO_RESCALE_KUNIT_TEST + bool "Test IIO rescale conversion functions" + depends on KUNIT=y && !IIO_RESCALE + default KUNIT_ALL_TESTS + help + If you want to run tests on the iio-rescale code say Y here. + + This takes advantage of ARCH=um to run tests and should be used by + developers to tests their changes to the rescaling logic. + config IIO_TEST_FORMAT bool "Test IIO formatting functions" depends on KUNIT=y diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile index 467519a2027e..f15ae0a6394f 100644 --- a/drivers/iio/test/Makefile +++ b/drivers/iio/test/Makefile @@ -4,5 +4,6 @@ # # Keep in alphabetical order +obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) += iio-test-rescale.o ../afe/iio-rescale.o obj-$(CONFIG_IIO_TEST_FORMAT) += iio-test-format.o CFLAGS_iio-test-format.o += $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c new file mode 100644 index 000000000000..0b6699bfd553 --- /dev/null +++ b/drivers/iio/test/iio-test-rescale.c @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Kunit tests for IIO rescale conversions + * + * Copyright (c) 2021 Liam Beguin <liambeguin@gmail.com> + */ + +#include <linux/gcd.h> +#include <linux/overflow.h> + +#include <linux/iio/afe/rescale.h> +#include <linux/iio/iio.h> + +#include <kunit/test.h> + +struct rescale_tc_data { + const char *name; + + const s32 numerator; + const s32 denominator; + const s32 offset; + + const int schan_val; + const int schan_val2; + const int schan_off; + const int schan_scale_type; + + const char *expected; + const char *expected_off; +}; + +const struct rescale_tc_data scale_cases[] = { + /* + * Typical use cases + */ + { + .name = "typical IIO_VAL_INT, positive", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT, + .schan_val = 42, + .expected = "5210.918114143", + }, + { + .name = "typical IIO_VAL_INT, negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT, + .schan_val = 42, + .expected = "-5210.918114143", + }, + { + .name = "typical IIO_VAL_FRACTIONAL, positive", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 42, + .schan_val2 = 20, + .expected = "260.545905707", + }, + { + .name = "typical IIO_VAL_FRACTIONAL, negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 42, + .schan_val2 = 20, + .expected = "-260.545905707", + }, + { + .name = "typical IIO_VAL_FRACTIONAL_LOG2, positive", + .numerator = 42, + .denominator = 53, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 4096, + .schan_val2 = 16, + .expected = "0.049528301", + }, + { + .name = "typical IIO_VAL_FRACTIONAL_LOG2, negative", + .numerator = -42, + .denominator = 53, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 4096, + .schan_val2 = 16, + .expected = "-0.049528301", + }, + { + .name = "typical IIO_VAL_INT_PLUS_NANO, positive", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = 123456, + .expected = "1240.710106203", + }, + { + .name = "typical IIO_VAL_INT_PLUS_NANO, negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = 123456, + .expected = "-1240.710106203", + }, + { + .name = "typical IIO_VAL_INT_PLUS_MICRO, positive", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = 1234, + .expected = "1240.84789", + }, + { + .name = "typical IIO_VAL_INT_PLUS_MICRO, negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = 1234, + .expected = "-1240.84789", + }, + /* + * Use cases with small scales involving divisions + */ + { + .name = "small IIO_VAL_FRACTIONAL, 261/509 scaled by 90/1373754273", + .numerator = 261, + .denominator = 509, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 90, + .schan_val2 = 1373754273, + .expected = "0.000000033594", + }, + { + .name = "small IIO_VAL_FRACTIONAL, 90/1373754273 scaled by 261/509", + .numerator = 90, + .denominator = 1373754273, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 261, + .schan_val2 = 509, + .expected = "0.000000033594", + }, + { + .name = "small IIO_VAL_FRACTIONAL, 760/1373754273 scaled by 427/2727", + .numerator = 760, + .denominator = 1373754273, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 427, + .schan_val2 = 2727, + .expected = "0.000000086626", + }, + { + .name = "small IIO_VAL_FRACTIONAL, 761/1373754273 scaled by 427/2727", + .numerator = 761, + .denominator = 1373754273, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 427, + .schan_val2 = 2727, + .expected = "0.000000086740", + }, + { + .name = "small IIO_VAL_FRACTIONAL, 5/32768 scaled by 3/10000", + .numerator = 5, + .denominator = 32768, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 3, + .schan_val2 = 10000, + .expected = "0.0000000457763671875", + }, + { + .name = "small IIO_VAL_FRACTIONAL, 0 < scale < 1", + .numerator = 6, + .denominator = 6, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 1, + .schan_val2 = 3, + .expected = "0.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL, -1 < scale < 0", + .numerator = -6, + .denominator = 6, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 1, + .schan_val2 = 3, + .expected = "-0.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL, 0 < scale < 2", + .numerator = 8, + .denominator = 2, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 1, + .schan_val2 = 3, + .expected = "1.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL, -2 < scale < 0", + .numerator = -8, + .denominator = 2, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 1, + .schan_val2 = 3, + .expected = "-1.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL_LOG2, 760/32768 scaled by 15/22", + .numerator = 760, + .denominator = 32768, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 15, + .schan_val2 = 22, + .expected = "0.000000082946", + }, + { + .name = "small IIO_VAL_FRACTIONAL_LOG2, 761/32768 scaled by 15/22", + .numerator = 761, + .denominator = 32768, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 15, + .schan_val2 = 22, + .expected = "0.000000083055", + }, + { + .name = "small IIO_VAL_FRACTIONAL_LOG2, 0 < scale < 1", + .numerator = 16, + .denominator = 3, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 1, + .schan_val2 = 4, + .expected = "0.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL_LOG2, -1 < scale < 0", + .numerator = -16, + .denominator = 3, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 1, + .schan_val2 = 4, + .expected = "-0.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL_LOG2, 0 < scale < 2", + .numerator = 8, + .denominator = 3, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 1, + .schan_val2 = 1, + .expected = "1.3333333333333333", + }, + { + .name = "small IIO_VAL_FRACTIONAL_LOG2, -2 < scale < 0", + .numerator = -8, + .denominator = 3, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 1, + .schan_val2 = 1, + .expected = "-1.3333333333333333", + }, + { + .name = "small IIO_VAL_INT_PLUS_MICRO, positive", + .numerator = 1, + .denominator = 2, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 5, + .schan_val2 = 1234, + .expected = "2.500617", + }, + { + .name = "small IIO_VAL_INT_PLUS_MICRO, negative", + .numerator = -1, + .denominator = 2, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 5, + .schan_val2 = 1234, + .expected = "-2.500617", + }, + /* + * INT_PLUS_{MICRO,NANO} positive/negative corner cases + */ + { + .name = "negative IIO_VAL_INT_PLUS_NANO, negative schan", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = -10, + .schan_val2 = 123456, + .expected = "-1240.710106203", + }, + { + .name = "negative IIO_VAL_INT_PLUS_NANO, both negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = -10, + .schan_val2 = 123456, + .expected = "1240.710106203", + }, + { + .name = "negative IIO_VAL_INT_PLUS_NANO, 3 negative", + .numerator = -1000000, + .denominator = -8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = -10, + .schan_val2 = 123456, + .expected = "-1240.710106203", + }, + { + .name = "negative IIO_VAL_INT_PLUS_NANO, 4 negative", + .numerator = -1000000, + .denominator = -8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = -10, + .schan_val2 = -123456, + .expected = "-1240.710106203", + }, + { + .name = "negative IIO_VAL_INT_PLUS_NANO, negative, *val = 0", + .numerator = 1, + .denominator = -10, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 0, + .schan_val2 = 123456789, + .expected = "-0.012345678", + }, + /* + * INT_PLUS_{MICRO,NANO} decimal part overflow + */ + { + .name = "decimal overflow IIO_VAL_INT_PLUS_NANO, positive", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = 123456789, + .expected = "1256.01200856", + }, + { + .name = "decimal overflow IIO_VAL_INT_PLUS_NANO, negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = 123456789, + .expected = "-1256.01200856", + }, + { + .name = "decimal overflow IIO_VAL_INT_PLUS_NANO, negative schan", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = -10, + .schan_val2 = 123456789, + .expected = "-1256.01200856", + }, + { + .name = "decimal overflow IIO_VAL_INT_PLUS_MICRO, positive", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = 123456789, + .expected = "16557.914267", + }, + { + .name = "decimal overflow IIO_VAL_INT_PLUS_MICRO, negative", + .numerator = -1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = 123456789, + .expected = "-16557.914267", + }, + { + .name = "decimal overflow IIO_VAL_INT_PLUS_MICRO, negative schan", + .numerator = 1000000, + .denominator = 8060, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = -10, + .schan_val2 = 123456789, + .expected = "-16557.914267", + }, + /* + * 32-bit overflow conditions + */ + { + .name = "overflow IIO_VAL_FRACTIONAL, positive", + .numerator = 2, + .denominator = 20, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = S32_MAX, + .schan_val2 = 1, + .expected = "214748364.7", + }, + { + .name = "overflow IIO_VAL_FRACTIONAL, negative", + .numerator = -2, + .denominator = 20, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = S32_MAX, + .schan_val2 = 1, + .expected = "-214748364.7", + }, + { + .name = "overflow IIO_VAL_FRACTIONAL_LOG2, positive", + .numerator = S32_MAX, + .denominator = 4096, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 4096, + .schan_val2 = 16, + .expected = "32767.99998474121", + }, + { + .name = "overflow IIO_VAL_FRACTIONAL_LOG2, negative", + .numerator = S32_MAX, + .denominator = 4096, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = -4096, + .schan_val2 = 16, + .expected = "-32767.99998474121", + }, + { + .name = "overflow IIO_VAL_INT_PLUS_NANO, positive", + .numerator = 2, + .denominator = 20, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = S32_MAX, + .expected = "1.214748364", + }, + { + .name = "overflow IIO_VAL_INT_PLUS_NANO, negative", + .numerator = -2, + .denominator = 20, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = S32_MAX, + .expected = "-1.214748364", + }, + { + .name = "overflow IIO_VAL_INT_PLUS_NANO, negative schan", + .numerator = 2, + .denominator = 20, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = -10, + .schan_val2 = S32_MAX, + .expected = "-1.214748364", + }, + { + .name = "overflow IIO_VAL_INT_PLUS_MICRO, positive", + .numerator = 2, + .denominator = 20, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = S32_MAX, + .expected = "215.748364", + }, + { + .name = "overflow IIO_VAL_INT_PLUS_MICRO, negative", + .numerator = -2, + .denominator = 20, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = S32_MAX, + .expected = "-215.748364", + }, + { + .name = "overflow IIO_VAL_INT_PLUS_MICRO, negative schan", + .numerator = 2, + .denominator = 20, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = -10, + .schan_val2 = S32_MAX, + .expected = "-215.748364", + }, +}; + +const struct rescale_tc_data offset_cases[] = { + /* + * Typical use cases + */ + { + .name = "typical IIO_VAL_INT, positive", + .offset = 1234, + .schan_scale_type = IIO_VAL_INT, + .schan_val = 123, + .schan_val2 = 0, + .schan_off = 14, + .expected_off = "24", /* 23.872 */ + }, + { + .name = "typical IIO_VAL_INT, negative", + .offset = -1234, + .schan_scale_type = IIO_VAL_INT, + .schan_val = 12, + .schan_val2 = 0, + .schan_off = 14, + .expected_off = "-88", /* -88.83333333333333 */ + }, + { + .name = "typical IIO_VAL_FRACTIONAL, positive", + .offset = 1234, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 12, + .schan_val2 = 34, + .schan_off = 14, + .expected_off = "3510", /* 3510.333333333333 */ + }, + { + .name = "typical IIO_VAL_FRACTIONAL, negative", + .offset = -1234, + .schan_scale_type = IIO_VAL_FRACTIONAL, + .schan_val = 12, + .schan_val2 = 34, + .schan_off = 14, + .expected_off = "-3482", /* -3482.333333333333 */ + }, + { + .name = "typical IIO_VAL_FRACTIONAL_LOG2, positive", + .offset = 1234, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 12, + .schan_val2 = 16, + .schan_off = 14, + .expected_off = "6739299", /* 6739299.333333333 */ + }, + { + .name = "typical IIO_VAL_FRACTIONAL_LOG2, negative", + .offset = -1234, + .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2, + .schan_val = 12, + .schan_val2 = 16, + .schan_off = 14, + .expected_off = "-6739271", /* -6739271.333333333 */ + }, + { + .name = "typical IIO_VAL_INT_PLUS_NANO, positive", + .offset = 1234, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = 123456789, + .schan_off = 14, + .expected_off = "135", /* 135.8951219647469 */ + }, + { + .name = "typical IIO_VAL_INT_PLUS_NANO, negative", + .offset = -1234, + .schan_scale_type = IIO_VAL_INT_PLUS_NANO, + .schan_val = 10, + .schan_val2 = 123456789, + .schan_off = 14, + .expected_off = "-107", /* -107.89512196474689 */ + }, + { + .name = "typical IIO_VAL_INT_PLUS_MICRO, positive", + .offset = 1234, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = 123456789, + .schan_off = 14, + .expected_off = "23", /* 23.246438560723952 */ + }, + { + .name = "typical IIO_VAL_INT_PLUS_MICRO, negative", + .offset = -12345, + .schan_scale_type = IIO_VAL_INT_PLUS_MICRO, + .schan_val = 10, + .schan_val2 = 123456789, + .schan_off = 14, + .expected_off = "-78", /* -78.50185091745313 */ + }, +}; + +static void case_to_desc(const struct rescale_tc_data *t, char *desc) +{ + strcpy(desc, t->name); +} + +KUNIT_ARRAY_PARAM(iio_rescale_scale, scale_cases, case_to_desc); +KUNIT_ARRAY_PARAM(iio_rescale_offset, offset_cases, case_to_desc); + +/** + * iio_str_to_nano() - Parse a fixed-point string to get an + * IIO_VAL_INT_PLUS_NANO value + * @str: The string to parse + * @nano: The number as an integer + * + * Returns 0 on success, or a negative error code if the string cound not be + * parsed. + */ +static int iio_str_to_nano(const char *str, s64 *nano) +{ + int tmp, tmp2; + int ret = 0; + + /* + * iio_str_to_fixpoint() uses 10^8 here instead of 10^9 as fract_mult is + * the multiplier for the first decimal place. + */ + ret = iio_str_to_fixpoint(str, 100000000, &tmp, &tmp2); + if (ret < 0) + return ret; + + if (tmp < 0) + tmp2 *= -1; + + *nano = (s64)tmp * 1000000000UL + tmp2; + + return ret; +} + +/** + * iio_test_relative_error_ppm() - Compute relative error (in parts-per-million) + * between two fixed-point strings + * @real_str: The real value as a string + * @exp_str: The expected value as a string + * + * Returns a negative error code if the strings cound not be parsed, or the + * relative error in parts-per-million. + */ +static int iio_test_relative_error_ppm(const char *real_str, const char *exp_str) +{ + s64 real, exp, err; + int ret; + + ret = iio_str_to_nano(real_str, &real); + if (ret < 0) + return ret; + + ret = iio_str_to_nano(exp_str, &exp); + if (ret < 0) + return ret; + + if (!exp) { + pr_err("Expected value is null, relative error is undefined\n"); + return -EINVAL; + } + + err = 1000000UL * abs(exp - real); + + return (int)div64_u64(err, abs(exp)); +} + +static void iio_rescale_test_scale(struct kunit *test) +{ + struct rescale_tc_data *t = (struct rescale_tc_data *)test->param_value; + char *buff = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + struct rescale rescale; + int values[2]; + int rel_ppm; + int ret; + + rescale.numerator = t->numerator; + rescale.denominator = t->denominator; + rescale.offset = t->offset; + values[0] = t->schan_val; + values[1] = t->schan_val2; + + ret = rescale_process_scale(&rescale, t->schan_scale_type, + &values[0], &values[1]); + + ret = iio_format_value(buff, ret, 2, values); + KUNIT_EXPECT_EQ(test, (int)strlen(buff), ret); + + rel_ppm = iio_test_relative_error_ppm(buff, t->expected); + KUNIT_EXPECT_GE_MSG(test, rel_ppm, 0, "failed to compute ppm\n"); + + KUNIT_EXPECT_EQ_MSG(test, rel_ppm, 0, + "\t real=%s" + "\texpected=%s\n", + buff, t->expected); +} + +static void iio_rescale_test_offset(struct kunit *test) +{ + struct rescale_tc_data *t = (struct rescale_tc_data *)test->param_value; + char *buff_off = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + struct rescale rescale; + int values[2]; + int ret; + + rescale.numerator = t->numerator; + rescale.denominator = t->denominator; + rescale.offset = t->offset; + values[0] = t->schan_val; + values[1] = t->schan_val2; + + ret = rescale_process_offset(&rescale, t->schan_scale_type, + t->schan_val, t->schan_val2, t->schan_off, + &values[0], &values[1]); + + ret = iio_format_value(buff_off, ret, 2, values); + KUNIT_EXPECT_EQ(test, (int)strlen(buff_off), ret); + + KUNIT_EXPECT_STREQ(test, strim(buff_off), t->expected_off); +} + +static struct kunit_case iio_rescale_test_cases[] = { + KUNIT_CASE_PARAM(iio_rescale_test_scale, iio_rescale_scale_gen_params), + KUNIT_CASE_PARAM(iio_rescale_test_offset, iio_rescale_offset_gen_params), + {} +}; + +static struct kunit_suite iio_rescale_test_suite = { + .name = "iio-rescale", + .test_cases = iio_rescale_test_cases, +}; +kunit_test_suite(iio_rescale_test_suite); diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 8cef2f7452e8..7ecb69725b1d 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -38,7 +38,7 @@ config IIO_STM32_LPTIMER_TRIGGER config IIO_STM32_TIMER_TRIGGER tristate "STM32 Timer Trigger" - depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST + depends on (ARCH_STM32 && MFD_STM32_TIMERS) || COMPILE_TEST help Select this option to enable STM32 Timer Trigger diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 4f9461e1412c..3643c4afae67 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -11,9 +11,10 @@ #include <linux/iio/timer/stm32-timer-trigger.h> #include <linux/iio/trigger.h> #include <linux/mfd/stm32-timers.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/of_device.h> +#include <linux/property.h> #define MAX_TRIGGERS 7 #define MAX_VALIDS 5 @@ -771,11 +772,11 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) unsigned int index; int ret; - if (of_property_read_u32(dev->of_node, "reg", &index)) - return -EINVAL; + ret = device_property_read_u32(dev, "reg", &index); + if (ret) + return ret; - cfg = (const struct stm32_timer_trigger_cfg *) - of_match_device(dev->driver->of_match_table, dev)->data; + cfg = device_get_match_data(dev); if (index >= ARRAY_SIZE(triggers_table) || index >= cfg->num_valids_table) @@ -827,7 +828,7 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev) +static int stm32_timer_trigger_suspend(struct device *dev) { struct stm32_timer_trigger *priv = dev_get_drvdata(dev); @@ -849,7 +850,7 @@ static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_timer_trigger_resume(struct device *dev) +static int stm32_timer_trigger_resume(struct device *dev) { struct stm32_timer_trigger *priv = dev_get_drvdata(dev); int ret; @@ -875,9 +876,9 @@ static int __maybe_unused stm32_timer_trigger_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops, - stm32_timer_trigger_suspend, - stm32_timer_trigger_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops, + stm32_timer_trigger_suspend, + stm32_timer_trigger_resume); static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = { .valids_table = valids_table, @@ -907,7 +908,7 @@ static struct platform_driver stm32_timer_trigger_driver = { .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, - .pm = &stm32_timer_trigger_pm_ops, + .pm = pm_sleep_ptr(&stm32_timer_trigger_pm_ops), }, }; module_platform_driver(stm32_timer_trigger_driver); diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c index c770951a909c..249ca25d1d55 100644 --- a/drivers/interconnect/imx/imx.c +++ b/drivers/interconnect/imx/imx.c @@ -25,6 +25,14 @@ struct imx_icc_node { struct dev_pm_qos_request qos_req; }; +static int imx_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + *avg = 0; + *peak = 0; + + return 0; +} + static int imx_icc_node_set(struct icc_node *node) { struct device *dev = node->provider->dev; @@ -241,6 +249,7 @@ int imx_icc_register(struct platform_device *pdev, if (!provider) return -ENOMEM; provider->set = imx_icc_set; + provider->get_bw = imx_icc_get_bw; provider->aggregate = icc_std_aggregate; provider->xlate = of_icc_xlate_onecell; provider->data = data; diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index d188f3636e4c..f9c2d7d3100d 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -1301,19 +1301,11 @@ static struct qcom_icc_node *msm8939_snoc_mm_nodes[] = { [SNOC_MM_INT_2] = &mm_int_2, }; -static const struct regmap_config msm8939_snoc_mm_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = 0x14080, - .fast_io = true, -}; - static struct qcom_icc_desc msm8939_snoc_mm = { .type = QCOM_ICC_NOC, .nodes = msm8939_snoc_mm_nodes, .num_nodes = ARRAY_SIZE(msm8939_snoc_mm_nodes), - .regmap_cfg = &msm8939_snoc_mm_regmap_config, + .regmap_cfg = &msm8939_snoc_regmap_config, .qos_offset = 0x7000, }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0f5a49fc7c9e..41d2bb0ae23a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -259,6 +259,7 @@ config QCOM_FASTRPC depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG select DMA_SHARED_BUFFER + select QCOM_SCM help Provides a communication mechanism that allows for clients to make remote method invocations across processor boundary to @@ -470,6 +471,18 @@ config HISI_HIKEY_USB switching between the dual-role USB-C port and the USB-A host ports using only one USB controller. +config OPEN_DICE + tristate "Open Profile for DICE driver" + depends on OF_RESERVED_MEM + help + This driver exposes a DICE reserved memory region to userspace via + a character device. The memory region contains Compound Device + Identifiers (CDIs) generated by firmware as an output of DICE + measured boot flow. Userspace can use CDIs for remote attestation + and sealing. + + If unsure, say N. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a086197af544..70e800e9127f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o +obj-$(CONFIG_OPEN_DICE) += open-dice.o diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index ad639ee85b2a..a16b99bdaa13 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1633,7 +1633,6 @@ static void bcm_vk_shutdown(struct pci_dev *pdev) static const struct pci_device_id bcm_vk_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE), }, - { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VIPER), }, { } }; MODULE_DEVICE_TABLE(pci, bcm_vk_ids); diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index de6d44a158bb..3f514d77a843 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -266,7 +266,7 @@ static int alcor_pci_probe(struct pci_dev *pdev, if (!priv) return -ENOMEM; - ret = ida_simple_get(&alcor_pci_idr, 0, 0, GFP_KERNEL); + ret = ida_alloc(&alcor_pci_idr, GFP_KERNEL); if (ret < 0) return ret; priv->id = ret; @@ -280,7 +280,8 @@ static int alcor_pci_probe(struct pci_dev *pdev, ret = pci_request_regions(pdev, DRV_NAME_ALCOR_PCI); if (ret) { dev_err(&pdev->dev, "Cannot request region\n"); - return -ENOMEM; + ret = -ENOMEM; + goto error_free_ida; } if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { @@ -324,6 +325,8 @@ static int alcor_pci_probe(struct pci_dev *pdev, error_release_regions: pci_release_regions(pdev); +error_free_ida: + ida_free(&alcor_pci_idr, priv->id); return ret; } @@ -337,7 +340,7 @@ static void alcor_pci_remove(struct pci_dev *pdev) mfd_remove_devices(&pdev->dev); - ida_simple_remove(&alcor_pci_idr, priv->id); + ida_free(&alcor_pci_idr, priv->id); pci_release_regions(pdev); pci_set_drvdata(pdev, NULL); diff --git a/drivers/misc/cardreader/rtl8411.c b/drivers/misc/cardreader/rtl8411.c index 4c5621b17a6f..06457e875a90 100644 --- a/drivers/misc/cardreader/rtl8411.c +++ b/drivers/misc/cardreader/rtl8411.c @@ -76,7 +76,7 @@ static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg)); } -static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); } diff --git a/drivers/misc/cardreader/rts5209.c b/drivers/misc/cardreader/rts5209.c index 29f5414072bf..52b0a476ba51 100644 --- a/drivers/misc/cardreader/rts5209.c +++ b/drivers/misc/cardreader/rts5209.c @@ -47,7 +47,7 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) } } -static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); } diff --git a/drivers/misc/cardreader/rts5227.c b/drivers/misc/cardreader/rts5227.c index 4bcfbc9afbac..d676cf63a966 100644 --- a/drivers/misc/cardreader/rts5227.c +++ b/drivers/misc/cardreader/rts5227.c @@ -72,6 +72,8 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + if (CHK_PCI_PID(pcr, 0x522A)) + pcr->rtd3_en = rtsx_reg_to_rtd3(reg); if (rtsx_check_mmc_support(reg)) pcr->extra_caps |= EXTRA_CAPS_NO_MMC; pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); @@ -171,6 +173,28 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x30, 0x00); + if (CHK_PCI_PID(pcr, 0x522A)) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); + + if (pcr->rtd3_en) { + if (CHK_PCI_PID(pcr, 0x522A)) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PM_CTRL3, 0x01, 0x01); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PME_FORCE_CTL, 0x30, 0x30); + } else { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x01, 0x01); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PME_FORCE_CTL, 0xFF, 0x33); + } + } else { + if (CHK_PCI_PID(pcr, 0x522A)) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PM_CTRL3, 0x01, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PME_FORCE_CTL, 0x30, 0x20); + } else { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PME_FORCE_CTL, 0xFF, 0x30); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x01, 0x00); + } + } + if (option->force_clkreq_0) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); @@ -438,6 +462,28 @@ static int rts522a_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) return rtsx_pci_send_cmd(pcr, 100); } +static void rts522a_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, + RELINK_TIME_MASK, 0); + + rtsx_pci_write_register(pcr, RTS522A_PM_CTRL3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + + if (!runtime) { + rtsx_pci_write_register(pcr, RTS522A_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, 0); + rtsx_pci_write_register(pcr, RTS522A_PM_CTRL3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS522A_PME_FORCE_CTL, 0x30, 0x20); + } + + rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN); +} + + static void rts522a_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) { struct rtsx_cr_option *option = &pcr->option; @@ -473,6 +519,7 @@ static const struct pcr_ops rts522a_pcr_ops = { .card_power_on = rts5227_card_power_on, .card_power_off = rts5227_card_power_off, .switch_output_voltage = rts522a_switch_output_voltage, + .force_power_down = rts522a_force_power_down, .cd_deglitch = NULL, .conv_clk_and_div_n = NULL, .set_l1off_cfg_sub_d0 = rts522a_set_l1off_cfg_sub_d0, diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c index ffc128278613..cfebad51d1d8 100644 --- a/drivers/misc/cardreader/rts5228.c +++ b/drivers/misc/cardreader/rts5228.c @@ -91,7 +91,7 @@ static int rts5228_optimize_phy(struct rtsx_pcr *pcr) return rtsx_pci_write_phy_register(pcr, 0x07, 0x8F40); } -static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); @@ -102,6 +102,14 @@ static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + if (!runtime) { + rtsx_pci_write_register(pcr, RTS5228_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, 0); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + } + rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, SSC_POWER_DOWN); } @@ -480,9 +488,18 @@ static int rts5228_extra_init_hw(struct rtsx_pcr *pcr) FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); - rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00); - rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL, - FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + + if (pcr->rtd3_en) { + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x01); + rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, + FORCE_PM_CONTROL | FORCE_PM_VALUE); + } else { + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + } + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, 0x00); return 0; } diff --git a/drivers/misc/cardreader/rts5229.c b/drivers/misc/cardreader/rts5229.c index c748eaf1ec1f..b0edd8006d52 100644 --- a/drivers/misc/cardreader/rts5229.c +++ b/drivers/misc/cardreader/rts5229.c @@ -44,7 +44,7 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); } -static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 53f3a1f45c4a..91d240dd68fa 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -74,7 +74,8 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); - pcr->rtd3_en = rtsx_reg_to_rtd3_uhsii(reg); + if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) + pcr->rtd3_en = rtsx_reg_to_rtd3_uhsii(reg); if (rtsx_check_mmc_support(reg)) pcr->extra_caps |= EXTRA_CAPS_NO_MMC; @@ -143,6 +144,27 @@ static int rts5249_init_from_hw(struct rtsx_pcr *pcr) return 0; } +static void rts52xa_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, + RELINK_TIME_MASK, 0); + + rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + + if (!runtime) { + rtsx_pci_write_register(pcr, RTS524A_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, 0); + rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS524A_PME_FORCE_CTL, 0x30, 0x20); + } + + rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN); +} + static void rts52xa_save_content_from_efuse(struct rtsx_pcr *pcr) { u8 cnt, sv; @@ -281,8 +303,11 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); - if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) + if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) { rtsx_pci_write_register(pcr, REG_VREF, PWD_SUSPND_EN, PWD_SUSPND_EN); + rtsx_pci_write_register(pcr, RTS524A_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); + } if (pcr->rtd3_en) { if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) { @@ -724,6 +749,7 @@ static const struct pcr_ops rts524a_pcr_ops = { .card_power_on = rtsx_base_card_power_on, .card_power_off = rtsx_base_card_power_off, .switch_output_voltage = rtsx_base_switch_output_voltage, + .force_power_down = rts52xa_force_power_down, .set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0, }; @@ -841,6 +867,7 @@ static const struct pcr_ops rts525a_pcr_ops = { .card_power_on = rts525a_card_power_on, .card_power_off = rtsx_base_card_power_off, .switch_output_voltage = rts525a_switch_output_voltage, + .force_power_down = rts52xa_force_power_down, .set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0, }; diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c index 1fd4e0e50730..a77585ab0f30 100644 --- a/drivers/misc/cardreader/rts5261.c +++ b/drivers/misc/cardreader/rts5261.c @@ -91,7 +91,7 @@ static void rtsx5261_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->sd30_drive_sel_3v3 = rts5261_reg_to_sd30_drive_sel_3v3(reg); } -static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); @@ -103,6 +103,24 @@ static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + if (!runtime) { + rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, 0); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + + } else { + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, 0); + + rtsx_pci_write_register(pcr, RTS5261_FW_CTL, + RTS5261_INFORM_RTD3_COLD, RTS5261_INFORM_RTD3_COLD); + rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG4, + RTS5261_FORCE_PRSNT_LOW, RTS5261_FORCE_PRSNT_LOW); + + } + rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL, SSC_POWER_DOWN, SSC_POWER_DOWN); } @@ -536,9 +554,18 @@ static int rts5261_extra_init_hw(struct rtsx_pcr *pcr) FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); - rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00); - rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, - FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + + if (pcr->rtd3_en) { + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x01); + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, + FORCE_PM_CONTROL | FORCE_PM_VALUE); + } else { + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + } + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, 0x00); /* Clear Enter RTD3_cold Information*/ rtsx_pci_write_register(pcr, RTS5261_FW_CTL, diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 6ac509c1821c..2a2619e3c72c 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -152,20 +152,12 @@ void rtsx_pci_start_run(struct rtsx_pcr *pcr) if (pcr->remove_pci) return; - if (pcr->rtd3_en) - if (pcr->is_runtime_suspended) { - pm_runtime_get(&(pcr->pci->dev)); - pcr->is_runtime_suspended = false; - } - if (pcr->state != PDEV_STAT_RUN) { pcr->state = PDEV_STAT_RUN; if (pcr->ops->enable_auto_blink) pcr->ops->enable_auto_blink(pcr); rtsx_pm_full_on(pcr); } - - mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); } EXPORT_SYMBOL_GPL(rtsx_pci_start_run); @@ -1062,73 +1054,7 @@ static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr) return 0; } -static void rtsx_enable_aspm(struct rtsx_pcr *pcr) -{ - if (pcr->ops->set_aspm) - pcr->ops->set_aspm(pcr, true); - else - rtsx_comm_set_aspm(pcr, true); -} - -static void rtsx_comm_pm_power_saving(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &pcr->option; - - if (option->ltr_enabled) { - u32 latency = option->ltr_l1off_latency; - - if (rtsx_check_dev_flag(pcr, L1_SNOOZE_TEST_EN)) - mdelay(option->l1_snooze_delay); - - rtsx_set_ltr_latency(pcr, latency); - } - - if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN)) - rtsx_set_l1off_sub_cfg_d0(pcr, 0); - - rtsx_enable_aspm(pcr); -} - -static void rtsx_pm_power_saving(struct rtsx_pcr *pcr) -{ - rtsx_comm_pm_power_saving(pcr); -} - -static void rtsx_pci_rtd3_work(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, rtd3_work); - - pcr_dbg(pcr, "--> %s\n", __func__); - if (!pcr->is_runtime_suspended) - pm_runtime_put(&(pcr->pci->dev)); -} - -static void rtsx_pci_idle_work(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work); - - pcr_dbg(pcr, "--> %s\n", __func__); - - mutex_lock(&pcr->pcr_mutex); - - pcr->state = PDEV_STAT_IDLE; - - if (pcr->ops->disable_auto_blink) - pcr->ops->disable_auto_blink(pcr); - if (pcr->ops->turn_off_led) - pcr->ops->turn_off_led(pcr); - - rtsx_pm_power_saving(pcr); - - mutex_unlock(&pcr->pcr_mutex); - - if (pcr->rtd3_en) - mod_delayed_work(system_wq, &pcr->rtd3_work, msecs_to_jiffies(10000)); -} - -static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rtsx_base_force_power_down(struct rtsx_pcr *pcr) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); @@ -1142,7 +1068,7 @@ static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN); } -static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) +static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { if (pcr->ops->turn_off_led) pcr->ops->turn_off_led(pcr); @@ -1154,9 +1080,9 @@ static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, pm_state); if (pcr->ops->force_power_down) - pcr->ops->force_power_down(pcr, pm_state); + pcr->ops->force_power_down(pcr, pm_state, runtime); else - rtsx_base_force_power_down(pcr, pm_state); + rtsx_base_force_power_down(pcr); } void rtsx_pci_enable_ocp(struct rtsx_pcr *pcr) @@ -1598,7 +1524,6 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pcr->card_inserted = 0; pcr->card_removed = 0; INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect); - INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work); pcr->msi_en = msi_en; if (pcr->msi_en) { @@ -1623,20 +1548,14 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, rtsx_pcr_cells[i].pdata_size = sizeof(*handle); } - if (pcr->rtd3_en) { - INIT_DELAYED_WORK(&pcr->rtd3_work, rtsx_pci_rtd3_work); - pm_runtime_allow(&pcidev->dev); - pm_runtime_enable(&pcidev->dev); - pcr->is_runtime_suspended = false; - } - ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells, ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL); if (ret < 0) goto free_slots; - schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); + pm_runtime_allow(&pcidev->dev); + pm_runtime_put(&pcidev->dev); return 0; @@ -1668,11 +1587,11 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; - if (pcr->rtd3_en) - pm_runtime_get_noresume(&pcr->pci->dev); - pcr->remove_pci = true; + pm_runtime_get_sync(&pcidev->dev); + pm_runtime_forbid(&pcidev->dev); + /* Disable interrupts at the pcr level */ spin_lock_irq(&pcr->lock); rtsx_pci_writel(pcr, RTSX_BIER, 0); @@ -1680,9 +1599,6 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) spin_unlock_irq(&pcr->lock); cancel_delayed_work_sync(&pcr->carddet_work); - cancel_delayed_work_sync(&pcr->idle_work); - if (pcr->rtd3_en) - cancel_delayed_work_sync(&pcr->rtd3_work); mfd_remove_devices(&pcidev->dev); @@ -1700,11 +1616,6 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) idr_remove(&rtsx_pci_idr, pcr->id); spin_unlock(&rtsx_pci_lock); - if (pcr->rtd3_en) { - pm_runtime_disable(&pcr->pci->dev); - pm_runtime_put_noidle(&pcr->pci->dev); - } - kfree(pcr->slots); kfree(pcr); kfree(handle); @@ -1717,22 +1628,16 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) { struct pci_dev *pcidev = to_pci_dev(dev_d); - struct pcr_handle *handle; - struct rtsx_pcr *pcr; + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - - cancel_delayed_work(&pcr->carddet_work); - cancel_delayed_work(&pcr->idle_work); + cancel_delayed_work_sync(&pcr->carddet_work); mutex_lock(&pcr->pcr_mutex); - rtsx_pci_power_off(pcr, HOST_ENTER_S3); - - device_wakeup_disable(dev_d); + rtsx_pci_power_off(pcr, HOST_ENTER_S3, false); mutex_unlock(&pcr->pcr_mutex); return 0; @@ -1741,15 +1646,12 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) static int __maybe_unused rtsx_pci_resume(struct device *dev_d) { struct pci_dev *pcidev = to_pci_dev(dev_d); - struct pcr_handle *handle; - struct rtsx_pcr *pcr; + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; int ret = 0; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - mutex_lock(&pcr->pcr_mutex); ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); @@ -1760,8 +1662,6 @@ static int __maybe_unused rtsx_pci_resume(struct device *dev_d) if (ret) goto out; - schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); - out: mutex_unlock(&pcr->pcr_mutex); return ret; @@ -1769,16 +1669,46 @@ out: #ifdef CONFIG_PM +static void rtsx_enable_aspm(struct rtsx_pcr *pcr) +{ + if (pcr->ops->set_aspm) + pcr->ops->set_aspm(pcr, true); + else + rtsx_comm_set_aspm(pcr, true); +} + +static void rtsx_comm_pm_power_saving(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ltr_enabled) { + u32 latency = option->ltr_l1off_latency; + + if (rtsx_check_dev_flag(pcr, L1_SNOOZE_TEST_EN)) + mdelay(option->l1_snooze_delay); + + rtsx_set_ltr_latency(pcr, latency); + } + + if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN)) + rtsx_set_l1off_sub_cfg_d0(pcr, 0); + + rtsx_enable_aspm(pcr); +} + +static void rtsx_pm_power_saving(struct rtsx_pcr *pcr) +{ + rtsx_comm_pm_power_saving(pcr); +} + static void rtsx_pci_shutdown(struct pci_dev *pcidev) { - struct pcr_handle *handle; - struct rtsx_pcr *pcr; + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - rtsx_pci_power_off(pcr, HOST_ENTER_S1); + rtsx_pci_power_off(pcr, HOST_ENTER_S1, false); pci_disable_device(pcidev); free_irq(pcr->irq, (void *)pcr); @@ -1786,47 +1716,63 @@ static void rtsx_pci_shutdown(struct pci_dev *pcidev) pci_disable_msi(pcr->pci); } +static int rtsx_pci_runtime_idle(struct device *device) +{ + struct pci_dev *pcidev = to_pci_dev(device); + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; + + dev_dbg(device, "--> %s\n", __func__); + + mutex_lock(&pcr->pcr_mutex); + + pcr->state = PDEV_STAT_IDLE; + + if (pcr->ops->disable_auto_blink) + pcr->ops->disable_auto_blink(pcr); + if (pcr->ops->turn_off_led) + pcr->ops->turn_off_led(pcr); + + rtsx_pm_power_saving(pcr); + + mutex_unlock(&pcr->pcr_mutex); + + if (pcr->rtd3_en) + pm_schedule_suspend(device, 10000); + + return -EBUSY; +} + static int rtsx_pci_runtime_suspend(struct device *device) { struct pci_dev *pcidev = to_pci_dev(device); - struct pcr_handle *handle; - struct rtsx_pcr *pcr; + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - dev_dbg(&(pcidev->dev), "--> %s\n", __func__); + dev_dbg(device, "--> %s\n", __func__); - cancel_delayed_work(&pcr->carddet_work); - cancel_delayed_work(&pcr->rtd3_work); - cancel_delayed_work(&pcr->idle_work); + cancel_delayed_work_sync(&pcr->carddet_work); mutex_lock(&pcr->pcr_mutex); - rtsx_pci_power_off(pcr, HOST_ENTER_S3); + rtsx_pci_power_off(pcr, HOST_ENTER_S3, true); mutex_unlock(&pcr->pcr_mutex); - pcr->is_runtime_suspended = true; - return 0; } static int rtsx_pci_runtime_resume(struct device *device) { struct pci_dev *pcidev = to_pci_dev(device); - struct pcr_handle *handle; - struct rtsx_pcr *pcr; + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - dev_dbg(&(pcidev->dev), "--> %s\n", __func__); + dev_dbg(device, "--> %s\n", __func__); mutex_lock(&pcr->pcr_mutex); rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); - if (pcr->ops->fetch_vendor_settings) - pcr->ops->fetch_vendor_settings(pcr); - rtsx_pci_init_hw(pcr); if (pcr->slots[RTSX_SD_CARD].p_dev != NULL) { @@ -1834,8 +1780,6 @@ static int rtsx_pci_runtime_resume(struct device *device) pcr->slots[RTSX_SD_CARD].p_dev); } - schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); - mutex_unlock(&pcr->pcr_mutex); return 0; } @@ -1850,7 +1794,7 @@ static int rtsx_pci_runtime_resume(struct device *device) static const struct dev_pm_ops rtsx_pci_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(rtsx_pci_suspend, rtsx_pci_resume) - SET_RUNTIME_PM_OPS(rtsx_pci_runtime_suspend, rtsx_pci_runtime_resume, NULL) + SET_RUNTIME_PM_OPS(rtsx_pci_runtime_suspend, rtsx_pci_runtime_resume, rtsx_pci_runtime_idle) }; static struct pci_driver rtsx_pci_driver = { diff --git a/drivers/misc/cardreader/rtsx_pcr.h b/drivers/misc/cardreader/rtsx_pcr.h index daf057c4eea6..37d1f316ae17 100644 --- a/drivers/misc/cardreader/rtsx_pcr.h +++ b/drivers/misc/cardreader/rtsx_pcr.h @@ -15,6 +15,8 @@ #define MIN_DIV_N_PCR 80 #define MAX_DIV_N_PCR 208 +#define RTS522A_PME_FORCE_CTL 0xFF78 +#define RTS522A_AUTOLOAD_CFG1 0xFF7C #define RTS522A_PM_CTRL3 0xFF7E #define RTS524A_PME_FORCE_CTL 0xFF78 @@ -25,6 +27,7 @@ #define REG_EFUSE_POWEROFF 0x00 #define RTS5250_CLK_CFG3 0xFF79 #define RTS525A_CFG_MEM_PD 0xF0 +#define RTS524A_AUTOLOAD_CFG1 0xFF7C #define RTS524A_PM_CTRL3 0xFF7E #define RTS525A_BIOS_CFG 0xFF2D #define RTS525A_LOAD_BIOS_FLAG 0x01 diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index bee727ed98db..91f96abbb3f9 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -309,7 +309,7 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) u32 val; int err; - strncpy(chip->name, "at25", sizeof(chip->name)); + strscpy(chip->name, "at25", sizeof(chip->name)); err = device_property_read_u32(dev, "size", &val); if (err) @@ -370,7 +370,7 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) u8 id[FM25_ID_LEN]; int i; - strncpy(chip->name, "fm25", sizeof(chip->name)); + strscpy(chip->name, "fm25", sizeof(chip->name)); /* Get ID of chip */ fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index d60b176ffa95..d80ada8cac09 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -17,6 +17,7 @@ #include <linux/rpmsg.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/qcom_scm.h> #include <uapi/misc/fastrpc.h> #define ADSP_DOMAIN_ID (0) @@ -25,16 +26,22 @@ #define CDSP_DOMAIN_ID (3) #define FASTRPC_DEV_MAX 4 /* adsp, mdsp, slpi, cdsp*/ #define FASTRPC_MAX_SESSIONS 13 /*12 compute, 1 cpz*/ +#define FASTRPC_MAX_VMIDS 16 #define FASTRPC_ALIGN 128 #define FASTRPC_MAX_FDLIST 16 #define FASTRPC_MAX_CRCLIST 64 #define FASTRPC_PHYS(p) ((p) & 0xffffffff) #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 +#define FASTRPC_DSP_UTILITIES_HANDLE 2 #define FASTRPC_CTXID_MASK (0xFF0) #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define FASTRPC_DEVICE_NAME "fastrpc" #define ADSP_MMAP_ADD_PAGES 0x1000 +#define DSP_UNSUPPORTED_API (0x80000414) +/* MAX NUMBER of DSP ATTRIBUTES SUPPORTED */ +#define FASTRPC_MAX_DSP_ATTRIBUTES (256) +#define FASTRPC_MAX_DSP_ATTRIBUTES_LEN (sizeof(u32) * FASTRPC_MAX_DSP_ATTRIBUTES) /* Retrives number of input buffers from the scalars parameter */ #define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff) @@ -72,13 +79,15 @@ #define FASTRPC_RMID_INIT_CREATE 6 #define FASTRPC_RMID_INIT_CREATE_ATTR 7 #define FASTRPC_RMID_INIT_CREATE_STATIC 8 +#define FASTRPC_RMID_INIT_MEM_MAP 10 +#define FASTRPC_RMID_INIT_MEM_UNMAP 11 /* Protection Domain(PD) ids */ #define AUDIO_PD (0) /* also GUEST_OS PD? */ #define USER_PD (1) #define SENSORS_PD (2) -#define miscdev_to_cctx(d) container_of(d, struct fastrpc_channel_ctx, miscdev) +#define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev) static const char *domains[FASTRPC_DEV_MAX] = { "adsp", "mdsp", "sdsp", "cdsp"}; @@ -92,9 +101,20 @@ struct fastrpc_invoke_buf { u32 pgidx; /* index to start of contiguous region */ }; -struct fastrpc_remote_arg { - u64 pv; - u64 len; +struct fastrpc_remote_dmahandle { + s32 fd; /* dma handle fd */ + u32 offset; /* dma handle offset */ + u32 len; /* dma handle length */ +}; + +struct fastrpc_remote_buf { + u64 pv; /* buffer pointer */ + u64 len; /* length of buffer */ +}; + +union fastrpc_remote_arg { + struct fastrpc_remote_buf buf; + struct fastrpc_remote_dmahandle dma; }; struct fastrpc_mmap_rsp_msg { @@ -108,12 +128,29 @@ struct fastrpc_mmap_req_msg { s32 num; }; +struct fastrpc_mem_map_req_msg { + s32 pgid; + s32 fd; + s32 offset; + u32 flags; + u64 vaddrin; + s32 num; + s32 data_len; +}; + struct fastrpc_munmap_req_msg { s32 pgid; u64 vaddr; u64 size; }; +struct fastrpc_mem_unmap_req_msg { + s32 pgid; + s32 fd; + u64 vaddrin; + u64 len; +}; + struct fastrpc_msg { int pid; /* process group id */ int tid; /* thread id */ @@ -170,6 +207,8 @@ struct fastrpc_map { u64 size; void *va; u64 len; + u64 raddr; + u32 attr; struct kref refcount; }; @@ -189,7 +228,7 @@ struct fastrpc_invoke_ctx { struct work_struct put_work; struct fastrpc_msg msg; struct fastrpc_user *fl; - struct fastrpc_remote_arg *rpra; + union fastrpc_remote_arg *rpra; struct fastrpc_map **maps; struct fastrpc_buf *buf; struct fastrpc_invoke_args *args; @@ -207,13 +246,28 @@ struct fastrpc_session_ctx { struct fastrpc_channel_ctx { int domain_id; int sesscount; + int vmcount; + u32 perms; + struct qcom_scm_vmperm vmperms[FASTRPC_MAX_VMIDS]; struct rpmsg_device *rpdev; struct fastrpc_session_ctx session[FASTRPC_MAX_SESSIONS]; spinlock_t lock; struct idr ctx_idr; struct list_head users; - struct miscdevice miscdev; struct kref refcount; + /* Flag if dsp attributes are cached */ + bool valid_attributes; + u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES]; + struct fastrpc_device *secure_fdevice; + struct fastrpc_device *fdevice; + bool secure; + bool unsigned_support; +}; + +struct fastrpc_device { + struct fastrpc_channel_ctx *cctx; + struct miscdevice miscdev; + bool secure; }; struct fastrpc_user { @@ -228,6 +282,7 @@ struct fastrpc_user { int tgid; int pd; + bool is_secure_dev; /* Lock for lists */ spinlock_t lock; /* lock for allocations */ @@ -241,6 +296,20 @@ static void fastrpc_free_map(struct kref *ref) map = container_of(ref, struct fastrpc_map, refcount); if (map->table) { + if (map->attr & FASTRPC_ATTR_SECUREMAP) { + struct qcom_scm_vmperm perm; + int err = 0; + + perm.vmid = QCOM_SCM_VMID_HLOS; + perm.perm = QCOM_SCM_PERM_RWX; + err = qcom_scm_assign_mem(map->phys, map->size, + &(map->fl->cctx->vmperms[0].vmid), &perm, 1); + if (err) { + dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d", + map->phys, map->size, err); + return; + } + } dma_buf_unmap_attachment(map->attach, map->table, DMA_BIDIRECTIONAL); dma_buf_detach(map->buf, map->attach); @@ -262,7 +331,8 @@ static void fastrpc_map_get(struct fastrpc_map *map) kref_get(&map->refcount); } -static int fastrpc_map_find(struct fastrpc_user *fl, int fd, + +static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, struct fastrpc_map **ppmap) { struct fastrpc_map *map = NULL; @@ -270,7 +340,6 @@ static int fastrpc_map_find(struct fastrpc_user *fl, int fd, mutex_lock(&fl->mutex); list_for_each_entry(map, &fl->maps, node) { if (map->fd == fd) { - fastrpc_map_get(map); *ppmap = map; mutex_unlock(&fl->mutex); return 0; @@ -281,6 +350,17 @@ static int fastrpc_map_find(struct fastrpc_user *fl, int fd, return -ENOENT; } +static int fastrpc_map_find(struct fastrpc_user *fl, int fd, + struct fastrpc_map **ppmap) +{ + int ret = fastrpc_map_lookup(fl, fd, ppmap); + + if (!ret) + fastrpc_map_get(*ppmap); + + return ret; +} + static void fastrpc_buf_free(struct fastrpc_buf *buf) { dma_free_coherent(buf->dev, buf->size, buf->virt, @@ -353,7 +433,7 @@ static void fastrpc_context_free(struct kref *ref) ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount); cctx = ctx->cctx; - for (i = 0; i < ctx->nscalars; i++) + for (i = 0; i < ctx->nbufs; i++) fastrpc_map_put(ctx->maps[i]); if (ctx->buf) @@ -617,7 +697,7 @@ static const struct dma_buf_ops fastrpc_dma_buf_ops = { }; static int fastrpc_map_create(struct fastrpc_user *fl, int fd, - u64 len, struct fastrpc_map **ppmap) + u64 len, u32 attr, struct fastrpc_map **ppmap) { struct fastrpc_session_ctx *sess = fl->sctx; struct fastrpc_map *map = NULL; @@ -659,6 +739,22 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd, map->len = len; kref_init(&map->refcount); + if (attr & FASTRPC_ATTR_SECUREMAP) { + /* + * If subsystem VMIDs are defined in DTSI, then do + * hyp_assign from HLOS to those VM(s) + */ + unsigned int perms = BIT(QCOM_SCM_VMID_HLOS); + + map->attr = attr; + err = qcom_scm_assign_mem(map->phys, (u64)map->size, &perms, + fl->cctx->vmperms, fl->cctx->vmcount); + if (err) { + dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d", + map->phys, map->size, err); + goto map_err; + } + } spin_lock(&fl->lock); list_add_tail(&map->node, &fl->maps); spin_unlock(&fl->lock); @@ -682,7 +778,7 @@ get_err: * >>>>>> START of METADATA <<<<<<<<< * +---------------------------------+ * | Arguments | - * | type:(struct fastrpc_remote_arg)| + * | type:(union fastrpc_remote_arg)| * | (0 - N) | * +---------------------------------+ * | Invoke Buffer list | @@ -707,7 +803,7 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx) { int size = 0; - size = (sizeof(struct fastrpc_remote_arg) + + size = (sizeof(struct fastrpc_remote_buf) + sizeof(struct fastrpc_invoke_buf) + sizeof(struct fastrpc_phy_page)) * ctx->nscalars + sizeof(u64) * FASTRPC_MAX_FDLIST + @@ -743,16 +839,13 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx) int i, err; for (i = 0; i < ctx->nscalars; ++i) { - /* Make sure reserved field is set to 0 */ - if (ctx->args[i].reserved) - return -EINVAL; if (ctx->args[i].fd == 0 || ctx->args[i].fd == -1 || ctx->args[i].length == 0) continue; err = fastrpc_map_create(ctx->fl, ctx->args[i].fd, - ctx->args[i].length, &ctx->maps[i]); + ctx->args[i].length, ctx->args[i].attr, &ctx->maps[i]); if (err) { dev_err(dev, "Error Creating map %d\n", err); return -EINVAL; @@ -762,10 +855,20 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx) return 0; } +static struct fastrpc_invoke_buf *fastrpc_invoke_buf_start(union fastrpc_remote_arg *pra, int len) +{ + return (struct fastrpc_invoke_buf *)(&pra[len]); +} + +static struct fastrpc_phy_page *fastrpc_phy_page_start(struct fastrpc_invoke_buf *buf, int len) +{ + return (struct fastrpc_phy_page *)(&buf[len]); +} + static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) { struct device *dev = ctx->fl->sctx->dev; - struct fastrpc_remote_arg *rpra; + union fastrpc_remote_arg *rpra; struct fastrpc_invoke_buf *list; struct fastrpc_phy_page *pages; int inbufs, i, oix, err = 0; @@ -789,9 +892,8 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) return err; rpra = ctx->buf->virt; - list = ctx->buf->virt + ctx->nscalars * sizeof(*rpra); - pages = ctx->buf->virt + ctx->nscalars * (sizeof(*list) + - sizeof(*rpra)); + list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); + pages = fastrpc_phy_page_start(list, ctx->nscalars); args = (uintptr_t)ctx->buf->virt + metalen; rlen = pkt_size - metalen; ctx->rpra = rpra; @@ -802,8 +904,8 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) i = ctx->olaps[oix].raix; len = ctx->args[i].length; - rpra[i].pv = 0; - rpra[i].len = len; + rpra[i].buf.pv = 0; + rpra[i].buf.len = len; list[i].num = len ? 1 : 0; list[i].pgidx = i; @@ -813,7 +915,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) if (ctx->maps[i]) { struct vm_area_struct *vma = NULL; - rpra[i].pv = (u64) ctx->args[i].ptr; + rpra[i].buf.pv = (u64) ctx->args[i].ptr; pages[i].addr = ctx->maps[i]->phys; mmap_read_lock(current->mm); @@ -840,7 +942,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) if (rlen < mlen) goto bail; - rpra[i].pv = args - ctx->olaps[oix].offset; + rpra[i].buf.pv = args - ctx->olaps[oix].offset; pages[i].addr = ctx->buf->phys - ctx->olaps[oix].offset + (pkt_size - rlen); @@ -854,7 +956,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) } if (i < inbufs && !ctx->maps[i]) { - void *dst = (void *)(uintptr_t)rpra[i].pv; + void *dst = (void *)(uintptr_t)rpra[i].buf.pv; void *src = (void *)(uintptr_t)ctx->args[i].ptr; if (!kernel) { @@ -870,12 +972,15 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) } for (i = ctx->nbufs; i < ctx->nscalars; ++i) { - rpra[i].pv = (u64) ctx->args[i].ptr; - rpra[i].len = ctx->args[i].length; list[i].num = ctx->args[i].length ? 1 : 0; list[i].pgidx = i; - pages[i].addr = ctx->maps[i]->phys; - pages[i].size = ctx->maps[i]->size; + if (ctx->maps[i]) { + pages[i].addr = ctx->maps[i]->phys; + pages[i].size = ctx->maps[i]->size; + } + rpra[i].dma.fd = ctx->args[i].fd; + rpra[i].dma.len = ctx->args[i].length; + rpra[i].dma.offset = (u64) ctx->args[i].ptr; } bail: @@ -888,16 +993,26 @@ bail: static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, u32 kernel) { - struct fastrpc_remote_arg *rpra = ctx->rpra; - int i, inbufs; + union fastrpc_remote_arg *rpra = ctx->rpra; + struct fastrpc_user *fl = ctx->fl; + struct fastrpc_map *mmap = NULL; + struct fastrpc_invoke_buf *list; + struct fastrpc_phy_page *pages; + u64 *fdlist; + int i, inbufs, outbufs, handles; inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); + outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc); + handles = REMOTE_SCALARS_INHANDLES(ctx->sc) + REMOTE_SCALARS_OUTHANDLES(ctx->sc); + list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); + pages = fastrpc_phy_page_start(list, ctx->nscalars); + fdlist = (uint64_t *)(pages + inbufs + outbufs + handles); for (i = inbufs; i < ctx->nbufs; ++i) { if (!ctx->maps[i]) { - void *src = (void *)(uintptr_t)rpra[i].pv; + void *src = (void *)(uintptr_t)rpra[i].buf.pv; void *dst = (void *)(uintptr_t)ctx->args[i].ptr; - u64 len = rpra[i].len; + u64 len = rpra[i].buf.len; if (!kernel) { if (copy_to_user((void __user *)dst, src, len)) @@ -908,6 +1023,13 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, } } + for (i = 0; i < FASTRPC_MAX_FDLIST; i++) { + if (!fdlist[i]) + break; + if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap)) + fastrpc_map_put(mmap); + } + return 0; } @@ -1016,6 +1138,24 @@ bail: return err; } +static bool is_session_rejected(struct fastrpc_user *fl, bool unsigned_pd_request) +{ + /* Check if the device node is non-secure and channel is secure*/ + if (!fl->is_secure_dev && fl->cctx->secure) { + /* + * Allow untrusted applications to offload only to Unsigned PD when + * channel is configured as secure and block untrusted apps on channel + * that does not support unsigned PD offload + */ + if (!fl->cctx->unsigned_support || !unsigned_pd_request) { + dev_err(&fl->cctx->rpdev->dev, "Error: Untrusted application trying to offload to signed PD"); + return true; + } + } + + return false; +} + static int fastrpc_init_create_process(struct fastrpc_user *fl, char __user *argp) { @@ -1035,6 +1175,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl, u32 siglen; } inbuf; u32 sc; + bool unsigned_module = false; args = kcalloc(FASTRPC_CREATE_PROCESS_NARGS, sizeof(*args), GFP_KERNEL); if (!args) @@ -1045,6 +1186,14 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl, goto err; } + if (init.attrs & FASTRPC_MODE_UNSIGNED_MODULE) + unsigned_module = true; + + if (is_session_rejected(fl, unsigned_module)) { + err = -ECONNREFUSED; + goto err; + } + if (init.filelen > INIT_FILELEN_MAX) { err = -EINVAL; goto err; @@ -1059,7 +1208,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl, fl->pd = USER_PD; if (init.filelen && init.filefd) { - err = fastrpc_map_create(fl, init.filefd, init.filelen, &map); + err = fastrpc_map_create(fl, init.filefd, init.filelen, 0, &map); if (err) goto err; } @@ -1168,7 +1317,6 @@ static int fastrpc_release_current_dsp_process(struct fastrpc_user *fl) args[0].ptr = (u64)(uintptr_t) &tgid; args[0].length = sizeof(tgid); args[0].fd = -1; - args[0].reserved = 0; sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_RELEASE, 1, 0); return fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, @@ -1220,10 +1368,14 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) static int fastrpc_device_open(struct inode *inode, struct file *filp) { - struct fastrpc_channel_ctx *cctx = miscdev_to_cctx(filp->private_data); + struct fastrpc_channel_ctx *cctx; + struct fastrpc_device *fdevice; struct fastrpc_user *fl = NULL; unsigned long flags; + fdevice = miscdev_to_fdevice(filp->private_data); + cctx = fdevice->cctx; + fl = kzalloc(sizeof(*fl), GFP_KERNEL); if (!fl) return -ENOMEM; @@ -1240,6 +1392,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) INIT_LIST_HEAD(&fl->user); fl->tgid = current->tgid; fl->cctx = cctx; + fl->is_secure_dev = fdevice->secure; fl->sctx = fastrpc_session_alloc(cctx); if (!fl->sctx) { @@ -1311,7 +1464,6 @@ static int fastrpc_init_attach(struct fastrpc_user *fl, int pd) args[0].ptr = (u64)(uintptr_t) &tgid; args[0].length = sizeof(tgid); args[0].fd = -1; - args[0].reserved = 0; sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_ATTACH, 1, 0); fl->pd = pd; @@ -1349,6 +1501,107 @@ static int fastrpc_invoke(struct fastrpc_user *fl, char __user *argp) return err; } +static int fastrpc_get_info_from_dsp(struct fastrpc_user *fl, uint32_t *dsp_attr_buf, + uint32_t dsp_attr_buf_len) +{ + struct fastrpc_invoke_args args[2] = { 0 }; + + /* Capability filled in userspace */ + dsp_attr_buf[0] = 0; + + args[0].ptr = (u64)(uintptr_t)&dsp_attr_buf_len; + args[0].length = sizeof(dsp_attr_buf_len); + args[0].fd = -1; + args[1].ptr = (u64)(uintptr_t)&dsp_attr_buf[1]; + args[1].length = dsp_attr_buf_len; + args[1].fd = -1; + fl->pd = 1; + + return fastrpc_internal_invoke(fl, true, FASTRPC_DSP_UTILITIES_HANDLE, + FASTRPC_SCALARS(0, 1, 1), args); +} + +static int fastrpc_get_info_from_kernel(struct fastrpc_ioctl_capability *cap, + struct fastrpc_user *fl) +{ + struct fastrpc_channel_ctx *cctx = fl->cctx; + uint32_t attribute_id = cap->attribute_id; + uint32_t *dsp_attributes; + unsigned long flags; + uint32_t domain = cap->domain; + int err; + + spin_lock_irqsave(&cctx->lock, flags); + /* check if we already have queried dsp for attributes */ + if (cctx->valid_attributes) { + spin_unlock_irqrestore(&cctx->lock, flags); + goto done; + } + spin_unlock_irqrestore(&cctx->lock, flags); + + dsp_attributes = kzalloc(FASTRPC_MAX_DSP_ATTRIBUTES_LEN, GFP_KERNEL); + if (!dsp_attributes) + return -ENOMEM; + + err = fastrpc_get_info_from_dsp(fl, dsp_attributes, FASTRPC_MAX_DSP_ATTRIBUTES_LEN); + if (err == DSP_UNSUPPORTED_API) { + dev_info(&cctx->rpdev->dev, + "Warning: DSP capabilities not supported on domain: %d\n", domain); + kfree(dsp_attributes); + return -EOPNOTSUPP; + } else if (err) { + dev_err(&cctx->rpdev->dev, "Error: dsp information is incorrect err: %d\n", err); + kfree(dsp_attributes); + return err; + } + + spin_lock_irqsave(&cctx->lock, flags); + memcpy(cctx->dsp_attributes, dsp_attributes, FASTRPC_MAX_DSP_ATTRIBUTES_LEN); + cctx->valid_attributes = true; + spin_unlock_irqrestore(&cctx->lock, flags); + kfree(dsp_attributes); +done: + cap->capability = cctx->dsp_attributes[attribute_id]; + return 0; +} + +static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_ioctl_capability cap = {0}; + int err = 0; + + if (copy_from_user(&cap, argp, sizeof(cap))) + return -EFAULT; + + cap.capability = 0; + if (cap.domain >= FASTRPC_DEV_MAX) { + dev_err(&fl->cctx->rpdev->dev, "Error: Invalid domain id:%d, err:%d\n", + cap.domain, err); + return -ECHRNG; + } + + /* Fastrpc Capablities does not support modem domain */ + if (cap.domain == MDSP_DOMAIN_ID) { + dev_err(&fl->cctx->rpdev->dev, "Error: modem not supported %d\n", err); + return -ECHRNG; + } + + if (cap.attribute_id >= FASTRPC_MAX_DSP_ATTRIBUTES) { + dev_err(&fl->cctx->rpdev->dev, "Error: invalid attribute: %d, err: %d\n", + cap.attribute_id, err); + return -EOVERFLOW; + } + + err = fastrpc_get_info_from_kernel(&cap, fl); + if (err) + return err; + + if (copy_to_user(argp, &cap.capability, sizeof(cap.capability))) + return -EFAULT; + + return 0; +} + static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_req_munmap *req) { @@ -1491,6 +1744,134 @@ err_invoke: return err; } +static int fastrpc_req_mem_unmap_impl(struct fastrpc_user *fl, struct fastrpc_mem_unmap *req) +{ + struct fastrpc_invoke_args args[1] = { [0] = { 0 } }; + struct fastrpc_map *map = NULL, *m; + struct fastrpc_mem_unmap_req_msg req_msg = { 0 }; + int err = 0; + u32 sc; + struct device *dev = fl->sctx->dev; + + spin_lock(&fl->lock); + list_for_each_entry_safe(map, m, &fl->maps, node) { + if ((req->fd < 0 || map->fd == req->fd) && (map->raddr == req->vaddr)) + break; + map = NULL; + } + + spin_unlock(&fl->lock); + + if (!map) { + dev_err(dev, "map not in list\n"); + return -EINVAL; + } + + req_msg.pgid = fl->tgid; + req_msg.len = map->len; + req_msg.vaddrin = map->raddr; + req_msg.fd = map->fd; + + args[0].ptr = (u64) (uintptr_t) &req_msg; + args[0].length = sizeof(req_msg); + + sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MEM_UNMAP, 1, 0); + err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc, + &args[0]); + fastrpc_map_put(map); + if (err) + dev_err(dev, "unmmap\tpt fd = %d, 0x%09llx error\n", map->fd, map->raddr); + + return err; +} + +static int fastrpc_req_mem_unmap(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_mem_unmap req; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + + return fastrpc_req_mem_unmap_impl(fl, &req); +} + +static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_invoke_args args[4] = { [0 ... 3] = { 0 } }; + struct fastrpc_mem_map_req_msg req_msg = { 0 }; + struct fastrpc_mmap_rsp_msg rsp_msg = { 0 }; + struct fastrpc_mem_unmap req_unmap = { 0 }; + struct fastrpc_phy_page pages = { 0 }; + struct fastrpc_mem_map req; + struct device *dev = fl->sctx->dev; + struct fastrpc_map *map = NULL; + int err; + u32 sc; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + + /* create SMMU mapping */ + err = fastrpc_map_create(fl, req.fd, req.length, 0, &map); + if (err) { + dev_err(dev, "failed to map buffer, fd = %d\n", req.fd); + return err; + } + + req_msg.pgid = fl->tgid; + req_msg.fd = req.fd; + req_msg.offset = req.offset; + req_msg.vaddrin = req.vaddrin; + map->va = (void *) (uintptr_t) req.vaddrin; + req_msg.flags = req.flags; + req_msg.num = sizeof(pages); + req_msg.data_len = 0; + + args[0].ptr = (u64) (uintptr_t) &req_msg; + args[0].length = sizeof(req_msg); + + pages.addr = map->phys; + pages.size = map->size; + + args[1].ptr = (u64) (uintptr_t) &pages; + args[1].length = sizeof(pages); + + args[2].ptr = (u64) (uintptr_t) &pages; + args[2].length = 0; + + args[3].ptr = (u64) (uintptr_t) &rsp_msg; + args[3].length = sizeof(rsp_msg); + + sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MEM_MAP, 3, 1); + err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc, &args[0]); + if (err) { + dev_err(dev, "mem mmap error, fd %d, vaddr %llx, size %lld\n", + req.fd, req.vaddrin, map->size); + goto err_invoke; + } + + /* update the buffer to be able to deallocate the memory on the DSP */ + map->raddr = rsp_msg.vaddr; + + /* let the client know the address to use */ + req.vaddrout = rsp_msg.vaddr; + + if (copy_to_user((void __user *)argp, &req, sizeof(req))) { + /* unmap the memory and release the buffer */ + req_unmap.vaddr = (uintptr_t) rsp_msg.vaddr; + req_unmap.length = map->size; + fastrpc_req_mem_unmap_impl(fl, &req_unmap); + return -EFAULT; + } + + return 0; + +err_invoke: + fastrpc_map_put(map); + + return err; +} + static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1520,6 +1901,15 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, case FASTRPC_IOCTL_MUNMAP: err = fastrpc_req_munmap(fl, argp); break; + case FASTRPC_IOCTL_MEM_MAP: + err = fastrpc_req_mem_map(fl, argp); + break; + case FASTRPC_IOCTL_MEM_UNMAP: + err = fastrpc_req_mem_unmap(fl, argp); + break; + case FASTRPC_IOCTL_GET_DSP_INFO: + err = fastrpc_get_dsp_info(fl, argp); + break; default: err = -ENOTTY; break; @@ -1615,12 +2005,41 @@ static struct platform_driver fastrpc_cb_driver = { }, }; +static int fastrpc_device_register(struct device *dev, struct fastrpc_channel_ctx *cctx, + bool is_secured, const char *domain) +{ + struct fastrpc_device *fdev; + int err; + + fdev = devm_kzalloc(dev, sizeof(*fdev), GFP_KERNEL); + if (!fdev) + return -ENOMEM; + + fdev->secure = is_secured; + fdev->cctx = cctx; + fdev->miscdev.minor = MISC_DYNAMIC_MINOR; + fdev->miscdev.fops = &fastrpc_fops; + fdev->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "fastrpc-%s%s", + domain, is_secured ? "-secure" : ""); + err = misc_register(&fdev->miscdev); + if (!err) { + if (is_secured) + cctx->secure_fdevice = fdev; + else + cctx->fdevice = fdev; + } + + return err; +} + static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *rdev = &rpdev->dev; struct fastrpc_channel_ctx *data; - int i, err, domain_id = -1; + int i, err, domain_id = -1, vmcount; const char *domain; + bool secure_dsp; + unsigned int vmids[FASTRPC_MAX_VMIDS]; err = of_property_read_string(rdev->of_node, "label", &domain); if (err) { @@ -1640,18 +2059,53 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) return -EINVAL; } + vmcount = of_property_read_variable_u32_array(rdev->of_node, + "qcom,vmids", &vmids[0], 0, FASTRPC_MAX_VMIDS); + if (vmcount < 0) + vmcount = 0; + else if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->miscdev.minor = MISC_DYNAMIC_MINOR; - data->miscdev.name = devm_kasprintf(rdev, GFP_KERNEL, "fastrpc-%s", - domains[domain_id]); - data->miscdev.fops = &fastrpc_fops; - err = misc_register(&data->miscdev); - if (err) { - kfree(data); - return err; + if (vmcount) { + data->vmcount = vmcount; + data->perms = BIT(QCOM_SCM_VMID_HLOS); + for (i = 0; i < data->vmcount; i++) { + data->vmperms[i].vmid = vmids[i]; + data->vmperms[i].perm = QCOM_SCM_PERM_RWX; + } + } + + secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); + data->secure = secure_dsp; + + switch (domain_id) { + case ADSP_DOMAIN_ID: + case MDSP_DOMAIN_ID: + case SDSP_DOMAIN_ID: + /* Unsigned PD offloading is only supported on CDSP*/ + data->unsigned_support = false; + err = fastrpc_device_register(rdev, data, secure_dsp, domains[domain_id]); + if (err) + goto fdev_error; + break; + case CDSP_DOMAIN_ID: + data->unsigned_support = true; + /* Create both device nodes so that we can allow both Signed and Unsigned PD */ + err = fastrpc_device_register(rdev, data, true, domains[domain_id]); + if (err) + goto fdev_error; + + err = fastrpc_device_register(rdev, data, false, domains[domain_id]); + if (err) + goto fdev_error; + break; + default: + err = -EINVAL; + goto fdev_error; } kref_init(&data->refcount); @@ -1665,6 +2119,9 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) data->rpdev = rpdev; return of_platform_populate(rdev->of_node, NULL, NULL, rdev); +fdev_error: + kfree(data); + return err; } static void fastrpc_notify_users(struct fastrpc_user *user) @@ -1688,7 +2145,12 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev) fastrpc_notify_users(user); spin_unlock_irqrestore(&cctx->lock, flags); - misc_deregister(&cctx->miscdev); + if (cctx->fdevice) + misc_deregister(&cctx->fdevice->miscdev); + + if (cctx->secure_fdevice) + misc_deregister(&cctx->secure_fdevice->miscdev); + of_platform_depopulate(&rpdev->dev); cctx->rpdev = NULL; diff --git a/drivers/misc/habanalabs/common/Makefile b/drivers/misc/habanalabs/common/Makefile index 82c3824cad00..6ebe3c7001ff 100644 --- a/drivers/misc/habanalabs/common/Makefile +++ b/drivers/misc/habanalabs/common/Makefile @@ -11,4 +11,4 @@ HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \ common/command_buffer.o common/hw_queue.o common/irq.o \ common/sysfs.o common/hwmon.o common/memory.o \ common/command_submission.o common/firmware_if.o \ - common/state_dump.o common/hwmgr.o + common/state_dump.o diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index 3c0ae07a2d80..a507110f6443 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -424,8 +424,8 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) { union hl_cb_args *args = data; struct hl_device *hdev = hpriv->hdev; + u64 handle = 0, device_va = 0; enum hl_device_status status; - u64 handle = 0, device_va; u32 usage_cnt = 0; int rc; @@ -464,6 +464,8 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) args->in.flags, &usage_cnt, &device_va); + if (rc) + break; memset(&args->out, 0, sizeof(args->out)); diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 0a4ef13d9ac4..d93ef9f1c45c 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -14,6 +14,8 @@ #define HL_CS_FLAGS_TYPE_MASK (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT | \ HL_CS_FLAGS_COLLECTIVE_WAIT) +#define MAX_TS_ITER_NUM 10 + /** * enum hl_cs_wait_status - cs wait status * @CS_WAIT_STATUS_BUSY: cs was not completed yet @@ -919,18 +921,21 @@ static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs) complete_job(hdev, job); } -void hl_cs_rollback_all(struct hl_device *hdev) +void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush) { int i; struct hl_cs *cs, *tmp; - flush_workqueue(hdev->sob_reset_wq); + if (!skip_wq_flush) { + flush_workqueue(hdev->ts_free_obj_wq); - /* flush all completions before iterating over the CS mirror list in - * order to avoid a race with the release functions - */ - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) - flush_workqueue(hdev->cq_wq[i]); + /* flush all completions before iterating over the CS mirror list in + * order to avoid a race with the release functions + */ + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + flush_workqueue(hdev->cq_wq[i]); + + } /* Make sure we don't have leftovers in the CS mirror list */ list_for_each_entry_safe(cs, tmp, &hdev->cs_mirror_list, mirror_node) { @@ -948,13 +953,19 @@ void hl_cs_rollback_all(struct hl_device *hdev) static void wake_pending_user_interrupt_threads(struct hl_user_interrupt *interrupt) { - struct hl_user_pending_interrupt *pend; + struct hl_user_pending_interrupt *pend, *temp; unsigned long flags; spin_lock_irqsave(&interrupt->wait_list_lock, flags); - list_for_each_entry(pend, &interrupt->wait_list_head, wait_list_node) { - pend->fence.error = -EIO; - complete_all(&pend->fence.completion); + list_for_each_entry_safe(pend, temp, &interrupt->wait_list_head, wait_list_node) { + if (pend->ts_reg_info.ts_buff) { + list_del(&pend->wait_list_node); + hl_ts_put(pend->ts_reg_info.ts_buff); + hl_cb_put(pend->ts_reg_info.cq_cb); + } else { + pend->fence.error = -EIO; + complete_all(&pend->fence.completion); + } } spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); } @@ -2063,13 +2074,16 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, idp = &ctx->sig_mgr.handles; idr_for_each_entry(idp, encaps_sig_hdl, id) { if (encaps_sig_hdl->cs_seq == signal_seq) { - handle_found = true; - /* get refcount to protect removing - * this handle from idr, needed when - * multiple wait cs are used with offset + /* get refcount to protect removing this handle from idr, + * needed when multiple wait cs are used with offset * to wait on reserved encaps signals. + * Since kref_put of this handle is executed outside the + * current lock, it is possible that the handle refcount + * is 0 but it yet to be removed from the list. In this + * case need to consider the handle as not valid. */ - kref_get(&encaps_sig_hdl->refcount); + if (kref_get_unless_zero(&encaps_sig_hdl->refcount)) + handle_found = true; break; } } @@ -2739,7 +2753,7 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) mcs_data.update_ts = false; rc = hl_cs_poll_fences(&mcs_data, mcs_compl); - if (mcs_data.completion_bitmap) + if (rc || mcs_data.completion_bitmap) break; /* @@ -2854,64 +2868,174 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) return 0; } +static int ts_buff_get_kernel_ts_record(struct hl_ts_buff *ts_buff, + struct hl_cb *cq_cb, + u64 ts_offset, u64 cq_offset, u64 target_value, + spinlock_t *wait_list_lock, + struct hl_user_pending_interrupt **pend) +{ + struct hl_user_pending_interrupt *requested_offset_record = + (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address + + ts_offset; + struct hl_user_pending_interrupt *cb_last = + (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address + + (ts_buff->kernel_buff_size / sizeof(struct hl_user_pending_interrupt)); + unsigned long flags, iter_counter = 0; + u64 current_cq_counter; + + /* Validate ts_offset not exceeding last max */ + if (requested_offset_record > cb_last) { + dev_err(ts_buff->hdev->dev, "Ts offset exceeds max CB offset(0x%llx)\n", + (u64)(uintptr_t)cb_last); + return -EINVAL; + } + +start_over: + spin_lock_irqsave(wait_list_lock, flags); + + /* Unregister only if we didn't reach the target value + * since in this case there will be no handling in irq context + * and then it's safe to delete the node out of the interrupt list + * then re-use it on other interrupt + */ + if (requested_offset_record->ts_reg_info.in_use) { + current_cq_counter = *requested_offset_record->cq_kernel_addr; + if (current_cq_counter < requested_offset_record->cq_target_value) { + list_del(&requested_offset_record->wait_list_node); + spin_unlock_irqrestore(wait_list_lock, flags); + + hl_ts_put(requested_offset_record->ts_reg_info.ts_buff); + hl_cb_put(requested_offset_record->ts_reg_info.cq_cb); + + dev_dbg(ts_buff->hdev->dev, "ts node removed from interrupt list now can re-use\n"); + } else { + dev_dbg(ts_buff->hdev->dev, "ts node in middle of irq handling\n"); + + /* irq handling in the middle give it time to finish */ + spin_unlock_irqrestore(wait_list_lock, flags); + usleep_range(1, 10); + if (++iter_counter == MAX_TS_ITER_NUM) { + dev_err(ts_buff->hdev->dev, "handling registration interrupt took too long!!\n"); + return -EINVAL; + } + + goto start_over; + } + } else { + spin_unlock_irqrestore(wait_list_lock, flags); + } + + /* Fill up the new registration node info */ + requested_offset_record->ts_reg_info.in_use = 1; + requested_offset_record->ts_reg_info.ts_buff = ts_buff; + requested_offset_record->ts_reg_info.cq_cb = cq_cb; + requested_offset_record->ts_reg_info.timestamp_kernel_addr = + (u64 *) ts_buff->user_buff_address + ts_offset; + requested_offset_record->cq_kernel_addr = + (u64 *) cq_cb->kernel_address + cq_offset; + requested_offset_record->cq_target_value = target_value; + + *pend = requested_offset_record; + + dev_dbg(ts_buff->hdev->dev, "Found available node in TS kernel CB(0x%llx)\n", + (u64)(uintptr_t)requested_offset_record); + return 0; +} + static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, - struct hl_cb_mgr *cb_mgr, u64 timeout_us, - u64 cq_counters_handle, u64 cq_counters_offset, + struct hl_cb_mgr *cb_mgr, struct hl_ts_mgr *ts_mgr, + u64 timeout_us, u64 cq_counters_handle, u64 cq_counters_offset, u64 target_value, struct hl_user_interrupt *interrupt, - u32 *status, - u64 *timestamp) + bool register_ts_record, u64 ts_handle, u64 ts_offset, + u32 *status, u64 *timestamp) { + u32 cq_patched_handle, ts_patched_handle; struct hl_user_pending_interrupt *pend; + struct hl_ts_buff *ts_buff; + struct hl_cb *cq_cb; unsigned long timeout, flags; long completion_rc; - struct hl_cb *cb; int rc = 0; - u32 handle; timeout = hl_usecs64_to_jiffies(timeout_us); hl_ctx_get(hdev, ctx); - cq_counters_handle >>= PAGE_SHIFT; - handle = (u32) cq_counters_handle; - - cb = hl_cb_get(hdev, cb_mgr, handle); - if (!cb) { - hl_ctx_put(ctx); - return -EINVAL; + cq_patched_handle = lower_32_bits(cq_counters_handle >> PAGE_SHIFT); + cq_cb = hl_cb_get(hdev, cb_mgr, cq_patched_handle); + if (!cq_cb) { + rc = -EINVAL; + goto put_ctx; } - pend = kzalloc(sizeof(*pend), GFP_KERNEL); - if (!pend) { - hl_cb_put(cb); - hl_ctx_put(ctx); - return -ENOMEM; - } + if (register_ts_record) { + dev_dbg(hdev->dev, "Timestamp registration: interrupt id: %u, ts offset: %llu, cq_offset: %llu\n", + interrupt->interrupt_id, ts_offset, cq_counters_offset); - hl_fence_init(&pend->fence, ULONG_MAX); + ts_patched_handle = lower_32_bits(ts_handle >> PAGE_SHIFT); + ts_buff = hl_ts_get(hdev, ts_mgr, ts_patched_handle); + if (!ts_buff) { + rc = -EINVAL; + goto put_cq_cb; + } - pend->cq_kernel_addr = (u64 *) cb->kernel_address + cq_counters_offset; - pend->cq_target_value = target_value; + /* Find first available record */ + rc = ts_buff_get_kernel_ts_record(ts_buff, cq_cb, ts_offset, + cq_counters_offset, target_value, + &interrupt->wait_list_lock, &pend); + if (rc) + goto put_ts_buff; + } else { + pend = kzalloc(sizeof(*pend), GFP_KERNEL); + if (!pend) { + rc = -ENOMEM; + goto put_cq_cb; + } + hl_fence_init(&pend->fence, ULONG_MAX); + pend->cq_kernel_addr = (u64 *) cq_cb->kernel_address + cq_counters_offset; + pend->cq_target_value = target_value; + } + + spin_lock_irqsave(&interrupt->wait_list_lock, flags); /* We check for completion value as interrupt could have been received * before we added the node to the wait list */ if (*pend->cq_kernel_addr >= target_value) { + if (register_ts_record) + pend->ts_reg_info.in_use = 0; + spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); + *status = HL_WAIT_CS_STATUS_COMPLETED; - /* There was no interrupt, we assume the completion is now. */ - pend->fence.timestamp = ktime_get(); - } - if (!timeout_us || (*status == HL_WAIT_CS_STATUS_COMPLETED)) + if (register_ts_record) { + *pend->ts_reg_info.timestamp_kernel_addr = ktime_get_ns(); + goto put_ts_buff; + } else { + pend->fence.timestamp = ktime_get(); + goto set_timestamp; + } + } else if (!timeout_us) { + spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); + *status = HL_WAIT_CS_STATUS_BUSY; + pend->fence.timestamp = ktime_get(); goto set_timestamp; + } /* Add pending user interrupt to relevant list for the interrupt - * handler to monitor + * handler to monitor. + * Note that we cannot have sorted list by target value, + * in order to shorten the list pass loop, since + * same list could have nodes for different cq counter handle. */ - spin_lock_irqsave(&interrupt->wait_list_lock, flags); list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head); spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); + if (register_ts_record) { + rc = *status = HL_WAIT_CS_STATUS_COMPLETED; + goto ts_registration_exit; + } + /* Wait for interrupt handler to signal completion */ completion_rc = wait_for_completion_interruptible_timeout(&pend->fence.completion, timeout); @@ -2932,23 +3056,41 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, rc = -EIO; *status = HL_WAIT_CS_STATUS_ABORTED; } else { - dev_err_ratelimited(hdev->dev, "Waiting for interrupt ID %d timedout\n", - interrupt->interrupt_id); - rc = -ETIMEDOUT; + /* The wait has timed-out. We don't know anything beyond that + * because the workload wasn't submitted through the driver. + * Therefore, from driver's perspective, the workload is still + * executing. + */ + rc = 0; + *status = HL_WAIT_CS_STATUS_BUSY; } - *status = HL_WAIT_CS_STATUS_BUSY; } } + /* + * We keep removing the node from list here, and not at the irq handler + * for completion timeout case. and if it's a registration + * for ts record, the node will be deleted in the irq handler after + * we reach the target value. + */ spin_lock_irqsave(&interrupt->wait_list_lock, flags); list_del(&pend->wait_list_node); spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); set_timestamp: *timestamp = ktime_to_ns(pend->fence.timestamp); - kfree(pend); - hl_cb_put(cb); + hl_cb_put(cq_cb); +ts_registration_exit: + hl_ctx_put(ctx); + + return rc; + +put_ts_buff: + hl_ts_put(ts_buff); +put_cq_cb: + hl_cb_put(cq_cb); +put_ctx: hl_ctx_put(ctx); return rc; @@ -3049,6 +3191,12 @@ wait_again: interrupt->interrupt_id); rc = -EINTR; } else { + /* The wait has timed-out. We don't know anything beyond that + * because the workload wasn't submitted through the driver. + * Therefore, from driver's perspective, the workload is still + * executing. + */ + rc = 0; *status = HL_WAIT_CS_STATUS_BUSY; } @@ -3101,23 +3249,20 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) interrupt = &hdev->user_interrupt[interrupt_id - first_interrupt]; if (args->in.flags & HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ) - rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->cb_mgr, + rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->cb_mgr, &hpriv->ts_mem_mgr, args->in.interrupt_timeout_us, args->in.cq_counters_handle, args->in.cq_counters_offset, - args->in.target, interrupt, &status, - ×tamp); + args->in.target, interrupt, + !!(args->in.flags & HL_WAIT_CS_FLAGS_REGISTER_INTERRUPT), + args->in.timestamp_handle, args->in.timestamp_offset, + &status, ×tamp); else rc = _hl_interrupt_wait_ioctl_user_addr(hdev, hpriv->ctx, args->in.interrupt_timeout_us, args->in.addr, args->in.target, interrupt, &status, ×tamp); - if (rc) { - if (rc != -EINTR) - dev_err_ratelimited(hdev->dev, - "interrupt_wait_ioctl failed (%d)\n", rc); - + if (rc) return rc; - } memset(args, 0, sizeof(*args)); args->out.status = status; diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index fc084ee5106e..f18495545854 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -890,6 +890,8 @@ static ssize_t hl_set_power_state(struct file *f, const char __user *buf, pci_set_power_state(hdev->pdev, PCI_D0); pci_restore_state(hdev->pdev); rc = pci_enable_device(hdev->pdev); + if (rc < 0) + return rc; } else if (value == 2) { pci_save_state(hdev->pdev); pci_disable_device(hdev->pdev); @@ -1054,42 +1056,12 @@ static ssize_t hl_device_write(struct file *f, const char __user *buf, static ssize_t hl_clk_gate_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[200]; - ssize_t rc; - - if (*ppos) - return 0; - - sprintf(tmp_buf, "0x%llx\n", hdev->clock_gating_mask); - rc = simple_read_from_buffer(buf, count, ppos, tmp_buf, - strlen(tmp_buf) + 1); - - return rc; + return 0; } static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u64 value; - ssize_t rc; - - if (hdev->reset_info.in_reset) { - dev_warn_ratelimited(hdev->dev, - "Can't change clock gating during reset\n"); - return 0; - } - - rc = kstrtoull_from_user(buf, count, 16, &value); - if (rc) - return rc; - - hdev->clock_gating_mask = value; - hdev->asic_funcs->set_clock_gating(hdev); - return count; } @@ -1101,6 +1073,9 @@ static ssize_t hl_stop_on_err_read(struct file *f, char __user *buf, char tmp_buf[200]; ssize_t rc; + if (!hdev->asic_prop.configurable_stop_on_err) + return -EOPNOTSUPP; + if (*ppos) return 0; @@ -1119,6 +1094,9 @@ static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf, u32 value; ssize_t rc; + if (!hdev->asic_prop.configurable_stop_on_err) + return -EOPNOTSUPP; + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't change stop on error during reset\n"); diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 733338ab6f1d..dc9341a64541 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2021 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -13,6 +13,8 @@ #include <linux/pci.h> #include <linux/hwmon.h> +#define HL_RESET_DELAY_USEC 10000 /* 10ms */ + enum hl_device_status hl_device_status(struct hl_device *hdev) { enum hl_device_status status; @@ -145,6 +147,7 @@ static int hl_device_release(struct inode *inode, struct file *filp) hl_release_pending_user_interrupts(hpriv->hdev); hl_cb_mgr_fini(hdev, &hpriv->cb_mgr); + hl_ts_mgr_fini(hpriv->hdev, &hpriv->ts_mem_mgr); hl_ctx_mgr_fini(hdev, &hpriv->ctx_mgr); if (!hl_hpriv_put(hpriv)) @@ -209,6 +212,9 @@ static int hl_mmap(struct file *filp, struct vm_area_struct *vma) case HL_MMAP_TYPE_BLOCK: return hl_hw_block_mmap(hpriv, vma); + + case HL_MMAP_TYPE_TS_BUFF: + return hl_ts_mmap(hpriv, vma); } return -EINVAL; @@ -410,10 +416,10 @@ static int device_early_init(struct hl_device *hdev) goto free_cq_wq; } - hdev->sob_reset_wq = alloc_workqueue("hl-sob-reset", WQ_UNBOUND, 0); - if (!hdev->sob_reset_wq) { + hdev->ts_free_obj_wq = alloc_workqueue("hl-ts-free-obj", WQ_UNBOUND, 0); + if (!hdev->ts_free_obj_wq) { dev_err(hdev->dev, - "Failed to allocate SOB reset workqueue\n"); + "Failed to allocate Timestamp registration free workqueue\n"); rc = -ENOMEM; goto free_eq_wq; } @@ -422,7 +428,7 @@ static int device_early_init(struct hl_device *hdev) GFP_KERNEL); if (!hdev->hl_chip_info) { rc = -ENOMEM; - goto free_sob_reset_wq; + goto free_ts_free_wq; } rc = hl_mmu_if_set_funcs(hdev); @@ -461,8 +467,8 @@ free_cb_mgr: hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); free_chip_info: kfree(hdev->hl_chip_info); -free_sob_reset_wq: - destroy_workqueue(hdev->sob_reset_wq); +free_ts_free_wq: + destroy_workqueue(hdev->ts_free_obj_wq); free_eq_wq: destroy_workqueue(hdev->eq_wq); free_cq_wq: @@ -501,7 +507,7 @@ static void device_early_fini(struct hl_device *hdev) kfree(hdev->hl_chip_info); - destroy_workqueue(hdev->sob_reset_wq); + destroy_workqueue(hdev->ts_free_obj_wq); destroy_workqueue(hdev->eq_wq); destroy_workqueue(hdev->device_reset_work.wq); @@ -610,7 +616,7 @@ int hl_device_utilization(struct hl_device *hdev, u32 *utilization) u64 max_power, curr_power, dc_power, dividend; int rc; - max_power = hdev->asic_prop.max_power_default; + max_power = hdev->max_power; dc_power = hdev->asic_prop.dc_power_default; rc = hl_fw_cpucp_power_get(hdev, &curr_power); @@ -644,9 +650,6 @@ int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool en hdev->in_debug = 0; - if (!hdev->reset_info.hard_reset_pending) - hdev->asic_funcs->set_clock_gating(hdev); - goto out; } @@ -657,7 +660,6 @@ int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool en goto out; } - hdev->asic_funcs->disable_clock_gating(hdev); hdev->in_debug = 1; out: @@ -685,7 +687,8 @@ static void take_release_locks(struct hl_device *hdev) mutex_unlock(&hdev->fpriv_ctrl_list_lock); } -static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset) +static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset, + bool skip_wq_flush) { if (hard_reset) device_late_fini(hdev); @@ -698,7 +701,7 @@ static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_r hdev->asic_funcs->halt_engines(hdev, hard_reset, fw_reset); /* Go over all the queues, release all CS and their jobs */ - hl_cs_rollback_all(hdev); + hl_cs_rollback_all(hdev, skip_wq_flush); /* Release all pending user interrupts, each pending user interrupt * holds a reference to user context @@ -978,7 +981,8 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) int hl_device_reset(struct hl_device *hdev, u32 flags) { bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false, - reset_upon_device_release = false, schedule_hard_reset = false; + reset_upon_device_release = false, schedule_hard_reset = false, + skip_wq_flush, delay_reset; u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; struct hl_ctx *ctx; int i, rc; @@ -991,6 +995,8 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) hard_reset = !!(flags & HL_DRV_RESET_HARD); from_hard_reset_thread = !!(flags & HL_DRV_RESET_FROM_RESET_THR); fw_reset = !!(flags & HL_DRV_RESET_BYPASS_REQ_TO_FW); + skip_wq_flush = !!(flags & HL_DRV_RESET_DEV_RELEASE); + delay_reset = !!(flags & HL_DRV_RESET_DELAY); if (!hard_reset && !hdev->asic_prop.supports_soft_reset) { hard_instead_soft = true; @@ -1040,6 +1046,9 @@ do_reset: hdev->reset_info.in_reset = 1; spin_unlock(&hdev->reset_info.lock); + if (delay_reset) + usleep_range(HL_RESET_DELAY_USEC, HL_RESET_DELAY_USEC << 1); + handle_reset_trigger(hdev, flags); /* This still allows the completion of some KDMA ops */ @@ -1076,7 +1085,7 @@ again: return 0; } - cleanup_resources(hdev, hard_reset, fw_reset); + cleanup_resources(hdev, hard_reset, fw_reset, skip_wq_flush); kill_processes: if (hard_reset) { @@ -1232,7 +1241,7 @@ kill_processes: goto out_err; } - hl_set_max_power(hdev); + hl_fw_set_max_power(hdev); } else { rc = hdev->asic_funcs->non_hard_reset_late_init(hdev); if (rc) { @@ -1297,11 +1306,14 @@ out_err: hdev->reset_info.hard_reset_cnt++; } else if (reset_upon_device_release) { dev_err(hdev->dev, "Failed to reset device after user release\n"); + flags |= HL_DRV_RESET_HARD; + flags &= ~HL_DRV_RESET_DEV_RELEASE; hard_reset = true; goto again; } else { dev_err(hdev->dev, "Failed to do soft-reset\n"); hdev->reset_info.soft_reset_cnt++; + flags |= HL_DRV_RESET_HARD; hard_reset = true; goto again; } @@ -1538,7 +1550,8 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) /* Need to call this again because the max power might change, * depending on card type for certain ASICs */ - hl_set_max_power(hdev); + if (hdev->asic_prop.set_max_power_on_device_init) + hl_fw_set_max_power(hdev); /* * hl_hwmon_init() must be called after device_late_init(), because only @@ -1682,7 +1695,7 @@ void hl_device_fini(struct hl_device *hdev) hl_hwmon_fini(hdev); - cleanup_resources(hdev, true, false); + cleanup_resources(hdev, true, false, false); /* Kill processes here after CS rollback. This is because the process * can't really exit until all its CSs are done, which is what we diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 6775c5c3166b..3262126cc7ca 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2021 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -214,7 +214,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, dma_addr_t pkt_dma_addr; struct hl_bd *sent_bd; u32 tmp, expected_ack_val, pi; - int rc = 0; + int rc; pkt = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, len, &pkt_dma_addr); @@ -228,8 +228,11 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, mutex_lock(&hdev->send_cpu_message_lock); - if (hdev->disabled) + /* CPU-CP messages can be sent during soft-reset */ + if (hdev->disabled && !hdev->reset_info.is_in_soft_reset) { + rc = 0; goto out; + } if (hdev->device_cpu_disabled) { rc = -EIO; @@ -958,15 +961,17 @@ int hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index, rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), HL_CPUCP_INFO_TIMEOUT_USEC, &result); - if (rc) + if (rc) { dev_err(hdev->dev, "Failed to read PLL info, error %d\n", rc); + return rc; + } pll_freq_arr[0] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT0_MASK, result); pll_freq_arr[1] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT1_MASK, result); pll_freq_arr[2] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT2_MASK, result); pll_freq_arr[3] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT3_MASK, result); - return rc; + return 0; } int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power) @@ -1202,8 +1207,6 @@ static int hl_fw_read_preboot_caps(struct hl_device *hdev, hdev, cpu_boot_status_reg, status, - (status == CPU_BOOT_STATUS_IN_UBOOT) || - (status == CPU_BOOT_STATUS_DRAM_RDY) || (status == CPU_BOOT_STATUS_NIC_FW_RDY) || (status == CPU_BOOT_STATUS_READY_TO_BOOT) || (status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT), @@ -2682,3 +2685,138 @@ int hl_fw_init_cpu(struct hl_device *hdev) hl_fw_dynamic_init_cpu(hdev, fw_loader) : hl_fw_static_init_cpu(hdev, fw_loader); } + +void hl_fw_set_pll_profile(struct hl_device *hdev) +{ + hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index, + hdev->asic_prop.max_freq_value); +} + +int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) +{ + long value; + + if (!hl_device_operational(hdev, NULL)) + return -ENODEV; + + if (!hdev->pdev) { + *cur_clk = 0; + *max_clk = 0; + return 0; + } + + value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); + + if (value < 0) { + dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", value); + return value; + } + + *max_clk = (value / 1000 / 1000); + + value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); + + if (value < 0) { + dev_err(hdev->dev, "Failed to retrieve device current clock %ld\n", value); + return value; + } + + *cur_clk = (value / 1000 / 1000); + + return 0; +} + +long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr) +{ + struct cpucp_packet pkt; + u32 used_pll_idx; + u64 result; + int rc; + + rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); + if (rc) + return rc; + + memset(&pkt, 0, sizeof(pkt)); + + if (curr) + pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_CURR_GET << + CPUCP_PKT_CTL_OPCODE_SHIFT); + else + pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); + + pkt.pll_index = cpu_to_le32((u32)used_pll_idx); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); + + if (rc) { + dev_err(hdev->dev, "Failed to get frequency of PLL %d, error %d\n", + used_pll_idx, rc); + return rc; + } + + return (long) result; +} + +void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq) +{ + struct cpucp_packet pkt; + u32 used_pll_idx; + int rc; + + rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); + if (rc) + return; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.pll_index = cpu_to_le32((u32)used_pll_idx); + pkt.value = cpu_to_le64(freq); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); + + if (rc) + dev_err(hdev->dev, "Failed to set frequency to PLL %d, error %d\n", + used_pll_idx, rc); +} + +long hl_fw_get_max_power(struct hl_device *hdev) +{ + struct cpucp_packet pkt; + u64 result; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); + + if (rc) { + dev_err(hdev->dev, "Failed to get max power, error %d\n", rc); + return rc; + } + + return result; +} + +void hl_fw_set_max_power(struct hl_device *hdev) +{ + struct cpucp_packet pkt; + int rc; + + /* TODO: remove this after simulator supports this packet */ + if (!hdev->pdev) + return; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.value = cpu_to_le64(hdev->max_power); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); + + if (rc) + dev_err(hdev->dev, "Failed to set max power, error %d\n", rc); +} diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index cb710fd478b6..1edaf6ab67bd 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2016-2021 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -31,14 +31,15 @@ #define HL_NAME "habanalabs" /* Use upper bits of mmap offset to store habana driver specific information. - * bits[63:61] - Encode mmap type + * bits[63:59] - Encode mmap type * bits[45:0] - mmap offset value * * NOTE: struct vm_area_struct.vm_pgoff uses offset in pages. Hence, these * defines are w.r.t to PAGE_SIZE */ -#define HL_MMAP_TYPE_SHIFT (61 - PAGE_SHIFT) -#define HL_MMAP_TYPE_MASK (0x7ull << HL_MMAP_TYPE_SHIFT) +#define HL_MMAP_TYPE_SHIFT (59 - PAGE_SHIFT) +#define HL_MMAP_TYPE_MASK (0x1full << HL_MMAP_TYPE_SHIFT) +#define HL_MMAP_TYPE_TS_BUFF (0x10ull << HL_MMAP_TYPE_SHIFT) #define HL_MMAP_TYPE_BLOCK (0x4ull << HL_MMAP_TYPE_SHIFT) #define HL_MMAP_TYPE_CB (0x2ull << HL_MMAP_TYPE_SHIFT) @@ -141,6 +142,9 @@ enum hl_mmu_page_table_location { * * - HL_DRV_RESET_FW_FATAL_ERR * Set if reset is due to a fatal error from FW + * + * - HL_DRV_RESET_DELAY + * Set if a delay should be added before the reset */ #define HL_DRV_RESET_HARD (1 << 0) @@ -150,6 +154,7 @@ enum hl_mmu_page_table_location { #define HL_DRV_RESET_DEV_RELEASE (1 << 4) #define HL_DRV_RESET_BYPASS_REQ_TO_FW (1 << 5) #define HL_DRV_RESET_FW_FATAL_ERR (1 << 6) +#define HL_DRV_RESET_DELAY (1 << 7) #define HL_MAX_SOBS_PER_MONITOR 8 @@ -402,8 +407,11 @@ enum hl_device_hw_state { * @hop4_mask: mask to get the PTE address in hop 4. * @hop5_mask: mask to get the PTE address in hop 5. * @last_mask: mask to get the bit indicating this is the last hop. + * @pgt_size: size for page tables. * @page_size: default page size used to allocate memory. * @num_hops: The amount of hops supported by the translation table. + * @hop_table_size: HOP table size. + * @hop0_tables_total_size: total size for all HOP0 tables. * @host_resident: Should the MMU page table reside in host memory or in the * device DRAM. */ @@ -423,8 +431,11 @@ struct hl_mmu_properties { u64 hop4_mask; u64 hop5_mask; u64 last_mask; + u64 pgt_size; u32 page_size; u32 num_hops; + u32 hop_table_size; + u32 hop0_tables_total_size; u8 host_resident; }; @@ -554,6 +565,9 @@ struct hl_hints_range { * use-case of doing soft-reset in training (due * to the fact that training runs on multiple * devices) + * @configurable_stop_on_err: is stop-on-error option configurable via debugfs. + * @set_max_power_on_device_init: true if need to set max power in F/W on device init. + * @supports_user_set_page_size: true if user can set the allocation page size. */ struct asic_fixed_properties { struct hw_queue_properties *hw_queues_props; @@ -637,6 +651,9 @@ struct asic_fixed_properties { u8 use_get_power_for_reset_history; u8 supports_soft_reset; u8 allow_inference_soft_reset; + u8 configurable_stop_on_err; + u8 set_max_power_on_device_init; + u8 supports_user_set_page_size; }; /** @@ -704,6 +721,40 @@ struct hl_cb_mgr { }; /** + * struct hl_ts_mgr - describes the timestamp registration memory manager. + * @ts_lock: protects ts_handles. + * @ts_handles: an idr to hold all ts bufferes handles. + */ +struct hl_ts_mgr { + spinlock_t ts_lock; + struct idr ts_handles; +}; + +/** + * struct hl_ts_buff - describes a timestamp buffer. + * @refcount: reference counter for usage of the buffer. + * @hdev: pointer to device this buffer belongs to. + * @mmap: true if the buff is currently mapped to user. + * @kernel_buff_address: Holds the internal buffer's kernel virtual address. + * @user_buff_address: Holds the user buffer's kernel virtual address. + * @id: the buffer ID. + * @mmap_size: Holds the buffer size that was mmaped. + * @kernel_buff_size: Holds the internal kernel buffer size. + * @user_buff_size: Holds the user buffer size. + */ +struct hl_ts_buff { + struct kref refcount; + struct hl_device *hdev; + atomic_t mmap; + void *kernel_buff_address; + void *user_buff_address; + u32 id; + u32 mmap_size; + u32 kernel_buff_size; + u32 user_buff_size; +}; + +/** * struct hl_cb - describes a Command Buffer. * @refcount: reference counter for usage of the CB. * @hdev: pointer to device this CB belongs to. @@ -881,8 +932,53 @@ struct hl_user_interrupt { }; /** + * struct timestamp_reg_free_node - holds the timestamp registration free objects node + * @free_objects_node: node in the list free_obj_jobs + * @cq_cb: pointer to cq command buffer to be freed + * @ts_buff: pointer to timestamp buffer to be freed + */ +struct timestamp_reg_free_node { + struct list_head free_objects_node; + struct hl_cb *cq_cb; + struct hl_ts_buff *ts_buff; +}; + +/* struct timestamp_reg_work_obj - holds the timestamp registration free objects job + * the job will be to pass over the free_obj_jobs list and put refcount to objects + * in each node of the list + * @free_obj: workqueue object to free timestamp registration node objects + * @hdev: pointer to the device structure + * @free_obj_head: list of free jobs nodes (node type timestamp_reg_free_node) + */ +struct timestamp_reg_work_obj { + struct work_struct free_obj; + struct hl_device *hdev; + struct list_head *free_obj_head; +}; + +/* struct timestamp_reg_info - holds the timestamp registration related data. + * @ts_buff: pointer to the timestamp buffer which include both user/kernel buffers. + * relevant only when doing timestamps records registration. + * @cq_cb: pointer to CQ counter CB. + * @timestamp_kernel_addr: timestamp handle address, where to set timestamp + * relevant only when doing timestamps records + * registration. + * @in_use: indicates if the node already in use. relevant only when doing + * timestamps records registration, since in this case the driver + * will have it's own buffer which serve as a records pool instead of + * allocating records dynamically. + */ +struct timestamp_reg_info { + struct hl_ts_buff *ts_buff; + struct hl_cb *cq_cb; + u64 *timestamp_kernel_addr; + u8 in_use; +}; + +/** * struct hl_user_pending_interrupt - holds a context to a user thread * pending on an interrupt + * @ts_reg_info: holds the timestamps registration nodes info * @wait_list_node: node in the list of user threads pending on an interrupt * @fence: hl fence object for interrupt completion * @cq_target_value: CQ target value @@ -890,10 +986,11 @@ struct hl_user_interrupt { * handler for taget value comparison */ struct hl_user_pending_interrupt { - struct list_head wait_list_node; - struct hl_fence fence; - u64 cq_target_value; - u64 *cq_kernel_addr; + struct timestamp_reg_info ts_reg_info; + struct list_head wait_list_node; + struct hl_fence fence; + u64 cq_target_value; + u64 *cq_kernel_addr; }; /** @@ -1155,7 +1252,6 @@ struct fw_load_mgr { * internal memory via DMA engine. * @add_device_attr: add ASIC specific device attributes. * @handle_eqe: handle event queue entry (IRQ) from CPU-CP. - * @set_pll_profile: change PLL profile (manual/automatic). * @get_events_stat: retrieve event queue entries histogram. * @read_pte: read MMU page table entry from DRAM. * @write_pte: write MMU page table entry to DRAM. @@ -1164,9 +1260,6 @@ struct fw_load_mgr { * @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with * ASID-VA-size mask. * @send_heartbeat: send is-alive packet to CPU-CP and verify response. - * @set_clock_gating: enable/disable clock gating per engine according to - * clock gating mask in hdev - * @disable_clock_gating: disable clock gating completely * @debug_coresight: perform certain actions on Coresight for debugging. * @is_device_idle: return true if device is idle, false otherwise. * @non_hard_reset_late_init: perform certain actions needed after a reset which is not hard-reset @@ -1187,7 +1280,6 @@ struct fw_load_mgr { * @halt_coresight: stop the ETF and ETR traces. * @ctx_init: context dependent initialization. * @ctx_fini: context dependent cleanup. - * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz * @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index. * @load_firmware_to_device: load the firmware to the device's memory * @load_boot_fit_to_device: load boot fit to device's memory @@ -1225,6 +1317,8 @@ struct fw_load_mgr { * @get_sob_addr: get SOB base address offset. * @set_pci_memory_regions: setting properties of PCI memory regions * @get_stream_master_qid_arr: get pointer to stream masters QID array + * @is_valid_dram_page_size: return true if page size is supported in device + * memory allocation, otherwise false. */ struct hl_asic_funcs { int (*early_init)(struct hl_device *hdev); @@ -1285,12 +1379,10 @@ struct hl_asic_funcs { bool user_address, u64 val); int (*debugfs_read_dma)(struct hl_device *hdev, u64 addr, u32 size, void *blob_addr); - void (*add_device_attr)(struct hl_device *hdev, - struct attribute_group *dev_attr_grp); + void (*add_device_attr)(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp, + struct attribute_group *dev_vrm_attr_grp); void (*handle_eqe)(struct hl_device *hdev, struct hl_eq_entry *eq_entry); - void (*set_pll_profile)(struct hl_device *hdev, - enum hl_pll_frequency freq); void* (*get_events_stat)(struct hl_device *hdev, bool aggregate, u32 *size); u64 (*read_pte)(struct hl_device *hdev, u64 addr); @@ -1300,8 +1392,6 @@ struct hl_asic_funcs { int (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard, u32 flags, u32 asid, u64 va, u64 size); int (*send_heartbeat)(struct hl_device *hdev); - void (*set_clock_gating)(struct hl_device *hdev); - void (*disable_clock_gating)(struct hl_device *hdev); int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data); bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, struct seq_file *s); @@ -1320,7 +1410,6 @@ struct hl_asic_funcs { void (*halt_coresight)(struct hl_device *hdev, struct hl_ctx *ctx); int (*ctx_init)(struct hl_ctx *ctx); void (*ctx_fini)(struct hl_ctx *ctx); - int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx); int (*load_firmware_to_device)(struct hl_device *hdev); int (*load_boot_fit_to_device)(struct hl_device *hdev); @@ -1355,6 +1444,7 @@ struct hl_asic_funcs { u32 (*get_sob_addr)(struct hl_device *hdev, u32 sob_id); void (*set_pci_memory_regions)(struct hl_device *hdev); u32* (*get_stream_master_qid_arr)(void); + bool (*is_valid_dram_page_size)(u32 page_size); }; @@ -1742,6 +1832,8 @@ struct hl_vm_hw_block_list_node { * @pages: the physical page array. * @npages: num physical pages in the pack. * @total_size: total size of all the pages in this list. + * @node: used to attach to deletion list that is used when all the allocations are cleared + * at the teardown of the context. * @mapping_cnt: number of shared mappings. * @exporting_cnt: number of dma-buf exporting. * @asid: the context related to this list. @@ -1757,6 +1849,7 @@ struct hl_vm_phys_pg_pack { u64 *pages; u64 npages; u64 total_size; + struct list_head node; atomic_t mapping_cnt; u32 exporting_cnt; u32 asid; @@ -1834,6 +1927,7 @@ struct hl_debug_params { * @ctx: current executing context. TODO: remove for multiple ctx per process * @ctx_mgr: context manager to handle multiple context for this FD. * @cb_mgr: command buffer manager to handle multiple buffers for this FD. + * @ts_mem_mgr: timestamp registration manager for alloc/free/map timestamp buffers. * @debugfs_list: list of relevant ASIC debugfs. * @dev_node: node in the device list of file private data * @refcount: number of related contexts. @@ -1846,6 +1940,7 @@ struct hl_fpriv { struct hl_ctx *ctx; struct hl_ctx_mgr ctx_mgr; struct hl_cb_mgr cb_mgr; + struct hl_ts_mgr ts_mem_mgr; struct list_head debugfs_list; struct list_head dev_node; struct kref refcount; @@ -2518,7 +2613,7 @@ struct hl_reset_info { * @cq_wq: work queues of completion queues for executing work in process * context. * @eq_wq: work queue of event queue for executing work in process context. - * @sob_reset_wq: work queue for sob reset executions. + * @ts_free_obj_wq: work queue for timestamp registration objects release. * @kernel_ctx: Kernel driver context structure. * @kernel_queues: array of hl_hw_queue. * @cs_mirror_list: CS mirror list for TDR. @@ -2569,9 +2664,6 @@ struct hl_reset_info { * @max_power: the max power of the device, as configured by the sysadmin. This * value is saved so in case of hard-reset, the driver will restore * this value and update the F/W after the re-initialization - * @clock_gating_mask: is clock gating enabled. bitmask that represents the - * different engines. See debugfs-driver-habanalabs for - * details. * @boot_error_status_mask: contains a mask of the device boot error status. * Each bit represents a different error, according to * the defines in hl_boot_if.h. If the bit is cleared, @@ -2611,8 +2703,6 @@ struct hl_reset_info { * @in_debug: whether the device is in a state where the profiling/tracing infrastructure * can be used. This indication is needed because in some ASICs we need to do * specific operations to enable that infrastructure. - * @power9_64bit_dma_enable: true to enable 64-bit DMA mask support. Relevant - * only to POWER9 machines. * @cdev_sysfs_created: were char devices and sysfs nodes created. * @stop_on_err: true if engines should stop on error. * @supports_sync_stream: is sync stream supported. @@ -2651,7 +2741,7 @@ struct hl_device { struct hl_user_interrupt common_user_interrupt; struct workqueue_struct **cq_wq; struct workqueue_struct *eq_wq; - struct workqueue_struct *sob_reset_wq; + struct workqueue_struct *ts_free_obj_wq; struct hl_ctx *kernel_ctx; struct hl_hw_queue *kernel_queues; struct list_head cs_mirror_list; @@ -2710,7 +2800,6 @@ struct hl_device { atomic64_t dram_used_mem; u64 timeout_jiffies; u64 max_power; - u64 clock_gating_mask; u64 boot_error_status_mask; u64 dram_pci_bar_start; u64 last_successful_open_jif; @@ -2736,7 +2825,6 @@ struct hl_device { u8 device_cpu_disabled; u8 dma_mask; u8 in_debug; - u8 power9_64bit_dma_enable; u8 cdev_sysfs_created; u8 stop_on_err; u8 supports_sync_stream; @@ -2970,7 +3058,7 @@ int hl_cb_pool_fini(struct hl_device *hdev); int hl_cb_va_pool_init(struct hl_ctx *ctx); void hl_cb_va_pool_fini(struct hl_ctx *ctx); -void hl_cs_rollback_all(struct hl_device *hdev); +void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush); struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, enum hl_queue_type queue_type, bool is_kernel_allocated_cb); void hl_sob_reset_error(struct kref *ref); @@ -3024,6 +3112,9 @@ int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size); int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags); int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard, u32 flags, u32 asid, u64 va, u64 size); +u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte); +u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop, + u8 hop_idx, u64 hop_addr, u64 virt_addr); void hl_mmu_swap_out(struct hl_ctx *ctx); void hl_mmu_swap_in(struct hl_ctx *ctx); int hl_mmu_if_set_funcs(struct hl_device *hdev); @@ -3094,39 +3185,26 @@ enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr); int hl_pci_init(struct hl_device *hdev); void hl_pci_fini(struct hl_device *hdev); -long hl_get_frequency(struct hl_device *hdev, u32 pll_index, - bool curr); -void hl_set_frequency(struct hl_device *hdev, u32 pll_index, - u64 freq); -int hl_get_temperature(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_set_temperature(struct hl_device *hdev, - int sensor_index, u32 attr, long value); -int hl_get_voltage(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_current(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_fan_speed(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_pwm_info(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, - long value); -u64 hl_get_max_power(struct hl_device *hdev); -void hl_set_max_power(struct hl_device *hdev); -int hl_set_voltage(struct hl_device *hdev, - int sensor_index, u32 attr, long value); -int hl_set_current(struct hl_device *hdev, - int sensor_index, u32 attr, long value); -int hl_set_power(struct hl_device *hdev, - int sensor_index, u32 attr, long value); -int hl_get_power(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_clk_rate(struct hl_device *hdev, - u32 *cur_clk, u32 *max_clk); -void hl_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq); -void hl_add_device_attr(struct hl_device *hdev, - struct attribute_group *dev_attr_grp); +long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr); +void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq); +int hl_get_temperature(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +int hl_set_temperature(struct hl_device *hdev, int sensor_index, u32 attr, long value); +int hl_get_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +int hl_get_current(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +int hl_get_fan_speed(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +int hl_get_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long value); +long hl_fw_get_max_power(struct hl_device *hdev); +void hl_fw_set_max_power(struct hl_device *hdev); +int hl_set_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long value); +int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value); +int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value); +int hl_get_power(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); +void hl_fw_set_pll_profile(struct hl_device *hdev); +void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp); +void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp); + void hw_sob_get(struct hl_hw_sob *hw_sob); void hw_sob_put(struct hl_hw_sob *hw_sob); void hl_encaps_handle_do_release(struct kref *ref); @@ -3146,6 +3224,11 @@ __printf(4, 5) int hl_snprintf_resize(char **buf, size_t *size, size_t *offset, const char *format, ...); char *hl_format_as_binary(char *buf, size_t buf_len, u32 n); const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type); +void hl_ts_mgr_init(struct hl_ts_mgr *mgr); +void hl_ts_mgr_fini(struct hl_device *hdev, struct hl_ts_mgr *mgr); +int hl_ts_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma); +struct hl_ts_buff *hl_ts_get(struct hl_device *hdev, struct hl_ts_mgr *mgr, u32 handle); +void hl_ts_put(struct hl_ts_buff *buff); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 690b763c7a95..ca404ed9d9a7 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -140,6 +140,7 @@ int hl_device_open(struct inode *inode, struct file *filp) hl_cb_mgr_init(&hpriv->cb_mgr); hl_ctx_mgr_init(&hpriv->ctx_mgr); + hl_ts_mgr_init(&hpriv->ts_mem_mgr); hpriv->taskpid = get_task_pid(current, PIDTYPE_PID); @@ -184,6 +185,7 @@ int hl_device_open(struct inode *inode, struct file *filp) out_err: mutex_unlock(&hdev->fpriv_list_lock); hl_cb_mgr_fini(hpriv->hdev, &hpriv->cb_mgr); + hl_ts_mgr_fini(hpriv->hdev, &hpriv->ts_mem_mgr); hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr); filp->private_data = NULL; mutex_destroy(&hpriv->restore_phase_mutex); @@ -256,7 +258,6 @@ static void set_driver_behavior_per_device(struct hl_device *hdev) hdev->cpu_queues_enable = 1; hdev->heartbeat = 1; hdev->mmu_enable = 1; - hdev->clock_gating_mask = ULONG_MAX; hdev->sram_scrambler_enable = 1; hdev->dram_scrambler_enable = 1; hdev->bmc_enable = 1; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 3ba3a8ffda3e..c13a3c2a7013 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -92,8 +92,8 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) hw_ip.psoc_pci_pll_od = prop->psoc_pci_pll_od; hw_ip.psoc_pci_pll_div_factor = prop->psoc_pci_pll_div_factor; - hw_ip.first_available_interrupt_id = - prop->first_available_user_msix_interrupt; + hw_ip.first_available_interrupt_id = prop->first_available_user_msix_interrupt; + hw_ip.number_of_user_interrupts = prop->user_interrupt_count; hw_ip.server_type = prop->server_type; return copy_to_user(out, &hw_ip, @@ -251,13 +251,12 @@ static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args) if ((!max_size) || (!out)) return -EINVAL; - rc = hdev->asic_funcs->get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, - &clk_rate.max_clk_rate_mhz); + rc = hl_fw_get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, &clk_rate.max_clk_rate_mhz); if (rc) return rc; - return copy_to_user(out, &clk_rate, - min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0; + return copy_to_user(out, &clk_rate, min_t(size_t, max_size, sizeof(clk_rate))) + ? -EFAULT : 0; } static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args) diff --git a/drivers/misc/habanalabs/common/hwmgr.c b/drivers/misc/habanalabs/common/hwmgr.c deleted file mode 100644 index 5451019f143f..000000000000 --- a/drivers/misc/habanalabs/common/hwmgr.c +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2019-2021 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -void hl_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) -{ - hl_set_frequency(hdev, hdev->asic_prop.clk_pll_index, - hdev->asic_prop.max_freq_value); -} - -int hl_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) -{ - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); - - if (value < 0) { - dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", - value); - return value; - } - - *max_clk = (value / 1000 / 1000); - - value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); - - if (value < 0) { - dev_err(hdev->dev, - "Failed to retrieve device current clock %ld\n", - value); - return value; - } - - *cur_clk = (value / 1000 / 1000); - - return 0; -} - -static ssize_t clk_max_freq_mhz_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); - - hdev->asic_prop.max_freq_value = value; - - return sprintf(buf, "%lu\n", (value / 1000 / 1000)); -} - -static ssize_t clk_max_freq_mhz_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - int rc; - u64 value; - - if (!hl_device_operational(hdev, NULL)) { - count = -ENODEV; - goto fail; - } - - rc = kstrtoull(buf, 0, &value); - if (rc) { - count = -EINVAL; - goto fail; - } - - hdev->asic_prop.max_freq_value = value * 1000 * 1000; - - hl_set_frequency(hdev, hdev->asic_prop.clk_pll_index, - hdev->asic_prop.max_freq_value); - -fail: - return count; -} - -static ssize_t clk_cur_freq_mhz_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); - - return sprintf(buf, "%lu\n", (value / 1000 / 1000)); -} - -static DEVICE_ATTR_RW(clk_max_freq_mhz); -static DEVICE_ATTR_RO(clk_cur_freq_mhz); - -static struct attribute *hl_dev_attrs[] = { - &dev_attr_clk_max_freq_mhz.attr, - &dev_attr_clk_cur_freq_mhz.attr, - NULL, -}; - -void hl_add_device_attr(struct hl_device *hdev, - struct attribute_group *dev_attr_grp) -{ - dev_attr_grp->attrs = hl_dev_attrs; -} diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 1b6bdc900c26..e2bc128f2291 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -137,22 +137,137 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) return IRQ_HANDLED; } +/* + * hl_ts_free_objects - handler of the free objects workqueue. + * This function should put refcount to objects that the registration node + * took refcount to them. + * @work: workqueue object pointer + */ +static void hl_ts_free_objects(struct work_struct *work) +{ + struct timestamp_reg_work_obj *job = + container_of(work, struct timestamp_reg_work_obj, free_obj); + struct timestamp_reg_free_node *free_obj, *temp_free_obj; + struct list_head *free_list_head = job->free_obj_head; + struct hl_device *hdev = job->hdev; + + list_for_each_entry_safe(free_obj, temp_free_obj, free_list_head, free_objects_node) { + dev_dbg(hdev->dev, "About to put refcount to ts_buff (%p) cq_cb(%p)\n", + free_obj->ts_buff, + free_obj->cq_cb); + + hl_ts_put(free_obj->ts_buff); + hl_cb_put(free_obj->cq_cb); + kfree(free_obj); + } + + kfree(free_list_head); + kfree(job); +} + +/* + * This function called with spin_lock of wait_list_lock taken + * This function will set timestamp and delete the registration node from the + * wait_list_lock. + * and since we're protected with spin_lock here, so we cannot just put the refcount + * for the objects here, since the release function may be called and it's also a long + * logic (which might sleep also) that cannot be handled in irq context. + * so here we'll be filling a list with nodes of "put" jobs and then will send this + * list to a dedicated workqueue to do the actual put. + */ +static int handle_registration_node(struct hl_device *hdev, struct hl_user_pending_interrupt *pend, + struct list_head **free_list) +{ + struct timestamp_reg_free_node *free_node; + u64 timestamp; + + if (!(*free_list)) { + /* Alloc/Init the timestamp registration free objects list */ + *free_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC); + if (!(*free_list)) + return -ENOMEM; + + INIT_LIST_HEAD(*free_list); + } + + free_node = kmalloc(sizeof(*free_node), GFP_ATOMIC); + if (!free_node) + return -ENOMEM; + + timestamp = ktime_get_ns(); + + *pend->ts_reg_info.timestamp_kernel_addr = timestamp; + + dev_dbg(hdev->dev, "Timestamp is set to ts cb address (%p), ts: 0x%llx\n", + pend->ts_reg_info.timestamp_kernel_addr, + *(u64 *)pend->ts_reg_info.timestamp_kernel_addr); + + list_del(&pend->wait_list_node); + + /* Mark kernel CB node as free */ + pend->ts_reg_info.in_use = 0; + + /* Putting the refcount for ts_buff and cq_cb objects will be handled + * in workqueue context, just add job to free_list. + */ + free_node->ts_buff = pend->ts_reg_info.ts_buff; + free_node->cq_cb = pend->ts_reg_info.cq_cb; + list_add(&free_node->free_objects_node, *free_list); + + return 0; +} + static void handle_user_cq(struct hl_device *hdev, struct hl_user_interrupt *user_cq) { - struct hl_user_pending_interrupt *pend; + struct hl_user_pending_interrupt *pend, *temp_pend; + struct list_head *ts_reg_free_list_head = NULL; + struct timestamp_reg_work_obj *job; + bool reg_node_handle_fail = false; ktime_t now = ktime_get(); + int rc; + + /* For registration nodes: + * As part of handling the registration nodes, we should put refcount to + * some objects. the problem is that we cannot do that under spinlock + * or in irq handler context at all (since release functions are long and + * might sleep), so we will need to handle that part in workqueue context. + * To avoid handling kmalloc failure which compels us rolling back actions + * and move nodes hanged on the free list back to the interrupt wait list + * we always alloc the job of the WQ at the beginning. + */ + job = kmalloc(sizeof(*job), GFP_ATOMIC); + if (!job) + return; spin_lock(&user_cq->wait_list_lock); - list_for_each_entry(pend, &user_cq->wait_list_head, wait_list_node) { - if ((pend->cq_kernel_addr && - *(pend->cq_kernel_addr) >= pend->cq_target_value) || + list_for_each_entry_safe(pend, temp_pend, &user_cq->wait_list_head, wait_list_node) { + if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) || !pend->cq_kernel_addr) { - pend->fence.timestamp = now; - complete_all(&pend->fence.completion); + if (pend->ts_reg_info.ts_buff) { + if (!reg_node_handle_fail) { + rc = handle_registration_node(hdev, pend, + &ts_reg_free_list_head); + if (rc) + reg_node_handle_fail = true; + } + } else { + /* Handle wait target value node */ + pend->fence.timestamp = now; + complete_all(&pend->fence.completion); + } } } spin_unlock(&user_cq->wait_list_lock); + + if (ts_reg_free_list_head) { + INIT_WORK(&job->free_obj, hl_ts_free_objects); + job->free_obj_head = ts_reg_free_list_head; + job->hdev = hdev; + queue_work(hdev->ts_free_obj_wq, &job->free_obj); + } else { + kfree(job); + } } /** diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index c1eefaebacb6..e008d82e4ba3 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -11,6 +11,7 @@ #include <linux/uaccess.h> #include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/pci-p2pdma.h> MODULE_IMPORT_NS(DMA_BUF); @@ -20,6 +21,34 @@ MODULE_IMPORT_NS(DMA_BUF); /* use small pages for supporting non-pow2 (32M/40M/48M) DRAM phys page sizes */ #define DRAM_POOL_PAGE_SIZE SZ_8M +static int allocate_timestamps_buffers(struct hl_fpriv *hpriv, + struct hl_mem_in *args, u64 *handle); + +static int set_alloc_page_size(struct hl_device *hdev, struct hl_mem_in *args, u32 *page_size) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + u32 psize; + + /* + * for ASIC that supports setting the allocation page size by user we will address + * user's choice only if it is not 0 (as 0 means taking the default page size) + */ + if (prop->supports_user_set_page_size && args->alloc.page_size) { + psize = args->alloc.page_size; + + if (!hdev->asic_funcs->is_valid_dram_page_size(psize)) { + dev_err(hdev->dev, "user page size (%#x) is not valid\n", psize); + return -EINVAL; + } + } else { + psize = hdev->asic_prop.dram_page_size; + } + + *page_size = psize; + + return 0; +} + /* * The va ranges in context object contain a list with the available chunks of * device virtual memory. @@ -61,11 +90,15 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args, struct hl_vm_phys_pg_pack *phys_pg_pack; u64 paddr = 0, total_size, num_pgs, i; u32 num_curr_pgs, page_size; - int handle, rc; bool contiguous; + int handle, rc; num_curr_pgs = 0; - page_size = hdev->asic_prop.dram_page_size; + + rc = set_alloc_page_size(hdev, args, &page_size); + if (rc) + return rc; + num_pgs = DIV_ROUND_UP_ULL(args->alloc.mem_size, page_size); total_size = num_pgs * page_size; @@ -77,7 +110,11 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args, contiguous = args->flags & HL_MEM_CONTIGUOUS; if (contiguous) { - paddr = (u64) gen_pool_alloc(vm->dram_pg_pool, total_size); + if (is_power_of_2(page_size)) + paddr = (u64) (uintptr_t) gen_pool_dma_alloc_align(vm->dram_pg_pool, + total_size, NULL, page_size); + else + paddr = (u64) (uintptr_t) gen_pool_alloc(vm->dram_pg_pool, total_size); if (!paddr) { dev_err(hdev->dev, "failed to allocate %llu contiguous pages with total size of %llu\n", @@ -111,9 +148,14 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args, phys_pg_pack->pages[i] = paddr + i * page_size; } else { for (i = 0 ; i < num_pgs ; i++) { - phys_pg_pack->pages[i] = (u64) gen_pool_alloc( - vm->dram_pg_pool, - page_size); + if (is_power_of_2(page_size)) + phys_pg_pack->pages[i] = + (u64) gen_pool_dma_alloc_align(vm->dram_pg_pool, + page_size, NULL, + page_size); + else + phys_pg_pack->pages[i] = (u64) gen_pool_alloc(vm->dram_pg_pool, + page_size); if (!phys_pg_pack->pages[i]) { dev_err(hdev->dev, "Failed to allocate device memory (out of memory)\n"); @@ -652,7 +694,7 @@ static u64 get_va_block(struct hl_device *hdev, continue; /* - * In case hint address is 0, and arc_hints_range_reservation + * In case hint address is 0, and hints_range_reservation * property enabled, then avoid allocating va blocks from the * range reserved for hint addresses */ @@ -1967,16 +2009,15 @@ err_dec_exporting_cnt: static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) { struct hl_device *hdev = hpriv->hdev; - struct hl_ctx *ctx = hpriv->ctx; u64 block_handle, device_addr = 0; + struct hl_ctx *ctx = hpriv->ctx; u32 handle = 0, block_size; - int rc, dmabuf_fd = -EBADF; + int rc; switch (args->in.op) { case HL_MEM_OP_ALLOC: if (args->in.alloc.mem_size == 0) { - dev_err(hdev->dev, - "alloc size must be larger than 0\n"); + dev_err(hdev->dev, "alloc size must be larger than 0\n"); rc = -EINVAL; goto out; } @@ -1997,15 +2038,14 @@ static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) case HL_MEM_OP_MAP: if (args->in.flags & HL_MEM_USERPTR) { - device_addr = args->in.map_host.host_virt_addr; - rc = 0; + dev_err(hdev->dev, "Failed to map host memory when MMU is disabled\n"); + rc = -EPERM; } else { - rc = get_paddr_from_handle(ctx, &args->in, - &device_addr); + rc = get_paddr_from_handle(ctx, &args->in, &device_addr); + memset(args, 0, sizeof(*args)); + args->out.device_virt_addr = device_addr; } - memset(args, 0, sizeof(*args)); - args->out.device_virt_addr = device_addr; break; case HL_MEM_OP_UNMAP: @@ -2013,22 +2053,19 @@ static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) break; case HL_MEM_OP_MAP_BLOCK: - rc = map_block(hdev, args->in.map_block.block_addr, - &block_handle, &block_size); + rc = map_block(hdev, args->in.map_block.block_addr, &block_handle, &block_size); args->out.block_handle = block_handle; args->out.block_size = block_size; break; case HL_MEM_OP_EXPORT_DMABUF_FD: - rc = export_dmabuf_from_addr(ctx, - args->in.export_dmabuf_fd.handle, - args->in.export_dmabuf_fd.mem_size, - args->in.flags, - &dmabuf_fd); - memset(args, 0, sizeof(*args)); - args->out.fd = dmabuf_fd; + dev_err(hdev->dev, "Failed to export dma-buf object when MMU is disabled\n"); + rc = -EPERM; break; + case HL_MEM_OP_TS_ALLOC: + rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle); + break; default: dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); rc = -EINVAL; @@ -2039,6 +2076,258 @@ out: return rc; } +static void ts_buff_release(struct kref *ref) +{ + struct hl_ts_buff *buff; + + buff = container_of(ref, struct hl_ts_buff, refcount); + + vfree(buff->kernel_buff_address); + vfree(buff->user_buff_address); + kfree(buff); +} + +struct hl_ts_buff *hl_ts_get(struct hl_device *hdev, struct hl_ts_mgr *mgr, + u32 handle) +{ + struct hl_ts_buff *buff; + + spin_lock(&mgr->ts_lock); + buff = idr_find(&mgr->ts_handles, handle); + if (!buff) { + spin_unlock(&mgr->ts_lock); + dev_warn(hdev->dev, + "TS buff get failed, no match to handle 0x%x\n", handle); + return NULL; + } + kref_get(&buff->refcount); + spin_unlock(&mgr->ts_lock); + + return buff; +} + +void hl_ts_put(struct hl_ts_buff *buff) +{ + kref_put(&buff->refcount, ts_buff_release); +} + +static void buff_vm_close(struct vm_area_struct *vma) +{ + struct hl_ts_buff *buff = (struct hl_ts_buff *) vma->vm_private_data; + long new_mmap_size; + + new_mmap_size = buff->mmap_size - (vma->vm_end - vma->vm_start); + + if (new_mmap_size > 0) { + buff->mmap_size = new_mmap_size; + return; + } + + atomic_set(&buff->mmap, 0); + hl_ts_put(buff); + vma->vm_private_data = NULL; +} + +static const struct vm_operations_struct ts_buff_vm_ops = { + .close = buff_vm_close +}; + +int hl_ts_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_ts_buff *buff; + u32 handle, user_buff_size; + int rc; + + /* We use the page offset to hold the idr and thus we need to clear + * it before doing the mmap itself + */ + handle = vma->vm_pgoff; + vma->vm_pgoff = 0; + + buff = hl_ts_get(hdev, &hpriv->ts_mem_mgr, handle); + if (!buff) { + dev_err(hdev->dev, + "TS buff mmap failed, no match to handle 0x%x\n", handle); + return -EINVAL; + } + + /* Validation check */ + user_buff_size = vma->vm_end - vma->vm_start; + if (user_buff_size != ALIGN(buff->user_buff_size, PAGE_SIZE)) { + dev_err(hdev->dev, + "TS buff mmap failed, mmap size 0x%x != 0x%x buff size\n", + user_buff_size, ALIGN(buff->user_buff_size, PAGE_SIZE)); + rc = -EINVAL; + goto put_buff; + } + +#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK + if (!access_ok(VERIFY_WRITE, + (void __user *) (uintptr_t) vma->vm_start, user_buff_size)) { +#else + if (!access_ok((void __user *) (uintptr_t) vma->vm_start, + user_buff_size)) { +#endif + dev_err(hdev->dev, + "user pointer is invalid - 0x%lx\n", + vma->vm_start); + + rc = -EINVAL; + goto put_buff; + } + + if (atomic_cmpxchg(&buff->mmap, 0, 1)) { + dev_err(hdev->dev, "TS buff memory mmap failed, already mmaped to user\n"); + rc = -EINVAL; + goto put_buff; + } + + vma->vm_ops = &ts_buff_vm_ops; + vma->vm_private_data = buff; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY | VM_NORESERVE; + rc = remap_vmalloc_range(vma, buff->user_buff_address, 0); + if (rc) { + atomic_set(&buff->mmap, 0); + goto put_buff; + } + + buff->mmap_size = buff->user_buff_size; + vma->vm_pgoff = handle; + + return 0; + +put_buff: + hl_ts_put(buff); + return rc; +} + +void hl_ts_mgr_init(struct hl_ts_mgr *mgr) +{ + spin_lock_init(&mgr->ts_lock); + idr_init(&mgr->ts_handles); +} + +void hl_ts_mgr_fini(struct hl_device *hdev, struct hl_ts_mgr *mgr) +{ + struct hl_ts_buff *buff; + struct idr *idp; + u32 id; + + idp = &mgr->ts_handles; + + idr_for_each_entry(idp, buff, id) { + if (kref_put(&buff->refcount, ts_buff_release) != 1) + dev_err(hdev->dev, "TS buff handle %d for CTX is still alive\n", + id); + } + + idr_destroy(&mgr->ts_handles); +} + +static struct hl_ts_buff *hl_ts_alloc_buff(struct hl_device *hdev, u32 num_elements) +{ + struct hl_ts_buff *ts_buff = NULL; + u32 size; + void *p; + + ts_buff = kzalloc(sizeof(*ts_buff), GFP_KERNEL); + if (!ts_buff) + return NULL; + + /* Allocate the user buffer */ + size = num_elements * sizeof(u64); + p = vmalloc_user(size); + if (!p) + goto free_mem; + + ts_buff->user_buff_address = p; + ts_buff->user_buff_size = size; + + /* Allocate the internal kernel buffer */ + size = num_elements * sizeof(struct hl_user_pending_interrupt); + p = vmalloc(size); + if (!p) + goto free_user_buff; + + ts_buff->kernel_buff_address = p; + ts_buff->kernel_buff_size = size; + + return ts_buff; + +free_user_buff: + vfree(ts_buff->user_buff_address); +free_mem: + kfree(ts_buff); + return NULL; +} + +/** + * allocate_timestamps_buffers() - allocate timestamps buffers + * This function will allocate ts buffer that will later on be mapped to the user + * in order to be able to read the timestamp. + * in additon it'll allocate an extra buffer for registration management. + * since we cannot fail during registration for out-of-memory situation, so + * we'll prepare a pool which will be used as user interrupt nodes and instead + * of dynamically allocating nodes while registration we'll pick the node from + * this pool. in addtion it'll add node to the mapping hash which will be used + * to map user ts buffer to the internal kernel ts buffer. + * @hpriv: pointer to the private data of the fd + * @args: ioctl input + * @handle: user timestamp buffer handle as an output + */ +static int allocate_timestamps_buffers(struct hl_fpriv *hpriv, struct hl_mem_in *args, u64 *handle) +{ + struct hl_ts_mgr *ts_mgr = &hpriv->ts_mem_mgr; + struct hl_device *hdev = hpriv->hdev; + struct hl_ts_buff *ts_buff; + int rc = 0; + + if (args->num_of_elements > TS_MAX_ELEMENTS_NUM) { + dev_err(hdev->dev, "Num of elements exceeds Max allowed number (0x%x > 0x%x)\n", + args->num_of_elements, TS_MAX_ELEMENTS_NUM); + return -EINVAL; + } + + /* Allocate ts buffer object + * This object will contain two buffers one that will be mapped to the user + * and another internal buffer for the driver use only, which won't be mapped + * to the user. + */ + ts_buff = hl_ts_alloc_buff(hdev, args->num_of_elements); + if (!ts_buff) { + rc = -ENOMEM; + goto out_err; + } + + spin_lock(&ts_mgr->ts_lock); + rc = idr_alloc(&ts_mgr->ts_handles, ts_buff, 1, 0, GFP_ATOMIC); + spin_unlock(&ts_mgr->ts_lock); + if (rc < 0) { + dev_err(hdev->dev, "Failed to allocate IDR for a new ts buffer\n"); + goto release_ts_buff; + } + + ts_buff->id = rc; + ts_buff->hdev = hdev; + + kref_init(&ts_buff->refcount); + + /* idr is 32-bit so we can safely OR it with a mask that is above 32 bit */ + *handle = (u64) ts_buff->id | HL_MMAP_TYPE_TS_BUFF; + *handle <<= PAGE_SHIFT; + + dev_dbg(hdev->dev, "Created ts buff object handle(%u)\n", ts_buff->id); + + return 0; + +release_ts_buff: + kref_put(&ts_buff->refcount, ts_buff_release); +out_err: + *handle = 0; + return rc; +} + int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) { enum hl_device_status status; @@ -2154,6 +2443,9 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) args->out.fd = dmabuf_fd; break; + case HL_MEM_OP_TS_ALLOC: + rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle); + break; default: dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); rc = -EINVAL; @@ -2607,11 +2899,12 @@ int hl_vm_ctx_init(struct hl_ctx *ctx) */ void hl_vm_ctx_fini(struct hl_ctx *ctx) { + struct hl_vm_phys_pg_pack *phys_pg_list, *tmp_phys_node; struct hl_device *hdev = ctx->hdev; - struct hl_vm *vm = &hdev->vm; - struct hl_vm_phys_pg_pack *phys_pg_list; struct hl_vm_hash_node *hnode; + struct hl_vm *vm = &hdev->vm; struct hlist_node *tmp_node; + struct list_head free_list; struct hl_mem_in args; int i; @@ -2644,19 +2937,24 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) mutex_unlock(&ctx->mmu_lock); + INIT_LIST_HEAD(&free_list); + spin_lock(&vm->idr_lock); idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i) if (phys_pg_list->asid == ctx->asid) { dev_dbg(hdev->dev, "page list 0x%px of asid %d is still alive\n", phys_pg_list, ctx->asid); - atomic64_sub(phys_pg_list->total_size, - &hdev->dram_used_mem); - free_phys_pg_pack(hdev, phys_pg_list); + + atomic64_sub(phys_pg_list->total_size, &hdev->dram_used_mem); idr_remove(&vm->phys_pg_pack_handles, i); + list_add(&phys_pg_list->node, &free_list); } spin_unlock(&vm->idr_lock); + list_for_each_entry_safe(phys_pg_list, tmp_phys_node, &free_list, node) + free_phys_pg_pack(hdev, phys_pg_list); + va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_DRAM]); va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_HOST]); diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index 9153a1f55175..810b73421ce1 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -662,3 +662,58 @@ int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard, return rc; } +u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte) +{ + return (curr_pte & PAGE_PRESENT_MASK) ? (curr_pte & HOP_PHYS_ADDR_MASK) : ULLONG_MAX; +} + +/** + * hl_mmu_get_hop_pte_phys_addr() - extract PTE address from HOP + * @ctx: pointer to the context structure to initialize. + * @hop_idx: HOP index. + * @hop_addr: HOP address. + * @virt_addr: virtual address fro the translation. + * + * @return the matching PTE value on success, otherwise U64_MAX. + */ +u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop, + u8 hop_idx, u64 hop_addr, u64 virt_addr) +{ + u64 mask, shift; + + if (hop_idx >= mmu_prop->num_hops) { + dev_err_ratelimited(ctx->hdev->dev, "Invalid hop index %d\n", hop_idx); + return U64_MAX; + } + + /* currently max number of HOPs is 6 */ + switch (hop_idx) { + case 0: + mask = mmu_prop->hop0_mask; + shift = mmu_prop->hop0_shift; + break; + case 1: + mask = mmu_prop->hop1_mask; + shift = mmu_prop->hop1_shift; + break; + case 2: + mask = mmu_prop->hop2_mask; + shift = mmu_prop->hop2_shift; + break; + case 3: + mask = mmu_prop->hop3_mask; + shift = mmu_prop->hop3_shift; + break; + case 4: + mask = mmu_prop->hop4_mask; + shift = mmu_prop->hop4_shift; + break; + default: + mask = mmu_prop->hop5_mask; + shift = mmu_prop->hop5_shift; + break; + } + + return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * ((virt_addr & mask) >> shift); +} + diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v1.c b/drivers/misc/habanalabs/common/mmu/mmu_v1.c index 6134b6ae7615..d03786d0c407 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu_v1.c +++ b/drivers/misc/habanalabs/common/mmu/mmu_v1.c @@ -217,18 +217,10 @@ static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, mmu_prop->hop4_shift); } -static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte) -{ - if (curr_pte & PAGE_PRESENT_MASK) - return curr_pte & HOP_PHYS_ADDR_MASK; - else - return ULLONG_MAX; -} - static inline u64 get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte, bool *is_new_hop) { - u64 hop_addr = get_next_hop_addr(ctx, curr_pte); + u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte); if (hop_addr == ULLONG_MAX) { hop_addr = alloc_hop(ctx); @@ -467,7 +459,7 @@ static void hl_mmu_v1_fini(struct hl_device *hdev) { /* MMU H/W fini was already done in device hw_fini() */ - if (!ZERO_OR_NULL_PTR(hdev->mmu_priv.hr.mmu_shadow_hop0)) { + if (!ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0)) { kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0); gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool); @@ -546,7 +538,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx, curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; - hop1_addr = get_next_hop_addr(ctx, curr_pte); + hop1_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte); if (hop1_addr == ULLONG_MAX) goto not_mapped; @@ -555,7 +547,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx, curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; - hop2_addr = get_next_hop_addr(ctx, curr_pte); + hop2_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte); if (hop2_addr == ULLONG_MAX) goto not_mapped; @@ -564,7 +556,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx, curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; - hop3_addr = get_next_hop_addr(ctx, curr_pte); + hop3_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte); if (hop3_addr == ULLONG_MAX) goto not_mapped; @@ -582,7 +574,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx, } if (!is_huge) { - hop4_addr = get_next_hop_addr(ctx, curr_pte); + hop4_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte); if (hop4_addr == ULLONG_MAX) goto not_mapped; @@ -845,27 +837,6 @@ static void hl_mmu_v1_swap_in(struct hl_ctx *ctx) } -static inline u64 get_hop_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_prop, - int hop_num, u64 hop_addr, u64 virt_addr) -{ - switch (hop_num) { - case 0: - return get_hop0_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - case 1: - return get_hop1_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - case 2: - return get_hop2_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - case 3: - return get_hop3_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - case 4: - return get_hop4_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - default: - break; - } - return U64_MAX; -} - static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops) { @@ -906,7 +877,7 @@ static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, hops->hop_info[0].hop_addr = get_phys_hop0_addr(ctx); hops->hop_info[0].hop_pte_addr = - get_hop_pte_addr(ctx, mmu_prop, 0, + hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0, hops->hop_info[0].hop_addr, virt_addr); hops->hop_info[0].hop_pte_val = hdev->asic_funcs->read_pte(hdev, @@ -914,13 +885,13 @@ static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, for (i = 1 ; i < used_hops ; i++) { hops->hop_info[i].hop_addr = - get_next_hop_addr(ctx, + hl_mmu_get_next_hop_addr(ctx, hops->hop_info[i - 1].hop_pte_val); if (hops->hop_info[i].hop_addr == ULLONG_MAX) return -EFAULT; hops->hop_info[i].hop_pte_addr = - get_hop_pte_addr(ctx, mmu_prop, i, + hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i, hops->hop_info[i].hop_addr, virt_addr); hops->hop_info[i].hop_pte_val = diff --git a/drivers/misc/habanalabs/common/pci/pci.c b/drivers/misc/habanalabs/common/pci/pci.c index 0b5366cc84fd..bb9ce22bafc4 100644 --- a/drivers/misc/habanalabs/common/pci/pci.c +++ b/drivers/misc/habanalabs/common/pci/pci.c @@ -338,10 +338,7 @@ int hl_pci_set_outbound_region(struct hl_device *hdev, lower_32_bits(outbound_region_end_address)); rc |= hl_pci_iatu_write(hdev, 0x014, 0); - if ((hdev->power9_64bit_dma_enable) && (hdev->dma_mask == 64)) - rc |= hl_pci_iatu_write(hdev, 0x018, 0x08000000); - else - rc |= hl_pci_iatu_write(hdev, 0x018, 0); + rc |= hl_pci_iatu_write(hdev, 0x018, 0); rc |= hl_pci_iatu_write(hdev, 0x020, upper_32_bits(outbound_region_end_address)); @@ -411,13 +408,13 @@ int hl_pci_init(struct hl_device *hdev) rc = hdev->asic_funcs->pci_bars_map(hdev); if (rc) { - dev_err(hdev->dev, "Failed to initialize PCI BARs\n"); + dev_err(hdev->dev, "Failed to map PCI BAR addresses\n"); goto disable_device; } rc = hdev->asic_funcs->init_iatu(hdev); if (rc) { - dev_err(hdev->dev, "Failed to initialize iATU\n"); + dev_err(hdev->dev, "PCI controller was not initialized successfully\n"); goto unmap_pci_bars; } diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 45c715325e2a..9ebeb18ab85e 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -9,105 +9,91 @@ #include <linux/pci.h> -long hl_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr) +static ssize_t clk_max_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct cpucp_packet pkt; - u32 used_pll_idx; - u64 result; - int rc; - - rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); - if (rc) - return rc; - - memset(&pkt, 0, sizeof(pkt)); + struct hl_device *hdev = dev_get_drvdata(dev); + long value; - if (curr) - pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_CURR_GET << - CPUCP_PKT_CTL_OPCODE_SHIFT); - else - pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_GET << - CPUCP_PKT_CTL_OPCODE_SHIFT); - pkt.pll_index = cpu_to_le32((u32)used_pll_idx); + if (!hl_device_operational(hdev, NULL)) + return -ENODEV; - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - 0, &result); + value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); + if (value < 0) + return value; - if (rc) { - dev_err(hdev->dev, - "Failed to get frequency of PLL %d, error %d\n", - used_pll_idx, rc); - return rc; - } + hdev->asic_prop.max_freq_value = value; - return (long) result; + return sprintf(buf, "%lu\n", (value / 1000 / 1000)); } -void hl_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq) +static ssize_t clk_max_freq_mhz_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - struct cpucp_packet pkt; - u32 used_pll_idx; + struct hl_device *hdev = dev_get_drvdata(dev); int rc; + u64 value; - rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); - if (rc) - return; + if (!hl_device_operational(hdev, NULL)) { + count = -ENODEV; + goto fail; + } - memset(&pkt, 0, sizeof(pkt)); + rc = kstrtoull(buf, 0, &value); + if (rc) { + count = -EINVAL; + goto fail; + } - pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_SET << - CPUCP_PKT_CTL_OPCODE_SHIFT); - pkt.pll_index = cpu_to_le32((u32)used_pll_idx); - pkt.value = cpu_to_le64(freq); + hdev->asic_prop.max_freq_value = value * 1000 * 1000; - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - 0, NULL); + hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index, hdev->asic_prop.max_freq_value); - if (rc) - dev_err(hdev->dev, - "Failed to set frequency to PLL %d, error %d\n", - used_pll_idx, rc); +fail: + return count; } -u64 hl_get_max_power(struct hl_device *hdev) +static ssize_t clk_cur_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct cpucp_packet pkt; - u64 result; - int rc; + struct hl_device *hdev = dev_get_drvdata(dev); + long value; - memset(&pkt, 0, sizeof(pkt)); + if (!hl_device_operational(hdev, NULL)) + return -ENODEV; - pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET << - CPUCP_PKT_CTL_OPCODE_SHIFT); + value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); + if (value < 0) + return value; - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - 0, &result); + return sprintf(buf, "%lu\n", (value / 1000 / 1000)); +} - if (rc) { - dev_err(hdev->dev, "Failed to get max power, error %d\n", rc); - return (u64) rc; - } +static DEVICE_ATTR_RW(clk_max_freq_mhz); +static DEVICE_ATTR_RO(clk_cur_freq_mhz); - return result; -} +static struct attribute *hl_dev_clk_attrs[] = { + &dev_attr_clk_max_freq_mhz.attr, + &dev_attr_clk_cur_freq_mhz.attr, +}; -void hl_set_max_power(struct hl_device *hdev) +static ssize_t vrm_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct cpucp_packet pkt; - int rc; + struct hl_device *hdev = dev_get_drvdata(dev); + struct cpucp_info *cpucp_info; - memset(&pkt, 0, sizeof(pkt)); + cpucp_info = &hdev->asic_prop.cpucp_info; - pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_SET << - CPUCP_PKT_CTL_OPCODE_SHIFT); - pkt.value = cpu_to_le64(hdev->max_power); + if (cpucp_info->infineon_second_stage_version) + return sprintf(buf, "%#04x %#04x\n", le32_to_cpu(cpucp_info->infineon_version), + le32_to_cpu(cpucp_info->infineon_second_stage_version)); + else + return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version)); +} - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - 0, NULL); +static DEVICE_ATTR_RO(vrm_ver); - if (rc) - dev_err(hdev->dev, "Failed to set max power, error %d\n", rc); -} +static struct attribute *hl_dev_vrm_attrs[] = { + &dev_attr_vrm_ver.attr, +}; static ssize_t uboot_ver_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -158,20 +144,6 @@ static ssize_t cpucp_ver_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.cpucp_version); } -static ssize_t infineon_ver_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - if (hdev->asic_prop.cpucp_info.infineon_second_stage_version) - return sprintf(buf, "%#04x %#04x\n", - le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_version), - le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_second_stage_version)); - else - return sprintf(buf, "%#04x\n", - le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_version)); -} - static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -188,6 +160,14 @@ static ssize_t thermal_ver_show(struct device *dev, return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.thermal_version); } +static ssize_t fw_os_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.fw_os_version); +} + static ssize_t preboot_btl_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -323,7 +303,9 @@ static ssize_t max_power_show(struct device *dev, struct device_attribute *attr, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - val = hl_get_max_power(hdev); + val = hl_fw_get_max_power(hdev); + if (val < 0) + return val; return sprintf(buf, "%lu\n", val); } @@ -348,7 +330,7 @@ static ssize_t max_power_store(struct device *dev, } hdev->max_power = value; - hl_set_max_power(hdev); + hl_fw_set_max_power(hdev); out: return count; @@ -394,7 +376,6 @@ static DEVICE_ATTR_RO(device_type); static DEVICE_ATTR_RO(fuse_ver); static DEVICE_ATTR_WO(hard_reset); static DEVICE_ATTR_RO(hard_reset_cnt); -static DEVICE_ATTR_RO(infineon_ver); static DEVICE_ATTR_RW(max_power); static DEVICE_ATTR_RO(pci_addr); static DEVICE_ATTR_RO(preboot_btl_ver); @@ -403,6 +384,7 @@ static DEVICE_ATTR_RO(soft_reset_cnt); static DEVICE_ATTR_RO(status); static DEVICE_ATTR_RO(thermal_ver); static DEVICE_ATTR_RO(uboot_ver); +static DEVICE_ATTR_RO(fw_os_ver); static struct bin_attribute bin_attr_eeprom = { .attr = {.name = "eeprom", .mode = (0444)}, @@ -420,13 +402,13 @@ static struct attribute *hl_dev_attrs[] = { &dev_attr_fuse_ver.attr, &dev_attr_hard_reset.attr, &dev_attr_hard_reset_cnt.attr, - &dev_attr_infineon_ver.attr, &dev_attr_max_power.attr, &dev_attr_pci_addr.attr, &dev_attr_preboot_btl_ver.attr, &dev_attr_status.attr, &dev_attr_thermal_ver.attr, &dev_attr_uboot_ver.attr, + &dev_attr_fw_os_ver.attr, NULL, }; @@ -441,10 +423,12 @@ static struct attribute_group hl_dev_attr_group = { }; static struct attribute_group hl_dev_clks_attr_group; +static struct attribute_group hl_dev_vrm_attr_group; static const struct attribute_group *hl_dev_attr_groups[] = { &hl_dev_attr_group, &hl_dev_clks_attr_group, + &hl_dev_vrm_attr_group, NULL, }; @@ -463,13 +447,23 @@ static const struct attribute_group *hl_dev_inference_attr_groups[] = { NULL, }; +void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp) +{ + dev_clk_attr_grp->attrs = hl_dev_clk_attrs; +} + +void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp) +{ + dev_vrm_attr_grp->attrs = hl_dev_vrm_attrs; +} + int hl_sysfs_init(struct hl_device *hdev) { int rc; hdev->max_power = hdev->asic_prop.max_power_default; - hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group); + hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group, &hl_dev_vrm_attr_group); rc = device_add_groups(hdev->dev, hl_dev_attr_groups); if (rc) { diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 013c6da2e3ca..21c2b678ff72 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2021 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -458,7 +458,6 @@ struct ecc_info_extract_params { u64 block_address; u32 num_memories; bool derr; - bool disable_clock_gating; }; static int gaudi_mmu_update_asid_hop0_addr(struct hl_device *hdev, u32 asid, @@ -614,6 +613,9 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev) prop->pmmu.page_size = PAGE_SIZE_4KB; prop->pmmu.num_hops = MMU_ARCH_5_HOPS; prop->pmmu.last_mask = LAST_MASK; + /* TODO: will be duplicated until implementing per-MMU props */ + prop->pmmu.hop_table_size = prop->mmu_hop_table_size; + prop->pmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size; /* PMMU and HPMMU are the same except of page size */ memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu)); @@ -667,6 +669,10 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev) prop->use_get_power_for_reset_history = true; + prop->configurable_stop_on_err = true; + + prop->set_max_power_on_device_init = true; + return 0; } @@ -1636,7 +1642,7 @@ static int gaudi_late_init(struct hl_device *hdev) */ gaudi_mmu_prepare(hdev, 1); - hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST); + hl_fw_set_pll_profile(hdev); return 0; @@ -1896,7 +1902,6 @@ static int gaudi_sw_init(struct hl_device *hdev) goto free_cpu_accessible_dma_pool; spin_lock_init(&gaudi->hw_queues_lock); - mutex_init(&gaudi->clk_gate_mutex); hdev->supports_sync_stream = true; hdev->supports_coresight = true; @@ -1946,8 +1951,6 @@ static int gaudi_sw_fini(struct hl_device *hdev) dma_pool_destroy(hdev->dma_pool); - mutex_destroy(&gaudi->clk_gate_mutex); - kfree(gaudi); return 0; @@ -3738,76 +3741,8 @@ static void gaudi_tpc_stall(struct hl_device *hdev) WREG32(mmTPC7_CFG_TPC_STALL, 1 << TPC0_CFG_TPC_STALL_V_SHIFT); } -static void gaudi_set_clock_gating(struct hl_device *hdev) -{ - struct gaudi_device *gaudi = hdev->asic_specific; - u32 qman_offset; - bool enable; - int i; - - /* In case we are during debug session, don't enable the clock gate - * as it may interfere - */ - if (hdev->in_debug) - return; - - if (hdev->asic_prop.fw_security_enabled) - return; - - for (i = GAUDI_PCI_DMA_1, qman_offset = 0 ; i < GAUDI_HBM_DMA_1 ; i++) { - enable = !!(hdev->clock_gating_mask & - (BIT_ULL(gaudi_dma_assignment[i]))); - - qman_offset = gaudi_dma_assignment[i] * DMA_QMAN_OFFSET; - WREG32(mmDMA0_QM_CGM_CFG1 + qman_offset, - enable ? QMAN_CGM1_PWR_GATE_EN : 0); - WREG32(mmDMA0_QM_CGM_CFG + qman_offset, - enable ? QMAN_UPPER_CP_CGM_PWR_GATE_EN : 0); - } - - for (i = GAUDI_HBM_DMA_1 ; i < GAUDI_DMA_MAX ; i++) { - enable = !!(hdev->clock_gating_mask & - (BIT_ULL(gaudi_dma_assignment[i]))); - - /* GC sends work to DMA engine through Upper CP in DMA5 so - * we need to not enable clock gating in that DMA - */ - if (i == GAUDI_HBM_DMA_4) - enable = 0; - - qman_offset = gaudi_dma_assignment[i] * DMA_QMAN_OFFSET; - WREG32(mmDMA0_QM_CGM_CFG1 + qman_offset, - enable ? QMAN_CGM1_PWR_GATE_EN : 0); - WREG32(mmDMA0_QM_CGM_CFG + qman_offset, - enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0); - } - - enable = !!(hdev->clock_gating_mask & (BIT_ULL(GAUDI_ENGINE_ID_MME_0))); - WREG32(mmMME0_QM_CGM_CFG1, enable ? QMAN_CGM1_PWR_GATE_EN : 0); - WREG32(mmMME0_QM_CGM_CFG, enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0); - - enable = !!(hdev->clock_gating_mask & (BIT_ULL(GAUDI_ENGINE_ID_MME_2))); - WREG32(mmMME2_QM_CGM_CFG1, enable ? QMAN_CGM1_PWR_GATE_EN : 0); - WREG32(mmMME2_QM_CGM_CFG, enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0); - - for (i = 0, qman_offset = 0 ; i < TPC_NUMBER_OF_ENGINES ; i++) { - enable = !!(hdev->clock_gating_mask & - (BIT_ULL(GAUDI_ENGINE_ID_TPC_0 + i))); - - WREG32(mmTPC0_QM_CGM_CFG1 + qman_offset, - enable ? QMAN_CGM1_PWR_GATE_EN : 0); - WREG32(mmTPC0_QM_CGM_CFG + qman_offset, - enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0); - - qman_offset += TPC_QMAN_OFFSET; - } - - gaudi->hw_cap_initialized |= HW_CAP_CLK_GATE; -} - static void gaudi_disable_clock_gating(struct hl_device *hdev) { - struct gaudi_device *gaudi = hdev->asic_specific; u32 qman_offset; int i; @@ -3832,8 +3767,6 @@ static void gaudi_disable_clock_gating(struct hl_device *hdev) qman_offset += (mmTPC1_QM_CGM_CFG - mmTPC0_QM_CGM_CFG); } - - gaudi->hw_cap_initialized &= ~(HW_CAP_CLK_GATE); } static void gaudi_enable_timestamp(struct hl_device *hdev) @@ -3876,8 +3809,6 @@ static void gaudi_halt_engines(struct hl_device *hdev, bool hard_reset, bool fw_ gaudi_stop_hbm_dma_qmans(hdev); gaudi_stop_pci_dma_qmans(hdev); - hdev->asic_funcs->disable_clock_gating(hdev); - msleep(wait_timeout_ms); gaudi_pci_dma_stall(hdev); @@ -3931,7 +3862,7 @@ static int gaudi_mmu_init(struct hl_device *hdev) /* mem cache invalidation */ WREG32(mmSTLB_MEM_CACHE_INVALIDATION, 1); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, 0); + hl_mmu_invalidate_cache(hdev, true, 0); WREG32(mmMMU_UP_MMU_ENABLE, 1); WREG32(mmMMU_UP_SPI_MASK, 0xF); @@ -4203,10 +4134,8 @@ static int gaudi_hw_init(struct hl_device *hdev) /* In case the clock gating was enabled in preboot we need to disable * it here before touching the MME/TPC registers. - * There is no need to take clk gating mutex because when this function - * runs, no other relevant code can run */ - hdev->asic_funcs->disable_clock_gating(hdev); + gaudi_disable_clock_gating(hdev); /* SRAM scrambler must be initialized after CPU is running from HBM */ gaudi_init_scrambler_sram(hdev); @@ -4232,8 +4161,6 @@ static int gaudi_hw_init(struct hl_device *hdev) gaudi_init_nic_qmans(hdev); - hdev->asic_funcs->set_clock_gating(hdev); - gaudi_enable_timestamp(hdev); /* MSI must be enabled before CPU queues and NIC are initialized */ @@ -4400,14 +4327,11 @@ skip_reset: status); if (gaudi) { - gaudi->hw_cap_initialized &= ~(HW_CAP_CPU | HW_CAP_CPU_Q | - HW_CAP_HBM | HW_CAP_PCI_DMA | - HW_CAP_MME | HW_CAP_TPC_MASK | - HW_CAP_HBM_DMA | HW_CAP_PLL | - HW_CAP_NIC_MASK | HW_CAP_MMU | - HW_CAP_SRAM_SCRAMBLER | - HW_CAP_HBM_SCRAMBLER | - HW_CAP_CLK_GATE); + gaudi->hw_cap_initialized &= ~(HW_CAP_CPU | HW_CAP_CPU_Q | HW_CAP_HBM | + HW_CAP_PCI_DMA | HW_CAP_MME | HW_CAP_TPC_MASK | + HW_CAP_HBM_DMA | HW_CAP_PLL | HW_CAP_NIC_MASK | + HW_CAP_MMU | HW_CAP_SRAM_SCRAMBLER | + HW_CAP_HBM_SCRAMBLER); memset(gaudi->events_stat, 0, sizeof(gaudi->events_stat)); @@ -4884,7 +4808,6 @@ static int gaudi_hbm_scrubbing(struct hl_device *hdev) static int gaudi_scrub_device_mem(struct hl_device *hdev, u64 addr, u64 size) { struct asic_fixed_properties *prop = &hdev->asic_prop; - struct gaudi_device *gaudi = hdev->asic_specific; int rc = 0; u64 val = 0; @@ -4919,17 +4842,11 @@ static int gaudi_scrub_device_mem(struct hl_device *hdev, u64 addr, u64 size) return rc; } - mutex_lock(&gaudi->clk_gate_mutex); - hdev->asic_funcs->disable_clock_gating(hdev); - /* Scrub HBM using all DMA channels in parallel */ rc = gaudi_hbm_scrubbing(hdev); if (rc) dev_err(hdev->dev, "Failed to clear HBM in mem scrub all\n"); - - hdev->asic_funcs->set_clock_gating(hdev); - mutex_unlock(&gaudi->clk_gate_mutex); } return rc; @@ -6188,7 +6105,6 @@ static int gaudi_debugfs_read32(struct hl_device *hdev, u64 addr, bool user_address, u32 *val) { struct asic_fixed_properties *prop = &hdev->asic_prop; - struct gaudi_device *gaudi = hdev->asic_specific; u64 hbm_bar_addr, host_phys_end; int rc = 0; @@ -6196,38 +6112,31 @@ static int gaudi_debugfs_read32(struct hl_device *hdev, u64 addr, if ((addr >= CFG_BASE) && (addr < CFG_BASE + CFG_SIZE)) { - if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) && - (hdev->clock_gating_mask & - GAUDI_CLK_GATE_DEBUGFS_MASK)) { + *val = RREG32(addr - CFG_BASE); - dev_err_ratelimited(hdev->dev, - "Can't read register - clock gating is enabled!\n"); - rc = -EFAULT; - } else { - *val = RREG32(addr - CFG_BASE); - } + } else if ((addr >= SRAM_BASE_ADDR) && (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) { + + *val = readl(hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR)); - } else if ((addr >= SRAM_BASE_ADDR) && - (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) { - *val = readl(hdev->pcie_bar[SRAM_BAR_ID] + - (addr - SRAM_BASE_ADDR)); } else if (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size) { - u64 bar_base_addr = DRAM_PHYS_BASE + - (addr & ~(prop->dram_pci_bar_size - 0x1ull)); + + u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr); - if (hbm_bar_addr != U64_MAX) { - *val = readl(hdev->pcie_bar[HBM_BAR_ID] + - (addr - bar_base_addr)); - hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, - hbm_bar_addr); + if (hbm_bar_addr != U64_MAX) { + *val = readl(hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr)); + hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr); } + if (hbm_bar_addr == U64_MAX) rc = -EIO; + } else if (addr >= HOST_PHYS_BASE && addr < host_phys_end && user_address && !iommu_present(&pci_bus_type)) { + *val = *(u32 *) phys_to_virt(addr - HOST_PHYS_BASE); + } else { rc = -EFAULT; } @@ -6239,7 +6148,6 @@ static int gaudi_debugfs_write32(struct hl_device *hdev, u64 addr, bool user_address, u32 val) { struct asic_fixed_properties *prop = &hdev->asic_prop; - struct gaudi_device *gaudi = hdev->asic_specific; u64 hbm_bar_addr, host_phys_end; int rc = 0; @@ -6247,38 +6155,31 @@ static int gaudi_debugfs_write32(struct hl_device *hdev, u64 addr, if ((addr >= CFG_BASE) && (addr < CFG_BASE + CFG_SIZE)) { - if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) && - (hdev->clock_gating_mask & - GAUDI_CLK_GATE_DEBUGFS_MASK)) { + WREG32(addr - CFG_BASE, val); - dev_err_ratelimited(hdev->dev, - "Can't write register - clock gating is enabled!\n"); - rc = -EFAULT; - } else { - WREG32(addr - CFG_BASE, val); - } + } else if ((addr >= SRAM_BASE_ADDR) && (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) { + + writel(val, hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR)); - } else if ((addr >= SRAM_BASE_ADDR) && - (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) { - writel(val, hdev->pcie_bar[SRAM_BAR_ID] + - (addr - SRAM_BASE_ADDR)); } else if (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size) { - u64 bar_base_addr = DRAM_PHYS_BASE + - (addr & ~(prop->dram_pci_bar_size - 0x1ull)); + + u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr); - if (hbm_bar_addr != U64_MAX) { - writel(val, hdev->pcie_bar[HBM_BAR_ID] + - (addr - bar_base_addr)); - hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, - hbm_bar_addr); + if (hbm_bar_addr != U64_MAX) { + writel(val, hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr)); + hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr); } + if (hbm_bar_addr == U64_MAX) rc = -EIO; + } else if (addr >= HOST_PHYS_BASE && addr < host_phys_end && user_address && !iommu_present(&pci_bus_type)) { + *(u32 *) phys_to_virt(addr - HOST_PHYS_BASE) = val; + } else { rc = -EFAULT; } @@ -6290,7 +6191,6 @@ static int gaudi_debugfs_read64(struct hl_device *hdev, u64 addr, bool user_address, u64 *val) { struct asic_fixed_properties *prop = &hdev->asic_prop; - struct gaudi_device *gaudi = hdev->asic_specific; u64 hbm_bar_addr, host_phys_end; int rc = 0; @@ -6298,42 +6198,35 @@ static int gaudi_debugfs_read64(struct hl_device *hdev, u64 addr, if ((addr >= CFG_BASE) && (addr <= CFG_BASE + CFG_SIZE - sizeof(u64))) { - if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) && - (hdev->clock_gating_mask & - GAUDI_CLK_GATE_DEBUGFS_MASK)) { - - dev_err_ratelimited(hdev->dev, - "Can't read register - clock gating is enabled!\n"); - rc = -EFAULT; - } else { - u32 val_l = RREG32(addr - CFG_BASE); - u32 val_h = RREG32(addr + sizeof(u32) - CFG_BASE); + u32 val_l = RREG32(addr - CFG_BASE); + u32 val_h = RREG32(addr + sizeof(u32) - CFG_BASE); - *val = (((u64) val_h) << 32) | val_l; - } + *val = (((u64) val_h) << 32) | val_l; } else if ((addr >= SRAM_BASE_ADDR) && - (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) { - *val = readq(hdev->pcie_bar[SRAM_BAR_ID] + - (addr - SRAM_BASE_ADDR)); - } else if (addr <= - DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) { - u64 bar_base_addr = DRAM_PHYS_BASE + - (addr & ~(prop->dram_pci_bar_size - 0x1ull)); + (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) { + + *val = readq(hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR)); + + } else if (addr <= DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) { + + u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr); - if (hbm_bar_addr != U64_MAX) { - *val = readq(hdev->pcie_bar[HBM_BAR_ID] + - (addr - bar_base_addr)); - hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, - hbm_bar_addr); + if (hbm_bar_addr != U64_MAX) { + *val = readq(hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr)); + hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr); } + if (hbm_bar_addr == U64_MAX) rc = -EIO; + } else if (addr >= HOST_PHYS_BASE && addr < host_phys_end && user_address && !iommu_present(&pci_bus_type)) { + *val = *(u64 *) phys_to_virt(addr - HOST_PHYS_BASE); + } else { rc = -EFAULT; } @@ -6345,7 +6238,6 @@ static int gaudi_debugfs_write64(struct hl_device *hdev, u64 addr, bool user_address, u64 val) { struct asic_fixed_properties *prop = &hdev->asic_prop; - struct gaudi_device *gaudi = hdev->asic_specific; u64 hbm_bar_addr, host_phys_end; int rc = 0; @@ -6353,41 +6245,33 @@ static int gaudi_debugfs_write64(struct hl_device *hdev, u64 addr, if ((addr >= CFG_BASE) && (addr <= CFG_BASE + CFG_SIZE - sizeof(u64))) { - if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) && - (hdev->clock_gating_mask & - GAUDI_CLK_GATE_DEBUGFS_MASK)) { - - dev_err_ratelimited(hdev->dev, - "Can't write register - clock gating is enabled!\n"); - rc = -EFAULT; - } else { - WREG32(addr - CFG_BASE, lower_32_bits(val)); - WREG32(addr + sizeof(u32) - CFG_BASE, - upper_32_bits(val)); - } + WREG32(addr - CFG_BASE, lower_32_bits(val)); + WREG32(addr + sizeof(u32) - CFG_BASE, upper_32_bits(val)); } else if ((addr >= SRAM_BASE_ADDR) && - (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) { - writeq(val, hdev->pcie_bar[SRAM_BAR_ID] + - (addr - SRAM_BASE_ADDR)); - } else if (addr <= - DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) { - u64 bar_base_addr = DRAM_PHYS_BASE + - (addr & ~(prop->dram_pci_bar_size - 0x1ull)); + (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) { + + writeq(val, hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR)); + + } else if (addr <= DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) { + + u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr); - if (hbm_bar_addr != U64_MAX) { - writeq(val, hdev->pcie_bar[HBM_BAR_ID] + - (addr - bar_base_addr)); - hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, - hbm_bar_addr); + if (hbm_bar_addr != U64_MAX) { + writeq(val, hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr)); + hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr); } + if (hbm_bar_addr == U64_MAX) rc = -EIO; + } else if (addr >= HOST_PHYS_BASE && addr < host_phys_end && user_address && !iommu_present(&pci_bus_type)) { + *(u64 *) phys_to_virt(addr - HOST_PHYS_BASE) = val; + } else { rc = -EFAULT; } @@ -6446,7 +6330,6 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, void *blob_addr) { u32 dma_core_sts0, err_cause, cfg1, size_left, pos, size_to_dma; - struct gaudi_device *gaudi = hdev->asic_specific; u32 qm_glbl_sts0, qm_cgm_sts; u64 dma_offset, qm_offset; dma_addr_t dma_addr; @@ -6462,10 +6345,6 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, if (!kernel_addr) return -ENOMEM; - mutex_lock(&gaudi->clk_gate_mutex); - - hdev->asic_funcs->disable_clock_gating(hdev); - hdev->asic_funcs->hw_queues_lock(hdev); dma_id = gaudi_dma_assignment[GAUDI_PCI_DMA_1]; @@ -6550,10 +6429,6 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, out: hdev->asic_funcs->hw_queues_unlock(hdev); - hdev->asic_funcs->set_clock_gating(hdev); - - mutex_unlock(&gaudi->clk_gate_mutex); - hdev->asic_funcs->asic_dma_free_coherent(hdev, SZ_2M, kernel_addr, dma_addr); @@ -6601,10 +6476,6 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) return; } - mutex_lock(&gaudi->clk_gate_mutex); - - hdev->asic_funcs->disable_clock_gating(hdev); - gaudi_mmu_prepare_reg(hdev, mmDMA0_QM_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmDMA0_QM_GLBL_NON_SECURE_PROPS_1, asid); gaudi_mmu_prepare_reg(hdev, mmDMA0_QM_GLBL_NON_SECURE_PROPS_2, asid); @@ -6882,10 +6753,6 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) gaudi_mmu_prepare_reg(hdev, mmPSOC_GLOBAL_CONF_TRACE_ARUSER, asid); gaudi_mmu_prepare_reg(hdev, mmPSOC_GLOBAL_CONF_TRACE_AWUSER, asid); - - hdev->asic_funcs->set_clock_gating(hdev); - - mutex_unlock(&gaudi->clk_gate_mutex); } static int gaudi_send_job_on_qman0(struct hl_device *hdev, @@ -7266,10 +7133,8 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev, struct ecc_info_extract_params *params, u64 *ecc_address, u64 *ecc_syndrom, u8 *memory_wrapper_idx) { - struct gaudi_device *gaudi = hdev->asic_specific; u32 i, num_mem_regs, reg, err_bit; u64 err_addr, err_word = 0; - int rc = 0; num_mem_regs = params->num_memories / 32 + ((params->num_memories % 32) ? 1 : 0); @@ -7282,11 +7147,6 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev, else err_addr = params->block_address + GAUDI_ECC_SERR0_OFFSET; - if (params->disable_clock_gating) { - mutex_lock(&gaudi->clk_gate_mutex); - hdev->asic_funcs->disable_clock_gating(hdev); - } - /* Set invalid wrapper index */ *memory_wrapper_idx = 0xFF; @@ -7303,8 +7163,7 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev, if (*memory_wrapper_idx == 0xFF) { dev_err(hdev->dev, "ECC error information cannot be found\n"); - rc = -EINVAL; - goto enable_clk_gate; + return -EINVAL; } WREG32(params->block_address + GAUDI_ECC_MEM_SEL_OFFSET, @@ -7324,14 +7183,7 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev, WREG32(params->block_address + GAUDI_ECC_MEM_INFO_CLR_OFFSET, reg); -enable_clk_gate: - if (params->disable_clock_gating) { - hdev->asic_funcs->set_clock_gating(hdev); - - mutex_unlock(&gaudi->clk_gate_mutex); - } - - return rc; + return 0; } /* @@ -7589,7 +7441,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, params.block_address = mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET; params.num_memories = 90; params.derr = false; - params.disable_clock_gating = true; extract_info_from_fw = false; break; case GAUDI_EVENT_TPC0_DERR ... GAUDI_EVENT_TPC7_DERR: @@ -7598,7 +7449,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET; params.num_memories = 90; params.derr = true; - params.disable_clock_gating = true; extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_ACC_SERR: @@ -7609,7 +7459,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, params.block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET; params.num_memories = 128; params.derr = false; - params.disable_clock_gating = true; extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_ACC_DERR: @@ -7620,7 +7469,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, params.block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET; params.num_memories = 128; params.derr = true; - params.disable_clock_gating = true; extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_SBAB_SERR: @@ -7632,7 +7480,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, mmMME0_SBAB_BASE + index * MME_ACC_OFFSET; params.num_memories = 33; params.derr = false; - params.disable_clock_gating = true; extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_SBAB_DERR: @@ -7644,7 +7491,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, mmMME0_SBAB_BASE + index * MME_ACC_OFFSET; params.num_memories = 33; params.derr = true; - params.disable_clock_gating = true; extract_info_from_fw = false; break; default: @@ -7819,6 +7665,48 @@ static void gaudi_print_fw_alive_info(struct hl_device *hdev, fw_alive->thread_id, fw_alive->uptime_seconds); } +static void gaudi_print_nic_axi_irq_info(struct hl_device *hdev, u16 event_type, + void *data) +{ + char desc[64] = "", *type; + struct eq_nic_sei_event *eq_nic_sei = data; + u16 nic_id = event_type - GAUDI_EVENT_NIC_SEI_0; + + switch (eq_nic_sei->axi_error_cause) { + case RXB: + type = "RXB"; + break; + case RXE: + type = "RXE"; + break; + case TXS: + type = "TXS"; + break; + case TXE: + type = "TXE"; + break; + case QPC_RESP: + type = "QPC_RESP"; + break; + case NON_AXI_ERR: + type = "NON_AXI_ERR"; + break; + case TMR: + type = "TMR"; + break; + default: + dev_err(hdev->dev, "unknown NIC AXI cause %d\n", + eq_nic_sei->axi_error_cause); + type = "N/A"; + break; + } + + snprintf(desc, sizeof(desc), "NIC%d_%s%d", nic_id, type, + eq_nic_sei->id); + dev_err_ratelimited(hdev->dev, "Received H/W interrupt %d [\"%s\"]\n", + event_type, desc); +} + static int gaudi_non_hard_reset_late_init(struct hl_device *hdev) { /* GAUDI doesn't support any reset except hard-reset */ @@ -7966,19 +7854,9 @@ static int gaudi_hbm_event_to_dev(u16 hbm_event_type) static bool gaudi_tpc_read_interrupts(struct hl_device *hdev, u8 tpc_id, char *interrupt_name) { - struct gaudi_device *gaudi = hdev->asic_specific; u32 tpc_offset = tpc_id * TPC_CFG_OFFSET, tpc_interrupts_cause, i; bool soft_reset_required = false; - /* Accessing the TPC_INTR_CAUSE registers requires disabling the clock - * gating, and thus cannot be done in CPU-CP and should be done instead - * by the driver. - */ - - mutex_lock(&gaudi->clk_gate_mutex); - - hdev->asic_funcs->disable_clock_gating(hdev); - tpc_interrupts_cause = RREG32(mmTPC0_CFG_TPC_INTR_CAUSE + tpc_offset) & TPC0_CFG_TPC_INTR_CAUSE_CAUSE_MASK; @@ -7996,10 +7874,6 @@ static bool gaudi_tpc_read_interrupts(struct hl_device *hdev, u8 tpc_id, /* Clear interrupts */ WREG32(mmTPC0_CFG_TPC_INTR_CAUSE + tpc_offset, 0); - hdev->asic_funcs->set_clock_gating(hdev); - - mutex_unlock(&gaudi->clk_gate_mutex); - return soft_reset_required; } @@ -8066,6 +7940,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) { struct gaudi_device *gaudi = hdev->asic_specific; + u64 data = le64_to_cpu(eq_entry->data[0]); u32 ctl = le32_to_cpu(eq_entry->hdr.ctl); u32 fw_fatal_err_flag = 0; u16 event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK) @@ -8102,6 +7977,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_PSOC_MEM_DERR: case GAUDI_EVENT_PSOC_CORESIGHT_DERR: case GAUDI_EVENT_SRAM0_DERR ... GAUDI_EVENT_SRAM28_DERR: + case GAUDI_EVENT_NIC0_DERR ... GAUDI_EVENT_NIC4_DERR: case GAUDI_EVENT_DMA_IF0_DERR ... GAUDI_EVENT_DMA_IF3_DERR: case GAUDI_EVENT_HBM_0_DERR ... GAUDI_EVENT_HBM_3_DERR: case GAUDI_EVENT_MMU_DERR: @@ -8202,6 +8078,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_PSOC_MEM_SERR: case GAUDI_EVENT_PSOC_CORESIGHT_SERR: case GAUDI_EVENT_SRAM0_SERR ... GAUDI_EVENT_SRAM28_SERR: + case GAUDI_EVENT_NIC0_SERR ... GAUDI_EVENT_NIC4_SERR: case GAUDI_EVENT_DMA_IF0_SERR ... GAUDI_EVENT_DMA_IF3_SERR: case GAUDI_EVENT_HBM_0_SERR ... GAUDI_EVENT_HBM_3_SERR: fallthrough; @@ -8263,6 +8140,11 @@ static void gaudi_handle_eqe(struct hl_device *hdev, hl_fw_unmask_irq(hdev, event_type); break; + case GAUDI_EVENT_NIC_SEI_0 ... GAUDI_EVENT_NIC_SEI_4: + gaudi_print_nic_axi_irq_info(hdev, event_type, &data); + hl_fw_unmask_irq(hdev, event_type); + break; + case GAUDI_EVENT_DMA_IF_SEI_0 ... GAUDI_EVENT_DMA_IF_SEI_3: gaudi_print_irq_info(hdev, event_type, false); gaudi_print_sm_sei_info(hdev, event_type, @@ -8274,6 +8156,9 @@ static void gaudi_handle_eqe(struct hl_device *hdev, hl_fw_unmask_irq(hdev, event_type); break; + case GAUDI_EVENT_STATUS_NIC0_ENG0 ... GAUDI_EVENT_STATUS_NIC4_ENG1: + break; + case GAUDI_EVENT_FIX_POWER_ENV_S ... GAUDI_EVENT_FIX_THERMAL_ENV_E: gaudi_print_clk_change_info(hdev, event_type); hl_fw_unmask_irq(hdev, event_type); @@ -8314,7 +8199,7 @@ reset_device: | HL_DRV_RESET_BYPASS_REQ_TO_FW | fw_fatal_err_flag); else if (hdev->hard_reset_on_fw_events) - hl_device_reset(hdev, HL_DRV_RESET_HARD | fw_fatal_err_flag); + hl_device_reset(hdev, HL_DRV_RESET_HARD | HL_DRV_RESET_DELAY | fw_fatal_err_flag); else hl_fw_unmask_irq(hdev, event_type); } @@ -8461,10 +8346,6 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u64 offset; int i, dma_id, port; - mutex_lock(&gaudi->clk_gate_mutex); - - hdev->asic_funcs->disable_clock_gating(hdev); - if (s) seq_puts(s, "\nDMA is_idle QM_GLBL_STS0 QM_CGM_STS DMA_CORE_STS0\n" @@ -8585,10 +8466,6 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (s) seq_puts(s, "\n"); - hdev->asic_funcs->set_clock_gating(hdev); - - mutex_unlock(&gaudi->clk_gate_mutex); - return is_idle; } @@ -8628,10 +8505,8 @@ static int gaudi_get_eeprom_data(struct hl_device *hdev, void *data, * this function should be used only during initialization and/or after reset, * when there are no active users. */ -static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, - u32 tpc_id) +static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, u32 tpc_id) { - struct gaudi_device *gaudi = hdev->asic_specific; u64 kernel_timeout; u32 status, offset; int rc; @@ -8643,10 +8518,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, else kernel_timeout = HL_DEVICE_TIMEOUT_USEC; - mutex_lock(&gaudi->clk_gate_mutex); - - hdev->asic_funcs->disable_clock_gating(hdev); - WREG32(mmTPC0_CFG_QM_KERNEL_BASE_ADDRESS_LOW + offset, lower_32_bits(tpc_kernel)); WREG32(mmTPC0_CFG_QM_KERNEL_BASE_ADDRESS_HIGH + offset, @@ -8686,8 +8557,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, dev_err(hdev->dev, "Timeout while waiting for TPC%d icache prefetch\n", tpc_id); - hdev->asic_funcs->set_clock_gating(hdev); - mutex_unlock(&gaudi->clk_gate_mutex); return -EIO; } @@ -8711,8 +8580,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, dev_err(hdev->dev, "Timeout while waiting for TPC%d vector pipe\n", tpc_id); - hdev->asic_funcs->set_clock_gating(hdev); - mutex_unlock(&gaudi->clk_gate_mutex); return -EIO; } @@ -8724,9 +8591,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, 1000, kernel_timeout); - hdev->asic_funcs->set_clock_gating(hdev); - mutex_unlock(&gaudi->clk_gate_mutex); - if (rc) { dev_err(hdev->dev, "Timeout while waiting for TPC%d kernel to execute\n", @@ -8791,7 +8655,7 @@ static int gaudi_internal_cb_pool_init(struct hl_device *hdev, hdev->internal_cb_pool_dma_addr, HOST_SPACE_INTERNAL_CB_SZ); - hdev->asic_funcs->mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); + hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); if (rc) @@ -8826,7 +8690,7 @@ static void gaudi_internal_cb_pool_fini(struct hl_device *hdev, HOST_SPACE_INTERNAL_CB_SZ); hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); + hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); gen_pool_destroy(hdev->internal_cb_pool); @@ -9204,14 +9068,7 @@ static void gaudi_reset_sob(struct hl_device *hdev, void *data) static void gaudi_set_dma_mask_from_fw(struct hl_device *hdev) { - if (RREG32(mmPSOC_GLOBAL_CONF_NON_RST_FLOPS_0) == - HL_POWER9_HOST_MAGIC) { - hdev->power9_64bit_dma_enable = 1; - hdev->dma_mask = 64; - } else { - hdev->power9_64bit_dma_enable = 0; - hdev->dma_mask = 48; - } + hdev->dma_mask = 48; } static u64 gaudi_get_device_time(struct hl_device *hdev) @@ -9293,23 +9150,15 @@ static int gaudi_gen_sync_to_engine_map(struct hl_device *hdev, struct hl_sync_to_engine_map *map) { struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - struct gaudi_device *gaudi = hdev->asic_specific; int i, j, rc; u32 reg_value; /* Iterate over TPC engines */ for (i = 0; i < sds->props[SP_NUM_OF_TPC_ENGINES]; ++i) { - /* TPC registered must be accessed with clock gating disabled */ - mutex_lock(&gaudi->clk_gate_mutex); - hdev->asic_funcs->disable_clock_gating(hdev); reg_value = RREG32(sds->props[SP_TPC0_CFG_SO] + sds->props[SP_NEXT_TPC] * i); - /* We can reenable clock_gating */ - hdev->asic_funcs->set_clock_gating(hdev); - mutex_unlock(&gaudi->clk_gate_mutex); - rc = gaudi_add_sync_to_engine_map_entry(map, reg_value, ENGINE_TPC, i); if (rc) @@ -9319,20 +9168,11 @@ static int gaudi_gen_sync_to_engine_map(struct hl_device *hdev, /* Iterate over MME engines */ for (i = 0; i < sds->props[SP_NUM_OF_MME_ENGINES]; ++i) { for (j = 0; j < sds->props[SP_SUB_MME_ENG_NUM]; ++j) { - /* MME registered must be accessed with clock gating - * disabled - */ - mutex_lock(&gaudi->clk_gate_mutex); - hdev->asic_funcs->disable_clock_gating(hdev); reg_value = RREG32(sds->props[SP_MME_CFG_SO] + sds->props[SP_NEXT_MME] * i + j * sizeof(u32)); - /* We can reenable clock_gating */ - hdev->asic_funcs->set_clock_gating(hdev); - mutex_unlock(&gaudi->clk_gate_mutex); - rc = gaudi_add_sync_to_engine_map_entry( map, reg_value, ENGINE_MME, i * sds->props[SP_SUB_MME_ENG_NUM] + j); @@ -9537,6 +9377,29 @@ static u32 *gaudi_get_stream_master_qid_arr(void) return gaudi_stream_master; } +static ssize_t infineon_ver_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + struct cpucp_info *cpucp_info; + + cpucp_info = &hdev->asic_prop.cpucp_info; + + return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version)); +} + +static DEVICE_ATTR_RO(infineon_ver); + +static struct attribute *gaudi_vrm_dev_attrs[] = { + &dev_attr_infineon_ver.attr, +}; + +static void gaudi_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp, + struct attribute_group *dev_vrm_attr_grp) +{ + hl_sysfs_add_dev_clk_attr(hdev, dev_clk_attr_grp); + dev_vrm_attr_grp->attrs = gaudi_vrm_dev_attrs; +} + static const struct hl_asic_funcs gaudi_funcs = { .early_init = gaudi_early_init, .early_fini = gaudi_early_fini, @@ -9574,17 +9437,14 @@ static const struct hl_asic_funcs gaudi_funcs = { .debugfs_read64 = gaudi_debugfs_read64, .debugfs_write64 = gaudi_debugfs_write64, .debugfs_read_dma = gaudi_debugfs_read_dma, - .add_device_attr = hl_add_device_attr, + .add_device_attr = gaudi_add_device_attr, .handle_eqe = gaudi_handle_eqe, - .set_pll_profile = hl_set_pll_profile, .get_events_stat = gaudi_get_events_stat, .read_pte = gaudi_read_pte, .write_pte = gaudi_write_pte, .mmu_invalidate_cache = gaudi_mmu_invalidate_cache, .mmu_invalidate_cache_range = gaudi_mmu_invalidate_cache_range, .send_heartbeat = gaudi_send_heartbeat, - .set_clock_gating = gaudi_set_clock_gating, - .disable_clock_gating = gaudi_disable_clock_gating, .debug_coresight = gaudi_debug_coresight, .is_device_idle = gaudi_is_device_idle, .non_hard_reset_late_init = gaudi_non_hard_reset_late_init, @@ -9600,7 +9460,6 @@ static const struct hl_asic_funcs gaudi_funcs = { .halt_coresight = gaudi_halt_coresight, .ctx_init = gaudi_ctx_init, .ctx_fini = gaudi_ctx_fini, - .get_clk_rate = hl_get_clk_rate, .get_queue_id_for_cq = gaudi_get_queue_id_for_cq, .load_firmware_to_device = gaudi_load_firmware_to_device, .load_boot_fit_to_device = gaudi_load_boot_fit_to_device, @@ -9626,7 +9485,8 @@ static const struct hl_asic_funcs gaudi_funcs = { .state_dump_init = gaudi_state_dump_init, .get_sob_addr = gaudi_get_sob_addr, .set_pci_memory_regions = gaudi_set_pci_memory_regions, - .get_stream_master_qid_arr = gaudi_get_stream_master_qid_arr + .get_stream_master_qid_arr = gaudi_get_stream_master_qid_arr, + .is_valid_dram_page_size = NULL }; /** diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index 8ac16a9b7d15..54de7c599072 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2019-2020 HabanaLabs, Ltd. + * Copyright 2019-2022 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -177,7 +177,6 @@ #define HW_CAP_MSI BIT(6) #define HW_CAP_CPU_Q BIT(7) #define HW_CAP_HBM_DMA BIT(8) -#define HW_CAP_CLK_GATE BIT(9) #define HW_CAP_SRAM_SCRAMBLER BIT(10) #define HW_CAP_HBM_SCRAMBLER BIT(11) @@ -313,8 +312,6 @@ struct gaudi_internal_qman_info { * struct gaudi_device - ASIC specific manage structure. * @cpucp_info_get: get information on device from CPU-CP * @hw_queues_lock: protects the H/W queues from concurrent access. - * @clk_gate_mutex: protects code areas that require clock gating to be disabled - * temporarily * @internal_qmans: Internal QMANs information. The array size is larger than * the actual number of internal queues because they are not in * consecutive order. @@ -337,7 +334,6 @@ struct gaudi_device { /* TODO: remove hw_queues_lock after moving to scheduler code */ spinlock_t hw_queues_lock; - struct mutex clk_gate_mutex; struct gaudi_internal_qman_info internal_qmans[GAUDI_QUEUE_ID_SIZE]; @@ -355,8 +351,6 @@ struct gaudi_device { void gaudi_init_security(struct hl_device *hdev); void gaudi_ack_protection_bits_errors(struct hl_device *hdev); -void gaudi_add_device_attr(struct hl_device *hdev, - struct attribute_group *dev_attr_grp); int gaudi_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data); void gaudi_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx); void gaudi_mmu_prepare_reg(struct hl_device *hdev, u64 reg, u32 asid); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index fbcc7bbf44b3..ec9358bcbf0b 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2021 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -430,6 +430,9 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->dmmu.page_size = PAGE_SIZE_2MB; prop->dmmu.num_hops = MMU_ARCH_5_HOPS; prop->dmmu.last_mask = LAST_MASK; + /* TODO: will be duplicated until implementing per-MMU props */ + prop->dmmu.hop_table_size = prop->mmu_hop_table_size; + prop->dmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size; /* shifts and masks are the same in PMMU and DMMU */ memcpy(&prop->pmmu, &prop->dmmu, sizeof(prop->dmmu)); @@ -438,6 +441,9 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->pmmu.page_size = PAGE_SIZE_4KB; prop->pmmu.num_hops = MMU_ARCH_5_HOPS; prop->pmmu.last_mask = LAST_MASK; + /* TODO: will be duplicated until implementing per-MMU props */ + prop->pmmu.hop_table_size = prop->mmu_hop_table_size; + prop->pmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size; /* PMMU and HPMMU are the same except of page size */ memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu)); @@ -477,6 +483,10 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->use_get_power_for_reset_history = true; + prop->configurable_stop_on_err = true; + + prop->set_max_power_on_device_init = true; + return 0; } @@ -893,7 +903,7 @@ int goya_late_init(struct hl_device *hdev) goya->pm_mng_profile = PM_AUTO; - hdev->asic_funcs->set_pll_profile(hdev, PLL_LOW); + goya_set_pll_profile(hdev, PLL_LOW); schedule_delayed_work(&goya->goya_work->work_freq, usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); @@ -2700,8 +2710,7 @@ int goya_mmu_init(struct hl_device *hdev) WREG32_AND(mmSTLB_STLB_FEATURE_EN, (~STLB_STLB_FEATURE_EN_FOLLOWER_EN_MASK)); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, - MMU_OP_USERPTR | MMU_OP_PHYS_PACK); + hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR | MMU_OP_PHYS_PACK); WREG32(mmMMU_MMU_ENABLE, 1); WREG32(mmMMU_SPI_MASK, 0xF); @@ -5341,7 +5350,7 @@ static int goya_mmu_invalidate_cache_range(struct hl_device *hdev, /* Treat as invalidate all because there is no range invalidation * in Goya */ - return hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags); + return hl_mmu_invalidate_cache(hdev, is_hard, flags); } int goya_send_heartbeat(struct hl_device *hdev) @@ -5391,16 +5400,6 @@ int goya_cpucp_info_get(struct hl_device *hdev) return 0; } -static void goya_set_clock_gating(struct hl_device *hdev) -{ - /* clock gating not supported in Goya */ -} - -static void goya_disable_clock_gating(struct hl_device *hdev) -{ - /* clock gating not supported in Goya */ -} - static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, struct seq_file *s) { @@ -5564,16 +5563,7 @@ static void goya_reset_sob_group(struct hl_device *hdev, u16 sob_group) static void goya_set_dma_mask_from_fw(struct hl_device *hdev) { - if (RREG32(mmPSOC_GLOBAL_CONF_NON_RST_FLOPS_0) == - HL_POWER9_HOST_MAGIC) { - dev_dbg(hdev->dev, "Working in 64-bit DMA mode\n"); - hdev->power9_64bit_dma_enable = 1; - hdev->dma_mask = 64; - } else { - dev_dbg(hdev->dev, "Working in 48-bit DMA mode\n"); - hdev->power9_64bit_dma_enable = 0; - hdev->dma_mask = 48; - } + hdev->dma_mask = 48; } u64 goya_get_device_time(struct hl_device *hdev) @@ -5727,15 +5717,12 @@ static const struct hl_asic_funcs goya_funcs = { .debugfs_read_dma = goya_debugfs_read_dma, .add_device_attr = goya_add_device_attr, .handle_eqe = goya_handle_eqe, - .set_pll_profile = goya_set_pll_profile, .get_events_stat = goya_get_events_stat, .read_pte = goya_read_pte, .write_pte = goya_write_pte, .mmu_invalidate_cache = goya_mmu_invalidate_cache, .mmu_invalidate_cache_range = goya_mmu_invalidate_cache_range, .send_heartbeat = goya_send_heartbeat, - .set_clock_gating = goya_set_clock_gating, - .disable_clock_gating = goya_disable_clock_gating, .debug_coresight = goya_debug_coresight, .is_device_idle = goya_is_device_idle, .non_hard_reset_late_init = goya_non_hard_reset_late_init, @@ -5751,7 +5738,6 @@ static const struct hl_asic_funcs goya_funcs = { .halt_coresight = goya_halt_coresight, .ctx_init = goya_ctx_init, .ctx_fini = goya_ctx_fini, - .get_clk_rate = hl_get_clk_rate, .get_queue_id_for_cq = goya_get_queue_id_for_cq, .load_firmware_to_device = goya_load_firmware_to_device, .load_boot_fit_to_device = goya_load_boot_fit_to_device, @@ -5778,6 +5764,7 @@ static const struct hl_asic_funcs goya_funcs = { .get_sob_addr = &goya_get_sob_addr, .set_pci_memory_regions = goya_set_pci_memory_regions, .get_stream_master_qid_arr = goya_get_stream_master_qid_arr, + .is_valid_dram_page_size = NULL }; /* diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 3740fd25bf84..647f57402616 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -217,8 +217,8 @@ u64 goya_get_max_power(struct hl_device *hdev); void goya_set_max_power(struct hl_device *hdev, u64 value); void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq); -void goya_add_device_attr(struct hl_device *hdev, - struct attribute_group *dev_attr_grp); +void goya_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp, + struct attribute_group *dev_vrm_attr_grp); int goya_cpucp_info_get(struct hl_device *hdev); int goya_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data); void goya_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx); diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/misc/habanalabs/goya/goya_hwmgr.c index 76b47749affe..6580fc6a486a 100644 --- a/drivers/misc/habanalabs/goya/goya_hwmgr.c +++ b/drivers/misc/habanalabs/goya/goya_hwmgr.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2021 HabanaLabs, Ltd. + * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -11,21 +11,24 @@ void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) { struct goya_device *goya = hdev->asic_specific; + if (!hdev->pdev) + return; + switch (freq) { case PLL_HIGH: - hl_set_frequency(hdev, HL_GOYA_MME_PLL, hdev->high_pll); - hl_set_frequency(hdev, HL_GOYA_TPC_PLL, hdev->high_pll); - hl_set_frequency(hdev, HL_GOYA_IC_PLL, hdev->high_pll); + hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, hdev->high_pll); + hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, hdev->high_pll); + hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, hdev->high_pll); break; case PLL_LOW: - hl_set_frequency(hdev, HL_GOYA_MME_PLL, GOYA_PLL_FREQ_LOW); - hl_set_frequency(hdev, HL_GOYA_TPC_PLL, GOYA_PLL_FREQ_LOW); - hl_set_frequency(hdev, HL_GOYA_IC_PLL, GOYA_PLL_FREQ_LOW); + hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, GOYA_PLL_FREQ_LOW); + hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, GOYA_PLL_FREQ_LOW); + hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, GOYA_PLL_FREQ_LOW); break; case PLL_LAST: - hl_set_frequency(hdev, HL_GOYA_MME_PLL, goya->mme_clk); - hl_set_frequency(hdev, HL_GOYA_TPC_PLL, goya->tpc_clk); - hl_set_frequency(hdev, HL_GOYA_IC_PLL, goya->ic_clk); + hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, goya->mme_clk); + hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, goya->tpc_clk); + hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, goya->ic_clk); break; default: dev_err(hdev->dev, "unknown frequency setting\n"); @@ -41,7 +44,7 @@ static ssize_t mme_clk_show(struct device *dev, struct device_attribute *attr, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - value = hl_get_frequency(hdev, HL_GOYA_MME_PLL, false); + value = hl_fw_get_frequency(hdev, HL_GOYA_MME_PLL, false); if (value < 0) return value; @@ -74,7 +77,7 @@ static ssize_t mme_clk_store(struct device *dev, struct device_attribute *attr, goto fail; } - hl_set_frequency(hdev, HL_GOYA_MME_PLL, value); + hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, value); goya->mme_clk = value; fail: @@ -90,7 +93,7 @@ static ssize_t tpc_clk_show(struct device *dev, struct device_attribute *attr, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - value = hl_get_frequency(hdev, HL_GOYA_TPC_PLL, false); + value = hl_fw_get_frequency(hdev, HL_GOYA_TPC_PLL, false); if (value < 0) return value; @@ -123,7 +126,7 @@ static ssize_t tpc_clk_store(struct device *dev, struct device_attribute *attr, goto fail; } - hl_set_frequency(hdev, HL_GOYA_TPC_PLL, value); + hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, value); goya->tpc_clk = value; fail: @@ -139,7 +142,7 @@ static ssize_t ic_clk_show(struct device *dev, struct device_attribute *attr, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - value = hl_get_frequency(hdev, HL_GOYA_IC_PLL, false); + value = hl_fw_get_frequency(hdev, HL_GOYA_IC_PLL, false); if (value < 0) return value; @@ -172,7 +175,7 @@ static ssize_t ic_clk_store(struct device *dev, struct device_attribute *attr, goto fail; } - hl_set_frequency(hdev, HL_GOYA_IC_PLL, value); + hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, value); goya->ic_clk = value; fail: @@ -188,7 +191,7 @@ static ssize_t mme_clk_curr_show(struct device *dev, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - value = hl_get_frequency(hdev, HL_GOYA_MME_PLL, true); + value = hl_fw_get_frequency(hdev, HL_GOYA_MME_PLL, true); if (value < 0) return value; @@ -205,7 +208,7 @@ static ssize_t tpc_clk_curr_show(struct device *dev, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - value = hl_get_frequency(hdev, HL_GOYA_TPC_PLL, true); + value = hl_fw_get_frequency(hdev, HL_GOYA_TPC_PLL, true); if (value < 0) return value; @@ -222,7 +225,7 @@ static ssize_t ic_clk_curr_show(struct device *dev, if (!hl_device_operational(hdev, NULL)) return -ENODEV; - value = hl_get_frequency(hdev, HL_GOYA_IC_PLL, true); + value = hl_fw_get_frequency(hdev, HL_GOYA_IC_PLL, true); if (value < 0) return value; @@ -347,7 +350,7 @@ static DEVICE_ATTR_RW(pm_mng_profile); static DEVICE_ATTR_RW(tpc_clk); static DEVICE_ATTR_RO(tpc_clk_curr); -static struct attribute *goya_dev_attrs[] = { +static struct attribute *goya_clk_dev_attrs[] = { &dev_attr_high_pll.attr, &dev_attr_ic_clk.attr, &dev_attr_ic_clk_curr.attr, @@ -356,11 +359,27 @@ static struct attribute *goya_dev_attrs[] = { &dev_attr_pm_mng_profile.attr, &dev_attr_tpc_clk.attr, &dev_attr_tpc_clk_curr.attr, - NULL, }; -void goya_add_device_attr(struct hl_device *hdev, - struct attribute_group *dev_attr_grp) +static ssize_t infineon_ver_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + struct cpucp_info *cpucp_info; + + cpucp_info = &hdev->asic_prop.cpucp_info; + + return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version)); +} + +static DEVICE_ATTR_RO(infineon_ver); + +static struct attribute *goya_vrm_dev_attrs[] = { + &dev_attr_infineon_ver.attr, +}; + +void goya_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp, + struct attribute_group *dev_vrm_attr_grp) { - dev_attr_grp->attrs = goya_dev_attrs; + dev_clk_attr_grp->attrs = goya_clk_dev_attrs; + dev_vrm_attr_grp->attrs = goya_vrm_dev_attrs; } diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index f9c4acc9bf5a..65668dac6a5f 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -780,6 +780,7 @@ struct cpucp_security_info { * (0 = functional 1 = binned) * @xbar_binning_mask: Xbar binning mask, 1 bit per Xbar instance * (0 = functional 1 = binned) + * @fw_os_version: Firmware OS Version */ struct cpucp_info { struct cpucp_sensor sensors[CPUCP_MAX_SENSORS]; @@ -807,6 +808,7 @@ struct cpucp_info { __le32 reserved6; __u8 pll_map[PLL_MAP_LEN]; __le64 mme_binning_mask; + __u8 fw_os_version[VERSION_MAX_LEN]; }; struct cpucp_mac_addr { diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index 135e21d6edc9..15f91ae9de6e 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -33,6 +33,7 @@ enum cpu_boot_err { CPU_BOOT_ERR_BOOT_FW_CRIT_ERR = 18, CPU_BOOT_ERR_BINNING_FAIL = 19, CPU_BOOT_ERR_TPM_FAIL = 20, + CPU_BOOT_ERR_TMP_THRESH_INIT_FAIL = 21, CPU_BOOT_ERR_ENABLED = 31, CPU_BOOT_ERR_SCND_EN = 63, CPU_BOOT_ERR_LAST = 64 /* we have 2 registers of 32 bits */ @@ -111,6 +112,9 @@ enum cpu_boot_err { * * CPU_BOOT_ERR0_TPM_FAIL TPM verification flow failed. * + * CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL Failed to set threshold for tmperature + * sensor. + * * CPU_BOOT_ERR0_ENABLED Error registers enabled. * This is a main indication that the * running FW populates the error @@ -134,6 +138,7 @@ enum cpu_boot_err { #define CPU_BOOT_ERR0_BOOT_FW_CRIT_ERR (1 << CPU_BOOT_ERR_BOOT_FW_CRIT_ERR) #define CPU_BOOT_ERR0_BINNING_FAIL (1 << CPU_BOOT_ERR_BINNING_FAIL) #define CPU_BOOT_ERR0_TPM_FAIL (1 << CPU_BOOT_ERR_TPM_FAIL) +#define CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL (1 << CPU_BOOT_ERR_TMP_THRESH_INIT_FAIL) #define CPU_BOOT_ERR0_ENABLED (1 << CPU_BOOT_ERR_ENABLED) #define CPU_BOOT_ERR1_ENABLED (1 << CPU_BOOT_ERR_ENABLED) diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h b/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h index d966bd4dfea6..c07ed4ed304c 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h @@ -311,6 +311,16 @@ enum gaudi_async_event_id { GAUDI_EVENT_FW_ALIVE_S = 645, GAUDI_EVENT_DEV_RESET_REQ = 646, GAUDI_EVENT_PKT_QUEUE_OUT_SYNC = 647, + GAUDI_EVENT_STATUS_NIC0_ENG0 = 648, + GAUDI_EVENT_STATUS_NIC0_ENG1 = 649, + GAUDI_EVENT_STATUS_NIC1_ENG0 = 650, + GAUDI_EVENT_STATUS_NIC1_ENG1 = 651, + GAUDI_EVENT_STATUS_NIC2_ENG0 = 652, + GAUDI_EVENT_STATUS_NIC2_ENG1 = 653, + GAUDI_EVENT_STATUS_NIC3_ENG0 = 654, + GAUDI_EVENT_STATUS_NIC3_ENG1 = 655, + GAUDI_EVENT_STATUS_NIC4_ENG0 = 656, + GAUDI_EVENT_STATUS_NIC4_ENG1 = 657, GAUDI_EVENT_FIX_POWER_ENV_S = 658, GAUDI_EVENT_FIX_POWER_ENV_E = 659, GAUDI_EVENT_FIX_THERMAL_ENV_S = 660, diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 67c5b452dd35..88b91ad8e541 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -1070,10 +1070,10 @@ static int kgdbts_option_setup(char *opt) { if (strlen(opt) >= MAX_CONFIG_LEN) { printk(KERN_ERR "kgdbts: config string too long\n"); - return -ENOSPC; + return 1; } strcpy(config, opt); - return 0; + return 1; } __setup("kgdbts=", kgdbts_option_setup); diff --git a/drivers/misc/lkdtm/fortify.c b/drivers/misc/lkdtm/fortify.c index d06458a4858e..ab33bb5e2e7a 100644 --- a/drivers/misc/lkdtm/fortify.c +++ b/drivers/misc/lkdtm/fortify.c @@ -44,14 +44,14 @@ void lkdtm_FORTIFIED_SUBOBJECT(void) strscpy(src, "over ten bytes", size); size = strlen(src) + 1; - pr_info("trying to strcpy past the end of a member of a struct\n"); + pr_info("trying to strncpy past the end of a member of a struct\n"); /* - * memcpy(target.a, src, 20); will hit a compile error because the + * strncpy(target.a, src, 20); will hit a compile error because the * compiler knows at build time that target.a < 20 bytes. Use a * volatile to force a runtime error. */ - memcpy(target.a, src, size); + strncpy(target.a, src, size); /* Store result to global to prevent the code from being eliminated */ fortify_scratch_space = target.a[3]; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 06734670a732..31264ab2eb13 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -2148,6 +2148,7 @@ void mei_cl_all_disconnect(struct mei_device *dev) list_for_each_entry(cl, &dev->file_list, link) mei_cl_set_disconnected(cl); } +EXPORT_SYMBOL_GPL(mei_cl_all_disconnect); static struct mei_cl *mei_cl_dma_map_find(struct mei_device *dev, u8 buffer_id) { diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 67bb6a25fd0a..64ce3f830262 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -107,6 +107,7 @@ #define MEI_DEV_ID_ADP_S 0x7AE8 /* Alder Lake Point S */ #define MEI_DEV_ID_ADP_LP 0x7A60 /* Alder Lake Point LP */ #define MEI_DEV_ID_ADP_P 0x51E0 /* Alder Lake Point P */ +#define MEI_DEV_ID_ADP_N 0x54E0 /* Alder Lake Point N */ /* * MEI HW Section @@ -120,6 +121,7 @@ #define PCI_CFG_HFS_2 0x48 #define PCI_CFG_HFS_3 0x60 # define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070 +# define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000 # define PCI_CFG_HFS_3_FW_SKU_SPS 0x00000060 #define PCI_CFG_HFS_4 0x64 #define PCI_CFG_HFS_5 0x68 diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index d3a6c0728645..719fee9af156 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1257,7 +1257,11 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { dev_warn(dev->dev, "FW not ready: resetting.\n"); - schedule_work(&dev->reset_work); + if (dev->dev_state == MEI_DEV_POWERING_DOWN || + dev->dev_state == MEI_DEV_POWER_DOWN) + mei_cl_all_disconnect(dev); + else if (dev->dev_state != MEI_DEV_DISABLED) + schedule_work(&dev->reset_work); goto end; } @@ -1289,12 +1293,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) if (rets == -ENODATA) break; - if (rets && - (dev->dev_state != MEI_DEV_RESETTING && - dev->dev_state != MEI_DEV_POWER_DOWN)) { - dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", - rets); - schedule_work(&dev->reset_work); + if (rets) { + dev_err(dev->dev, "mei_irq_read_handler ret = %d, state = %d.\n", + rets, dev->dev_state); + if (dev->dev_state != MEI_DEV_RESETTING && + dev->dev_state != MEI_DEV_DISABLED && + dev->dev_state != MEI_DEV_POWERING_DOWN && + dev->dev_state != MEI_DEV_POWER_DOWN) + schedule_work(&dev->reset_work); goto end; } } @@ -1405,16 +1411,16 @@ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev) .quirk_probe = mei_me_fw_type_sps_4 /** - * mei_me_fw_type_sps() - check for sps sku + * mei_me_fw_type_sps_ign() - check for sps or ign sku * - * Read ME FW Status register to check for SPS Firmware. - * The SPS FW is only signaled in pci function 0 + * Read ME FW Status register to check for SPS or IGN Firmware. + * The SPS/IGN FW is only signaled in pci function 0 * * @pdev: pci device * - * Return: true in case of SPS firmware + * Return: true in case of SPS/IGN firmware */ -static bool mei_me_fw_type_sps(const struct pci_dev *pdev) +static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) { u32 reg; u32 fw_type; @@ -1427,14 +1433,15 @@ static bool mei_me_fw_type_sps(const struct pci_dev *pdev) dev_dbg(&pdev->dev, "fw type is %d\n", fw_type); - return fw_type == PCI_CFG_HFS_3_FW_SKU_SPS; + return fw_type == PCI_CFG_HFS_3_FW_SKU_IGN || + fw_type == PCI_CFG_HFS_3_FW_SKU_SPS; } #define MEI_CFG_KIND_ITOUCH \ .kind = "itouch" -#define MEI_CFG_FW_SPS \ - .quirk_probe = mei_me_fw_type_sps +#define MEI_CFG_FW_SPS_IGN \ + .quirk_probe = mei_me_fw_type_sps_ign #define MEI_CFG_FW_VER_SUPP \ .fw_ver_supported = 1 @@ -1535,7 +1542,7 @@ static const struct mei_cfg mei_me_pch12_sps_cfg = { MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, - MEI_CFG_FW_SPS, + MEI_CFG_FW_SPS_IGN, }; /* Cannon Lake itouch with quirk for SPS 5.0 and newer Firmware exclusion @@ -1545,7 +1552,7 @@ static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = { MEI_CFG_KIND_ITOUCH, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, - MEI_CFG_FW_SPS, + MEI_CFG_FW_SPS_IGN, }; /* Tiger Lake and newer devices */ @@ -1562,7 +1569,7 @@ static const struct mei_cfg mei_me_pch15_sps_cfg = { MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, MEI_CFG_TRC, - MEI_CFG_FW_SPS, + MEI_CFG_FW_SPS_IGN, }; /* diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index f79076c67256..eb052005ca86 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -161,6 +161,11 @@ int mei_reset(struct mei_device *dev) return ret; } + if (dev->dev_state != MEI_DEV_RESETTING) { + dev_dbg(dev->dev, "wrong state = %d on link start\n", dev->dev_state); + return 0; + } + dev_dbg(dev->dev, "link is established start sending messages.\n"); mei_set_devstate(dev, MEI_DEV_INIT_CLIENTS); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index a67f4f2d33a9..0706322154cb 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -424,31 +424,26 @@ int mei_irq_read_handler(struct mei_device *dev, list_for_each_entry(cl, &dev->file_list, link) { if (mei_cl_hbm_equal(cl, mei_hdr)) { cl_dbg(dev, cl, "got a message\n"); - break; + ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list); + goto reset_slots; } } /* if no recipient cl was found we assume corrupted header */ - if (&cl->link == &dev->file_list) { - /* A message for not connected fixed address clients - * should be silently discarded - * On power down client may be force cleaned, - * silently discard such messages - */ - if (hdr_is_fixed(mei_hdr) || - dev->dev_state == MEI_DEV_POWER_DOWN) { - mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length); - ret = 0; - goto reset_slots; - } - dev_err(dev->dev, "no destination client found 0x%08X\n", - dev->rd_msg_hdr[0]); - ret = -EBADMSG; - goto end; + /* A message for not connected fixed address clients + * should be silently discarded + * On power down client may be force cleaned, + * silently discard such messages + */ + if (hdr_is_fixed(mei_hdr) || + dev->dev_state == MEI_DEV_POWER_DOWN) { + mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length); + ret = 0; + goto reset_slots; } - - ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list); - + dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr[0]); + ret = -EBADMSG; + goto end; reset_slots: /* reset the number of slots and header */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 3a45aaf002ac..33e58821e478 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/pci.h> +#include <linux/dma-mapping.h> #include <linux/sched.h> #include <linux/interrupt.h> @@ -113,6 +114,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_S, MEI_ME_PCH15_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_LP, MEI_ME_PCH15_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_P, MEI_ME_PCH15_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_N, MEI_ME_PCH15_CFG)}, /* required last entry */ {0, } @@ -192,14 +194,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto end; } - if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || - dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { - - err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (err) - err = dma_set_coherent_mask(&pdev->dev, - DMA_BIT_MASK(32)); - } + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); goto end; diff --git a/drivers/misc/ocxl/link.c b/drivers/misc/ocxl/link.c index ab039c115381..9670d02c927f 100644 --- a/drivers/misc/ocxl/link.c +++ b/drivers/misc/ocxl/link.c @@ -94,7 +94,7 @@ struct ocxl_link { struct spa *spa; void *platform_data; }; -static struct list_head links_list = LIST_HEAD_INIT(links_list); +static LIST_HEAD(links_list); static DEFINE_MUTEX(links_list_lock); enum xsl_response { diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c new file mode 100644 index 000000000000..c61be3404c6f --- /dev/null +++ b/drivers/misc/open-dice.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 - Google LLC + * Author: David Brazdil <dbrazdil@google.com> + * + * Driver for Open Profile for DICE. + * + * This driver takes ownership of a reserved memory region containing data + * generated by the Open Profile for DICE measured boot protocol. The memory + * contents are not interpreted by the kernel but can be mapped into a userspace + * process via a misc device. Userspace can also request a wipe of the memory. + * + * Userspace can access the data with (w/o error handling): + * + * fd = open("/dev/open-dice0", O_RDWR); + * read(fd, &size, sizeof(unsigned long)); + * data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + * write(fd, NULL, 0); // wipe + * close(fd); + */ + +#include <linux/io.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> + +#define DRIVER_NAME "open-dice" + +struct open_dice_drvdata { + struct mutex lock; + char name[16]; + struct reserved_mem *rmem; + struct miscdevice misc; +}; + +static inline struct open_dice_drvdata *to_open_dice_drvdata(struct file *filp) +{ + return container_of(filp->private_data, struct open_dice_drvdata, misc); +} + +static int open_dice_wipe(struct open_dice_drvdata *drvdata) +{ + void *kaddr; + + mutex_lock(&drvdata->lock); + kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base, + drvdata->rmem->size, MEMREMAP_WC); + if (IS_ERR(kaddr)) { + mutex_unlock(&drvdata->lock); + return PTR_ERR(kaddr); + } + + memset(kaddr, 0, drvdata->rmem->size); + devm_memunmap(drvdata->misc.this_device, kaddr); + mutex_unlock(&drvdata->lock); + return 0; +} + +/* + * Copies the size of the reserved memory region to the user-provided buffer. + */ +static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len, + loff_t *off) +{ + unsigned long val = to_open_dice_drvdata(filp)->rmem->size; + + return simple_read_from_buffer(ptr, len, off, &val, sizeof(val)); +} + +/* + * Triggers a wipe of the reserved memory region. The user-provided pointer + * is never dereferenced. + */ +static ssize_t open_dice_write(struct file *filp, const char __user *ptr, + size_t len, loff_t *off) +{ + if (open_dice_wipe(to_open_dice_drvdata(filp))) + return -EIO; + + /* Consume the input buffer. */ + return len; +} + +/* + * Creates a mapping of the reserved memory region in user address space. + */ +static int open_dice_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct open_dice_drvdata *drvdata = to_open_dice_drvdata(filp); + + /* Do not allow userspace to modify the underlying data. */ + if ((vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_SHARED)) + return -EPERM; + + /* Ensure userspace cannot acquire VM_WRITE + VM_SHARED later. */ + if (vma->vm_flags & VM_WRITE) + vma->vm_flags &= ~VM_MAYSHARE; + else if (vma->vm_flags & VM_SHARED) + vma->vm_flags &= ~VM_MAYWRITE; + + /* Create write-combine mapping so all clients observe a wipe. */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_flags |= VM_DONTCOPY | VM_DONTDUMP; + return vm_iomap_memory(vma, drvdata->rmem->base, drvdata->rmem->size); +} + +static const struct file_operations open_dice_fops = { + .owner = THIS_MODULE, + .read = open_dice_read, + .write = open_dice_write, + .mmap = open_dice_mmap, +}; + +static int __init open_dice_probe(struct platform_device *pdev) +{ + static unsigned int dev_idx; + struct device *dev = &pdev->dev; + struct reserved_mem *rmem; + struct open_dice_drvdata *drvdata; + int ret; + + rmem = of_reserved_mem_lookup(dev->of_node); + if (!rmem) { + dev_err(dev, "failed to lookup reserved memory\n"); + return -EINVAL; + } + + if (!rmem->size || (rmem->size > ULONG_MAX)) { + dev_err(dev, "invalid memory region size\n"); + return -EINVAL; + } + + if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) { + dev_err(dev, "memory region must be page-aligned\n"); + return -EINVAL; + } + + drvdata = devm_kmalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + *drvdata = (struct open_dice_drvdata){ + .lock = __MUTEX_INITIALIZER(drvdata->lock), + .rmem = rmem, + .misc = (struct miscdevice){ + .parent = dev, + .name = drvdata->name, + .minor = MISC_DYNAMIC_MINOR, + .fops = &open_dice_fops, + .mode = 0600, + }, + }; + + /* Index overflow check not needed, misc_register() will fail. */ + snprintf(drvdata->name, sizeof(drvdata->name), DRIVER_NAME"%u", dev_idx++); + + ret = misc_register(&drvdata->misc); + if (ret) { + dev_err(dev, "failed to register misc device '%s': %d\n", + drvdata->name, ret); + return ret; + } + + platform_set_drvdata(pdev, drvdata); + return 0; +} + +static int open_dice_remove(struct platform_device *pdev) +{ + struct open_dice_drvdata *drvdata = platform_get_drvdata(pdev); + + misc_deregister(&drvdata->misc); + return 0; +} + +static const struct of_device_id open_dice_of_match[] = { + { .compatible = "google,open-dice" }, + {}, +}; + +static struct platform_driver open_dice_driver = { + .remove = open_dice_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = open_dice_of_match, + }, +}; + +static int __init open_dice_init(void) +{ + int ret = platform_driver_probe(&open_dice_driver, open_dice_probe); + + /* DICE regions are optional. Succeed even with zero instances. */ + return (ret == -ENODEV) ? 0 : ret; +} + +static void __exit open_dice_exit(void) +{ + platform_driver_unregister(&open_dice_driver); +} + +module_init(open_dice_init); +module_exit(open_dice_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>"); diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index 0ea923fe6371..19dbdad8ad8a 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c @@ -1016,7 +1016,7 @@ static int quicktest1(unsigned long arg) break; } if (ret != MQE_QUEUE_FULL || i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest1: unexpect status %d, i %d\n", + printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n", smp_processor_id(), ret, i); goto done; } diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index e4c067c61251..5efc869fe59a 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -530,12 +530,6 @@ struct gru_blade_state { for ((i) = (k)*GRU_CBR_AU_SIZE; \ (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) -/* Scan each DSR in a DSR bitmap. Note: multiple DSRs in an allocation unit */ -#define for_each_dsr_in_allocation_map(i, map, k) \ - for_each_set_bit((k), (const unsigned long *)(map), GRU_DSR_AU) \ - for ((i) = (k) * GRU_DSR_AU_CL; \ - (i) < ((k) + 1) * GRU_DSR_AU_CL; (i)++) - #define gseg_physical_address(gru, ctxnum) \ ((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE) #define gseg_virtual_address(gru, ctxnum) \ diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 1018dc77269d..57a6157209a1 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/processor.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> @@ -31,6 +32,12 @@ #define VMCI_UTIL_NUM_RESOURCES 1 +/* + * Datagram buffers for DMA send/receive must accommodate at least + * a maximum sized datagram and the header. + */ +#define VMCI_DMA_DG_BUFFER_SIZE (VMCI_MAX_DG_SIZE + PAGE_SIZE) + static bool vmci_disable_msi; module_param_named(disable_msi, vmci_disable_msi, bool, 0); MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)"); @@ -45,13 +52,18 @@ static u32 vm_context_id = VMCI_INVALID_ID; struct vmci_guest_device { struct device *dev; /* PCI device we are attached to */ void __iomem *iobase; + void __iomem *mmio_base; bool exclusive_vectors; struct tasklet_struct datagram_tasklet; struct tasklet_struct bm_tasklet; + struct wait_queue_head inout_wq; void *data_buffer; + dma_addr_t data_buffer_base; + void *tx_buffer; + dma_addr_t tx_buffer_base; void *notification_bitmap; dma_addr_t notification_base; }; @@ -89,6 +101,92 @@ u32 vmci_get_vm_context_id(void) return vm_context_id; } +static unsigned int vmci_read_reg(struct vmci_guest_device *dev, u32 reg) +{ + if (dev->mmio_base != NULL) + return readl(dev->mmio_base + reg); + return ioread32(dev->iobase + reg); +} + +static void vmci_write_reg(struct vmci_guest_device *dev, u32 val, u32 reg) +{ + if (dev->mmio_base != NULL) + writel(val, dev->mmio_base + reg); + else + iowrite32(val, dev->iobase + reg); +} + +static void vmci_read_data(struct vmci_guest_device *vmci_dev, + void *dest, size_t size) +{ + if (vmci_dev->mmio_base == NULL) + ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR, + dest, size); + else { + /* + * For DMA datagrams, the data_buffer will contain the header on the + * first page, followed by the incoming datagram(s) on the following + * pages. The header uses an S/G element immediately following the + * header on the first page to point to the data area. + */ + struct vmci_data_in_out_header *buffer_header = vmci_dev->data_buffer; + struct vmci_sg_elem *sg_array = (struct vmci_sg_elem *)(buffer_header + 1); + size_t buffer_offset = dest - vmci_dev->data_buffer; + + buffer_header->opcode = 1; + buffer_header->size = 1; + buffer_header->busy = 0; + sg_array[0].addr = vmci_dev->data_buffer_base + buffer_offset; + sg_array[0].size = size; + + vmci_write_reg(vmci_dev, lower_32_bits(vmci_dev->data_buffer_base), + VMCI_DATA_IN_LOW_ADDR); + + wait_event(vmci_dev->inout_wq, buffer_header->busy == 1); + } +} + +static int vmci_write_data(struct vmci_guest_device *dev, + struct vmci_datagram *dg) +{ + int result; + + if (dev->mmio_base != NULL) { + struct vmci_data_in_out_header *buffer_header = dev->tx_buffer; + u8 *dg_out_buffer = (u8 *)(buffer_header + 1); + + if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) + return VMCI_ERROR_INVALID_ARGS; + + /* + * Initialize send buffer with outgoing datagram + * and set up header for inline data. Device will + * not access buffer asynchronously - only after + * the write to VMCI_DATA_OUT_LOW_ADDR. + */ + memcpy(dg_out_buffer, dg, VMCI_DG_SIZE(dg)); + buffer_header->opcode = 0; + buffer_header->size = VMCI_DG_SIZE(dg); + buffer_header->busy = 1; + + vmci_write_reg(dev, lower_32_bits(dev->tx_buffer_base), + VMCI_DATA_OUT_LOW_ADDR); + + /* Caller holds a spinlock, so cannot block. */ + spin_until_cond(buffer_header->busy == 0); + + result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR); + if (result == VMCI_SUCCESS) + result = (int)buffer_header->result; + } else { + iowrite8_rep(dev->iobase + VMCI_DATA_OUT_ADDR, + dg, VMCI_DG_SIZE(dg)); + result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR); + } + + return result; +} + /* * VM to hypervisor call mechanism. We use the standard VMware naming * convention since shared code is calling this function as well. @@ -114,9 +212,8 @@ int vmci_send_datagram(struct vmci_datagram *dg) spin_lock_irqsave(&vmci_dev_spinlock, flags); if (vmci_dev_g) { - iowrite8_rep(vmci_dev_g->iobase + VMCI_DATA_OUT_ADDR, - dg, VMCI_DG_SIZE(dg)); - result = ioread32(vmci_dev_g->iobase + VMCI_RESULT_LOW_ADDR); + vmci_write_data(vmci_dev_g, dg); + result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR); } else { result = VMCI_ERROR_UNAVAILABLE; } @@ -156,9 +253,9 @@ static void vmci_guest_cid_update(u32 sub_id, /* * Verify that the host supports the hypercalls we need. If it does not, - * try to find fallback hypercalls and use those instead. Returns - * true if required hypercalls (or fallback hypercalls) are - * supported by the host, false otherwise. + * try to find fallback hypercalls and use those instead. Returns 0 if + * required hypercalls (or fallback hypercalls) are supported by the host, + * an error code otherwise. */ static int vmci_check_host_caps(struct pci_dev *pdev) { @@ -195,15 +292,17 @@ static int vmci_check_host_caps(struct pci_dev *pdev) } /* - * Reads datagrams from the data in port and dispatches them. We - * always start reading datagrams into only the first page of the - * datagram buffer. If the datagrams don't fit into one page, we - * use the maximum datagram buffer size for the remainder of the - * invocation. This is a simple heuristic for not penalizing - * small datagrams. + * Reads datagrams from the device and dispatches them. For IO port + * based access to the device, we always start reading datagrams into + * only the first page of the datagram buffer. If the datagrams don't + * fit into one page, we use the maximum datagram buffer size for the + * remainder of the invocation. This is a simple heuristic for not + * penalizing small datagrams. For DMA-based datagrams, we always + * use the maximum datagram buffer size, since there is no performance + * penalty for doing so. * * This function assumes that it has exclusive access to the data - * in port for the duration of the call. + * in register(s) for the duration of the call. */ static void vmci_dispatch_dgs(unsigned long data) { @@ -211,23 +310,41 @@ static void vmci_dispatch_dgs(unsigned long data) u8 *dg_in_buffer = vmci_dev->data_buffer; struct vmci_datagram *dg; size_t dg_in_buffer_size = VMCI_MAX_DG_SIZE; - size_t current_dg_in_buffer_size = PAGE_SIZE; + size_t current_dg_in_buffer_size; size_t remaining_bytes; + bool is_io_port = vmci_dev->mmio_base == NULL; BUILD_BUG_ON(VMCI_MAX_DG_SIZE < PAGE_SIZE); - ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR, - vmci_dev->data_buffer, current_dg_in_buffer_size); + if (!is_io_port) { + /* For mmio, the first page is used for the header. */ + dg_in_buffer += PAGE_SIZE; + + /* + * For DMA-based datagram operations, there is no performance + * penalty for reading the maximum buffer size. + */ + current_dg_in_buffer_size = VMCI_MAX_DG_SIZE; + } else { + current_dg_in_buffer_size = PAGE_SIZE; + } + vmci_read_data(vmci_dev, dg_in_buffer, current_dg_in_buffer_size); dg = (struct vmci_datagram *)dg_in_buffer; remaining_bytes = current_dg_in_buffer_size; + /* + * Read through the buffer until an invalid datagram header is + * encountered. The exit condition for datagrams read through + * VMCI_DATA_IN_ADDR is a bit more complicated, since a datagram + * can start on any page boundary in the buffer. + */ while (dg->dst.resource != VMCI_INVALID_ID || - remaining_bytes > PAGE_SIZE) { + (is_io_port && remaining_bytes > PAGE_SIZE)) { unsigned dg_in_size; /* - * When the input buffer spans multiple pages, a datagram can - * start on any page boundary in the buffer. + * If using VMCI_DATA_IN_ADDR, skip to the next page + * as a datagram can start on any page boundary. */ if (dg->dst.resource == VMCI_INVALID_ID) { dg = (struct vmci_datagram *)roundup( @@ -277,11 +394,10 @@ static void vmci_dispatch_dgs(unsigned long data) current_dg_in_buffer_size = dg_in_buffer_size; - ioread8_rep(vmci_dev->iobase + - VMCI_DATA_IN_ADDR, - vmci_dev->data_buffer + + vmci_read_data(vmci_dev, + dg_in_buffer + remaining_bytes, - current_dg_in_buffer_size - + current_dg_in_buffer_size - remaining_bytes); } @@ -319,10 +435,8 @@ static void vmci_dispatch_dgs(unsigned long data) current_dg_in_buffer_size = dg_in_buffer_size; for (;;) { - ioread8_rep(vmci_dev->iobase + - VMCI_DATA_IN_ADDR, - vmci_dev->data_buffer, - current_dg_in_buffer_size); + vmci_read_data(vmci_dev, dg_in_buffer, + current_dg_in_buffer_size); if (bytes_to_skip <= current_dg_in_buffer_size) break; @@ -339,8 +453,7 @@ static void vmci_dispatch_dgs(unsigned long data) if (remaining_bytes < VMCI_DG_HEADERSIZE) { /* Get the next batch of datagrams. */ - ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR, - vmci_dev->data_buffer, + vmci_read_data(vmci_dev, dg_in_buffer, current_dg_in_buffer_size); dg = (struct vmci_datagram *)dg_in_buffer; remaining_bytes = current_dg_in_buffer_size; @@ -384,7 +497,7 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev) unsigned int icr; /* Acknowledge interrupt and determine what needs doing. */ - icr = ioread32(dev->iobase + VMCI_ICR_ADDR); + icr = vmci_read_reg(dev, VMCI_ICR_ADDR); if (icr == 0 || icr == ~0) return IRQ_NONE; @@ -398,6 +511,12 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev) icr &= ~VMCI_ICR_NOTIFICATION; } + + if (icr & VMCI_ICR_DMA_DATAGRAM) { + wake_up_all(&dev->inout_wq); + icr &= ~VMCI_ICR_DMA_DATAGRAM; + } + if (icr != 0) dev_warn(dev->dev, "Ignoring unknown interrupt cause (%d)\n", @@ -423,13 +542,47 @@ static irqreturn_t vmci_interrupt_bm(int irq, void *_dev) } /* + * Interrupt handler for MSI-X interrupt vector VMCI_INTR_DMA_DATAGRAM, + * which is for the completion of a DMA datagram send or receive operation. + * Will only get called if we are using MSI-X with exclusive vectors. + */ +static irqreturn_t vmci_interrupt_dma_datagram(int irq, void *_dev) +{ + struct vmci_guest_device *dev = _dev; + + wake_up_all(&dev->inout_wq); + + return IRQ_HANDLED; +} + +static void vmci_free_dg_buffers(struct vmci_guest_device *vmci_dev) +{ + if (vmci_dev->mmio_base != NULL) { + if (vmci_dev->tx_buffer != NULL) + dma_free_coherent(vmci_dev->dev, + VMCI_DMA_DG_BUFFER_SIZE, + vmci_dev->tx_buffer, + vmci_dev->tx_buffer_base); + if (vmci_dev->data_buffer != NULL) + dma_free_coherent(vmci_dev->dev, + VMCI_DMA_DG_BUFFER_SIZE, + vmci_dev->data_buffer, + vmci_dev->data_buffer_base); + } else { + vfree(vmci_dev->data_buffer); + } +} + +/* * Most of the initialization at module load time is done here. */ static int vmci_guest_probe_device(struct pci_dev *pdev, const struct pci_device_id *id) { struct vmci_guest_device *vmci_dev; - void __iomem *iobase; + void __iomem *iobase = NULL; + void __iomem *mmio_base = NULL; + unsigned int num_irq_vectors; unsigned int capabilities; unsigned int caps_in_use; unsigned long cmd; @@ -445,16 +598,29 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, return error; } - error = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME); - if (error) { - dev_err(&pdev->dev, "Failed to reserve/map IO regions\n"); - return error; - } + /* + * The VMCI device with mmio access to registers requests 256KB + * for BAR1. If present, driver will use new VMCI device + * functionality for register access and datagram send/recv. + */ - iobase = pcim_iomap_table(pdev)[0]; + if (pci_resource_len(pdev, 1) == VMCI_WITH_MMIO_ACCESS_BAR_SIZE) { + dev_info(&pdev->dev, "MMIO register access is available\n"); + mmio_base = pci_iomap_range(pdev, 1, VMCI_MMIO_ACCESS_OFFSET, + VMCI_MMIO_ACCESS_SIZE); + /* If the map fails, we fall back to IOIO access. */ + if (!mmio_base) + dev_warn(&pdev->dev, "Failed to map MMIO register access\n"); + } - dev_info(&pdev->dev, "Found VMCI PCI device at %#lx, irq %u\n", - (unsigned long)iobase, pdev->irq); + if (!mmio_base) { + error = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (error) { + dev_err(&pdev->dev, "Failed to reserve/map IO regions\n"); + return error; + } + iobase = pcim_iomap_table(pdev)[0]; + } vmci_dev = devm_kzalloc(&pdev->dev, sizeof(*vmci_dev), GFP_KERNEL); if (!vmci_dev) { @@ -466,17 +632,35 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, vmci_dev->dev = &pdev->dev; vmci_dev->exclusive_vectors = false; vmci_dev->iobase = iobase; + vmci_dev->mmio_base = mmio_base; tasklet_init(&vmci_dev->datagram_tasklet, vmci_dispatch_dgs, (unsigned long)vmci_dev); tasklet_init(&vmci_dev->bm_tasklet, vmci_process_bitmap, (unsigned long)vmci_dev); + init_waitqueue_head(&vmci_dev->inout_wq); - vmci_dev->data_buffer = vmalloc(VMCI_MAX_DG_SIZE); + if (mmio_base != NULL) { + vmci_dev->tx_buffer = dma_alloc_coherent(&pdev->dev, VMCI_DMA_DG_BUFFER_SIZE, + &vmci_dev->tx_buffer_base, + GFP_KERNEL); + if (!vmci_dev->tx_buffer) { + dev_err(&pdev->dev, + "Can't allocate memory for datagram tx buffer\n"); + return -ENOMEM; + } + + vmci_dev->data_buffer = dma_alloc_coherent(&pdev->dev, VMCI_DMA_DG_BUFFER_SIZE, + &vmci_dev->data_buffer_base, + GFP_KERNEL); + } else { + vmci_dev->data_buffer = vmalloc(VMCI_MAX_DG_SIZE); + } if (!vmci_dev->data_buffer) { dev_err(&pdev->dev, "Can't allocate memory for datagram buffer\n"); - return -ENOMEM; + error = -ENOMEM; + goto err_free_data_buffers; } pci_set_master(pdev); /* To enable queue_pair functionality. */ @@ -490,11 +674,11 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * * Right now, we need datagrams. There are no fallbacks. */ - capabilities = ioread32(vmci_dev->iobase + VMCI_CAPS_ADDR); + capabilities = vmci_read_reg(vmci_dev, VMCI_CAPS_ADDR); if (!(capabilities & VMCI_CAPS_DATAGRAM)) { dev_err(&pdev->dev, "Device does not support datagrams\n"); error = -ENXIO; - goto err_free_data_buffer; + goto err_free_data_buffers; } caps_in_use = VMCI_CAPS_DATAGRAM; @@ -522,19 +706,39 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, vmci_dev->notification_bitmap = dma_alloc_coherent( &pdev->dev, PAGE_SIZE, &vmci_dev->notification_base, GFP_KERNEL); - if (!vmci_dev->notification_bitmap) { + if (!vmci_dev->notification_bitmap) dev_warn(&pdev->dev, "Unable to allocate notification bitmap\n"); - } else { - memset(vmci_dev->notification_bitmap, 0, PAGE_SIZE); + else caps_in_use |= VMCI_CAPS_NOTIFICATIONS; + } + + if (mmio_base != NULL) { + if (capabilities & VMCI_CAPS_DMA_DATAGRAM) { + caps_in_use |= VMCI_CAPS_DMA_DATAGRAM; + } else { + dev_err(&pdev->dev, + "Missing capability: VMCI_CAPS_DMA_DATAGRAM\n"); + error = -ENXIO; + goto err_free_notification_bitmap; } } dev_info(&pdev->dev, "Using capabilities 0x%x\n", caps_in_use); /* Let the host know which capabilities we intend to use. */ - iowrite32(caps_in_use, vmci_dev->iobase + VMCI_CAPS_ADDR); + vmci_write_reg(vmci_dev, caps_in_use, VMCI_CAPS_ADDR); + + if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM) { + /* Let the device know the size for pages passed down. */ + vmci_write_reg(vmci_dev, PAGE_SHIFT, VMCI_GUEST_PAGE_SHIFT); + + /* Configure the high order parts of the data in/out buffers. */ + vmci_write_reg(vmci_dev, upper_32_bits(vmci_dev->data_buffer_base), + VMCI_DATA_IN_HIGH_ADDR); + vmci_write_reg(vmci_dev, upper_32_bits(vmci_dev->tx_buffer_base), + VMCI_DATA_OUT_HIGH_ADDR); + } /* Set up global device so that we can start sending datagrams */ spin_lock_irq(&vmci_dev_spinlock); @@ -561,7 +765,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, /* Check host capabilities. */ error = vmci_check_host_caps(pdev); if (error) - goto err_remove_bitmap; + goto err_remove_vmci_dev_g; /* Enable device. */ @@ -581,13 +785,17 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * Enable interrupts. Try MSI-X first, then MSI, and then fallback on * legacy interrupts. */ - error = pci_alloc_irq_vectors(pdev, VMCI_MAX_INTRS, VMCI_MAX_INTRS, - PCI_IRQ_MSIX); + if (vmci_dev->mmio_base != NULL) + num_irq_vectors = VMCI_MAX_INTRS; + else + num_irq_vectors = VMCI_MAX_INTRS_NOTIFICATION; + error = pci_alloc_irq_vectors(pdev, num_irq_vectors, num_irq_vectors, + PCI_IRQ_MSIX); if (error < 0) { error = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY); if (error < 0) - goto err_remove_bitmap; + goto err_unsubscribe_event; } else { vmci_dev->exclusive_vectors = true; } @@ -620,6 +828,17 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, pci_irq_vector(pdev, 1), error); goto err_free_irq; } + if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM) { + error = request_irq(pci_irq_vector(pdev, 2), + vmci_interrupt_dma_datagram, + 0, KBUILD_MODNAME, vmci_dev); + if (error) { + dev_err(&pdev->dev, + "Failed to allocate irq %u: %d\n", + pci_irq_vector(pdev, 2), error); + goto err_free_bm_irq; + } + } } dev_dbg(&pdev->dev, "Registered device\n"); @@ -630,17 +849,22 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, cmd = VMCI_IMR_DATAGRAM; if (caps_in_use & VMCI_CAPS_NOTIFICATIONS) cmd |= VMCI_IMR_NOTIFICATION; - iowrite32(cmd, vmci_dev->iobase + VMCI_IMR_ADDR); + if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM) + cmd |= VMCI_IMR_DMA_DATAGRAM; + vmci_write_reg(vmci_dev, cmd, VMCI_IMR_ADDR); /* Enable interrupts. */ - iowrite32(VMCI_CONTROL_INT_ENABLE, - vmci_dev->iobase + VMCI_CONTROL_ADDR); + vmci_write_reg(vmci_dev, VMCI_CONTROL_INT_ENABLE, VMCI_CONTROL_ADDR); pci_set_drvdata(pdev, vmci_dev); vmci_call_vsock_callback(false); return 0; +err_free_bm_irq: + if (vmci_dev->exclusive_vectors) + free_irq(pci_irq_vector(pdev, 1), vmci_dev); + err_free_irq: free_irq(pci_irq_vector(pdev, 0), vmci_dev); tasklet_kill(&vmci_dev->datagram_tasklet); @@ -649,29 +873,29 @@ err_free_irq: err_disable_msi: pci_free_irq_vectors(pdev); +err_unsubscribe_event: vmci_err = vmci_event_unsubscribe(ctx_update_sub_id); if (vmci_err < VMCI_SUCCESS) dev_warn(&pdev->dev, "Failed to unsubscribe from event (type=%d) with subscriber (ID=0x%x): %d\n", VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id, vmci_err); -err_remove_bitmap: - if (vmci_dev->notification_bitmap) { - iowrite32(VMCI_CONTROL_RESET, - vmci_dev->iobase + VMCI_CONTROL_ADDR); - dma_free_coherent(&pdev->dev, PAGE_SIZE, - vmci_dev->notification_bitmap, - vmci_dev->notification_base); - } - err_remove_vmci_dev_g: spin_lock_irq(&vmci_dev_spinlock); vmci_pdev = NULL; vmci_dev_g = NULL; spin_unlock_irq(&vmci_dev_spinlock); -err_free_data_buffer: - vfree(vmci_dev->data_buffer); +err_free_notification_bitmap: + if (vmci_dev->notification_bitmap) { + vmci_write_reg(vmci_dev, VMCI_CONTROL_RESET, VMCI_CONTROL_ADDR); + dma_free_coherent(&pdev->dev, PAGE_SIZE, + vmci_dev->notification_bitmap, + vmci_dev->notification_base); + } + +err_free_data_buffers: + vmci_free_dg_buffers(vmci_dev); /* The rest are managed resources and will be freed by PCI core */ return error; @@ -700,15 +924,18 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) spin_unlock_irq(&vmci_dev_spinlock); dev_dbg(&pdev->dev, "Resetting vmci device\n"); - iowrite32(VMCI_CONTROL_RESET, vmci_dev->iobase + VMCI_CONTROL_ADDR); + vmci_write_reg(vmci_dev, VMCI_CONTROL_RESET, VMCI_CONTROL_ADDR); /* * Free IRQ and then disable MSI/MSI-X as appropriate. For * MSI-X, we might have multiple vectors, each with their own * IRQ, which we must free too. */ - if (vmci_dev->exclusive_vectors) + if (vmci_dev->exclusive_vectors) { free_irq(pci_irq_vector(pdev, 1), vmci_dev); + if (vmci_dev->mmio_base != NULL) + free_irq(pci_irq_vector(pdev, 2), vmci_dev); + } free_irq(pci_irq_vector(pdev, 0), vmci_dev); pci_free_irq_vectors(pdev); @@ -726,7 +953,10 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) vmci_dev->notification_base); } - vfree(vmci_dev->data_buffer); + vmci_free_dg_buffers(vmci_dev); + + if (vmci_dev->mmio_base != NULL) + pci_iounmap(pdev, vmci_dev->mmio_base); /* The rest are managed resources and will be freed by PCI core */ } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 219029224727..e1580f78c6b2 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1504,12 +1504,12 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) realtek_init_host(host); - if (pcr->rtd3_en) { - pm_runtime_set_autosuspend_delay(&pdev->dev, 5000); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_enable(&pdev->dev); - } - + pm_runtime_no_callbacks(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 200); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); mmc_add_host(mmc); @@ -1530,11 +1530,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pcr->slots[RTSX_SD_CARD].card_event = NULL; mmc = host->mmc; - if (pcr->rtd3_en) { - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_disable(&pdev->dev); - } - cancel_work_sync(&host->work); mutex_lock(&host->host_mutex); @@ -1557,6 +1552,9 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) flush_work(&host->work); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + mmc_free_host(mmc); dev_dbg(&(pdev->dev), diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c5c3e9387647..7731796024e0 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -725,8 +725,7 @@ int del_mtd_device(struct mtd_info *mtd) debugfs_remove_recursive(mtd->dbg.dfs_dir); /* Try to remove the NVMEM provider */ - if (mtd->nvmem) - nvmem_unregister(mtd->nvmem); + nvmem_unregister(mtd->nvmem); device_unregister(&mtd->dev); @@ -905,8 +904,7 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd) return 0; err: - if (mtd->otp_user_nvmem) - nvmem_unregister(mtd->otp_user_nvmem); + nvmem_unregister(mtd->otp_user_nvmem); return err; } @@ -1010,11 +1008,8 @@ int mtd_device_unregister(struct mtd_info *master) memset(&master->reboot_notifier, 0, sizeof(master->reboot_notifier)); } - if (master->otp_user_nvmem) - nvmem_unregister(master->otp_user_nvmem); - - if (master->otp_factory_nvmem) - nvmem_unregister(master->otp_factory_nvmem); + nvmem_unregister(master->otp_user_nvmem); + nvmem_unregister(master->otp_factory_nvmem); err = del_mtd_partitions(master); if (err) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index 22f4709768d1..49bedbe6316c 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -29,6 +29,20 @@ */ #define MUX_CACHE_UNKNOWN MUX_IDLE_AS_IS +/** + * struct mux_state - Represents a mux controller state specific to a given + * consumer. + * @mux: Pointer to a mux controller. + * @state: State of the mux to be selected. + * + * This structure is specific to the consumer that acquires it and has + * information specific to that consumer. + */ +struct mux_state { + struct mux_control *mux; + unsigned int state; +}; + static struct class mux_class = { .name = "mux", .owner = THIS_MODULE, @@ -341,7 +355,8 @@ static void mux_control_delay(struct mux_control *mux, unsigned int delay_us) * On successfully selecting the mux-control state, it will be locked until * there is a call to mux_control_deselect(). If the mux-control is already * selected when mux_control_select() is called, the caller will be blocked - * until mux_control_deselect() is called (by someone else). + * until mux_control_deselect() or mux_state_deselect() is called (by someone + * else). * * Therefore, make sure to call mux_control_deselect() when the operation is * complete and the mux-control is free for others to use, but do not call @@ -371,13 +386,37 @@ int mux_control_select_delay(struct mux_control *mux, unsigned int state, EXPORT_SYMBOL_GPL(mux_control_select_delay); /** + * mux_state_select_delay() - Select the given multiplexer state. + * @mstate: The mux-state to select. + * @delay_us: The time to delay (in microseconds) if the mux state is changed. + * + * On successfully selecting the mux-state, its mux-control will be locked + * until there is a call to mux_state_deselect(). If the mux-control is already + * selected when mux_state_select() is called, the caller will be blocked + * until mux_state_deselect() or mux_control_deselect() is called (by someone + * else). + * + * Therefore, make sure to call mux_state_deselect() when the operation is + * complete and the mux-control is free for others to use, but do not call + * mux_state_deselect() if mux_state_select() fails. + * + * Return: 0 when the mux-state has been selected or a negative + * errno on error. + */ +int mux_state_select_delay(struct mux_state *mstate, unsigned int delay_us) +{ + return mux_control_select_delay(mstate->mux, mstate->state, delay_us); +} +EXPORT_SYMBOL_GPL(mux_state_select_delay); + +/** * mux_control_try_select_delay() - Try to select the given multiplexer state. * @mux: The mux-control to request a change of state from. * @state: The new requested state. * @delay_us: The time to delay (in microseconds) if the mux state is changed. * * On successfully selecting the mux-control state, it will be locked until - * mux_control_deselect() called. + * mux_control_deselect() is called. * * Therefore, make sure to call mux_control_deselect() when the operation is * complete and the mux-control is free for others to use, but do not call @@ -406,6 +445,27 @@ int mux_control_try_select_delay(struct mux_control *mux, unsigned int state, EXPORT_SYMBOL_GPL(mux_control_try_select_delay); /** + * mux_state_try_select_delay() - Try to select the given multiplexer state. + * @mstate: The mux-state to select. + * @delay_us: The time to delay (in microseconds) if the mux state is changed. + * + * On successfully selecting the mux-state, its mux-control will be locked + * until mux_state_deselect() is called. + * + * Therefore, make sure to call mux_state_deselect() when the operation is + * complete and the mux-control is free for others to use, but do not call + * mux_state_deselect() if mux_state_try_select() fails. + * + * Return: 0 when the mux-state has been selected or a negative errno on + * error. Specifically -EBUSY if the mux-control is contended. + */ +int mux_state_try_select_delay(struct mux_state *mstate, unsigned int delay_us) +{ + return mux_control_try_select_delay(mstate->mux, mstate->state, delay_us); +} +EXPORT_SYMBOL_GPL(mux_state_try_select_delay); + +/** * mux_control_deselect() - Deselect the previously selected multiplexer state. * @mux: The mux-control to deselect. * @@ -431,6 +491,24 @@ int mux_control_deselect(struct mux_control *mux) } EXPORT_SYMBOL_GPL(mux_control_deselect); +/** + * mux_state_deselect() - Deselect the previously selected multiplexer state. + * @mstate: The mux-state to deselect. + * + * It is required that a single call is made to mux_state_deselect() for + * each and every successful call made to either of mux_state_select() or + * mux_state_try_select(). + * + * Return: 0 on success and a negative errno on error. An error can only + * occur if the mux has an idle state. Note that even if an error occurs, the + * mux-control is unlocked and is thus free for the next access. + */ +int mux_state_deselect(struct mux_state *mstate) +{ + return mux_control_deselect(mstate->mux); +} +EXPORT_SYMBOL_GPL(mux_state_deselect); + /* Note this function returns a reference to the mux_chip dev. */ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np) { @@ -441,14 +519,17 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np) return dev ? to_mux_chip(dev) : NULL; } -/** - * mux_control_get() - Get the mux-control for a device. +/* + * mux_get() - Get the mux-control for a device. * @dev: The device that needs a mux-control. * @mux_name: The name identifying the mux-control. + * @state: Pointer to where the requested state is returned, or NULL when + * the required multiplexer states are handled by other means. * * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno. */ -struct mux_control *mux_control_get(struct device *dev, const char *mux_name) +static struct mux_control *mux_get(struct device *dev, const char *mux_name, + unsigned int *state) { struct device_node *np = dev->of_node; struct of_phandle_args args; @@ -458,8 +539,12 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) int ret; if (mux_name) { - index = of_property_match_string(np, "mux-control-names", - mux_name); + if (state) + index = of_property_match_string(np, "mux-state-names", + mux_name); + else + index = of_property_match_string(np, "mux-control-names", + mux_name); if (index < 0) { dev_err(dev, "mux controller '%s' not found\n", mux_name); @@ -467,12 +552,17 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) } } - ret = of_parse_phandle_with_args(np, - "mux-controls", "#mux-control-cells", - index, &args); + if (state) + ret = of_parse_phandle_with_args(np, + "mux-states", "#mux-state-cells", + index, &args); + else + ret = of_parse_phandle_with_args(np, + "mux-controls", "#mux-control-cells", + index, &args); if (ret) { - dev_err(dev, "%pOF: failed to get mux-control %s(%i)\n", - np, mux_name ?: "", index); + dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n", + np, state ? "state" : "control", mux_name ?: "", index); return ERR_PTR(ret); } @@ -481,17 +571,35 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) if (!mux_chip) return ERR_PTR(-EPROBE_DEFER); - if (args.args_count > 1 || - (!args.args_count && (mux_chip->controllers > 1))) { - dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n", - np, args.np); - put_device(&mux_chip->dev); - return ERR_PTR(-EINVAL); - } - controller = 0; - if (args.args_count) - controller = args.args[0]; + if (state) { + if (args.args_count > 2 || args.args_count == 0 || + (args.args_count < 2 && mux_chip->controllers > 1)) { + dev_err(dev, "%pOF: wrong #mux-state-cells for %pOF\n", + np, args.np); + put_device(&mux_chip->dev); + return ERR_PTR(-EINVAL); + } + + if (args.args_count == 2) { + controller = args.args[0]; + *state = args.args[1]; + } else { + *state = args.args[0]; + } + + } else { + if (args.args_count > 1 || + (!args.args_count && mux_chip->controllers > 1)) { + dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n", + np, args.np); + put_device(&mux_chip->dev); + return ERR_PTR(-EINVAL); + } + + if (args.args_count) + controller = args.args[0]; + } if (controller >= mux_chip->controllers) { dev_err(dev, "%pOF: bad mux controller %u specified in %pOF\n", @@ -502,6 +610,18 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) return &mux_chip->mux[controller]; } + +/** + * mux_control_get() - Get the mux-control for a device. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * + * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno. + */ +struct mux_control *mux_control_get(struct device *dev, const char *mux_name) +{ + return mux_get(dev, mux_name, NULL); +} EXPORT_SYMBOL_GPL(mux_control_get); /** @@ -554,6 +674,81 @@ struct mux_control *devm_mux_control_get(struct device *dev, EXPORT_SYMBOL_GPL(devm_mux_control_get); /* + * mux_state_get() - Get the mux-state for a device. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno. + */ +static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) +{ + struct mux_state *mstate; + + mstate = kzalloc(sizeof(*mstate), GFP_KERNEL); + if (!mstate) + return ERR_PTR(-ENOMEM); + + mstate->mux = mux_get(dev, mux_name, &mstate->state); + if (IS_ERR(mstate->mux)) { + int err = PTR_ERR(mstate->mux); + + kfree(mstate); + return ERR_PTR(err); + } + + return mstate; +} + +/* + * mux_state_put() - Put away the mux-state for good. + * @mstate: The mux-state to put away. + * + * mux_state_put() reverses the effects of mux_state_get(). + */ +static void mux_state_put(struct mux_state *mstate) +{ + mux_control_put(mstate->mux); + kfree(mstate); +} + +static void devm_mux_state_release(struct device *dev, void *res) +{ + struct mux_state *mstate = *(struct mux_state **)res; + + mux_state_put(mstate); +} + +/** + * devm_mux_state_get() - Get the mux-state for a device, with resource + * management. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * + * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + */ +struct mux_state *devm_mux_state_get(struct device *dev, + const char *mux_name) +{ + struct mux_state **ptr, *mstate; + + ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + mstate = mux_state_get(dev, mux_name); + if (IS_ERR(mstate)) { + devres_free(ptr); + return mstate; + } + + *ptr = mstate; + devres_add(dev, ptr); + + return mstate; +} +EXPORT_SYMBOL_GPL(devm_mux_state_get); + +/* * Using subsys_initcall instead of module_init here to try to ensure - for * the non-modular case - that the subsystem is initialized when mux consumers * and mux controllers start to use it. diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index da414617a54d..555aa77a574d 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -300,4 +300,28 @@ config NVMEM_BRCM_NVRAM This driver provides support for Broadcom's NVRAM that can be accessed using I/O mapping. +config NVMEM_LAYERSCAPE_SFP + tristate "Layerscape SFP (Security Fuse Processor) support" + depends on ARCH_LAYERSCAPE || COMPILE_TEST + depends on HAS_IOMEM + help + This driver provides support to read the eFuses on Freescale + Layerscape SoC's. For example, the vendor provides a per part + unique ID there. + + This driver can also be built as a module. If so, the module + will be called layerscape-sfp. + +config NVMEM_SUNPLUS_OCOTP + tristate "Sunplus SoC OTP support" + depends on SOC_SP7021 || COMPILE_TEST + depends on HAS_IOMEM + help + This is a driver for the On-chip OTP controller (OCOTP) available + on Sunplus SoCs. It provides access to 128 bytes of one-time + programmable eFuse. + + This driver can also be built as a module. If so, the module + will be called nvmem-sunplus-ocotp. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index dcbbde35b6a8..891958e29d25 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -61,3 +61,7 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o nvmem-rmem-y := rmem.o obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o nvmem_brcm_nvram-y := brcm_nvram.o +obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o +nvmem-layerscape-sfp-y := layerscape-sfp.o +obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o +nvmem_sunplus_ocotp-y := sunplus-ocotp.o diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index bd2ecaaf4585..439f00b9eef6 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -6,12 +6,26 @@ #include <linux/io.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> #include <linux/platform_device.h> +#include <linux/slab.h> + +#define NVRAM_MAGIC "FLSH" struct brcm_nvram { struct device *dev; void __iomem *base; + struct nvmem_cell_info *cells; + int ncells; +}; + +struct brcm_nvram_header { + char magic[4]; + __le32 len; + __le32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ + __le32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ + __le32 config_ncdl; /* ncdl values for memc */ }; static int brcm_nvram_read(void *context, unsigned int offset, void *val, @@ -26,6 +40,75 @@ static int brcm_nvram_read(void *context, unsigned int offset, void *val, return 0; } +static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data, + size_t len) +{ + struct device *dev = priv->dev; + char *var, *value, *eq; + int idx; + + priv->ncells = 0; + for (var = data + sizeof(struct brcm_nvram_header); + var < (char *)data + len && *var; + var += strlen(var) + 1) { + priv->ncells++; + } + + priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); + if (!priv->cells) + return -ENOMEM; + + for (var = data + sizeof(struct brcm_nvram_header), idx = 0; + var < (char *)data + len && *var; + var = value + strlen(value) + 1, idx++) { + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; + value = eq + 1; + + priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); + if (!priv->cells[idx].name) + return -ENOMEM; + priv->cells[idx].offset = value - (char *)data; + priv->cells[idx].bytes = strlen(value); + } + + return 0; +} + +static int brcm_nvram_parse(struct brcm_nvram *priv) +{ + struct device *dev = priv->dev; + struct brcm_nvram_header header; + uint8_t *data; + size_t len; + int err; + + memcpy_fromio(&header, priv->base, sizeof(header)); + + if (memcmp(header.magic, NVRAM_MAGIC, 4)) { + dev_err(dev, "Invalid NVRAM magic\n"); + return -EINVAL; + } + + len = le32_to_cpu(header.len); + + data = kcalloc(1, len, GFP_KERNEL); + memcpy_fromio(data, priv->base, len); + data[len - 1] = '\0'; + + err = brcm_nvram_add_cells(priv, data, len); + if (err) { + dev_err(dev, "Failed to add cells: %d\n", err); + return err; + } + + kfree(data); + + return 0; +} + static int brcm_nvram_probe(struct platform_device *pdev) { struct nvmem_config config = { @@ -35,6 +118,7 @@ static int brcm_nvram_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct brcm_nvram *priv; + int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -46,7 +130,13 @@ static int brcm_nvram_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + err = brcm_nvram_parse(priv); + if (err) + return err; + config.dev = dev; + config.cells = priv->cells; + config.ncells = priv->ncells; config.priv = priv; config.size = resource_size(res); diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 9fd1602b539d..f58d9bc7aa08 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -903,13 +903,14 @@ static void nvmem_device_release(struct kref *kref) */ void nvmem_unregister(struct nvmem_device *nvmem) { - kref_put(&nvmem->refcnt, nvmem_device_release); + if (nvmem) + kref_put(&nvmem->refcnt, nvmem_device_release); } EXPORT_SYMBOL_GPL(nvmem_unregister); -static void devm_nvmem_release(struct device *dev, void *res) +static void devm_nvmem_unregister(void *nvmem) { - nvmem_unregister(*(struct nvmem_device **)res); + nvmem_unregister(nvmem); } /** @@ -926,47 +927,21 @@ static void devm_nvmem_release(struct device *dev, void *res) struct nvmem_device *devm_nvmem_register(struct device *dev, const struct nvmem_config *config) { - struct nvmem_device **ptr, *nvmem; - - ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct nvmem_device *nvmem; + int ret; nvmem = nvmem_register(config); + if (IS_ERR(nvmem)) + return nvmem; - if (!IS_ERR(nvmem)) { - *ptr = nvmem; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + ret = devm_add_action_or_reset(dev, devm_nvmem_unregister, nvmem); + if (ret) + return ERR_PTR(ret); return nvmem; } EXPORT_SYMBOL_GPL(devm_nvmem_register); -static int devm_nvmem_match(struct device *dev, void *res, void *data) -{ - struct nvmem_device **r = res; - - return *r == data; -} - -/** - * devm_nvmem_unregister() - Unregister previously registered managed nvmem - * device. - * - * @dev: Device that uses the nvmem device. - * @nvmem: Pointer to previously registered nvmem device. - * - * Return: Will be negative on error or zero on success. - */ -int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) -{ - return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem); -} -EXPORT_SYMBOL(devm_nvmem_unregister); - static struct nvmem_device *__nvmem_device_get(void *data, int (*match)(struct device *dev, const void *data)) { diff --git a/drivers/nvmem/layerscape-sfp.c b/drivers/nvmem/layerscape-sfp.c new file mode 100644 index 000000000000..e591c1511e33 --- /dev/null +++ b/drivers/nvmem/layerscape-sfp.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Layerscape SFP driver + * + * Copyright (c) 2022 Michael Walle <michael@walle.cc> + * + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +#define LAYERSCAPE_SFP_OTP_OFFSET 0x0200 + +struct layerscape_sfp_priv { + void __iomem *base; +}; + +struct layerscape_sfp_data { + int size; +}; + +static int layerscape_sfp_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct layerscape_sfp_priv *priv = context; + + memcpy_fromio(val, priv->base + LAYERSCAPE_SFP_OTP_OFFSET + offset, + bytes); + + return 0; +} + +static struct nvmem_config layerscape_sfp_nvmem_config = { + .name = "fsl-sfp", + .reg_read = layerscape_sfp_read, +}; + +static int layerscape_sfp_probe(struct platform_device *pdev) +{ + const struct layerscape_sfp_data *data; + struct layerscape_sfp_priv *priv; + struct nvmem_device *nvmem; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + data = device_get_match_data(&pdev->dev); + + layerscape_sfp_nvmem_config.size = data->size; + layerscape_sfp_nvmem_config.dev = &pdev->dev; + layerscape_sfp_nvmem_config.priv = priv; + + nvmem = devm_nvmem_register(&pdev->dev, &layerscape_sfp_nvmem_config); + + return PTR_ERR_OR_ZERO(nvmem); +} + +static const struct layerscape_sfp_data ls1028a_data = { + .size = 0x88, +}; + +static const struct of_device_id layerscape_sfp_dt_ids[] = { + { .compatible = "fsl,ls1028a-sfp", .data = &ls1028a_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, layerscape_sfp_dt_ids); + +static struct platform_driver layerscape_sfp_driver = { + .probe = layerscape_sfp_probe, + .driver = { + .name = "layerscape_sfp", + .of_match_table = layerscape_sfp_dt_ids, + }, +}; +module_platform_driver(layerscape_sfp_driver); + +MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); +MODULE_DESCRIPTION("Layerscape Security Fuse Processor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c index 07c9f38c1c60..13eb14316f46 100644 --- a/drivers/nvmem/meson-mx-efuse.c +++ b/drivers/nvmem/meson-mx-efuse.c @@ -209,8 +209,7 @@ static int meson_mx_efuse_probe(struct platform_device *pdev) if (IS_ERR(efuse->base)) return PTR_ERR(efuse->base); - efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name, - GFP_KERNEL); + efuse->config.name = drvdata->name; efuse->config.owner = THIS_MODULE; efuse->config.dev = &pdev->dev; efuse->config.priv = efuse; diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index c500d6235bf6..162132c7dab9 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -22,7 +22,7 @@ /* Amount of time required to hold charge to blow fuse in micro-seconds */ #define QFPROM_FUSE_BLOW_POLL_US 100 -#define QFPROM_FUSE_BLOW_TIMEOUT_US 1000 +#define QFPROM_FUSE_BLOW_TIMEOUT_US 10000 #define QFPROM_BLOW_STATUS_OFFSET 0x048 #define QFPROM_BLOW_STATUS_BUSY 0x1 @@ -244,7 +244,7 @@ err_clk_prepared: } /** - * qfprom_efuse_reg_write() - Write to fuses. + * qfprom_reg_write() - Write to fuses. * @context: Our driver data. * @reg: The offset to write at. * @_val: Pointer to data to write. diff --git a/drivers/nvmem/sunplus-ocotp.c b/drivers/nvmem/sunplus-ocotp.c new file mode 100644 index 000000000000..2dc59c22eb55 --- /dev/null +++ b/drivers/nvmem/sunplus-ocotp.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * The OCOTP driver for Sunplus SP7021 + * + * Copyright (C) 2019 Sunplus Technology Inc., All rights reserved. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +/* + * OTP memory + * Each bank contains 4 words (32 bits). + * Bank 0 starts at offset 0 from the base. + */ + +#define OTP_WORDS_PER_BANK 4 +#define OTP_WORD_SIZE sizeof(u32) +#define OTP_BIT_ADDR_OF_BANK (8 * OTP_WORD_SIZE * OTP_WORDS_PER_BANK) +#define QAC628_OTP_NUM_BANKS 8 +#define QAC628_OTP_SIZE (QAC628_OTP_NUM_BANKS * OTP_WORDS_PER_BANK * OTP_WORD_SIZE) +#define OTP_READ_TIMEOUT_US 200000 + +/* HB_GPIO */ +#define ADDRESS_8_DATA 0x20 + +/* OTP_RX */ +#define OTP_CONTROL_2 0x48 +#define OTP_RD_PERIOD GENMASK(15, 8) +#define OTP_RD_PERIOD_MASK ~GENMASK(15, 8) +#define CPU_CLOCK FIELD_PREP(OTP_RD_PERIOD, 30) +#define SEL_BAK_KEY2 BIT(5) +#define SEL_BAK_KEY2_MASK ~BIT(5) +#define SW_TRIM_EN BIT(4) +#define SW_TRIM_EN_MASK ~BIT(4) +#define SEL_BAK_KEY BIT(3) +#define SEL_BAK_KEY_MASK ~BIT(3) +#define OTP_READ BIT(2) +#define OTP_LOAD_SECURE_DATA BIT(1) +#define OTP_LOAD_SECURE_DATA_MASK ~BIT(1) +#define OTP_DO_CRC BIT(0) +#define OTP_DO_CRC_MASK ~BIT(0) +#define OTP_STATUS 0x4c +#define OTP_READ_DONE BIT(4) +#define OTP_READ_DONE_MASK ~BIT(4) +#define OTP_LOAD_SECURE_DONE_MASK ~BIT(2) +#define OTP_READ_ADDRESS 0x50 + +enum base_type { + HB_GPIO, + OTPRX, + BASEMAX, +}; + +struct sp_ocotp_priv { + struct device *dev; + void __iomem *base[BASEMAX]; + struct clk *clk; +}; + +struct sp_ocotp_data { + int size; +}; + +const struct sp_ocotp_data sp_otp_v0 = { + .size = QAC628_OTP_SIZE, +}; + +static int sp_otp_read_real(struct sp_ocotp_priv *otp, int addr, char *value) +{ + unsigned int addr_data; + unsigned int byte_shift; + unsigned int status; + int ret; + + addr_data = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK); + addr_data = addr_data / OTP_WORD_SIZE; + + byte_shift = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK); + byte_shift = byte_shift % OTP_WORD_SIZE; + + addr = addr / (OTP_WORD_SIZE * OTP_WORDS_PER_BANK); + addr = addr * OTP_BIT_ADDR_OF_BANK; + + writel(readl(otp->base[OTPRX] + OTP_STATUS) & OTP_READ_DONE_MASK & + OTP_LOAD_SECURE_DONE_MASK, otp->base[OTPRX] + OTP_STATUS); + writel(addr, otp->base[OTPRX] + OTP_READ_ADDRESS); + writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) | OTP_READ, + otp->base[OTPRX] + OTP_CONTROL_2); + writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) & SEL_BAK_KEY2_MASK & SW_TRIM_EN_MASK + & SEL_BAK_KEY_MASK & OTP_LOAD_SECURE_DATA_MASK & OTP_DO_CRC_MASK, + otp->base[OTPRX] + OTP_CONTROL_2); + writel((readl(otp->base[OTPRX] + OTP_CONTROL_2) & OTP_RD_PERIOD_MASK) | CPU_CLOCK, + otp->base[OTPRX] + OTP_CONTROL_2); + + ret = readl_poll_timeout(otp->base[OTPRX] + OTP_STATUS, status, + status & OTP_READ_DONE, 10, OTP_READ_TIMEOUT_US); + + if (ret < 0) + return ret; + + *value = (readl(otp->base[HB_GPIO] + ADDRESS_8_DATA + addr_data * OTP_WORD_SIZE) + >> (8 * byte_shift)) & 0xff; + + return ret; +} + +static int sp_ocotp_read(void *priv, unsigned int offset, void *value, size_t bytes) +{ + struct sp_ocotp_priv *otp = priv; + unsigned int addr; + char *buf = value; + char val[4]; + int ret; + + ret = clk_enable(otp->clk); + if (ret) + return ret; + + *buf = 0; + for (addr = offset; addr < (offset + bytes); addr++) { + ret = sp_otp_read_real(otp, addr, val); + if (ret < 0) { + dev_err(otp->dev, "OTP read fail:%d at %d", ret, addr); + goto disable_clk; + } + + *buf++ = *val; + } + +disable_clk: + clk_disable(otp->clk); + + return ret; +} + +static struct nvmem_config sp_ocotp_nvmem_config = { + .name = "sp-ocotp", + .read_only = true, + .word_size = 1, + .size = QAC628_OTP_SIZE, + .stride = 1, + .reg_read = sp_ocotp_read, + .owner = THIS_MODULE, +}; + +static int sp_ocotp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nvmem_device *nvmem; + struct sp_ocotp_priv *otp; + struct resource *res; + int ret; + + otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); + if (!otp) + return -ENOMEM; + + otp->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio"); + otp->base[HB_GPIO] = devm_ioremap_resource(dev, res); + if (IS_ERR(otp->base[HB_GPIO])) + return PTR_ERR(otp->base[HB_GPIO]); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx"); + otp->base[OTPRX] = devm_ioremap_resource(dev, res); + if (IS_ERR(otp->base[OTPRX])) + return PTR_ERR(otp->base[OTPRX]); + + otp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(otp->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(otp->clk), + "devm_clk_get fail\n"); + + ret = clk_prepare(otp->clk); + if (ret < 0) { + dev_err(dev, "failed to prepare clk: %d\n", ret); + return ret; + } + + sp_ocotp_nvmem_config.priv = otp; + sp_ocotp_nvmem_config.dev = dev; + + nvmem = devm_nvmem_register(dev, &sp_ocotp_nvmem_config); + if (IS_ERR(nvmem)) + return dev_err_probe(&pdev->dev, PTR_ERR(nvmem), + "register nvmem device fail\n"); + + platform_set_drvdata(pdev, nvmem); + + dev_dbg(dev, "banks:%d x wpb:%d x wsize:%d = %d", + (int)QAC628_OTP_NUM_BANKS, (int)OTP_WORDS_PER_BANK, + (int)OTP_WORD_SIZE, (int)QAC628_OTP_SIZE); + + dev_info(dev, "by Sunplus (C) 2020"); + + return 0; +} + +static const struct of_device_id sp_ocotp_dt_ids[] = { + { .compatible = "sunplus,sp7021-ocotp", .data = &sp_otp_v0 }, + { } +}; +MODULE_DEVICE_TABLE(of, sp_ocotp_dt_ids); + +static struct platform_driver sp_otp_driver = { + .probe = sp_ocotp_probe, + .driver = { + .name = "sunplus,sp7021-ocotp", + .of_match_table = sp_ocotp_dt_ids, + } +}; +module_platform_driver(sp_otp_driver); + +MODULE_AUTHOR("Vincent Shih <vincent.sunplus@gmail.com>"); +MODULE_DESCRIPTION("Sunplus On-Chip OTP driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 275b9155e473..5750e1f4bcdb 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -184,6 +184,11 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = { .need_register_readout = true, }; +static const struct sunxi_sid_cfg sun20i_d1_cfg = { + .value_offset = 0x200, + .size = 0x100, +}; + static const struct sunxi_sid_cfg sun50i_a64_cfg = { .value_offset = 0x200, .size = 0x100, @@ -200,6 +205,7 @@ static const struct of_device_id sunxi_sid_of_match[] = { { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, + { .compatible = "allwinner,sun20i-d1-sid", .data = &sun20i_d1_cfg }, { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg }, diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 793350028906..a16b74f32aa9 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -514,6 +514,7 @@ static const struct of_device_id reserved_mem_matches[] = { { .compatible = "qcom,smem" }, { .compatible = "ramoops" }, { .compatible = "nvmem-rmem" }, + { .compatible = "google,open-dice" }, {} }; diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index e78a9f0302c7..68a4fe4cd60b 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -42,7 +42,7 @@ if PARPORT config PARPORT_PC tristate "PC-style hardware" - depends on ARCH_MIGHT_HAVE_PC_PARPORT + depends on ARCH_MIGHT_HAVE_PC_PARPORT || (PCI && !S390) help You should say Y here if you have a PC-style parallel port. All IBM PC compatible computers and some Alphas have PC-style @@ -77,7 +77,7 @@ config PARPORT_PC_FIFO config PARPORT_PC_SUPERIO bool "SuperIO chipset support" - depends on PARPORT_PC && !PARISC + depends on ARCH_MIGHT_HAVE_PC_PARPORT && PARPORT_PC && !PARISC help Saying Y here enables some probes for Super-IO chipsets in order to find out things like base addresses, IRQ lines and DMA channels. It diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 401fb5eb7645..09d9bf465d72 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1631,9 +1631,7 @@ static int advk_pcie_enable_phy(struct advk_pcie *pcie) } ret = phy_power_on(pcie->phy); - if (ret == -EOPNOTSUPP) { - dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n"); - } else if (ret) { + if (ret) { phy_exit(pcie->phy); return ret; } diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig new file mode 100644 index 000000000000..89872ad83320 --- /dev/null +++ b/drivers/peci/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig PECI + tristate "PECI support" + help + The Platform Environment Control Interface (PECI) is an interface + that provides a communication channel to Intel processors and + chipset components from external monitoring or control devices. + + If you are building a Baseboard Management Controller (BMC) kernel + for Intel platform say Y here and also to the specific driver for + your adapter(s) below. If unsure say N. + + This support is also available as a module. If so, the module + will be called peci. + +if PECI + +config PECI_CPU + tristate "PECI CPU" + select AUXILIARY_BUS + help + This option enables peci-cpu driver for Intel processors. It is + responsible for creating auxiliary devices that can subsequently + be used by other drivers in order to perform various + functionalities such as e.g. temperature monitoring. + + Additional drivers must be enabled in order to use the functionality + of the device. + + This driver can also be built as a module. If so, the module + will be called peci-cpu. + +source "drivers/peci/controller/Kconfig" + +endif # PECI diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile new file mode 100644 index 000000000000..7de18137e738 --- /dev/null +++ b/drivers/peci/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# Core functionality +peci-y := core.o request.o device.o sysfs.o +obj-$(CONFIG_PECI) += peci.o +peci-cpu-y := cpu.o +obj-$(CONFIG_PECI_CPU) += peci-cpu.o + +# Hardware specific bus drivers +obj-y += controller/ diff --git a/drivers/peci/controller/Kconfig b/drivers/peci/controller/Kconfig new file mode 100644 index 000000000000..2fc5e2abb74a --- /dev/null +++ b/drivers/peci/controller/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config PECI_ASPEED + tristate "ASPEED PECI support" + depends on ARCH_ASPEED || COMPILE_TEST + depends on OF + depends on HAS_IOMEM + depends on COMMON_CLK + help + This option enables PECI controller driver for ASPEED AST2400, + AST2500 and AST2600 SoCs. It allows BMC to discover devices + connected to it, and communicate with them using PECI protocol. + + Say Y here if your system runs on ASPEED SoC and you are using it + as BMC for Intel platform. + + This driver can also be built as a module. If so, the module will + be called peci-aspeed. diff --git a/drivers/peci/controller/Makefile b/drivers/peci/controller/Makefile new file mode 100644 index 000000000000..022c28ef1bf0 --- /dev/null +++ b/drivers/peci/controller/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o diff --git a/drivers/peci/controller/peci-aspeed.c b/drivers/peci/controller/peci-aspeed.c new file mode 100644 index 000000000000..1925ddc13f00 --- /dev/null +++ b/drivers/peci/controller/peci-aspeed.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2012-2017 ASPEED Technology Inc. +// Copyright (c) 2018-2021 Intel Corporation + +#include <asm/unaligned.h> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/jiffies.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/peci.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +/* ASPEED PECI Registers */ +/* Control Register */ +#define ASPEED_PECI_CTRL 0x00 +#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) +#define ASPEED_PECI_CTRL_RD_MODE_MASK GENMASK(13, 12) +#define ASPEED_PECI_CTRL_RD_MODE_DBG BIT(13) +#define ASPEED_PECI_CTRL_RD_MODE_COUNT BIT(12) +#define ASPEED_PECI_CTRL_CLK_SRC_HCLK BIT(11) +#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) +#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7) +#define ASPEED_PECI_CTRL_INVERT_IN BIT(6) +#define ASPEED_PECI_CTRL_BUS_CONTENTION_EN BIT(5) +#define ASPEED_PECI_CTRL_PECI_EN BIT(4) +#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0) + +/* Timing Negotiation Register */ +#define ASPEED_PECI_TIMING_NEGOTIATION 0x04 +#define ASPEED_PECI_T_NEGO_MSG_MASK GENMASK(15, 8) +#define ASPEED_PECI_T_NEGO_ADDR_MASK GENMASK(7, 0) + +/* Command Register */ +#define ASPEED_PECI_CMD 0x08 +#define ASPEED_PECI_CMD_PIN_MONITORING BIT(31) +#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24) +#define ASPEED_PECI_CMD_STS_ADDR_T_NEGO 0x3 +#define ASPEED_PECI_CMD_IDLE_MASK \ + (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MONITORING) +#define ASPEED_PECI_CMD_FIRE BIT(0) + +/* Read/Write Length Register */ +#define ASPEED_PECI_RW_LENGTH 0x0c +#define ASPEED_PECI_AW_FCS_EN BIT(31) +#define ASPEED_PECI_RD_LEN_MASK GENMASK(23, 16) +#define ASPEED_PECI_WR_LEN_MASK GENMASK(15, 8) +#define ASPEED_PECI_TARGET_ADDR_MASK GENMASK(7, 0) + +/* Expected FCS Data Register */ +#define ASPEED_PECI_EXPECTED_FCS 0x10 +#define ASPEED_PECI_EXPECTED_RD_FCS_MASK GENMASK(23, 16) +#define ASPEED_PECI_EXPECTED_AW_FCS_AUTO_MASK GENMASK(15, 8) +#define ASPEED_PECI_EXPECTED_WR_FCS_MASK GENMASK(7, 0) + +/* Captured FCS Data Register */ +#define ASPEED_PECI_CAPTURED_FCS 0x14 +#define ASPEED_PECI_CAPTURED_RD_FCS_MASK GENMASK(23, 16) +#define ASPEED_PECI_CAPTURED_WR_FCS_MASK GENMASK(7, 0) + +/* Interrupt Register */ +#define ASPEED_PECI_INT_CTRL 0x18 +#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30) +#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0 +#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1 +#define ASPEED_PECI_MESSAGE_NEGO 2 +#define ASPEED_PECI_INT_MASK GENMASK(4, 0) +#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4) +#define ASPEED_PECI_INT_BUS_CONTENTION BIT(3) +#define ASPEED_PECI_INT_WR_FCS_BAD BIT(2) +#define ASPEED_PECI_INT_WR_FCS_ABORT BIT(1) +#define ASPEED_PECI_INT_CMD_DONE BIT(0) + +/* Interrupt Status Register */ +#define ASPEED_PECI_INT_STS 0x1c +#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16) + /* bits[4..0]: Same bit fields in the 'Interrupt Register' */ + +/* Rx/Tx Data Buffer Registers */ +#define ASPEED_PECI_WR_DATA0 0x20 +#define ASPEED_PECI_WR_DATA1 0x24 +#define ASPEED_PECI_WR_DATA2 0x28 +#define ASPEED_PECI_WR_DATA3 0x2c +#define ASPEED_PECI_RD_DATA0 0x30 +#define ASPEED_PECI_RD_DATA1 0x34 +#define ASPEED_PECI_RD_DATA2 0x38 +#define ASPEED_PECI_RD_DATA3 0x3c +#define ASPEED_PECI_WR_DATA4 0x40 +#define ASPEED_PECI_WR_DATA5 0x44 +#define ASPEED_PECI_WR_DATA6 0x48 +#define ASPEED_PECI_WR_DATA7 0x4c +#define ASPEED_PECI_RD_DATA4 0x50 +#define ASPEED_PECI_RD_DATA5 0x54 +#define ASPEED_PECI_RD_DATA6 0x58 +#define ASPEED_PECI_RD_DATA7 0x5c +#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32 + +/* Timing Negotiation */ +#define ASPEED_PECI_CLK_FREQUENCY_MIN 2000 +#define ASPEED_PECI_CLK_FREQUENCY_DEFAULT 1000000 +#define ASPEED_PECI_CLK_FREQUENCY_MAX 2000000 +#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8 +/* Timeout */ +#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_US (50 * USEC_PER_MSEC) +#define ASPEED_PECI_IDLE_CHECK_INTERVAL_US (10 * USEC_PER_MSEC) +#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000 +#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 1000 + +#define ASPEED_PECI_CLK_DIV1(msg_timing) (4 * (msg_timing) + 1) +#define ASPEED_PECI_CLK_DIV2(clk_div_exp) BIT(clk_div_exp) +#define ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp) \ + (4 * ASPEED_PECI_CLK_DIV1(msg_timing) * ASPEED_PECI_CLK_DIV2(clk_div_exp)) + +struct aspeed_peci { + struct peci_controller *controller; + struct device *dev; + void __iomem *base; + struct reset_control *rst; + int irq; + spinlock_t lock; /* to sync completion status handling */ + struct completion xfer_complete; + struct clk *clk; + u32 clk_frequency; + u32 status; + u32 cmd_timeout_ms; +}; + +struct clk_aspeed_peci { + struct clk_hw hw; + struct aspeed_peci *aspeed_peci; +}; + +static void aspeed_peci_controller_enable(struct aspeed_peci *priv) +{ + u32 val = readl(priv->base + ASPEED_PECI_CTRL); + + val |= ASPEED_PECI_CTRL_PECI_CLK_EN; + val |= ASPEED_PECI_CTRL_PECI_EN; + + writel(val, priv->base + ASPEED_PECI_CTRL); +} + +static void aspeed_peci_init_regs(struct aspeed_peci *priv) +{ + u32 val; + + /* Clear interrupts */ + writel(ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_STS); + + /* Set timing negotiation mode and enable interrupts */ + val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO); + val |= ASPEED_PECI_INT_MASK; + writel(val, priv->base + ASPEED_PECI_INT_CTRL); + + val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT); + writel(val, priv->base + ASPEED_PECI_CTRL); +} + +static int aspeed_peci_check_idle(struct aspeed_peci *priv) +{ + u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD); + int ret; + + /* + * Under normal circumstances, we expect to be idle here. + * In case there were any errors/timeouts that led to the situation + * where the hardware is not in idle state - we need to reset and + * reinitialize it to avoid potential controller hang. + */ + if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts)) { + ret = reset_control_assert(priv->rst); + if (ret) { + dev_err(priv->dev, "cannot assert reset control\n"); + return ret; + } + + ret = reset_control_deassert(priv->rst); + if (ret) { + dev_err(priv->dev, "cannot deassert reset control\n"); + return ret; + } + + aspeed_peci_init_regs(priv); + + ret = clk_set_rate(priv->clk, priv->clk_frequency); + if (ret < 0) { + dev_err(priv->dev, "cannot set clock frequency\n"); + return ret; + } + + aspeed_peci_controller_enable(priv); + } + + return readl_poll_timeout(priv->base + ASPEED_PECI_CMD, + cmd_sts, + !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK), + ASPEED_PECI_IDLE_CHECK_INTERVAL_US, + ASPEED_PECI_IDLE_CHECK_TIMEOUT_US); +} + +static int aspeed_peci_xfer(struct peci_controller *controller, + u8 addr, struct peci_request *req) +{ + struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent); + unsigned long timeout = msecs_to_jiffies(priv->cmd_timeout_ms); + u32 peci_head; + int ret, i; + + if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX || + req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX) + return -EINVAL; + + /* Check command sts and bus idle state */ + ret = aspeed_peci_check_idle(priv); + if (ret) + return ret; /* -ETIMEDOUT */ + + spin_lock_irq(&priv->lock); + reinit_completion(&priv->xfer_complete); + + peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) | + FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) | + FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len); + + writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); + + for (i = 0; i < req->tx.len; i += 4) { + u32 reg = (i < 16 ? ASPEED_PECI_WR_DATA0 : ASPEED_PECI_WR_DATA4) + i % 16; + + writel(get_unaligned_le32(&req->tx.buf[i]), priv->base + reg); + } + +#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) + dev_dbg(priv->dev, "HEAD : %#08x\n", peci_head); + print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len); +#endif + + priv->status = 0; + writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); + spin_unlock_irq(&priv->lock); + + ret = wait_for_completion_interruptible_timeout(&priv->xfer_complete, timeout); + if (ret < 0) + return ret; + + if (ret == 0) { + dev_dbg(priv->dev, "timeout waiting for a response\n"); + return -ETIMEDOUT; + } + + spin_lock_irq(&priv->lock); + + if (priv->status != ASPEED_PECI_INT_CMD_DONE) { + spin_unlock_irq(&priv->lock); + dev_dbg(priv->dev, "no valid response, status: %#02x\n", priv->status); + return -EIO; + } + + spin_unlock_irq(&priv->lock); + + /* + * We need to use dword reads for register access, make sure that the + * buffer size is multiple of 4-bytes. + */ + BUILD_BUG_ON(PECI_REQUEST_MAX_BUF_SIZE % 4); + + for (i = 0; i < req->rx.len; i += 4) { + u32 reg = (i < 16 ? ASPEED_PECI_RD_DATA0 : ASPEED_PECI_RD_DATA4) + i % 16; + u32 rx_data = readl(priv->base + reg); + + put_unaligned_le32(rx_data, &req->rx.buf[i]); + } + +#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) + print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req->rx.len); +#endif + return 0; +} + +static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) +{ + struct aspeed_peci *priv = arg; + u32 status; + + spin_lock(&priv->lock); + status = readl(priv->base + ASPEED_PECI_INT_STS); + writel(status, priv->base + ASPEED_PECI_INT_STS); + priv->status |= (status & ASPEED_PECI_INT_MASK); + + /* + * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit + * set even in an error case. + */ + if (status & ASPEED_PECI_INT_CMD_DONE) + complete(&priv->xfer_complete); + + writel(0, priv->base + ASPEED_PECI_CMD); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static void clk_aspeed_peci_find_div_values(unsigned long rate, int *msg_timing, int *clk_div_exp) +{ + unsigned long best_diff = ~0ul, diff; + int msg_timing_temp, clk_div_exp_temp, i, j; + + for (i = 1; i <= 255; i++) + for (j = 0; j < 8; j++) { + diff = abs(rate - ASPEED_PECI_CLK_DIV1(i) * ASPEED_PECI_CLK_DIV2(j)); + if (diff < best_diff) { + msg_timing_temp = i; + clk_div_exp_temp = j; + best_diff = diff; + } + } + + *msg_timing = msg_timing_temp; + *clk_div_exp = clk_div_exp_temp; +} + +static int clk_aspeed_peci_get_div(unsigned long rate, const unsigned long *prate) +{ + unsigned long this_rate = *prate / (4 * rate); + int msg_timing, clk_div_exp; + + clk_aspeed_peci_find_div_values(this_rate, &msg_timing, &clk_div_exp); + + return ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp); +} + +static int clk_aspeed_peci_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct clk_aspeed_peci *peci_clk = container_of(hw, struct clk_aspeed_peci, hw); + struct aspeed_peci *aspeed_peci = peci_clk->aspeed_peci; + unsigned long this_rate = prate / (4 * rate); + int clk_div_exp, msg_timing; + u32 val; + + clk_aspeed_peci_find_div_values(this_rate, &msg_timing, &clk_div_exp); + + val = readl(aspeed_peci->base + ASPEED_PECI_CTRL); + val |= FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_exp); + writel(val, aspeed_peci->base + ASPEED_PECI_CTRL); + + val = FIELD_PREP(ASPEED_PECI_T_NEGO_MSG_MASK, msg_timing); + val |= FIELD_PREP(ASPEED_PECI_T_NEGO_ADDR_MASK, msg_timing); + writel(val, aspeed_peci->base + ASPEED_PECI_TIMING_NEGOTIATION); + + return 0; +} + +static long clk_aspeed_peci_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int div = clk_aspeed_peci_get_div(rate, prate); + + return DIV_ROUND_UP_ULL(*prate, div); +} + +static unsigned long clk_aspeed_peci_recalc_rate(struct clk_hw *hw, unsigned long prate) +{ + struct clk_aspeed_peci *peci_clk = container_of(hw, struct clk_aspeed_peci, hw); + struct aspeed_peci *aspeed_peci = peci_clk->aspeed_peci; + int div, msg_timing, addr_timing, clk_div_exp; + u32 reg; + + reg = readl(aspeed_peci->base + ASPEED_PECI_TIMING_NEGOTIATION); + msg_timing = FIELD_GET(ASPEED_PECI_T_NEGO_MSG_MASK, reg); + addr_timing = FIELD_GET(ASPEED_PECI_T_NEGO_ADDR_MASK, reg); + + if (msg_timing != addr_timing) + return 0; + + reg = readl(aspeed_peci->base + ASPEED_PECI_CTRL); + clk_div_exp = FIELD_GET(ASPEED_PECI_CTRL_CLK_DIV_MASK, reg); + + div = ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp); + + return DIV_ROUND_UP_ULL(prate, div); +} + +static const struct clk_ops clk_aspeed_peci_ops = { + .set_rate = clk_aspeed_peci_set_rate, + .round_rate = clk_aspeed_peci_round_rate, + .recalc_rate = clk_aspeed_peci_recalc_rate, +}; + +/* + * PECI HW contains a clock divider which is a combination of: + * div0: 4 (fixed divider) + * div1: x + 1 + * div2: 1 << y + * In other words, out_clk = in_clk / (div0 * div1 * div2) + * The resulting frequency is used by PECI Controller to drive the PECI bus to + * negotiate optimal transfer rate. + */ +static struct clk *devm_aspeed_peci_register_clk_div(struct device *dev, struct clk *parent, + struct aspeed_peci *priv) +{ + struct clk_aspeed_peci *peci_clk; + struct clk_init_data init; + const char *parent_name; + char name[32]; + int ret; + + snprintf(name, sizeof(name), "%s_div", dev_name(dev)); + + parent_name = __clk_get_name(parent); + + init.ops = &clk_aspeed_peci_ops; + init.name = name; + init.parent_names = (const char* []) { parent_name }; + init.num_parents = 1; + init.flags = 0; + + peci_clk = devm_kzalloc(dev, sizeof(struct clk_aspeed_peci), GFP_KERNEL); + if (!peci_clk) + return ERR_PTR(-ENOMEM); + + peci_clk->hw.init = &init; + peci_clk->aspeed_peci = priv; + + ret = devm_clk_hw_register(dev, &peci_clk->hw); + if (ret) + return ERR_PTR(ret); + + return peci_clk->hw.clk; +} + +static void aspeed_peci_property_sanitize(struct device *dev, const char *propname, + u32 min, u32 max, u32 default_val, u32 *propval) +{ + u32 val; + int ret; + + ret = device_property_read_u32(dev, propname, &val); + if (ret) { + val = default_val; + } else if (val > max || val < min) { + dev_warn(dev, "invalid %s: %u, falling back to: %u\n", + propname, val, default_val); + + val = default_val; + } + + *propval = val; +} + +static void aspeed_peci_property_setup(struct aspeed_peci *priv) +{ + aspeed_peci_property_sanitize(priv->dev, "clock-frequency", + ASPEED_PECI_CLK_FREQUENCY_MIN, ASPEED_PECI_CLK_FREQUENCY_MAX, + ASPEED_PECI_CLK_FREQUENCY_DEFAULT, &priv->clk_frequency); + aspeed_peci_property_sanitize(priv->dev, "cmd-timeout-ms", + 1, ASPEED_PECI_CMD_TIMEOUT_MS_MAX, + ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, &priv->cmd_timeout_ms); +} + +static struct peci_controller_ops aspeed_ops = { + .xfer = aspeed_peci_xfer, +}; + +static void aspeed_peci_reset_control_release(void *data) +{ + reset_control_assert(data); +} + +static int devm_aspeed_peci_reset_control_deassert(struct device *dev, struct reset_control *rst) +{ + int ret; + + ret = reset_control_deassert(rst); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, aspeed_peci_reset_control_release, rst); +} + +static void aspeed_peci_clk_release(void *data) +{ + clk_disable_unprepare(data); +} + +static int devm_aspeed_peci_clk_enable(struct device *dev, struct clk *clk) +{ + int ret; + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, aspeed_peci_clk_release, clk); +} + +static int aspeed_peci_probe(struct platform_device *pdev) +{ + struct peci_controller *controller; + struct aspeed_peci *priv; + struct clk *ref_clk; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + dev_set_drvdata(priv->dev, priv); + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->irq = platform_get_irq(pdev, 0); + if (!priv->irq) + return priv->irq; + + ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, + 0, "peci-aspeed", priv); + if (ret) + return ret; + + init_completion(&priv->xfer_complete); + spin_lock_init(&priv->lock); + + priv->rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(priv->rst)) + return dev_err_probe(priv->dev, PTR_ERR(priv->rst), + "failed to get reset control\n"); + + ret = devm_aspeed_peci_reset_control_deassert(priv->dev, priv->rst); + if (ret) + return dev_err_probe(priv->dev, ret, "cannot deassert reset control\n"); + + aspeed_peci_property_setup(priv); + + aspeed_peci_init_regs(priv); + + ref_clk = devm_clk_get(priv->dev, NULL); + if (IS_ERR(ref_clk)) + return dev_err_probe(priv->dev, PTR_ERR(ref_clk), "failed to get ref clock\n"); + + priv->clk = devm_aspeed_peci_register_clk_div(priv->dev, ref_clk, priv); + if (IS_ERR(priv->clk)) + return dev_err_probe(priv->dev, PTR_ERR(priv->clk), "cannot register clock\n"); + + ret = clk_set_rate(priv->clk, priv->clk_frequency); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "cannot set clock frequency\n"); + + ret = devm_aspeed_peci_clk_enable(priv->dev, priv->clk); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to enable clock\n"); + + aspeed_peci_controller_enable(priv); + + controller = devm_peci_controller_add(priv->dev, &aspeed_ops); + if (IS_ERR(controller)) + return dev_err_probe(priv->dev, PTR_ERR(controller), + "failed to add aspeed peci controller\n"); + + priv->controller = controller; + + return 0; +} + +static const struct of_device_id aspeed_peci_of_table[] = { + { .compatible = "aspeed,ast2400-peci", }, + { .compatible = "aspeed,ast2500-peci", }, + { .compatible = "aspeed,ast2600-peci", }, + { } +}; +MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); + +static struct platform_driver aspeed_peci_driver = { + .probe = aspeed_peci_probe, + .driver = { + .name = "peci-aspeed", + .of_match_table = aspeed_peci_of_table, + }, +}; +module_platform_driver(aspeed_peci_driver); + +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_DESCRIPTION("ASPEED PECI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI); diff --git a/drivers/peci/core.c b/drivers/peci/core.c new file mode 100644 index 000000000000..9c8cf07e51c7 --- /dev/null +++ b/drivers/peci/core.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/idr.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/peci.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/slab.h> + +#include "internal.h" + +static DEFINE_IDA(peci_controller_ida); + +static void peci_controller_dev_release(struct device *dev) +{ + struct peci_controller *controller = to_peci_controller(dev); + + mutex_destroy(&controller->bus_lock); + ida_free(&peci_controller_ida, controller->id); + kfree(controller); +} + +struct device_type peci_controller_type = { + .release = peci_controller_dev_release, +}; + +int peci_controller_scan_devices(struct peci_controller *controller) +{ + int ret; + u8 addr; + + for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) { + ret = peci_device_create(controller, addr); + if (ret) + return ret; + } + + return 0; +} + +static struct peci_controller *peci_controller_alloc(struct device *dev, + struct peci_controller_ops *ops) +{ + struct peci_controller *controller; + int ret; + + if (!ops->xfer) + return ERR_PTR(-EINVAL); + + controller = kzalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + return ERR_PTR(-ENOMEM); + + ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL); + if (ret < 0) + goto err; + controller->id = ret; + + controller->ops = ops; + + controller->dev.parent = dev; + controller->dev.bus = &peci_bus_type; + controller->dev.type = &peci_controller_type; + + device_initialize(&controller->dev); + + mutex_init(&controller->bus_lock); + + return controller; + +err: + kfree(controller); + return ERR_PTR(ret); +} + +static int unregister_child(struct device *dev, void *dummy) +{ + peci_device_destroy(to_peci_device(dev)); + + return 0; +} + +static void unregister_controller(void *_controller) +{ + struct peci_controller *controller = _controller; + + /* + * Detach any active PECI devices. This can't fail, thus we do not + * check the returned value. + */ + device_for_each_child_reverse(&controller->dev, NULL, unregister_child); + + device_unregister(&controller->dev); + + fwnode_handle_put(controller->dev.fwnode); + + pm_runtime_disable(&controller->dev); +} + +/** + * devm_peci_controller_add() - add PECI controller + * @dev: device for devm operations + * @ops: pointer to controller specific methods + * + * In final stage of its probe(), peci_controller driver calls + * devm_peci_controller_add() to register itself with the PECI bus. + * + * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure. + */ +struct peci_controller *devm_peci_controller_add(struct device *dev, + struct peci_controller_ops *ops) +{ + struct peci_controller *controller; + int ret; + + controller = peci_controller_alloc(dev, ops); + if (IS_ERR(controller)) + return controller; + + ret = dev_set_name(&controller->dev, "peci-%d", controller->id); + if (ret) + goto err_put; + + pm_runtime_no_callbacks(&controller->dev); + pm_suspend_ignore_children(&controller->dev, true); + pm_runtime_enable(&controller->dev); + + device_set_node(&controller->dev, fwnode_handle_get(dev_fwnode(dev))); + + ret = device_add(&controller->dev); + if (ret) + goto err_fwnode; + + ret = devm_add_action_or_reset(dev, unregister_controller, controller); + if (ret) + return ERR_PTR(ret); + + /* + * Ignoring retval since failures during scan are non-critical for + * controller itself. + */ + peci_controller_scan_devices(controller); + + return controller; + +err_fwnode: + fwnode_handle_put(controller->dev.fwnode); + + pm_runtime_disable(&controller->dev); + +err_put: + put_device(&controller->dev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI); + +static const struct peci_device_id * +peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device) +{ + while (id->family != 0) { + if (id->family == device->info.family && + id->model == device->info.model) + return id; + id++; + } + + return NULL; +} + +static int peci_bus_device_match(struct device *dev, struct device_driver *drv) +{ + struct peci_device *device = to_peci_device(dev); + struct peci_driver *peci_drv = to_peci_driver(drv); + + if (dev->type != &peci_device_type) + return 0; + + return !!peci_bus_match_device_id(peci_drv->id_table, device); +} + +static int peci_bus_device_probe(struct device *dev) +{ + struct peci_device *device = to_peci_device(dev); + struct peci_driver *driver = to_peci_driver(dev->driver); + + return driver->probe(device, peci_bus_match_device_id(driver->id_table, device)); +} + +static void peci_bus_device_remove(struct device *dev) +{ + struct peci_device *device = to_peci_device(dev); + struct peci_driver *driver = to_peci_driver(dev->driver); + + if (driver->remove) + driver->remove(device); +} + +struct bus_type peci_bus_type = { + .name = "peci", + .match = peci_bus_device_match, + .probe = peci_bus_device_probe, + .remove = peci_bus_device_remove, + .bus_groups = peci_bus_groups, +}; + +static int __init peci_init(void) +{ + int ret; + + ret = bus_register(&peci_bus_type); + if (ret < 0) { + pr_err("peci: failed to register PECI bus type!\n"); + return ret; + } + + return 0; +} +module_init(peci_init); + +static void __exit peci_exit(void) +{ + bus_unregister(&peci_bus_type); +} +module_exit(peci_exit); + +MODULE_AUTHOR("Jason M Bills <jason.m.bills@linux.intel.com>"); +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); +MODULE_DESCRIPTION("PECI bus core module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/peci/cpu.c b/drivers/peci/cpu.c new file mode 100644 index 000000000000..68eb61c65d34 --- /dev/null +++ b/drivers/peci/cpu.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Intel Corporation + +#include <linux/auxiliary_bus.h> +#include <linux/module.h> +#include <linux/peci.h> +#include <linux/peci-cpu.h> +#include <linux/slab.h> + +#include "internal.h" + +/** + * peci_temp_read() - read the maximum die temperature from PECI target device + * @device: PECI device to which request is going to be sent + * @temp_raw: where to store the read temperature + * + * It uses GetTemp PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_temp_read(struct peci_device *device, s16 *temp_raw) +{ + struct peci_request *req; + + req = peci_xfer_get_temp(device); + if (IS_ERR(req)) + return PTR_ERR(req); + + *temp_raw = peci_request_temp_read(req); + + peci_request_free(req); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(peci_temp_read, PECI_CPU); + +/** + * peci_pcs_read() - read PCS register + * @device: PECI device to which request is going to be sent + * @index: PCS index + * @param: PCS parameter + * @data: where to store the read data + * + * It uses RdPkgConfig PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_pcs_read(struct peci_device *device, u8 index, u16 param, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_xfer_pkg_cfg_readl(device, index, param); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_pcs_read, PECI_CPU); + +/** + * peci_pci_local_read() - read 32-bit memory location using raw address + * @device: PECI device to which request is going to be sent + * @bus: bus + * @dev: device + * @func: function + * @reg: register + * @data: where to store the read data + * + * It uses RdPCIConfigLocal PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_pci_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func, + u16 reg, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_xfer_pci_cfg_local_readl(device, bus, dev, func, reg); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_pci_local_read, PECI_CPU); + +/** + * peci_ep_pci_local_read() - read 32-bit memory location using raw address + * @device: PECI device to which request is going to be sent + * @seg: PCI segment + * @bus: bus + * @dev: device + * @func: function + * @reg: register + * @data: where to store the read data + * + * Like &peci_pci_local_read, but it uses RdEndpointConfig PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_ep_pci_local_read(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_xfer_ep_pci_cfg_local_readl(device, seg, bus, dev, func, reg); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_ep_pci_local_read, PECI_CPU); + +/** + * peci_mmio_read() - read 32-bit memory location using 64-bit bar offset address + * @device: PECI device to which request is going to be sent + * @bar: PCI bar + * @seg: PCI segment + * @bus: bus + * @dev: device + * @func: function + * @address: 64-bit MMIO address + * @data: where to store the read data + * + * It uses RdEndpointConfig PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_mmio_read(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 address, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_xfer_ep_mmio64_readl(device, bar, seg, bus, dev, func, address); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_mmio_read, PECI_CPU); + +static const char * const peci_adev_types[] = { + "cputemp", + "dimmtemp", +}; + +struct peci_cpu { + struct peci_device *device; + const struct peci_device_id *id; +}; + +static void adev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + auxiliary_device_uninit(adev); + + kfree(adev->name); + kfree(adev); +} + +static struct auxiliary_device *adev_alloc(struct peci_cpu *priv, int idx) +{ + struct peci_controller *controller = to_peci_controller(priv->device->dev.parent); + struct auxiliary_device *adev; + const char *name; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return ERR_PTR(-ENOMEM); + + name = kasprintf(GFP_KERNEL, "%s.%s", peci_adev_types[idx], (const char *)priv->id->data); + if (!name) { + ret = -ENOMEM; + goto free_adev; + } + + adev->name = name; + adev->dev.parent = &priv->device->dev; + adev->dev.release = adev_release; + adev->id = (controller->id << 16) | (priv->device->addr); + + ret = auxiliary_device_init(adev); + if (ret) + goto free_name; + + return adev; + +free_name: + kfree(name); +free_adev: + kfree(adev); + return ERR_PTR(ret); +} + +static void unregister_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); +} + +static int devm_adev_add(struct device *dev, int idx) +{ + struct peci_cpu *priv = dev_get_drvdata(dev); + struct auxiliary_device *adev; + int ret; + + adev = adev_alloc(priv, idx); + if (IS_ERR(adev)) + return PTR_ERR(adev); + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + ret = devm_add_action_or_reset(&priv->device->dev, unregister_adev, adev); + if (ret) + return ret; + + return 0; +} + +static void peci_cpu_add_adevices(struct peci_cpu *priv) +{ + struct device *dev = &priv->device->dev; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(peci_adev_types); i++) { + ret = devm_adev_add(dev, i); + if (ret) { + dev_warn(dev, "Failed to register PECI auxiliary: %s, ret = %d\n", + peci_adev_types[i], ret); + continue; + } + } +} + +static int +peci_cpu_probe(struct peci_device *device, const struct peci_device_id *id) +{ + struct device *dev = &device->dev; + struct peci_cpu *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + priv->device = device; + priv->id = id; + + peci_cpu_add_adevices(priv); + + return 0; +} + +static const struct peci_device_id peci_cpu_device_ids[] = { + { /* Haswell Xeon */ + .family = 6, + .model = INTEL_FAM6_HASWELL_X, + .data = "hsx", + }, + { /* Broadwell Xeon */ + .family = 6, + .model = INTEL_FAM6_BROADWELL_X, + .data = "bdx", + }, + { /* Broadwell Xeon D */ + .family = 6, + .model = INTEL_FAM6_BROADWELL_D, + .data = "bdxd", + }, + { /* Skylake Xeon */ + .family = 6, + .model = INTEL_FAM6_SKYLAKE_X, + .data = "skx", + }, + { /* Icelake Xeon */ + .family = 6, + .model = INTEL_FAM6_ICELAKE_X, + .data = "icx", + }, + { /* Icelake Xeon D */ + .family = 6, + .model = INTEL_FAM6_ICELAKE_D, + .data = "icxd", + }, + { } +}; +MODULE_DEVICE_TABLE(peci, peci_cpu_device_ids); + +static struct peci_driver peci_cpu_driver = { + .probe = peci_cpu_probe, + .id_table = peci_cpu_device_ids, + .driver = { + .name = "peci-cpu", + }, +}; +module_peci_driver(peci_cpu_driver); + +MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); +MODULE_DESCRIPTION("PECI CPU driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI); diff --git a/drivers/peci/device.c b/drivers/peci/device.c new file mode 100644 index 000000000000..e6b0bffb14f4 --- /dev/null +++ b/drivers/peci/device.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include <linux/bitfield.h> +#include <linux/peci.h> +#include <linux/peci-cpu.h> +#include <linux/slab.h> + +#include "internal.h" + +/* + * PECI device can be removed using sysfs, but the removal can also happen as + * a result of controller being removed. + * Mutex is used to protect PECI device from being double-deleted. + */ +static DEFINE_MUTEX(peci_device_del_lock); + +#define REVISION_NUM_MASK GENMASK(15, 8) +static int peci_get_revision(struct peci_device *device, u8 *revision) +{ + struct peci_request *req; + u64 dib; + + req = peci_xfer_get_dib(device); + if (IS_ERR(req)) + return PTR_ERR(req); + + /* + * PECI device may be in a state where it is unable to return a proper + * DIB, in which case it returns 0 as DIB value. + * Let's treat this as an error to avoid carrying on with the detection + * using invalid revision. + */ + dib = peci_request_dib_read(req); + if (dib == 0) { + peci_request_free(req); + return -EIO; + } + + *revision = FIELD_GET(REVISION_NUM_MASK, dib); + + peci_request_free(req); + + return 0; +} + +static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id) +{ + struct peci_request *req; + int ret; + + req = peci_xfer_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *cpu_id = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} + +static unsigned int peci_x86_cpu_family(unsigned int sig) +{ + unsigned int x86; + + x86 = (sig >> 8) & 0xf; + + if (x86 == 0xf) + x86 += (sig >> 20) & 0xff; + + return x86; +} + +static unsigned int peci_x86_cpu_model(unsigned int sig) +{ + unsigned int fam, model; + + fam = peci_x86_cpu_family(sig); + + model = (sig >> 4) & 0xf; + + if (fam >= 0x6) + model += ((sig >> 16) & 0xf) << 4; + + return model; +} + +static int peci_device_info_init(struct peci_device *device) +{ + u8 revision; + u32 cpu_id; + int ret; + + ret = peci_get_cpu_id(device, &cpu_id); + if (ret) + return ret; + + device->info.family = peci_x86_cpu_family(cpu_id); + device->info.model = peci_x86_cpu_model(cpu_id); + + ret = peci_get_revision(device, &revision); + if (ret) + return ret; + device->info.peci_revision = revision; + + device->info.socket_id = device->addr - PECI_BASE_ADDR; + + return 0; +} + +static int peci_detect(struct peci_controller *controller, u8 addr) +{ + /* + * PECI Ping is a command encoded by tx_len = 0, rx_len = 0. + * We expect correct Write FCS if the device at the target address + * is able to respond. + */ + struct peci_request req = { 0 }; + int ret; + + mutex_lock(&controller->bus_lock); + ret = controller->ops->xfer(controller, addr, &req); + mutex_unlock(&controller->bus_lock); + + return ret; +} + +static bool peci_addr_valid(u8 addr) +{ + return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; +} + +static int peci_dev_exists(struct device *dev, void *data) +{ + struct peci_device *device = to_peci_device(dev); + u8 *addr = data; + + if (device->addr == *addr) + return -EBUSY; + + return 0; +} + +int peci_device_create(struct peci_controller *controller, u8 addr) +{ + struct peci_device *device; + int ret; + + if (!peci_addr_valid(addr)) + return -EINVAL; + + /* Check if we have already detected this device before. */ + ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists); + if (ret) + return 0; + + ret = peci_detect(controller, addr); + if (ret) { + /* + * Device not present or host state doesn't allow successful + * detection at this time. + */ + if (ret == -EIO || ret == -ETIMEDOUT) + return 0; + + return ret; + } + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + device_initialize(&device->dev); + + device->addr = addr; + device->dev.parent = &controller->dev; + device->dev.bus = &peci_bus_type; + device->dev.type = &peci_device_type; + + ret = peci_device_info_init(device); + if (ret) + goto err_put; + + ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr); + if (ret) + goto err_put; + + ret = device_add(&device->dev); + if (ret) + goto err_put; + + return 0; + +err_put: + put_device(&device->dev); + + return ret; +} + +void peci_device_destroy(struct peci_device *device) +{ + mutex_lock(&peci_device_del_lock); + if (!device->deleted) { + device_unregister(&device->dev); + device->deleted = true; + } + mutex_unlock(&peci_device_del_lock); +} + +int __peci_driver_register(struct peci_driver *driver, struct module *owner, + const char *mod_name) +{ + driver->driver.bus = &peci_bus_type; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + + if (!driver->probe) { + pr_err("peci: trying to register driver without probe callback\n"); + return -EINVAL; + } + + if (!driver->id_table) { + pr_err("peci: trying to register driver without device id table\n"); + return -EINVAL; + } + + return driver_register(&driver->driver); +} +EXPORT_SYMBOL_NS_GPL(__peci_driver_register, PECI); + +void peci_driver_unregister(struct peci_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, PECI); + +static void peci_device_release(struct device *dev) +{ + struct peci_device *device = to_peci_device(dev); + + kfree(device); +} + +struct device_type peci_device_type = { + .groups = peci_device_groups, + .release = peci_device_release, +}; diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h new file mode 100644 index 000000000000..9d75ea54504c --- /dev/null +++ b/drivers/peci/internal.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2018-2021 Intel Corporation */ + +#ifndef __PECI_INTERNAL_H +#define __PECI_INTERNAL_H + +#include <linux/device.h> +#include <linux/types.h> + +struct peci_controller; +struct attribute_group; +struct peci_device; +struct peci_request; + +/* PECI CPU address range 0x30-0x37 */ +#define PECI_BASE_ADDR 0x30 +#define PECI_DEVICE_NUM_MAX 8 + +struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len); +void peci_request_free(struct peci_request *req); + +int peci_request_status(struct peci_request *req); + +u64 peci_request_dib_read(struct peci_request *req); +s16 peci_request_temp_read(struct peci_request *req); + +u8 peci_request_data_readb(struct peci_request *req); +u16 peci_request_data_readw(struct peci_request *req); +u32 peci_request_data_readl(struct peci_request *req); +u64 peci_request_data_readq(struct peci_request *req); + +struct peci_request *peci_xfer_get_dib(struct peci_device *device); +struct peci_request *peci_xfer_get_temp(struct peci_device *device); + +struct peci_request *peci_xfer_pkg_cfg_readb(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_xfer_pkg_cfg_readw(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_xfer_pkg_cfg_readl(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_xfer_pkg_cfg_readq(struct peci_device *device, u8 index, u16 param); + +struct peci_request *peci_xfer_pci_cfg_local_readb(struct peci_device *device, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_xfer_pci_cfg_local_readw(struct peci_device *device, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_xfer_pci_cfg_local_readl(struct peci_device *device, + u8 bus, u8 dev, u8 func, u16 reg); + +struct peci_request *peci_xfer_ep_pci_cfg_local_readb(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_xfer_ep_pci_cfg_local_readw(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_xfer_ep_pci_cfg_local_readl(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); + +struct peci_request *peci_xfer_ep_pci_cfg_readb(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_xfer_ep_pci_cfg_readw(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_xfer_ep_pci_cfg_readl(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); + +struct peci_request *peci_xfer_ep_mmio32_readl(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 offset); + +struct peci_request *peci_xfer_ep_mmio64_readl(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 offset); +/** + * struct peci_device_id - PECI device data to match + * @data: pointer to driver private data specific to device + * @family: device family + * @model: device model + */ +struct peci_device_id { + const void *data; + u16 family; + u8 model; +}; + +extern struct device_type peci_device_type; +extern const struct attribute_group *peci_device_groups[]; + +int peci_device_create(struct peci_controller *controller, u8 addr); +void peci_device_destroy(struct peci_device *device); + +extern struct bus_type peci_bus_type; +extern const struct attribute_group *peci_bus_groups[]; + +/** + * struct peci_driver - PECI driver + * @driver: inherit device driver + * @probe: probe callback + * @remove: remove callback + * @id_table: PECI device match table to decide which device to bind + */ +struct peci_driver { + struct device_driver driver; + int (*probe)(struct peci_device *device, const struct peci_device_id *id); + void (*remove)(struct peci_device *device); + const struct peci_device_id *id_table; +}; + +static inline struct peci_driver *to_peci_driver(struct device_driver *d) +{ + return container_of(d, struct peci_driver, driver); +} + +int __peci_driver_register(struct peci_driver *driver, struct module *owner, + const char *mod_name); +/** + * peci_driver_register() - register PECI driver + * @driver: the driver to be registered + * + * PECI drivers that don't need to do anything special in module init should + * use the convenience "module_peci_driver" macro instead + * + * Return: zero on success, else a negative error code. + */ +#define peci_driver_register(driver) \ + __peci_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) +void peci_driver_unregister(struct peci_driver *driver); + +/** + * module_peci_driver() - helper macro for registering a modular PECI driver + * @__peci_driver: peci_driver struct + * + * Helper macro for PECI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_peci_driver(__peci_driver) \ + module_driver(__peci_driver, peci_driver_register, peci_driver_unregister) + +extern struct device_type peci_controller_type; + +int peci_controller_scan_devices(struct peci_controller *controller); + +#endif /* __PECI_INTERNAL_H */ diff --git a/drivers/peci/request.c b/drivers/peci/request.c new file mode 100644 index 000000000000..8d6dd7b6b559 --- /dev/null +++ b/drivers/peci/request.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Intel Corporation + +#include <linux/bug.h> +#include <linux/export.h> +#include <linux/pci.h> +#include <linux/peci.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <asm/unaligned.h> + +#include "internal.h" + +#define PECI_GET_DIB_CMD 0xf7 +#define PECI_GET_DIB_WR_LEN 1 +#define PECI_GET_DIB_RD_LEN 8 + +#define PECI_GET_TEMP_CMD 0x01 +#define PECI_GET_TEMP_WR_LEN 1 +#define PECI_GET_TEMP_RD_LEN 2 + +#define PECI_RDPKGCFG_CMD 0xa1 +#define PECI_RDPKGCFG_WR_LEN 5 +#define PECI_RDPKGCFG_RD_LEN_BASE 1 +#define PECI_WRPKGCFG_CMD 0xa5 +#define PECI_WRPKGCFG_WR_LEN_BASE 6 +#define PECI_WRPKGCFG_RD_LEN 1 + +#define PECI_RDIAMSR_CMD 0xb1 +#define PECI_RDIAMSR_WR_LEN 5 +#define PECI_RDIAMSR_RD_LEN 9 +#define PECI_WRIAMSR_CMD 0xb5 +#define PECI_RDIAMSREX_CMD 0xd1 +#define PECI_RDIAMSREX_WR_LEN 6 +#define PECI_RDIAMSREX_RD_LEN 9 + +#define PECI_RDPCICFG_CMD 0x61 +#define PECI_RDPCICFG_WR_LEN 6 +#define PECI_RDPCICFG_RD_LEN 5 +#define PECI_RDPCICFG_RD_LEN_MAX 24 +#define PECI_WRPCICFG_CMD 0x65 + +#define PECI_RDPCICFGLOCAL_CMD 0xe1 +#define PECI_RDPCICFGLOCAL_WR_LEN 5 +#define PECI_RDPCICFGLOCAL_RD_LEN_BASE 1 +#define PECI_WRPCICFGLOCAL_CMD 0xe5 +#define PECI_WRPCICFGLOCAL_WR_LEN_BASE 6 +#define PECI_WRPCICFGLOCAL_RD_LEN 1 + +#define PECI_ENDPTCFG_TYPE_LOCAL_PCI 0x03 +#define PECI_ENDPTCFG_TYPE_PCI 0x04 +#define PECI_ENDPTCFG_TYPE_MMIO 0x05 +#define PECI_ENDPTCFG_ADDR_TYPE_PCI 0x04 +#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_D 0x05 +#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q 0x06 +#define PECI_RDENDPTCFG_CMD 0xc1 +#define PECI_RDENDPTCFG_PCI_WR_LEN 12 +#define PECI_RDENDPTCFG_MMIO_WR_LEN_BASE 10 +#define PECI_RDENDPTCFG_MMIO_D_WR_LEN 14 +#define PECI_RDENDPTCFG_MMIO_Q_WR_LEN 18 +#define PECI_RDENDPTCFG_RD_LEN_BASE 1 +#define PECI_WRENDPTCFG_CMD 0xc5 +#define PECI_WRENDPTCFG_PCI_WR_LEN_BASE 13 +#define PECI_WRENDPTCFG_MMIO_D_WR_LEN_BASE 15 +#define PECI_WRENDPTCFG_MMIO_Q_WR_LEN_BASE 19 +#define PECI_WRENDPTCFG_RD_LEN 1 + +/* Device Specific Completion Code (CC) Definition */ +#define PECI_CC_SUCCESS 0x40 +#define PECI_CC_NEED_RETRY 0x80 +#define PECI_CC_OUT_OF_RESOURCE 0x81 +#define PECI_CC_UNAVAIL_RESOURCE 0x82 +#define PECI_CC_INVALID_REQ 0x90 +#define PECI_CC_MCA_ERROR 0x91 +#define PECI_CC_CATASTROPHIC_MCA_ERROR 0x93 +#define PECI_CC_FATAL_MCA_ERROR 0x94 +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB 0x98 +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR 0x9B +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA 0x9C + +#define PECI_RETRY_BIT BIT(0) + +#define PECI_RETRY_TIMEOUT msecs_to_jiffies(700) +#define PECI_RETRY_INTERVAL_MIN msecs_to_jiffies(1) +#define PECI_RETRY_INTERVAL_MAX msecs_to_jiffies(128) + +static u8 peci_request_data_cc(struct peci_request *req) +{ + return req->rx.buf[0]; +} + +/** + * peci_request_status() - return -errno based on PECI completion code + * @req: the PECI request that contains response data with completion code + * + * It can't be used for Ping(), GetDIB() and GetTemp() - for those commands we + * don't expect completion code in the response. + * + * Return: -errno + */ +int peci_request_status(struct peci_request *req) +{ + u8 cc = peci_request_data_cc(req); + + if (cc != PECI_CC_SUCCESS) + dev_dbg(&req->device->dev, "ret: %#02x\n", cc); + + switch (cc) { + case PECI_CC_SUCCESS: + return 0; + case PECI_CC_NEED_RETRY: + case PECI_CC_OUT_OF_RESOURCE: + case PECI_CC_UNAVAIL_RESOURCE: + return -EAGAIN; + case PECI_CC_INVALID_REQ: + return -EINVAL; + case PECI_CC_MCA_ERROR: + case PECI_CC_CATASTROPHIC_MCA_ERROR: + case PECI_CC_FATAL_MCA_ERROR: + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB: + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR: + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA: + return -EIO; + } + + WARN_ONCE(1, "Unknown PECI completion code: %#02x\n", cc); + + return -EIO; +} +EXPORT_SYMBOL_NS_GPL(peci_request_status, PECI); + +static int peci_request_xfer(struct peci_request *req) +{ + struct peci_device *device = req->device; + struct peci_controller *controller = to_peci_controller(device->dev.parent); + int ret; + + mutex_lock(&controller->bus_lock); + ret = controller->ops->xfer(controller, device->addr, req); + mutex_unlock(&controller->bus_lock); + + return ret; +} + +static int peci_request_xfer_retry(struct peci_request *req) +{ + long wait_interval = PECI_RETRY_INTERVAL_MIN; + struct peci_device *device = req->device; + struct peci_controller *controller = to_peci_controller(device->dev.parent); + unsigned long start = jiffies; + int ret; + + /* Don't try to use it for ping */ + if (WARN_ON(req->tx.len == 0)) + return 0; + + do { + ret = peci_request_xfer(req); + if (ret) { + dev_dbg(&controller->dev, "xfer error: %d\n", ret); + return ret; + } + + if (peci_request_status(req) != -EAGAIN) + return 0; + + /* Set the retry bit to indicate a retry attempt */ + req->tx.buf[1] |= PECI_RETRY_BIT; + + if (schedule_timeout_interruptible(wait_interval)) + return -ERESTARTSYS; + + wait_interval = min_t(long, wait_interval * 2, PECI_RETRY_INTERVAL_MAX); + } while (time_before(jiffies, start + PECI_RETRY_TIMEOUT)); + + dev_dbg(&controller->dev, "request timed out\n"); + + return -ETIMEDOUT; +} + +/** + * peci_request_alloc() - allocate &struct peci_requests + * @device: PECI device to which request is going to be sent + * @tx_len: TX length + * @rx_len: RX length + * + * Return: A pointer to a newly allocated &struct peci_request on success or NULL otherwise. + */ +struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len) +{ + struct peci_request *req; + + /* + * TX and RX buffers are fixed length members of peci_request, this is + * just a warn for developers to make sure to expand the buffers (or + * change the allocation method) if we go over the current limit. + */ + if (WARN_ON_ONCE(tx_len > PECI_REQUEST_MAX_BUF_SIZE || rx_len > PECI_REQUEST_MAX_BUF_SIZE)) + return NULL; + /* + * PECI controllers that we are using now don't support DMA, this + * should be converted to DMA API once support for controllers that do + * allow it is added to avoid an extra copy. + */ + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + req->device = device; + req->tx.len = tx_len; + req->rx.len = rx_len; + + return req; +} +EXPORT_SYMBOL_NS_GPL(peci_request_alloc, PECI); + +/** + * peci_request_free() - free peci_request + * @req: the PECI request to be freed + */ +void peci_request_free(struct peci_request *req) +{ + kfree(req); +} +EXPORT_SYMBOL_NS_GPL(peci_request_free, PECI); + +struct peci_request *peci_xfer_get_dib(struct peci_device *device) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_GET_DIB_CMD; + + ret = peci_request_xfer(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} +EXPORT_SYMBOL_NS_GPL(peci_xfer_get_dib, PECI); + +struct peci_request *peci_xfer_get_temp(struct peci_device *device) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_GET_TEMP_CMD; + + ret = peci_request_xfer(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} +EXPORT_SYMBOL_NS_GPL(peci_xfer_get_temp, PECI); + +static struct peci_request * +__pkg_cfg_read(struct peci_device *device, u8 index, u16 param, u8 len) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, PECI_RDPKGCFG_WR_LEN, PECI_RDPKGCFG_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_RDPKGCFG_CMD; + req->tx.buf[1] = 0; + req->tx.buf[2] = index; + put_unaligned_le16(param, &req->tx.buf[3]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +static u32 __get_pci_addr(u8 bus, u8 dev, u8 func, u16 reg) +{ + return reg | PCI_DEVID(bus, PCI_DEVFN(dev, func)) << 12; +} + +static struct peci_request * +__pci_cfg_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func, u16 reg, u8 len) +{ + struct peci_request *req; + u32 pci_addr; + int ret; + + req = peci_request_alloc(device, PECI_RDPCICFGLOCAL_WR_LEN, + PECI_RDPCICFGLOCAL_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + pci_addr = __get_pci_addr(bus, dev, func, reg); + + req->tx.buf[0] = PECI_RDPCICFGLOCAL_CMD; + req->tx.buf[1] = 0; + put_unaligned_le24(pci_addr, &req->tx.buf[2]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +static struct peci_request * +__ep_pci_cfg_read(struct peci_device *device, u8 msg_type, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg, u8 len) +{ + struct peci_request *req; + u32 pci_addr; + int ret; + + req = peci_request_alloc(device, PECI_RDENDPTCFG_PCI_WR_LEN, + PECI_RDENDPTCFG_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + pci_addr = __get_pci_addr(bus, dev, func, reg); + + req->tx.buf[0] = PECI_RDENDPTCFG_CMD; + req->tx.buf[1] = 0; + req->tx.buf[2] = msg_type; + req->tx.buf[3] = 0; + req->tx.buf[4] = 0; + req->tx.buf[5] = 0; + req->tx.buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; + req->tx.buf[7] = seg; /* PCI Segment */ + put_unaligned_le32(pci_addr, &req->tx.buf[8]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +static struct peci_request * +__ep_mmio_read(struct peci_device *device, u8 bar, u8 addr_type, u8 seg, + u8 bus, u8 dev, u8 func, u64 offset, u8 tx_len, u8 len) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, tx_len, PECI_RDENDPTCFG_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_RDENDPTCFG_CMD; + req->tx.buf[1] = 0; + req->tx.buf[2] = PECI_ENDPTCFG_TYPE_MMIO; + req->tx.buf[3] = 0; /* Endpoint ID */ + req->tx.buf[4] = 0; /* Reserved */ + req->tx.buf[5] = bar; + req->tx.buf[6] = addr_type; + req->tx.buf[7] = seg; /* PCI Segment */ + req->tx.buf[8] = PCI_DEVFN(dev, func); + req->tx.buf[9] = bus; /* PCI Bus */ + + if (addr_type == PECI_ENDPTCFG_ADDR_TYPE_MMIO_D) + put_unaligned_le32(offset, &req->tx.buf[10]); + else + put_unaligned_le64(offset, &req->tx.buf[10]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +u8 peci_request_data_readb(struct peci_request *req) +{ + return req->rx.buf[1]; +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readb, PECI); + +u16 peci_request_data_readw(struct peci_request *req) +{ + return get_unaligned_le16(&req->rx.buf[1]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readw, PECI); + +u32 peci_request_data_readl(struct peci_request *req) +{ + return get_unaligned_le32(&req->rx.buf[1]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readl, PECI); + +u64 peci_request_data_readq(struct peci_request *req) +{ + return get_unaligned_le64(&req->rx.buf[1]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readq, PECI); + +u64 peci_request_dib_read(struct peci_request *req) +{ + return get_unaligned_le64(&req->rx.buf[0]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_dib_read, PECI); + +s16 peci_request_temp_read(struct peci_request *req) +{ + return get_unaligned_le16(&req->rx.buf[0]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_temp_read, PECI); + +#define __read_pkg_config(x, type) \ +struct peci_request *peci_xfer_pkg_cfg_##x(struct peci_device *device, u8 index, u16 param) \ +{ \ + return __pkg_cfg_read(device, index, param, sizeof(type)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_xfer_pkg_cfg_##x, PECI) + +__read_pkg_config(readb, u8); +__read_pkg_config(readw, u16); +__read_pkg_config(readl, u32); +__read_pkg_config(readq, u64); + +#define __read_pci_config_local(x, type) \ +struct peci_request * \ +peci_xfer_pci_cfg_local_##x(struct peci_device *device, u8 bus, u8 dev, u8 func, u16 reg) \ +{ \ + return __pci_cfg_local_read(device, bus, dev, func, reg, sizeof(type)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_xfer_pci_cfg_local_##x, PECI) + +__read_pci_config_local(readb, u8); +__read_pci_config_local(readw, u16); +__read_pci_config_local(readl, u32); + +#define __read_ep_pci_config(x, msg_type, type) \ +struct peci_request * \ +peci_xfer_ep_pci_cfg_##x(struct peci_device *device, u8 seg, u8 bus, u8 dev, u8 func, u16 reg) \ +{ \ + return __ep_pci_cfg_read(device, msg_type, seg, bus, dev, func, reg, sizeof(type)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_xfer_ep_pci_cfg_##x, PECI) + +__read_ep_pci_config(local_readb, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u8); +__read_ep_pci_config(local_readw, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u16); +__read_ep_pci_config(local_readl, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u32); +__read_ep_pci_config(readb, PECI_ENDPTCFG_TYPE_PCI, u8); +__read_ep_pci_config(readw, PECI_ENDPTCFG_TYPE_PCI, u16); +__read_ep_pci_config(readl, PECI_ENDPTCFG_TYPE_PCI, u32); + +#define __read_ep_mmio(x, y, addr_type, type1, type2) \ +struct peci_request *peci_xfer_ep_mmio##y##_##x(struct peci_device *device, u8 bar, u8 seg, \ + u8 bus, u8 dev, u8 func, u64 offset) \ +{ \ + return __ep_mmio_read(device, bar, addr_type, seg, bus, dev, func, \ + offset, PECI_RDENDPTCFG_MMIO_WR_LEN_BASE + sizeof(type1), \ + sizeof(type2)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_xfer_ep_mmio##y##_##x, PECI) + +__read_ep_mmio(readl, 32, PECI_ENDPTCFG_ADDR_TYPE_MMIO_D, u32, u32); +__read_ep_mmio(readl, 64, PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q, u64, u32); diff --git a/drivers/peci/sysfs.c b/drivers/peci/sysfs.c new file mode 100644 index 000000000000..db9ef05776e3 --- /dev/null +++ b/drivers/peci/sysfs.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Intel Corporation + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/peci.h> + +#include "internal.h" + +static int rescan_controller(struct device *dev, void *data) +{ + if (dev->type != &peci_controller_type) + return 0; + + return peci_controller_scan_devices(to_peci_controller(dev)); +} + +static ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count) +{ + bool res; + int ret; + + ret = kstrtobool(buf, &res); + if (ret) + return ret; + + if (!res) + return count; + + ret = bus_for_each_dev(&peci_bus_type, NULL, NULL, rescan_controller); + if (ret) + return ret; + + return count; +} +static BUS_ATTR_WO(rescan); + +static struct attribute *peci_bus_attrs[] = { + &bus_attr_rescan.attr, + NULL +}; + +static const struct attribute_group peci_bus_group = { + .attrs = peci_bus_attrs, +}; + +const struct attribute_group *peci_bus_groups[] = { + &peci_bus_group, + NULL +}; + +static ssize_t remove_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct peci_device *device = to_peci_device(dev); + bool res; + int ret; + + ret = kstrtobool(buf, &res); + if (ret) + return ret; + + if (res && device_remove_file_self(dev, attr)) + peci_device_destroy(device); + + return count; +} +static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, remove_store); + +static struct attribute *peci_device_attrs[] = { + &dev_attr_remove.attr, + NULL +}; + +static const struct attribute_group peci_device_group = { + .attrs = peci_device_attrs, +}; + +const struct attribute_group *peci_device_groups[] = { + &peci_device_group, + NULL +}; diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 788dd5cdbb7d..d5f3b42eb8ce 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -43,7 +43,7 @@ #define REG_PHYCTL_A33 0x10 #define REG_PHY_OTGCTL 0x20 -#define REG_PMU_UNK1 0x10 +#define REG_HCI_PHY_CTL 0x10 #define PHYCTL_DATA BIT(7) @@ -82,6 +82,7 @@ /* A83T specific control bits for PHY0 */ #define PHY_CTL_VBUSVLDEXT BIT(5) #define PHY_CTL_SIDDQ BIT(3) +#define PHY_CTL_H3_SIDDQ BIT(1) /* A83T specific control bits for PHY2 HSIC */ #define SUNXI_EHCI_HS_FORCE BIT(20) @@ -115,9 +116,9 @@ struct sun4i_usb_phy_cfg { int hsic_index; enum sun4i_usb_phy_type type; u32 disc_thresh; + u32 hci_phy_ctl_clear; u8 phyctl_offset; bool dedicated_clocks; - bool enable_pmu_unk1; bool phy0_dual_route; int missing_phys; }; @@ -288,6 +289,12 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } + if (phy->pmu && data->cfg->hci_phy_ctl_clear) { + val = readl(phy->pmu + REG_HCI_PHY_CTL); + val &= ~data->cfg->hci_phy_ctl_clear; + writel(val, phy->pmu + REG_HCI_PHY_CTL); + } + if (data->cfg->type == sun8i_a83t_phy || data->cfg->type == sun50i_h6_phy) { if (phy->index == 0) { @@ -297,11 +304,6 @@ static int sun4i_usb_phy_init(struct phy *_phy) writel(val, data->base + data->cfg->phyctl_offset); } } else { - if (phy->pmu && data->cfg->enable_pmu_unk1) { - val = readl(phy->pmu + REG_PMU_UNK1); - writel(val & ~2, phy->pmu + REG_PMU_UNK1); - } - /* Enable USB 45 Ohm resistor calibration */ if (phy->index == 0) sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); @@ -863,7 +865,6 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { @@ -872,7 +873,6 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { @@ -881,7 +881,6 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { @@ -890,7 +889,6 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { @@ -899,7 +897,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { @@ -908,7 +905,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { @@ -925,7 +921,7 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; @@ -935,7 +931,7 @@ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; @@ -945,7 +941,16 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, + .phy0_dual_route = true, +}; + +static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = { + .num_phys = 2, + .type = sun50i_h6_phy, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .hci_phy_ctl_clear = PHY_CTL_SIDDQ, .phy0_dual_route = true, }; @@ -955,14 +960,13 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { .num_phys = 4, .type = sun50i_h6_phy, - .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, .phy0_dual_route = true, @@ -980,6 +984,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, + { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = &sun50i_a64_cfg}, { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg }, diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c index 2b3c0d730f20..db17c3448bfe 100644 --- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c +++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c @@ -114,8 +114,10 @@ static int phy_meson_gxl_usb2_init(struct phy *phy) return ret; ret = clk_prepare_enable(priv->clk); - if (ret) + if (ret) { + reset_control_rearm(priv->reset); return ret; + } return 0; } @@ -125,6 +127,7 @@ static int phy_meson_gxl_usb2_exit(struct phy *phy) struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy); clk_disable_unprepare(priv->clk); + reset_control_rearm(priv->reset); return 0; } diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c index cf10bed40528..dd96763911b8 100644 --- a/drivers/phy/amlogic/phy-meson8b-usb2.c +++ b/drivers/phy/amlogic/phy-meson8b-usb2.c @@ -154,6 +154,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy) ret = clk_prepare_enable(priv->clk_usb_general); if (ret) { dev_err(&phy->dev, "Failed to enable USB general clock\n"); + reset_control_rearm(priv->reset); return ret; } @@ -161,6 +162,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy) if (ret) { dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); clk_disable_unprepare(priv->clk_usb_general); + reset_control_rearm(priv->reset); return ret; } @@ -199,6 +201,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy) dev_warn(&phy->dev, "USB ID detect failed!\n"); clk_disable_unprepare(priv->clk_usb); clk_disable_unprepare(priv->clk_usb_general); + reset_control_rearm(priv->reset); return -EINVAL; } } @@ -218,6 +221,7 @@ static int phy_meson8b_usb2_power_off(struct phy *phy) clk_disable_unprepare(priv->clk_usb); clk_disable_unprepare(priv->clk_usb_general); + reset_control_rearm(priv->reset); /* power off the PHY by putting it into reset mode */ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, @@ -265,8 +269,9 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev) return PTR_ERR(priv->clk_usb); priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); - if (PTR_ERR(priv->reset) == -EPROBE_DEFER) - return PTR_ERR(priv->reset); + if (IS_ERR(priv->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset), + "Failed to get the reset line"); priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index e63457e145c7..d2524b70ea16 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -47,6 +47,8 @@ #define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 #define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 #define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 +#define USB_CTRL_USB_PM_XHC_PME_EN_MASK 0x00000010 +#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK 0x00000008 #define USB_CTRL_USB_PM_STATUS 0x08 #define USB_CTRL_USB_DEVICE_CTL1 0x10 #define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 @@ -190,10 +192,6 @@ static void usb_init_common(struct brcm_usb_init_params *params) pr_debug("%s\n", __func__); - USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); - /* 1 millisecond - for USB clocks to settle down */ - usleep_range(1000, 2000); - if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) { reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); @@ -222,6 +220,17 @@ static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params, USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en); } +static void usb_wake_enable_7216(struct brcm_usb_init_params *params, + bool enable) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + if (enable) + USB_CTRL_SET(ctrl, USB_PM, XHC_PME_EN); + else + USB_CTRL_UNSET(ctrl, USB_PM, XHC_PME_EN); +} + static void usb_init_common_7211b0(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; @@ -295,6 +304,20 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) usb2_eye_fix_7211b0(params); } +static void usb_init_common_7216(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + USB_CTRL_UNSET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN); + USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); + + /* 1 millisecond - for USB clocks to settle down */ + usleep_range(1000, 2000); + + usb_wake_enable_7216(params, false); + usb_init_common(params); +} + static void usb_init_xhci(struct brcm_usb_init_params *params) { pr_debug("%s\n", __func__); @@ -302,14 +325,20 @@ static void usb_init_xhci(struct brcm_usb_init_params *params) xhci_soft_reset(params, 0); } -static void usb_uninit_common(struct brcm_usb_init_params *params) +static void usb_uninit_common_7216(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; pr_debug("%s\n", __func__); - USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + if (!params->wake_enabled) { + USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + /* Switch to using slower clock during suspend to save power */ + USB_CTRL_SET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN); + } else { + usb_wake_enable_7216(params, true); + } } static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params) @@ -371,9 +400,9 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) static const struct brcm_usb_init_ops bcm7216_ops = { .init_ipp = usb_init_ipp, - .init_common = usb_init_common, + .init_common = usb_init_common_7216, .init_xhci = usb_init_xhci, - .uninit_common = usb_uninit_common, + .uninit_common = usb_uninit_common_7216, .uninit_xhci = usb_uninit_xhci, .get_dual_select = usb_get_dual_select, .set_dual_select = usb_set_dual_select, @@ -396,6 +425,7 @@ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) params->family_name = "7216"; params->ops = &bcm7216_ops; + params->suspend_with_clocks = true; } void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 9391ab42a12b..dd0f66288fbd 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -79,6 +79,7 @@ enum brcm_family_type { BRCM_FAMILY_3390A0, + BRCM_FAMILY_4908, BRCM_FAMILY_7250B0, BRCM_FAMILY_7271A0, BRCM_FAMILY_7364A0, @@ -96,6 +97,7 @@ enum brcm_family_type { static const char *family_names[BRCM_FAMILY_COUNT] = { USB_BRCM_FAMILY(3390A0), + USB_BRCM_FAMILY(4908), USB_BRCM_FAMILY(7250B0), USB_BRCM_FAMILY(7271A0), USB_BRCM_FAMILY(7364A0), @@ -203,6 +205,27 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ }, + /* 4908 */ + [BRCM_FAMILY_4908] = { + 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ + 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ + 0, /* USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK */ + 0, /* USB_CTRL_SETUP ENDIAN bits */ + }, /* 7250b0 */ [BRCM_FAMILY_7250B0] = { USB_CTRL_SETUP_SCB1_EN_MASK, @@ -559,6 +582,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params) */ switch (params->selected_family) { case BRCM_FAMILY_3390A0: + case BRCM_FAMILY_4908: case BRCM_FAMILY_7250B0: case BRCM_FAMILY_7366C0: case BRCM_FAMILY_74371A0: @@ -1004,6 +1028,18 @@ static const struct brcm_usb_init_ops bcm7445_ops = { .set_dual_select = usb_set_dual_select, }; +void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params) +{ + int fam; + + fam = BRCM_FAMILY_4908; + params->selected_family = fam; + params->usb_reg_bits_map = + &usb_reg_bits_map_table[fam][0]; + params->family_name = family_names[fam]; + params->ops = &bcm7445_ops; +} + void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params) { int fam; diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index a39f30fa2e99..1ccb5ddab865 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -64,6 +64,7 @@ struct brcm_usb_init_params { bool suspend_with_clocks; }; +void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params); diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 0f1deb6e0eab..2cb3779fcdf8 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -283,6 +283,15 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; +static const struct match_chip_info chip_info_4908 = { + .init_func = &brcm_usb_dvr_init_4908, + .required_regs = { + BRCM_REGS_CTRL, + BRCM_REGS_XHCI_EC, + -1, + }, +}; + static const struct match_chip_info chip_info_7216 = { .init_func = &brcm_usb_dvr_init_7216, .required_regs = { @@ -318,7 +327,7 @@ static const struct match_chip_info chip_info_7445 = { static const struct of_device_id brcm_usb_dt_ids[] = { { .compatible = "brcm,bcm4908-usb-phy", - .data = &chip_info_7445, + .data = &chip_info_4908, }, { .compatible = "brcm,bcm7216-usb-phy", diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig index a62910ff5591..1adde2d99ae7 100644 --- a/drivers/phy/cadence/Kconfig +++ b/drivers/phy/cadence/Kconfig @@ -22,6 +22,14 @@ config PHY_CADENCE_DPHY system. If M is selected, the module will be called cdns-dphy. +config PHY_CADENCE_DPHY_RX + tristate "Cadence D-PHY Rx Support" + depends on HAS_IOMEM && OF + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Support for Cadence D-PHY in Rx configuration. + config PHY_CADENCE_SIERRA tristate "Cadence Sierra PHY Driver" depends on OF && HAS_IOMEM && RESET_CONTROLLER diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index 26e16bd34efe..e17f035ddece 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_CADENCE_TORRENT) += phy-cadence-torrent.o obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o +obj-$(CONFIG_PHY_CADENCE_DPHY_RX) += cdns-dphy-rx.o obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o obj-$(CONFIG_PHY_CADENCE_SALVO) += phy-cadence-salvo.o diff --git a/drivers/phy/cadence/cdns-dphy-rx.c b/drivers/phy/cadence/cdns-dphy-rx.c new file mode 100644 index 000000000000..572c70089a94 --- /dev/null +++ b/drivers/phy/cadence/cdns-dphy-rx.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> + +#define DPHY_PMA_CMN(reg) (reg) +#define DPHY_PCS(reg) (0xb00 + (reg)) +#define DPHY_ISO(reg) (0xc00 + (reg)) + +#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) +#define DPHY_CMN_RX_MODE_EN BIT(10) +#define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1) +#define DPHY_CMN_SSM_EN BIT(0) + +#define DPHY_CMN_RX_BANDGAP_TIMER 0x14 + +#define DPHY_BAND_CFG DPHY_PCS(0x0) +#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) +#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) + +#define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8) +#define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa + +#define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc) +#define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa + +#define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10) +#define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14) +#define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20) +#define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30) +#define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c) + +#define DPHY_ISO_LANE_READY_BIT 0 +#define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL + +#define DPHY_LANES_MIN 1 +#define DPHY_LANES_MAX 4 + +struct cdns_dphy_rx { + void __iomem *regs; + struct device *dev; + struct phy *phy; +}; + +struct cdns_dphy_rx_band { + /* Rates are in Mbps. */ + unsigned int min_rate; + unsigned int max_rate; +}; + +/* Order of bands is important since the index is the band number. */ +static const struct cdns_dphy_rx_band bands[] = { + { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 }, + { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 }, + { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 }, + { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 }, + { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 } +}; + +static int cdns_dphy_rx_power_on(struct phy *phy) +{ + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); + + /* Start RX state machine. */ + writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN | + FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK, + DPHY_CMN_RX_BANDGAP_TIMER), + dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_rx_power_off(struct phy *phy) +{ + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); + + writel(0, dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate) +{ + unsigned int rate, i; + + rate = hs_clk_rate / 1000000UL; + /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */ + rate *= 2; + + if (rate < bands[0].min_rate) + return -EOPNOTSUPP; + + for (i = 0; i < ARRAY_SIZE(bands); i++) + if (rate < bands[i].max_rate) + return i; + + return -EOPNOTSUPP; +} + +static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr, + unsigned int bit) +{ + u32 val; + + return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10, + DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000); +} + +static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy, + unsigned int lanes) +{ + static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0, + DPHY_ISO_DL_CTRL_L1, + DPHY_ISO_DL_CTRL_L2, + DPHY_ISO_DL_CTRL_L3}; + void __iomem *reg = dphy->regs; + unsigned int i; + int ret; + + /* Clock lane */ + ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L, + DPHY_ISO_LANE_READY_BIT); + if (ret) + return ret; + + for (i = 0; i < lanes; i++) { + ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i], + DPHY_ISO_LANE_READY_BIT); + if (ret) + return ret; + } + + return 0; +} + +static int cdns_dphy_rx_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); + unsigned int reg, lanes = opts->mipi_dphy.lanes; + int band_ctrl, ret; + + /* Data lanes. Minimum one lane is mandatory. */ + if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX) + return -EINVAL; + + band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (band_ctrl < 0) + return band_ctrl; + + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); + writel(reg, dphy->regs + DPHY_BAND_CFG); + + /* + * Set the required power island phase 2 time. This is mandated by DPHY + * specs. + */ + reg = DPHY_POWER_ISLAND_EN_DATA_VAL; + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA); + reg = DPHY_POWER_ISLAND_EN_CLK_VAL; + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK); + + ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes); + if (ret) { + dev_err(dphy->dev, "DPHY wait for lane ready timeout\n"); + return ret; + } + + return 0; +} + +static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode, + int submode, union phy_configure_opts *opts) +{ + int ret; + + if (mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; + + ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (ret < 0) + return ret; + + return phy_mipi_dphy_config_validate(&opts->mipi_dphy); +} + +static const struct phy_ops cdns_dphy_rx_ops = { + .power_on = cdns_dphy_rx_power_on, + .power_off = cdns_dphy_rx_power_off, + .configure = cdns_dphy_rx_configure, + .validate = cdns_dphy_rx_validate, +}; + +static int cdns_dphy_rx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct cdns_dphy_rx *dphy; + + dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + + dev_set_drvdata(dev, dphy); + dphy->dev = dev; + + dphy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops); + if (IS_ERR(dphy->phy)) { + dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy)); + return PTR_ERR(dphy->phy); + } + + phy_set_drvdata(dphy->phy, dphy); + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "Failed to register PHY provider: %ld\n", + PTR_ERR(provider)); + return PTR_ERR(provider); + } + + return 0; +} + +static const struct of_device_id cdns_dphy_rx_of_match[] = { + { .compatible = "cdns,dphy-rx" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match); + +static struct platform_driver cdns_dphy_rx_platform_driver = { + .probe = cdns_dphy_rx_probe, + .driver = { + .name = "cdns-mipi-dphy-rx", + .of_match_table = cdns_dphy_rx_of_match, + }, +}; +module_platform_driver(cdns_dphy_rx_platform_driver); + +MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>"); +MODULE_DESCRIPTION("Cadence D-PHY Rx Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c index 51c0b98f5fd7..e569f5f67578 100644 --- a/drivers/phy/cadence/phy-cadence-salvo.c +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -263,14 +263,9 @@ static int cdns_salvo_phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct cdns_salvo_phy *salvo_phy; - const struct of_device_id *match; struct cdns_salvo_data *data; - match = of_match_device(cdns_salvo_phy_of_match, dev); - if (!match) - return -EINVAL; - - data = (struct cdns_salvo_data *)match->data; + data = (struct cdns_salvo_data *)of_device_get_match_data(dev); salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL); if (!salvo_phy) return -ENOMEM; diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index e265647e29a2..6b917f7bddbe 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -370,6 +370,7 @@ struct cdns_sierra_phy { int nsubnodes; u32 num_lanes; bool autoconf; + int already_configured; struct clk_onecell_data clk_data; struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS]; }; @@ -517,7 +518,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) int i, j; /* Initialise the PHY registers, unless auto configured */ - if (phy->autoconf || phy->nsubnodes > 1) + if (phy->autoconf || phy->already_configured || phy->nsubnodes > 1) return 0; clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000); @@ -646,6 +647,18 @@ static const struct phy_ops ops = { .owner = THIS_MODULE, }; +static int cdns_sierra_noop_phy_on(struct phy *gphy) +{ + usleep_range(5000, 10000); + + return 0; +} + +static const struct phy_ops noop_ops = { + .power_on = cdns_sierra_noop_phy_on, + .owner = THIS_MODULE, +}; + static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw) { struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw); @@ -1118,13 +1131,6 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp, struct clk *clk; int ret; - clk = devm_clk_get_optional(dev, "phy_clk"); - if (IS_ERR(clk)) { - dev_err(dev, "failed to get clock phy_clk\n"); - return PTR_ERR(clk); - } - sp->input_clks[PHY_CLK] = clk; - clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); if (IS_ERR(clk)) { dev_err(dev, "cmn_refclk_dig_div clock not found\n"); @@ -1160,17 +1166,33 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp, return 0; } -static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp) +static int cdns_sierra_phy_clk(struct cdns_sierra_phy *sp) { + struct device *dev = sp->dev; + struct clk *clk; int ret; + clk = devm_clk_get_optional(dev, "phy_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get clock phy_clk\n"); + return PTR_ERR(clk); + } + sp->input_clks[PHY_CLK] = clk; + ret = clk_prepare_enable(sp->input_clks[PHY_CLK]); if (ret) return ret; + return 0; +} + +static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp) +{ + int ret; + ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); if (ret) - goto err_pll_cmnlc; + return ret; ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]); if (ret) @@ -1181,9 +1203,6 @@ static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp) err_pll_cmnlc1: clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); -err_pll_cmnlc: - clk_disable_unprepare(sp->input_clks[PHY_CLK]); - return ret; } @@ -1191,7 +1210,8 @@ static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp) { clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]); clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); - clk_disable_unprepare(sp->input_clks[PHY_CLK]); + if (!sp->already_configured) + clk_disable_unprepare(sp->input_clks[PHY_CLK]); } static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp, @@ -1382,22 +1402,30 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) if (ret) return ret; - ret = cdns_sierra_phy_get_resets(sp, dev); - if (ret) - goto unregister_clk; - ret = cdns_sierra_phy_enable_clocks(sp); if (ret) goto unregister_clk; - /* Enable APB */ - reset_control_deassert(sp->apb_rst); + regmap_field_read(sp->pma_cmn_ready, &sp->already_configured); + + if (!sp->already_configured) { + ret = cdns_sierra_phy_clk(sp); + if (ret) + goto clk_disable; + + ret = cdns_sierra_phy_get_resets(sp, dev); + if (ret) + goto clk_disable; + + /* Enable APB */ + reset_control_deassert(sp->apb_rst); + } /* Check that PHY is present */ regmap_field_read(sp->macro_id_type, &id_value); if (sp->init_data->id_value != id_value) { ret = -EINVAL; - goto clk_disable; + goto ctrl_assert; } sp->autoconf = of_property_read_bool(dn, "cdns,autoconf"); @@ -1433,8 +1461,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) sp->num_lanes += sp->phys[node].num_lanes; - gphy = devm_phy_create(dev, child, &ops); - + if (!sp->already_configured) + gphy = devm_phy_create(dev, child, &ops); + else + gphy = devm_phy_create(dev, child, &noop_ops); if (IS_ERR(gphy)) { ret = PTR_ERR(gphy); of_node_put(child); @@ -1455,7 +1485,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) } /* If more than one subnode, configure the PHY as multilink */ - if (!sp->autoconf && sp->nsubnodes > 1) { + if (!sp->already_configured && !sp->autoconf && sp->nsubnodes > 1) { ret = cdns_sierra_phy_configure_multilink(sp); if (ret) goto put_control; @@ -1473,9 +1503,11 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) put_control: while (--node >= 0) reset_control_put(sp->phys[node].lnk_rst); +ctrl_assert: + if (!sp->already_configured) + reset_control_assert(sp->apb_rst); clk_disable: cdns_sierra_phy_disable_clocks(sp); - reset_control_assert(sp->apb_rst); unregister_clk: cdns_sierra_clk_unregister(sp); return ret; diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 0e91cd99c36b..8d945211c7b4 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only + +if (ARCH_MXC && ARM64) || COMPILE_TEST + config PHY_FSL_IMX8MQ_USB tristate "Freescale i.MX8M USB3 PHY" depends on OF && HAS_IOMEM @@ -32,3 +35,5 @@ config PHY_FSL_LYNX_28G found on NXP's Layerscape platforms such as LX2160A. Used to change the protocol running on SerDes lanes at runtime. Only useful for a restricted set of Ethernet protocols. + +endif diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c index 04b1aafb29f4..f1eb03ba25d6 100644 --- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -5,9 +5,9 @@ #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/delay.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/delay.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx7-iomuxc-gpr.h> #include <linux/module.h> @@ -15,6 +15,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/reset.h> + #include <dt-bindings/phy/phy-imx8-pcie.h> #define IMX8MM_PCIE_PHY_CMN_REG061 0x184 diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c index 6781488cfc58..a4d7d9bd100d 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c @@ -5,12 +5,16 @@ * Authors: * Evan Wang <xswang@marvell.com> * Miquèl Raynal <miquel.raynal@bootlin.com> + * Pali Rohár <pali@kernel.org> + * Marek Behún <kabel@kernel.org> * * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. - * SMC call initial support done by Grzegorz Jaszczyk. + * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org> + * and Marek Behún <kabel@kernel.org>. */ -#include <linux/arm-smccc.h> +#include <linux/bitfield.h> +#include <linux/clk.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/mfd/syscon.h> @@ -18,109 +22,1118 @@ #include <linux/phy.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/spinlock.h> -#define MVEBU_A3700_COMPHY_LANES 3 -#define MVEBU_A3700_COMPHY_PORTS 2 - -/* COMPHY Fast SMC function identifiers */ -#define COMPHY_SIP_POWER_ON 0x82000001 -#define COMPHY_SIP_POWER_OFF 0x82000002 -#define COMPHY_SIP_PLL_LOCK 0x82000003 - -#define COMPHY_FW_MODE_SATA 0x1 -#define COMPHY_FW_MODE_SGMII 0x2 -#define COMPHY_FW_MODE_2500BASEX 0x3 -#define COMPHY_FW_MODE_USB3H 0x4 -#define COMPHY_FW_MODE_USB3D 0x5 -#define COMPHY_FW_MODE_PCIE 0x6 -#define COMPHY_FW_MODE_USB3 0xa - -#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ -#define COMPHY_FW_SPEED_2_5G 1 -#define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */ -#define COMPHY_FW_SPEED_5G 3 -#define COMPHY_FW_SPEED_MAX 0x3F - -#define COMPHY_FW_MODE(mode) ((mode) << 12) -#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ - ((idx) << 8) | \ - ((speed) << 2)) -#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \ - ((width) << 18)) +#define PLL_SET_DELAY_US 600 +#define COMPHY_PLL_SLEEP 1000 +#define COMPHY_PLL_TIMEOUT 150000 + +/* Comphy lane2 indirect access register offset */ +#define COMPHY_LANE2_INDIR_ADDR 0x0 +#define COMPHY_LANE2_INDIR_DATA 0x4 + +/* SATA and USB3 PHY offset compared to SATA PHY */ +#define COMPHY_LANE2_REGS_BASE 0x200 + +/* + * When accessing common PHY lane registers directly, we need to shift by 1, + * since the registers are 16-bit. + */ +#define COMPHY_LANE_REG_DIRECT(reg) (((reg) & 0x7FF) << 1) + +/* COMPHY registers */ +#define COMPHY_POWER_PLL_CTRL 0x01 +#define PU_IVREF_BIT BIT(15) +#define PU_PLL_BIT BIT(14) +#define PU_RX_BIT BIT(13) +#define PU_TX_BIT BIT(12) +#define PU_TX_INTP_BIT BIT(11) +#define PU_DFE_BIT BIT(10) +#define RESET_DTL_RX_BIT BIT(9) +#define PLL_LOCK_BIT BIT(8) +#define REF_FREF_SEL_MASK GENMASK(4, 0) +#define REF_FREF_SEL_SERDES_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x1) +#define REF_FREF_SEL_SERDES_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3) +#define REF_FREF_SEL_SERDES_50MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x4) +#define REF_FREF_SEL_PCIE_USB3_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x2) +#define REF_FREF_SEL_PCIE_USB3_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3) +#define COMPHY_MODE_MASK GENMASK(7, 5) +#define COMPHY_MODE_SATA FIELD_PREP(COMPHY_MODE_MASK, 0x0) +#define COMPHY_MODE_PCIE FIELD_PREP(COMPHY_MODE_MASK, 0x3) +#define COMPHY_MODE_SERDES FIELD_PREP(COMPHY_MODE_MASK, 0x4) +#define COMPHY_MODE_USB3 FIELD_PREP(COMPHY_MODE_MASK, 0x5) + +#define COMPHY_KVCO_CAL_CTRL 0x02 +#define USE_MAX_PLL_RATE_BIT BIT(12) +#define SPEED_PLL_MASK GENMASK(7, 2) +#define SPEED_PLL_VALUE_16 FIELD_PREP(SPEED_PLL_MASK, 0x10) + +#define COMPHY_DIG_LOOPBACK_EN 0x23 +#define SEL_DATA_WIDTH_MASK GENMASK(11, 10) +#define DATA_WIDTH_10BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0) +#define DATA_WIDTH_20BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1) +#define DATA_WIDTH_40BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2) +#define PLL_READY_TX_BIT BIT(4) + +#define COMPHY_SYNC_PATTERN 0x24 +#define TXD_INVERT_BIT BIT(10) +#define RXD_INVERT_BIT BIT(11) + +#define COMPHY_SYNC_MASK_GEN 0x25 +#define PHY_GEN_MAX_MASK GENMASK(11, 10) +#define PHY_GEN_MAX_USB3_5G FIELD_PREP(PHY_GEN_MAX_MASK, 0x1) + +#define COMPHY_ISOLATION_CTRL 0x26 +#define PHY_ISOLATE_MODE BIT(15) + +#define COMPHY_GEN2_SET2 0x3e +#define GS2_TX_SSC_AMP_MASK GENMASK(15, 9) +#define GS2_TX_SSC_AMP_4128 FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20) +#define GS2_VREG_RXTX_MAS_ISET_MASK GENMASK(8, 7) +#define GS2_VREG_RXTX_MAS_ISET_60U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x0) +#define GS2_VREG_RXTX_MAS_ISET_80U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x1) +#define GS2_VREG_RXTX_MAS_ISET_100U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x2) +#define GS2_VREG_RXTX_MAS_ISET_120U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x3) +#define GS2_RSVD_6_0_MASK GENMASK(6, 0) + +#define COMPHY_GEN3_SET2 0x3f + +#define COMPHY_IDLE_SYNC_EN 0x48 +#define IDLE_SYNC_EN BIT(12) + +#define COMPHY_MISC_CTRL0 0x4F +#define CLK100M_125M_EN BIT(4) +#define TXDCLK_2X_SEL BIT(6) +#define CLK500M_EN BIT(7) +#define PHY_REF_CLK_SEL BIT(10) + +#define COMPHY_SFT_RESET 0x52 +#define SFT_RST BIT(9) +#define SFT_RST_NO_REG BIT(10) + +#define COMPHY_MISC_CTRL1 0x73 +#define SEL_BITS_PCIE_FORCE BIT(15) + +#define COMPHY_GEN2_SET3 0x112 +#define GS3_FFE_CAP_SEL_MASK GENMASK(3, 0) +#define GS3_FFE_CAP_SEL_VALUE FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF) + +/* PIPE registers */ +#define COMPHY_PIPE_LANE_CFG0 0x180 +#define PRD_TXDEEMPH0_MASK BIT(0) +#define PRD_TXMARGIN_MASK GENMASK(3, 1) +#define PRD_TXSWING_MASK BIT(4) +#define CFG_TX_ALIGN_POS_MASK GENMASK(8, 5) + +#define COMPHY_PIPE_LANE_CFG1 0x181 +#define PRD_TXDEEMPH1_MASK BIT(15) +#define USE_MAX_PLL_RATE_EN BIT(9) +#define TX_DET_RX_MODE BIT(6) +#define GEN2_TX_DATA_DLY_MASK GENMASK(4, 3) +#define GEN2_TX_DATA_DLY_DEFT FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2) +#define TX_ELEC_IDLE_MODE_EN BIT(0) + +#define COMPHY_PIPE_LANE_STAT1 0x183 +#define TXDCLK_PCLK_EN BIT(0) + +#define COMPHY_PIPE_LANE_CFG4 0x188 +#define SPREAD_SPECTRUM_CLK_EN BIT(7) + +#define COMPHY_PIPE_RST_CLK_CTRL 0x1C1 +#define PIPE_SOFT_RESET BIT(0) +#define PIPE_REG_RESET BIT(1) +#define MODE_CORE_CLK_FREQ_SEL BIT(9) +#define MODE_PIPE_WIDTH_32 BIT(3) +#define MODE_REFDIV_MASK GENMASK(5, 4) +#define MODE_REFDIV_BY_4 FIELD_PREP(MODE_REFDIV_MASK, 0x2) + +#define COMPHY_PIPE_TEST_MODE_CTRL 0x1C2 +#define MODE_MARGIN_OVERRIDE BIT(2) + +#define COMPHY_PIPE_CLK_SRC_LO 0x1C3 +#define MODE_CLK_SRC BIT(0) +#define BUNDLE_PERIOD_SEL BIT(1) +#define BUNDLE_PERIOD_SCALE_MASK GENMASK(3, 2) +#define BUNDLE_SAMPLE_CTRL BIT(4) +#define PLL_READY_DLY_MASK GENMASK(7, 5) +#define CFG_SEL_20B BIT(15) + +#define COMPHY_PIPE_PWR_MGM_TIM1 0x1D0 +#define CFG_PM_OSCCLK_WAIT_MASK GENMASK(15, 12) +#define CFG_PM_RXDEN_WAIT_MASK GENMASK(11, 8) +#define CFG_PM_RXDEN_WAIT_1_UNIT FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1) +#define CFG_PM_RXDLOZ_WAIT_MASK GENMASK(7, 0) +#define CFG_PM_RXDLOZ_WAIT_7_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7) +#define CFG_PM_RXDLOZ_WAIT_12_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC) + +/* + * This register is not from PHY lane register space. It only exists in the + * indirect register space, before the actual PHY lane 2 registers. So the + * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE. + * It is used only for SATA PHY initialization. + */ +#define COMPHY_RESERVED_REG 0x0E +#define PHYCTRL_FRM_PIN_BIT BIT(13) + +/* South Bridge PHY Configuration Registers */ +#define COMPHY_PHY_REG(lane, reg) (((1 - (lane)) * 0x28) + ((reg) & 0x3f)) + +/* + * lane0: USB3/GbE1 PHY Configuration 1 + * lane1: PCIe/GbE0 PHY Configuration 1 + * (used only by SGMII code) + */ +#define COMPHY_PHY_CFG1 0x0 +#define PIN_PU_IVREF_BIT BIT(1) +#define PIN_RESET_CORE_BIT BIT(11) +#define PIN_RESET_COMPHY_BIT BIT(12) +#define PIN_PU_PLL_BIT BIT(16) +#define PIN_PU_RX_BIT BIT(17) +#define PIN_PU_TX_BIT BIT(18) +#define PIN_TX_IDLE_BIT BIT(19) +#define GEN_RX_SEL_MASK GENMASK(25, 22) +#define GEN_RX_SEL_VALUE(val) FIELD_PREP(GEN_RX_SEL_MASK, (val)) +#define GEN_TX_SEL_MASK GENMASK(29, 26) +#define GEN_TX_SEL_VALUE(val) FIELD_PREP(GEN_TX_SEL_MASK, (val)) +#define SERDES_SPEED_1_25_G 0x6 +#define SERDES_SPEED_3_125_G 0x8 +#define PHY_RX_INIT_BIT BIT(30) + +/* + * lane0: USB3/GbE1 PHY Status 1 + * lane1: PCIe/GbE0 PHY Status 1 + * (used only by SGMII code) + */ +#define COMPHY_PHY_STAT1 0x18 +#define PHY_RX_INIT_DONE_BIT BIT(0) +#define PHY_PLL_READY_RX_BIT BIT(2) +#define PHY_PLL_READY_TX_BIT BIT(3) + +/* PHY Selector */ +#define COMPHY_SELECTOR_PHY_REG 0xFC +/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */ +#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT BIT(0) +/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */ +#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT BIT(4) +/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */ +#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT BIT(8) struct mvebu_a3700_comphy_conf { unsigned int lane; enum phy_mode mode; int submode; - unsigned int port; - u32 fw_mode; }; -#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \ +#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode) \ { \ .lane = _lane, \ .mode = _mode, \ .submode = _smode, \ - .port = _port, \ - .fw_mode = _fw, \ } -#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \ - MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw) +#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \ + MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA) -#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \ - MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw) +#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \ + MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode) static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { /* lane 0 */ - MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0, - COMPHY_FW_MODE_USB3H), - MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, - COMPHY_FW_MODE_SGMII), - MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, - COMPHY_FW_MODE_2500BASEX), + MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX), /* lane 1 */ - MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0, - COMPHY_FW_MODE_PCIE), - MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0, - COMPHY_FW_MODE_SGMII), - MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, - COMPHY_FW_MODE_2500BASEX), + MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX), /* lane 2 */ - MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, - COMPHY_FW_MODE_SATA), - MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0, - COMPHY_FW_MODE_USB3H), + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA), + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS), +}; + +struct mvebu_a3700_comphy_priv { + void __iomem *comphy_regs; + void __iomem *lane0_phy_regs; /* USB3 and GbE1 */ + void __iomem *lane1_phy_regs; /* PCIe and GbE0 */ + void __iomem *lane2_phy_indirect; /* SATA and USB3 */ + spinlock_t lock; /* for PHY selector access */ + bool xtal_is_40m; }; struct mvebu_a3700_comphy_lane { + struct mvebu_a3700_comphy_priv *priv; struct device *dev; unsigned int id; enum phy_mode mode; int submode; - int port; + bool invert_tx; + bool invert_rx; + bool needs_reset; +}; + +struct gbe_phy_init_data_fix { + u16 addr; + u16 value; }; -static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, - unsigned long mode) +/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */ +static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = { + { 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 }, + { 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 }, + { 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 }, + { 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC }, + { 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 }, + { 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 }, + { 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 }, + { 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 }, + { 0x104, 0x0C10 } +}; + +/* 40M1G25 mode init data */ +static u16 gbe_phy_init[512] = { + /* 0 1 2 3 4 5 6 7 */ + /*-----------------------------------------------------------*/ + /* 8 9 A B C D E F */ + 0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26, /* 00 */ + 0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52, /* 08 */ + 0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000, /* 10 */ + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF, /* 18 */ + 0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000, /* 20 */ + 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 28 */ + 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */ + 0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100, /* 38 */ + 0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00, /* 40 */ + 0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A, /* 48 */ + 0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001, /* 50 */ + 0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF, /* 58 */ + 0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000, /* 60 */ + 0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002, /* 68 */ + 0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780, /* 70 */ + 0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000, /* 78 */ + 0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000, /* 80 */ + 0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210, /* 88 */ + 0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F, /* 90 */ + 0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651, /* 98 */ + 0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000, /* A0 */ + 0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* A8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* B0 */ + 0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, /* B8 */ + 0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003, /* C0 */ + 0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000, /* C8 */ + 0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00, /* D0 */ + 0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000, /* D8 */ + 0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541, /* E0 */ + 0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200, /* E8 */ + 0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000, /* F0 */ + 0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000, /* F8 */ + 0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000, /*100 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*108 */ + 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000, /*110 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*118 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*120 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*128 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*130 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*138 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*140 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*148 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*150 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*158 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*160 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*168 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*170 */ + 0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000, /*178 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*180 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*188 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*190 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*198 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1F0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 /*1F8 */ +}; + +static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask) { - struct arm_smccc_res res; - s32 ret; + u32 val; - arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); - ret = res.a0; + val = readl(addr); + val = (val & ~mask) | (data & mask); + writel(val, addr); +} + +static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask) +{ + u16 val; + + val = readw(addr); + val = (val & ~mask) | (data & mask); + writew(val, addr); +} + +/* Used for accessing lane 2 registers (SATA/USB3 PHY) */ +static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv, + u32 offset, u16 data, u16 mask) +{ + writel(offset, + priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR); + comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA, + data, mask); +} + +static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane, + u16 reg, u16 data, u16 mask) +{ + if (lane->id == 2) { + /* lane 2 PHY registers are accessed indirectly */ + comphy_set_indirect(lane->priv, + reg + COMPHY_LANE2_REGS_BASE, + data, mask); + } else { + void __iomem *base = lane->id == 1 ? + lane->priv->lane1_phy_regs : + lane->priv->lane0_phy_regs; + + comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg), + data, mask); + } +} + +static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane, + u16 reg, u16 bits, + ulong sleep_us, ulong timeout_us) +{ + int ret; + + if (lane->id == 2) { + u32 data; + + /* lane 2 PHY registers are accessed indirectly */ + writel(reg + COMPHY_LANE2_REGS_BASE, + lane->priv->lane2_phy_indirect + + COMPHY_LANE2_INDIR_ADDR); + + ret = readl_poll_timeout(lane->priv->lane2_phy_indirect + + COMPHY_LANE2_INDIR_DATA, + data, (data & bits) == bits, + sleep_us, timeout_us); + } else { + void __iomem *base = lane->id == 1 ? + lane->priv->lane1_phy_regs : + lane->priv->lane0_phy_regs; + u16 data; + + ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg), + data, (data & bits) == bits, + sleep_us, timeout_us); + } + + return ret; +} + +static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane, + u8 reg, u32 data, u32 mask) +{ + comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg), + data, mask); +} + +static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane, + u8 reg, u32 bits, + ulong sleep_us, ulong timeout_us) +{ + u32 data; + + return readl_poll_timeout(lane->priv->comphy_regs + + COMPHY_PHY_REG(lane->id, reg), + data, (data & bits) == bits, + sleep_us, timeout_us); +} + +/* PHY selector configures with corresponding modes */ +static int +mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane) +{ + u32 old, new, clr = 0, set = 0; + unsigned long flags; + + switch (lane->mode) { + case PHY_MODE_SATA: + /* SATA must be in Lane2 */ + if (lane->id == 2) + clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT; + else + goto error; + break; + + case PHY_MODE_ETHERNET: + if (lane->id == 0) + clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT; + else if (lane->id == 1) + clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT; + else + goto error; + break; + + case PHY_MODE_USB_HOST_SS: + if (lane->id == 2) + set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT; + else if (lane->id == 0) + set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT; + else + goto error; + break; + + case PHY_MODE_PCIE: + /* PCIE must be in Lane1 */ + if (lane->id == 1) + set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT; + else + goto error; + break; - switch (ret) { - case SMCCC_RET_SUCCESS: - return 0; - case SMCCC_RET_NOT_SUPPORTED: - return -EOPNOTSUPP; default: + goto error; + } + + spin_lock_irqsave(&lane->priv->lock, flags); + + old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG); + new = (old & ~clr) | set; + writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG); + + spin_unlock_irqrestore(&lane->priv->lock, flags); + + dev_dbg(lane->dev, + "COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n", + lane->id, lane->mode, old, new); + + return 0; +error: + dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id, + lane->mode); + return -EINVAL; +} + +static int +mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, ref_clk; + int ret; + + /* Configure phy selector for SATA */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* Clear phy isolation mode to make it work in normal mode */ + comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL, + 0x0, PHY_ISOLATE_MODE); + + /* 0. Check the Polarity invert bits */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* 1. Select 40-bit data width */ + comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, + DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK); + + /* 2. Select reference clock(25M) and PHY mode (SATA) */ + if (lane->priv->xtal_is_40m) + ref_clk = REF_FREF_SEL_SERDES_40MHZ; + else + ref_clk = REF_FREF_SEL_SERDES_25MHZ; + + data = ref_clk | COMPHY_MODE_SATA; + mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* 3. Use maximum PLL rate (no power save) */ + comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, + USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT); + + /* 4. Reset reserved bit */ + comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG, + 0x0, PHYCTRL_FRM_PIN_BIT); + + /* 5. Set vendor-specific configuration (It is done in sata driver) */ + /* XXX: in U-Boot below sequence was executed in this place, in Linux + * not. Now it is done only in U-Boot before this comphy + * initialization - tests shows that it works ok, but in case of any + * future problem it is left for reference. + * reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff); + * reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6)); + */ + + /* Wait for > 55 us to allow PLL be enabled */ + udelay(PLL_SET_DELAY_US); + + /* Polling status */ + ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN, + PLL_READY_TX_BIT, COMPHY_PLL_SLEEP, + COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to lock SATA PLL\n"); + + return ret; +} + +static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane, + bool is_1gbps) +{ + int addr, fix_idx; + u16 val; + + fix_idx = 0; + for (addr = 0; addr < 512; addr++) { + /* + * All PHY register values are defined in full for 3.125Gbps + * SERDES speed. The values required for 1.25 Gbps are almost + * the same and only few registers should be "fixed" in + * comparison to 3.125 Gbps values. These register values are + * stored in "gbe_phy_init_fix" array. + */ + if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) { + /* Use new value */ + val = gbe_phy_init_fix[fix_idx].value; + if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix)) + fix_idx++; + } else { + val = gbe_phy_init[addr]; + } + + comphy_lane_reg_set(lane, addr, val, 0xFFFF); + } +} + +static int +mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, speed_sel; + int ret; + + /* Set selector */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* + * 1. Reset PHY by setting PHY input port PIN_RESET=1. + * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep + * PHY TXP/TXN output to idle state during PHY initialization + * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0. + */ + data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT; + mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT | + PIN_PU_TX_BIT | PHY_RX_INIT_BIT; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* 4. Release reset to the PHY by setting PIN_RESET=0. */ + data = 0x0; + mask = PIN_RESET_COMPHY_BIT; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* + * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY + * bit rate + */ + switch (lane->submode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + /* SGMII 1G, SerDes speed 1.25G */ + speed_sel = SERDES_SPEED_1_25_G; + break; + case PHY_INTERFACE_MODE_2500BASEX: + /* 2500Base-X, SerDes speed 3.125G */ + speed_sel = SERDES_SPEED_3_125_G; + break; + default: + /* Other rates are not supported */ + dev_err(lane->dev, + "unsupported phy speed %d on comphy lane%d\n", + lane->submode, lane->id); return -EINVAL; } + data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel); + mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* + * 6. Wait 10mS for bandgap and reference clocks to stabilize; then + * start SW programming. + */ + mdelay(10); + + /* 7. Program COMPHY register PHY_MODE */ + data = COMPHY_MODE_SERDES; + mask = COMPHY_MODE_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* + * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK + * source + */ + data = 0x0; + mask = PHY_REF_CLK_SEL; + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask); + + /* + * 9. Set correct reference clock frequency in COMPHY register + * REF_FREF_SEL. + */ + if (lane->priv->xtal_is_40m) + data = REF_FREF_SEL_SERDES_50MHZ; + else + data = REF_FREF_SEL_SERDES_25MHZ; + + mask = REF_FREF_SEL_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* + * 10. Program COMPHY register PHY_GEN_MAX[1:0] + * This step is mentioned in the flow received from verification team. + * However the PHY_GEN_MAX value is only meaningful for other interfaces + * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or + * PCIe speed 2.5/5 Gbps + */ + + /* + * 11. Program COMPHY register SEL_BITS to set correct parallel data + * bus width + */ + data = DATA_WIDTH_10BIT; + mask = SEL_DATA_WIDTH_MASK; + comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask); + + /* + * 12. As long as DFE function needs to be enabled in any mode, + * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F + * for real chip during COMPHY power on. + * The value of the DFE_UPDATE_EN already is 0x3F, because it is the + * default value after reset of the PHY. + */ + + /* + * 13. Program COMPHY GEN registers. + * These registers should be programmed based on the lab testing result + * to achieve optimal performance. Please contact the CEA group to get + * the related GEN table during real chip bring-up. We only required to + * run though the entire registers programming flow defined by + * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock + * 25 MHz the default values stored in PHY registers are OK. + */ + dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n", + lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G"); + if (lane->priv->xtal_is_40m) + comphy_gbe_phy_init(lane, + lane->submode != PHY_INTERFACE_MODE_2500BASEX); + + /* + * 14. Check the PHY Polarity invert bit + */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* + * 15. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to + * start PHY power up sequence. All the PHY register programming should + * be done before PIN_PU_PLL=1. There should be no register programming + * for normal PHY operation from this point. + */ + data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT; + mask = data; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* + * 16. Wait for PHY power up sequence to finish by checking output ports + * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1. + */ + ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, + PHY_PLL_READY_TX_BIT | + PHY_PLL_READY_RX_BIT, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) { + dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n", + lane->id); + return ret; + } + + /* + * 17. Set COMPHY input port PIN_TX_IDLE=0 + */ + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT); + + /* + * 18. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To + * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the + * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to + * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please + * refer to RX initialization part for details. + */ + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, + PHY_RX_INIT_BIT, PHY_RX_INIT_BIT); + + ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, + PHY_PLL_READY_TX_BIT | + PHY_PLL_READY_RX_BIT, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) { + dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n", + lane->id); + return ret; + } + + ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, + PHY_RX_INIT_DONE_BIT, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n", + lane->id); + + return ret; +} + +static int +mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, cfg, ref_clk; + int ret; + + /* Set phy seclector */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* + * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The + * register belong to UTMI module, so it is set in UTMI phy driver. + */ + + /* + * 1. Set PRD_TXDEEMPH (3.5db de-emph) + */ + data = PRD_TXDEEMPH0_MASK; + mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK | + CFG_TX_ALIGN_POS_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask); + + /* + * 2. Set BIT0: enable transmitter in high impedance mode + * Set BIT[3:4]: delay 2 clock cycles for HiZ off latency + * Set BIT6: Tx detect Rx at HiZ mode + * Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db + * together with bit 0 of COMPHY_PIPE_LANE_CFG0 register + */ + data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN; + mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK | + TX_ELEC_IDLE_MODE_EN; + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask); + + /* + * 3. Set Spread Spectrum Clock Enabled + */ + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4, + SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN); + + /* + * 4. Set Override Margining Controls From the MAC: + * Use margining signals from lane configuration + */ + comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL, + MODE_MARGIN_OVERRIDE, 0xFFFF); + + /* + * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles + * set Mode Clock Source = PCLK is generated from REFCLK + */ + data = 0x0; + mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK | + BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask); + + /* + * 6. Set G2 Spread Spectrum Clock Amplitude at 4K + */ + comphy_lane_reg_set(lane, COMPHY_GEN2_SET2, + GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK); + + /* + * 7. Unset G3 Spread Spectrum Clock Amplitude + * set G3 TX and RX Register Master Current Select + */ + data = GS2_VREG_RXTX_MAS_ISET_60U; + mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK | + GS2_RSVD_6_0_MASK; + comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask); + + /* + * 8. Check crystal jumper setting and program the Power and PLL Control + * accordingly Change RX wait + */ + if (lane->priv->xtal_is_40m) { + ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ; + cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT; + } else { + ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ; + cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT; + } + + data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | + PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk; + mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | + PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK | + REF_FREF_SEL_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg; + mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK | + CFG_PM_RXDLOZ_WAIT_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask); + + /* + * 9. Enable idle sync + */ + comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN, + IDLE_SYNC_EN, IDLE_SYNC_EN); + + /* + * 10. Enable the output of 500M clock + */ + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN); + + /* + * 11. Set 20-bit data width + */ + comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, + DATA_WIDTH_20BIT, 0xFFFF); + + /* + * 12. Override Speed_PLL value and use MAC PLL + */ + data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT; + mask = 0xFFFF; + comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask); + + /* + * 13. Check the Polarity invert bit + */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* + * 14. Set max speed generation to USB3.0 5Gbps + */ + comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN, + PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK); + + /* + * 15. Set capacitor value for FFE gain peaking to 0xF + */ + comphy_lane_reg_set(lane, COMPHY_GEN2_SET3, + GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK); + + /* + * 16. Release SW reset + */ + data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4; + mask = 0xFFFF; + comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); + + /* Wait for > 55 us to allow PCLK be enabled */ + udelay(PLL_SET_DELAY_US); + + ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to lock USB3 PLL\n"); + + return ret; +} + +static int +mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, ref_clk; + int ret; + + /* Configure phy selector for PCIe */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* 1. Enable max PLL. */ + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, + USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN); + + /* 2. Select 20 bit SERDES interface. */ + comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, + CFG_SEL_20B, CFG_SEL_20B); + + /* 3. Force to use reg setting for PCIe mode */ + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1, + SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE); + + /* 4. Change RX wait */ + data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT; + mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK | + CFG_PM_RXDLOZ_WAIT_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask); + + /* 5. Enable idle sync */ + comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN, + IDLE_SYNC_EN, IDLE_SYNC_EN); + + /* 6. Enable the output of 100M/125M/500M clock */ + data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN; + mask = data; + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask); + + /* + * 7. Enable TX, PCIE global register, 0xd0074814, it is done in + * PCI-E driver + */ + + /* + * 8. Check crystal jumper setting and program the Power and PLL + * Control accordingly + */ + + if (lane->priv->xtal_is_40m) + ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ; + else + ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ; + + data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | + PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk; + mask = 0xFFFF; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* 9. Override Speed_PLL value and use MAC PLL */ + comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, + SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT, + 0xFFFF); + + /* 10. Check the Polarity invert bit */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* 11. Release SW reset */ + data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32; + mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); + + /* Wait for > 55 us to allow PCLK be enabled */ + udelay(PLL_SET_DELAY_US); + + ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to lock PCIE PLL\n"); + + return ret; +} + +static void +mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + /* Set phy isolation mode */ + comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL, + PHY_ISOLATE_MODE, PHY_ISOLATE_MODE); + + /* Power off PLL, Tx, Rx */ + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, + 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); +} + +static void +mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data; + + data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT | + PHY_RX_INIT_BIT; + mask = data; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); +} + +static void +mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + /* Power off PLL, Tx, Rx */ + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, + 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); +} + +static int mvebu_a3700_comphy_reset(struct phy *phy) +{ + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + u16 mask, data; + + dev_dbg(lane->dev, "resetting lane %d\n", lane->id); + + /* COMPHY reset for internal logic */ + comphy_lane_reg_set(lane, COMPHY_SFT_RESET, + SFT_RST_NO_REG, SFT_RST_NO_REG); + + /* COMPHY register reset (cleared automatically) */ + comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST); + + /* PIPE soft and register reset */ + data = PIPE_SOFT_RESET | PIPE_REG_RESET; + mask = data; + comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); + + /* Release PIPE register reset */ + comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, + 0x0, PIPE_REG_RESET); + + /* Reset SB configuration register (only for lanes 0 and 1) */ + if (lane->id == 0 || lane->id == 1) { + u32 mask, data; + + data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | + PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT; + mask = data | PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + } + + return 0; } -static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, +static bool mvebu_a3700_comphy_check_mode(int lane, enum phy_mode mode, int submode) { @@ -128,38 +1141,40 @@ static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, /* Unused PHY mux value is 0x0 */ if (mode == PHY_MODE_INVALID) - return -EINVAL; + return false; for (i = 0; i < n; i++) { if (mvebu_a3700_comphy_modes[i].lane == lane && - mvebu_a3700_comphy_modes[i].port == port && mvebu_a3700_comphy_modes[i].mode == mode && mvebu_a3700_comphy_modes[i].submode == submode) break; } if (i == n) - return -EINVAL; + return false; - return mvebu_a3700_comphy_modes[i].fw_mode; + return true; } static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); - int fw_mode; - - if (submode == PHY_INTERFACE_MODE_1000BASEX) - submode = PHY_INTERFACE_MODE_SGMII; - fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode, - submode); - if (fw_mode < 0) { + if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) { dev_err(lane->dev, "invalid COMPHY mode\n"); - return fw_mode; + return -EINVAL; } + /* Mode cannot be changed while the PHY is powered on */ + if (phy->power_count && + (lane->mode != mode || lane->submode != submode)) + return -EBUSY; + + /* If changing mode, ensure reset is called */ + if (lane->mode != PHY_MODE_INVALID && lane->mode != mode) + lane->needs_reset = true; + /* Just remember the mode, ->power_on() will do the real setup */ lane->mode = mode; lane->submode = submode; @@ -170,75 +1185,77 @@ static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, static int mvebu_a3700_comphy_power_on(struct phy *phy) { struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); - u32 fw_param; - int fw_mode; int ret; - fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, - lane->mode, lane->submode); - if (fw_mode < 0) { + if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode, + lane->submode)) { dev_err(lane->dev, "invalid COMPHY mode\n"); - return fw_mode; + return -EINVAL; + } + + if (lane->needs_reset) { + ret = mvebu_a3700_comphy_reset(phy); + if (ret) + return ret; + + lane->needs_reset = false; } switch (lane->mode) { case PHY_MODE_USB_HOST_SS: dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id); - fw_param = COMPHY_FW_MODE(fw_mode); - break; + return mvebu_a3700_comphy_usb3_power_on(lane); case PHY_MODE_SATA: dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id); - fw_param = COMPHY_FW_MODE(fw_mode); - break; + return mvebu_a3700_comphy_sata_power_on(lane); case PHY_MODE_ETHERNET: - switch (lane->submode) { - case PHY_INTERFACE_MODE_SGMII: - dev_dbg(lane->dev, "set lane %d to SGMII mode\n", - lane->id); - fw_param = COMPHY_FW_NET(fw_mode, lane->port, - COMPHY_FW_SPEED_1_25G); - break; - case PHY_INTERFACE_MODE_2500BASEX: - dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n", - lane->id); - fw_param = COMPHY_FW_NET(fw_mode, lane->port, - COMPHY_FW_SPEED_3_125G); - break; - default: - dev_err(lane->dev, "unsupported PHY submode (%d)\n", - lane->submode); - return -ENOTSUPP; - } - break; + dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id); + return mvebu_a3700_comphy_ethernet_power_on(lane); case PHY_MODE_PCIE: dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); - fw_param = COMPHY_FW_PCIE(fw_mode, lane->port, - COMPHY_FW_SPEED_5G, - phy->attrs.bus_width); - break; + return mvebu_a3700_comphy_pcie_power_on(lane); default: dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); - return -ENOTSUPP; + return -EOPNOTSUPP; } - - ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); - if (ret == -EOPNOTSUPP) - dev_err(lane->dev, - "unsupported SMC call, try updating your firmware\n"); - - return ret; } static int mvebu_a3700_comphy_power_off(struct phy *phy) { struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); - return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); + switch (lane->mode) { + case PHY_MODE_USB_HOST_SS: + /* + * The USB3 MAC sets the USB3 PHY to low state, so we do not + * need to power off USB3 PHY again. + */ + break; + + case PHY_MODE_SATA: + mvebu_a3700_comphy_sata_power_off(lane); + break; + + case PHY_MODE_ETHERNET: + mvebu_a3700_comphy_ethernet_power_off(lane); + break; + + case PHY_MODE_PCIE: + mvebu_a3700_comphy_pcie_power_off(lane); + break; + + default: + dev_err(lane->dev, "invalid COMPHY mode\n"); + return -EINVAL; + } + + return 0; } static const struct phy_ops mvebu_a3700_comphy_ops = { .power_on = mvebu_a3700_comphy_power_on, .power_off = mvebu_a3700_comphy_power_off, + .reset = mvebu_a3700_comphy_reset, .set_mode = mvebu_a3700_comphy_set_mode, .owner = THIS_MODULE, }; @@ -247,25 +1264,90 @@ static struct phy *mvebu_a3700_comphy_xlate(struct device *dev, struct of_phandle_args *args) { struct mvebu_a3700_comphy_lane *lane; + unsigned int port; struct phy *phy; - if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS)) - return ERR_PTR(-EINVAL); - phy = of_phy_simple_xlate(dev, args); if (IS_ERR(phy)) return phy; lane = phy_get_drvdata(phy); - lane->port = args->args[0]; + + port = args->args[0]; + if (port != 0 && (port != 1 || lane->id != 0)) { + dev_err(lane->dev, "invalid port number %u\n", port); + return ERR_PTR(-EINVAL); + } + + lane->invert_tx = args->args[1] & BIT(0); + lane->invert_rx = args->args[1] & BIT(1); return phy; } static int mvebu_a3700_comphy_probe(struct platform_device *pdev) { + struct mvebu_a3700_comphy_priv *priv; struct phy_provider *provider; struct device_node *child; + struct resource *res; + struct clk *clk; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy"); + priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->comphy_regs)) + return PTR_ERR(priv->comphy_regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lane1_pcie_gbe"); + priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lane1_phy_regs)) + return PTR_ERR(priv->lane1_phy_regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lane0_usb3_gbe"); + priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lane0_phy_regs)) + return PTR_ERR(priv->lane0_phy_regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lane2_sata_usb3"); + priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lane2_phy_indirect)) + return PTR_ERR(priv->lane2_phy_indirect); + + /* + * Driver needs to know if reference xtal clock is 40MHz or 25MHz. + * Old DT bindings do not have xtal clk present. So do not fail here + * and expects that default 25MHz reference clock is used. + */ + clk = clk_get(&pdev->dev, "xtal"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n", + PTR_ERR(clk)); + } else { + ret = clk_prepare_enable(clk); + if (ret) { + dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n", + ret); + } else { + if (clk_get_rate(clk) == 40000000) + priv->xtal_is_40m = true; + clk_disable_unprepare(clk); + } + clk_put(clk); + } + + dev_set_drvdata(&pdev->dev, priv); for_each_available_child_of_node(pdev->dev.of_node, child) { struct mvebu_a3700_comphy_lane *lane; @@ -280,7 +1362,7 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev) continue; } - if (lane_id >= MVEBU_A3700_COMPHY_LANES) { + if (lane_id >= 3) { dev_err(&pdev->dev, "invalid 'reg' property\n"); continue; } @@ -298,16 +1380,26 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev) return PTR_ERR(phy); } + lane->priv = priv; lane->dev = &pdev->dev; lane->mode = PHY_MODE_INVALID; lane->submode = PHY_INTERFACE_MODE_NA; lane->id = lane_id; - lane->port = -1; + lane->invert_tx = false; + lane->invert_rx = false; phy_set_drvdata(phy, lane); + + /* + * To avoid relying on the bootloader/firmware configuration, + * power off all comphys. + */ + mvebu_a3700_comphy_reset(phy); + lane->needs_reset = false; } provider = devm_of_phy_provider_register(&pdev->dev, mvebu_a3700_comphy_xlate); + return PTR_ERR_OR_ZERO(provider); } @@ -327,5 +1419,7 @@ static struct platform_driver mvebu_a3700_comphy_driver = { module_platform_driver(mvebu_a3700_comphy_driver); MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>"); +MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); +MODULE_AUTHOR("Marek Behún <kabel@kernel.org>"); MODULE_DESCRIPTION("Common PHY driver for A3700"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index ccb4045685cd..929e86d6558e 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -64,10 +64,10 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui); cfg->init = 100; - cfg->lpx = 60000; + cfg->lpx = 50000; cfg->ta_get = 5 * cfg->lpx; cfg->ta_go = 4 * cfg->lpx; - cfg->ta_sure = 2 * cfg->lpx; + cfg->ta_sure = cfg->lpx; cfg->wakeup = 1000; cfg->hs_clk_rate = hs_clk_rate; diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c index a8ecd2e8442d..cacd32f6e0cc 100644 --- a/drivers/phy/qualcomm/phy-qcom-edp.c +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -335,9 +335,11 @@ static int qcom_edp_phy_power_on(struct phy *phy) writel(0x00, edp->tx0 + TXn_LANE_MODE_1); writel(0x00, edp->tx1 + TXn_LANE_MODE_1); - ret = qcom_edp_configure_ssc(edp); - if (ret) - return ret; + if (edp->dp_opts.ssc) { + ret = qcom_edp_configure_ssc(edp); + if (ret) + return ret; + } ret = qcom_edp_configure_pll(edp); if (ret) @@ -654,6 +656,7 @@ static int qcom_edp_phy_probe(struct platform_device *pdev) } static const struct of_device_id qcom_edp_phy_match_table[] = { + { .compatible = "qcom,sc7280-edp-phy" }, { .compatible = "qcom,sc8180x-edp-phy" }, { } }; diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c index fec1da470d26..7bacc527fbad 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> +#include <linux/bitfield.h> /* USB QSCRATCH Hardware registers */ #define QSCRATCH_GENERAL_CFG (0x08) @@ -74,20 +75,20 @@ PHY_PARAM_CTRL1_LOS_BIAS_MASK) #define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \ - (((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK) + FIELD_PREP(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK, (x)) #define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \ - (((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK) + FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK, (x)) #define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \ - (((x) << 8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK) + FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK, x) #define PHY_PARAM_CTRL1_LOS_BIAS(x) \ - (((x) << 3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK) + FIELD_PREP(PHY_PARAM_CTRL1_LOS_BIAS_MASK, (x)) /* RX OVRD IN HI bits */ #define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) #define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) #define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) #define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7) -#define RX_OVRD_IN_HI_RX_EQ(x) ((x) << 8) +#define RX_OVRD_IN_HI_RX_EQ(x) FIELD_PREP(RX_OVRD_IN_HI_RX_EQ_MASK, (x)) #define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) #define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) #define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) @@ -111,6 +112,9 @@ #define SS_CR_READ_REG BIT(0) #define SS_CR_WRITE_REG BIT(0) +#define LATCH_SLEEP 40 +#define LATCH_TIMEOUT 100 + struct usb_phy { void __iomem *base; struct device *dev; @@ -156,19 +160,9 @@ static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3, static int wait_for_latch(void __iomem *addr) { - u32 retry = 10; - - while (true) { - if (!readl(addr)) - break; - - if (--retry == 0) - return -ETIMEDOUT; - - usleep_range(10, 20); - } + u32 val; - return 0; + return readl_poll_timeout(addr, val, !val, LATCH_SLEEP, LATCH_TIMEOUT); } /** diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 8ea87c69f463..b144ae1f729a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -5978,6 +5978,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,sc8180x-qmp-ufs-phy", .data = &sm8150_ufsphy_cfg, }, { + .compatible = "qcom,sc8280xp-qmp-ufs-phy", + .data = &sm8350_ufsphy_cfg, + }, { .compatible = "qcom,sc8180x-qmp-usb3-phy", .data = &sm8150_usb3phy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 032d02bf50c5..7529a7e6e5df 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -912,6 +912,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { .compatible = "qcom,ipq8074-qusb2-phy", .data = &msm8996_phy_cfg, }, { + .compatible = "qcom,msm8953-qusb2-phy", + .data = &msm8996_phy_cfg, + }, { .compatible = "qcom,msm8996-qusb2-phy", .data = &msm8996_phy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index 7e61202aa234..5d203784f75d 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -32,6 +32,7 @@ #define POR BIT(1) #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) +#define SIDDQ BIT(2) #define RETENABLEN BIT(3) #define FSEL_MASK GENMASK(6, 4) #define FSEL_DEFAULT (0x3 << 4) @@ -233,6 +234,9 @@ static int qcom_snps_hsphy_init(struct phy *phy) qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0, SLEEPM, SLEEPM); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0, + SIDDQ, 0); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5, POR, 0); @@ -275,6 +279,7 @@ static const struct phy_ops qcom_snps_hsphy_gen_ops = { static const struct of_device_id qcom_snps_hsphy_of_match_table[] = { { .compatible = "qcom,sm8150-usb-hs-phy", }, + { .compatible = "qcom,usb-snps-hs-5nm-phy", }, { .compatible = "qcom,usb-snps-hs-7nm-phy", }, { .compatible = "qcom,usb-snps-femto-v2-phy", }, { } diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index e812adad7242..9022e395c056 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -66,6 +66,14 @@ config PHY_ROCKCHIP_INNO_DSIDPHY Enable this to support the Rockchip MIPI/LVDS/TTL PHY with Innosilicon IP block. +config PHY_ROCKCHIP_NANENG_COMBO_PHY + tristate "Rockchip NANENG COMBO PHY Driver" + depends on ARCH_ROCKCHIP && OF + select GENERIC_PHY + help + Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII + combo PHY with NaNeng IP block. + config PHY_ROCKCHIP_PCIE tristate "Rockchip PCIe PHY Driver" depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index f0eec212b2aa..a5041efb5b8f 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o +obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c new file mode 100644 index 000000000000..7b213825fb5d --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip PIPE USB3.0 PCIE SATA Combo Phy driver + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + */ + +#include <dt-bindings/phy/phy.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/units.h> + +#define BIT_WRITEABLE_SHIFT 16 +#define REF_CLOCK_24MHz (24 * HZ_PER_MHZ) +#define REF_CLOCK_25MHz (25 * HZ_PER_MHZ) +#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ) + +/* COMBO PHY REG */ +#define PHYREG6 0x14 +#define PHYREG6_PLL_DIV_MASK GENMASK(7, 6) +#define PHYREG6_PLL_DIV_SHIFT 6 +#define PHYREG6_PLL_DIV_2 1 + +#define PHYREG7 0x18 +#define PHYREG7_TX_RTERM_MASK GENMASK(7, 4) +#define PHYREG7_TX_RTERM_SHIFT 4 +#define PHYREG7_TX_RTERM_50OHM 8 +#define PHYREG7_RX_RTERM_MASK GENMASK(3, 0) +#define PHYREG7_RX_RTERM_SHIFT 0 +#define PHYREG7_RX_RTERM_44OHM 15 + +#define PHYREG8 0x1C +#define PHYREG8_SSC_EN BIT(4) + +#define PHYREG11 0x28 +#define PHYREG11_SU_TRIM_0_7 0xF0 + +#define PHYREG12 0x2C +#define PHYREG12_PLL_LPF_ADJ_VALUE 4 + +#define PHYREG13 0x30 +#define PHYREG13_RESISTER_MASK GENMASK(5, 4) +#define PHYREG13_RESISTER_SHIFT 0x4 +#define PHYREG13_RESISTER_HIGH_Z 3 +#define PHYREG13_CKRCV_AMP0 BIT(7) + +#define PHYREG14 0x34 +#define PHYREG14_CKRCV_AMP1 BIT(0) + +#define PHYREG15 0x38 +#define PHYREG15_CTLE_EN BIT(0) +#define PHYREG15_SSC_CNT_MASK GENMASK(7, 6) +#define PHYREG15_SSC_CNT_SHIFT 6 +#define PHYREG15_SSC_CNT_VALUE 1 + +#define PHYREG16 0x3C +#define PHYREG16_SSC_CNT_VALUE 0x5f + +#define PHYREG18 0x44 +#define PHYREG18_PLL_LOOP 0x32 + +#define PHYREG32 0x7C +#define PHYREG32_SSC_MASK GENMASK(7, 4) +#define PHYREG32_SSC_DIR_SHIFT 4 +#define PHYREG32_SSC_UPWARD 0 +#define PHYREG32_SSC_DOWNWARD 1 +#define PHYREG32_SSC_OFFSET_SHIFT 6 +#define PHYREG32_SSC_OFFSET_500PPM 1 + +#define PHYREG33 0x80 +#define PHYREG33_PLL_KVCO_MASK GENMASK(4, 2) +#define PHYREG33_PLL_KVCO_SHIFT 2 +#define PHYREG33_PLL_KVCO_VALUE 2 + +struct rockchip_combphy_priv; + +struct combphy_reg { + u16 offset; + u16 bitend; + u16 bitstart; + u16 disable; + u16 enable; +}; + +struct rockchip_combphy_grfcfg { + struct combphy_reg pcie_mode_set; + struct combphy_reg usb_mode_set; + struct combphy_reg sgmii_mode_set; + struct combphy_reg qsgmii_mode_set; + struct combphy_reg pipe_rxterm_set; + struct combphy_reg pipe_txelec_set; + struct combphy_reg pipe_txcomp_set; + struct combphy_reg pipe_clk_25m; + struct combphy_reg pipe_clk_100m; + struct combphy_reg pipe_phymode_sel; + struct combphy_reg pipe_rate_sel; + struct combphy_reg pipe_rxterm_sel; + struct combphy_reg pipe_txelec_sel; + struct combphy_reg pipe_txcomp_sel; + struct combphy_reg pipe_clk_ext; + struct combphy_reg pipe_sel_usb; + struct combphy_reg pipe_sel_qsgmii; + struct combphy_reg pipe_phy_status; + struct combphy_reg con0_for_pcie; + struct combphy_reg con1_for_pcie; + struct combphy_reg con2_for_pcie; + struct combphy_reg con3_for_pcie; + struct combphy_reg con0_for_sata; + struct combphy_reg con1_for_sata; + struct combphy_reg con2_for_sata; + struct combphy_reg con3_for_sata; + struct combphy_reg pipe_con0_for_sata; + struct combphy_reg pipe_xpcs_phy_ready; +}; + +struct rockchip_combphy_cfg { + const struct rockchip_combphy_grfcfg *grfcfg; + int (*combphy_cfg)(struct rockchip_combphy_priv *priv); +}; + +struct rockchip_combphy_priv { + u8 type; + void __iomem *mmio; + int num_clks; + struct clk_bulk_data *clks; + struct device *dev; + struct regmap *pipe_grf; + struct regmap *phy_grf; + struct phy *phy; + struct reset_control *phy_rst; + const struct rockchip_combphy_cfg *cfg; + bool enable_ssc; + bool ext_refclk; + struct clk *refclk; +}; + +static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv, + int mask, int val, int reg) +{ + unsigned int temp; + + temp = readl(priv->mmio + reg); + temp = (temp & ~(mask)) | val; + writel(temp, priv->mmio + reg); +} + +static int rockchip_combphy_param_write(struct regmap *base, + const struct combphy_reg *reg, bool en) +{ + u32 val, mask, tmp; + + tmp = en ? reg->enable : reg->disable; + mask = GENMASK(reg->bitend, reg->bitstart); + val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); + + return regmap_write(base, reg->offset, val); +} + +static u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv) +{ + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + u32 mask, val; + + mask = GENMASK(cfg->pipe_phy_status.bitend, + cfg->pipe_phy_status.bitstart); + + regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val); + val = (val & mask) >> cfg->pipe_phy_status.bitstart; + + return val; +} + +static int rockchip_combphy_init(struct phy *phy) +{ + struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + u32 val; + int ret; + + ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); + if (ret) { + dev_err(priv->dev, "failed to enable clks\n"); + return ret; + } + + switch (priv->type) { + case PHY_TYPE_PCIE: + case PHY_TYPE_USB3: + case PHY_TYPE_SATA: + case PHY_TYPE_SGMII: + case PHY_TYPE_QSGMII: + if (priv->cfg->combphy_cfg) + ret = priv->cfg->combphy_cfg(priv); + break; + default: + dev_err(priv->dev, "incompatible PHY type\n"); + ret = -EINVAL; + break; + } + + if (ret) { + dev_err(priv->dev, "failed to init phy for phy type %x\n", priv->type); + goto err_clk; + } + + ret = reset_control_deassert(priv->phy_rst); + if (ret) + goto err_clk; + + if (priv->type == PHY_TYPE_USB3) { + ret = readx_poll_timeout_atomic(rockchip_combphy_is_ready, + priv, val, + val == cfg->pipe_phy_status.enable, + 10, 1000); + if (ret) + dev_warn(priv->dev, "wait phy status ready timeout\n"); + } + + return 0; + +err_clk: + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + + return ret; +} + +static int rockchip_combphy_exit(struct phy *phy) +{ + struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + reset_control_assert(priv->phy_rst); + + return 0; +} + +static const struct phy_ops rochchip_combphy_ops = { + .init = rockchip_combphy_init, + .exit = rockchip_combphy_exit, + .owner = THIS_MODULE, +}; + +static struct phy *rockchip_combphy_xlate(struct device *dev, struct of_phandle_args *args) +{ + struct rockchip_combphy_priv *priv = dev_get_drvdata(dev); + + if (args->args_count != 1) { + dev_err(dev, "invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + if (priv->type != PHY_NONE && priv->type != args->args[0]) + dev_warn(dev, "phy type select %d overwriting type %d\n", + args->args[0], priv->type); + + priv->type = args->args[0]; + + return priv->phy; +} + +static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy_priv *priv) +{ + int i; + + priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); + if (priv->num_clks < 1) + return -EINVAL; + + priv->refclk = NULL; + for (i = 0; i < priv->num_clks; i++) { + if (!strncmp(priv->clks[i].id, "ref", 3)) { + priv->refclk = priv->clks[i].clk; + break; + } + } + + if (!priv->refclk) { + dev_err(dev, "no refclk found\n"); + return -EINVAL; + } + + priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf"); + if (IS_ERR(priv->pipe_grf)) { + dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n"); + return PTR_ERR(priv->pipe_grf); + } + + priv->phy_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-phy-grf"); + if (IS_ERR(priv->phy_grf)) { + dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n"); + return PTR_ERR(priv->phy_grf); + } + + priv->enable_ssc = device_property_present(dev, "rockchip,enable-ssc"); + + priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk"); + + priv->phy_rst = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(priv->phy_rst)) + return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n"); + + return 0; +} + +static int rockchip_combphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct rockchip_combphy_priv *priv; + const struct rockchip_combphy_cfg *phy_cfg; + struct resource *res; + int ret; + + phy_cfg = of_device_get_match_data(dev); + if (!phy_cfg) { + dev_err(dev, "no OF match data provided\n"); + return -EINVAL; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->mmio)) { + ret = PTR_ERR(priv->mmio); + return ret; + } + + priv->dev = dev; + priv->type = PHY_NONE; + priv->cfg = phy_cfg; + + ret = rockchip_combphy_parse_dt(dev, priv); + if (ret) + return ret; + + ret = reset_control_assert(priv->phy_rst); + if (ret) { + dev_err(dev, "failed to reset phy\n"); + return ret; + } + + priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create combphy\n"); + return PTR_ERR(priv->phy); + } + + dev_set_drvdata(dev, priv); + phy_set_drvdata(priv->phy, priv); + + phy_provider = devm_of_phy_provider_register(dev, rockchip_combphy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) +{ + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + unsigned long rate; + u32 val; + + switch (priv->type) { + case PHY_TYPE_PCIE: + /* Set SSC downward spread spectrum. */ + rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, + PHYREG32); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true); + break; + + case PHY_TYPE_USB3: + /* Set SSC downward spread spectrum. */ + rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, + PHYREG32); + + /* Enable adaptive CTLE for USB3.0 Rx. */ + val = readl(priv->mmio + PHYREG15); + val |= PHYREG15_CTLE_EN; + writel(val, priv->mmio + PHYREG15); + + /* Set PLL KVCO fine tuning signals. */ + rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, + PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, + PHYREG33); + + /* Enable controlling random jitter. */ + writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + + /* Set PLL input clock divider 1/2. */ + rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, + PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, + PHYREG6); + + writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); + rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true); + break; + + case PHY_TYPE_SATA: + /* Enable adaptive CTLE for SATA Rx. */ + val = readl(priv->mmio + PHYREG15); + val |= PHYREG15_CTLE_EN; + writel(val, priv->mmio + PHYREG15); + /* + * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. + * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) + */ + val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; + val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; + writel(val, priv->mmio + PHYREG7); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_sata, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_sata, true); + rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true); + break; + + case PHY_TYPE_SGMII: + rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->sgmii_mode_set, true); + break; + + case PHY_TYPE_QSGMII: + rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_rate_sel, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true); + break; + + default: + dev_err(priv->dev, "incompatible PHY type\n"); + return -EINVAL; + } + + rate = clk_get_rate(priv->refclk); + + switch (rate) { + case REF_CLOCK_24MHz: + if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) { + /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ + val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; + rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, + val, PHYREG15); + + writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + } + break; + + case REF_CLOCK_25MHz: + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true); + break; + + case REF_CLOCK_100MHz: + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); + if (priv->type == PHY_TYPE_PCIE) { + /* PLL KVCO fine tuning. */ + val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, + val, PHYREG33); + + /* Enable controlling random jitter. */ + writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + + val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT; + rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, + val, PHYREG6); + + writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + } else if (priv->type == PHY_TYPE_SATA) { + /* downward spread spectrum +500ppm */ + val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; + val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; + rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + } + break; + + default: + dev_err(priv->dev, "unsupported rate: %lu\n", rate); + return -EINVAL; + } + + if (priv->ext_refclk) { + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); + if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { + val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; + val |= PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13); + + val = readl(priv->mmio + PHYREG14); + val |= PHYREG14_CKRCV_AMP1; + writel(val, priv->mmio + PHYREG14); + } + } + + if (priv->enable_ssc) { + val = readl(priv->mmio + PHYREG8); + val |= PHYREG8_SSC_EN; + writel(val, priv->mmio + PHYREG8); + } + + return 0; +} + +static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = { + /* pipe-phy-grf */ + .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 }, + .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 }, + .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 }, + .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 }, + .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 }, + .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 }, + .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 }, + .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 }, + .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 }, + .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 }, + .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 }, + .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 }, + .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 }, + .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 }, + .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 }, + .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 }, + .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 }, + .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 }, + .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 }, + .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 }, + .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 }, + .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, + .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 }, + .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 }, + .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 }, + .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 }, + /* pipe-grf */ + .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 }, + .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 }, +}; + +static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = { + .grfcfg = &rk3568_combphy_grfcfgs, + .combphy_cfg = rk3568_combphy_cfg, +}; + +static const struct of_device_id rockchip_combphy_of_match[] = { + { + .compatible = "rockchip,rk3568-naneng-combphy", + .data = &rk3568_combphy_cfgs, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match); + +static struct platform_driver rockchip_combphy_driver = { + .probe = rockchip_combphy_probe, + .driver = { + .name = "rockchip-naneng-combphy", + .of_match_table = rockchip_combphy_of_match, + }, +}; +module_platform_driver(rockchip_combphy_driver); + +MODULE_DESCRIPTION("Rockchip NANENG COMBPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c index 15c1c79e5c29..a0cdbcadf09e 100644 --- a/drivers/phy/ti/phy-tusb1210.c +++ b/drivers/phy/ti/phy-tusb1210.c @@ -8,24 +8,93 @@ */ #include <linux/module.h> #include <linux/bitfield.h> +#include <linux/delay.h> #include <linux/ulpi/driver.h> #include <linux/ulpi/regs.h> #include <linux/gpio/consumer.h> #include <linux/phy/ulpi_phy.h> - -#define TUSB1210_VENDOR_SPECIFIC2 0x80 -#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0) -#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4) -#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6) +#include <linux/power_supply.h> +#include <linux/workqueue.h> + +#define TUSB1211_POWER_CONTROL 0x3d +#define TUSB1211_POWER_CONTROL_SET 0x3e +#define TUSB1211_POWER_CONTROL_CLEAR 0x3f +#define TUSB1211_POWER_CONTROL_SW_CONTROL BIT(0) +#define TUSB1211_POWER_CONTROL_DET_COMP BIT(1) +#define TUSB1211_POWER_CONTROL_DP_VSRC_EN BIT(6) + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0) +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4) +#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6) + +#define TUSB1211_VENDOR_SPECIFIC3 0x85 +#define TUSB1211_VENDOR_SPECIFIC3_SET 0x86 +#define TUSB1211_VENDOR_SPECIFIC3_CLEAR 0x87 +#define TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET BIT(4) +#define TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN BIT(6) + +#define TUSB1210_RESET_TIME_MS 50 + +#define TUSB1210_CHG_DET_MAX_RETRIES 5 + +/* TUSB1210 charger detection work states */ +enum tusb1210_chg_det_state { + TUSB1210_CHG_DET_CONNECTING, + TUSB1210_CHG_DET_START_DET, + TUSB1210_CHG_DET_READ_DET, + TUSB1210_CHG_DET_FINISH_DET, + TUSB1210_CHG_DET_CONNECTED, + TUSB1210_CHG_DET_DISCONNECTING, + TUSB1210_CHG_DET_DISCONNECTING_DONE, + TUSB1210_CHG_DET_DISCONNECTED, +}; struct tusb1210 { struct ulpi *ulpi; struct phy *phy; struct gpio_desc *gpio_reset; struct gpio_desc *gpio_cs; + u8 otg_ctrl; u8 vendor_specific2; +#ifdef CONFIG_POWER_SUPPLY + enum power_supply_usb_type chg_type; + enum tusb1210_chg_det_state chg_det_state; + int chg_det_retries; + struct delayed_work chg_det_work; + struct notifier_block psy_nb; + struct power_supply *psy; + struct power_supply *charger; +#endif }; +static int tusb1210_ulpi_write(struct tusb1210 *tusb, u8 reg, u8 val) +{ + int ret; + + ret = ulpi_write(tusb->ulpi, reg, val); + if (ret) + dev_err(&tusb->ulpi->dev, "error %d writing val 0x%02x to reg 0x%02x\n", + ret, val, reg); + + return ret; +} + +static int tusb1210_ulpi_read(struct tusb1210 *tusb, u8 reg, u8 *val) +{ + int ret; + + ret = ulpi_read(tusb->ulpi, reg); + if (ret >= 0) { + *val = ret; + ret = 0; + } else { + dev_err(&tusb->ulpi->dev, "error %d reading reg 0x%02x\n", ret, reg); + } + + return ret; +} + static int tusb1210_power_on(struct phy *phy) { struct tusb1210 *tusb = phy_get_drvdata(phy); @@ -33,12 +102,11 @@ static int tusb1210_power_on(struct phy *phy) gpiod_set_value_cansleep(tusb->gpio_reset, 1); gpiod_set_value_cansleep(tusb->gpio_cs, 1); - /* Restore the optional eye diagram optimization value */ - if (tusb->vendor_specific2) - ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, - tusb->vendor_specific2); + msleep(TUSB1210_RESET_TIME_MS); - return 0; + /* Restore the optional eye diagram optimization value */ + return tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); } static int tusb1210_power_off(struct phy *phy) @@ -55,35 +123,357 @@ static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct tusb1210 *tusb = phy_get_drvdata(phy); int ret; + u8 reg; - ret = ulpi_read(tusb->ulpi, ULPI_OTG_CTRL); + ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, ®); if (ret < 0) return ret; switch (mode) { case PHY_MODE_USB_HOST: - ret |= (ULPI_OTG_CTRL_DRVVBUS_EXT + reg |= (ULPI_OTG_CTRL_DRVVBUS_EXT | ULPI_OTG_CTRL_ID_PULLUP | ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN); - ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); - ret |= ULPI_OTG_CTRL_DRVVBUS; + tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); + reg |= ULPI_OTG_CTRL_DRVVBUS; break; case PHY_MODE_USB_DEVICE: - ret &= ~(ULPI_OTG_CTRL_DRVVBUS + reg &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN); - ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); - ret &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; + tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); + reg &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; break; default: /* nothing */ return 0; } - return ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); + tusb->otg_ctrl = reg; + return tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); } +#ifdef CONFIG_POWER_SUPPLY +const char * const tusb1210_chg_det_states[] = { + "CHG_DET_CONNECTING", + "CHG_DET_START_DET", + "CHG_DET_READ_DET", + "CHG_DET_FINISH_DET", + "CHG_DET_CONNECTED", + "CHG_DET_DISCONNECTING", + "CHG_DET_DISCONNECTING_DONE", + "CHG_DET_DISCONNECTED", +}; + +static void tusb1210_reset(struct tusb1210 *tusb) +{ + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + usleep_range(200, 500); + gpiod_set_value_cansleep(tusb->gpio_reset, 1); +} + +static void tusb1210_chg_det_set_type(struct tusb1210 *tusb, + enum power_supply_usb_type type) +{ + dev_dbg(&tusb->ulpi->dev, "charger type: %d\n", type); + tusb->chg_type = type; + tusb->chg_det_retries = 0; + power_supply_changed(tusb->psy); +} + +static void tusb1210_chg_det_set_state(struct tusb1210 *tusb, + enum tusb1210_chg_det_state new_state, + int delay_ms) +{ + if (delay_ms) + dev_dbg(&tusb->ulpi->dev, "chg_det new state %s in %d ms\n", + tusb1210_chg_det_states[new_state], delay_ms); + + tusb->chg_det_state = new_state; + mod_delayed_work(system_long_wq, &tusb->chg_det_work, + msecs_to_jiffies(delay_ms)); +} + +static void tusb1210_chg_det_handle_ulpi_error(struct tusb1210 *tusb) +{ + tusb1210_reset(tusb); + if (tusb->chg_det_retries < TUSB1210_CHG_DET_MAX_RETRIES) { + tusb->chg_det_retries++; + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, + TUSB1210_RESET_TIME_MS); + } else { + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, + TUSB1210_RESET_TIME_MS); + } +} + +/* + * Boards using a TUSB121x for charger-detection have 3 power_supply class devs: + * + * tusb1211-charger-detect(1) -> charger -> fuel-gauge + * + * To determine if an USB charger is connected to the board, the online prop of + * the charger psy needs to be read. Since the tusb1211-charger-detect psy is + * the start of the supplier -> supplied-to chain, power_supply_am_i_supplied() + * cannot be used here. + * + * Instead, below is a list of the power_supply names of known chargers for + * these boards and the charger psy is looked up by name from this list. + * + * (1) modelling the external USB charger + */ +static const char * const tusb1210_chargers[] = { + "bq24190-charger", +}; + +static bool tusb1210_get_online(struct tusb1210 *tusb) +{ + union power_supply_propval val; + int i; + + for (i = 0; i < ARRAY_SIZE(tusb1210_chargers) && !tusb->charger; i++) + tusb->charger = power_supply_get_by_name(tusb1210_chargers[i]); + + if (!tusb->charger) + return false; + + if (power_supply_get_property(tusb->charger, POWER_SUPPLY_PROP_ONLINE, &val)) + return false; + + return val.intval; +} + +static void tusb1210_chg_det_work(struct work_struct *work) +{ + struct tusb1210 *tusb = container_of(work, struct tusb1210, chg_det_work.work); + bool vbus_present = tusb1210_get_online(tusb); + int ret; + u8 val; + + dev_dbg(&tusb->ulpi->dev, "chg_det state %s vbus_present %d\n", + tusb1210_chg_det_states[tusb->chg_det_state], vbus_present); + + switch (tusb->chg_det_state) { + case TUSB1210_CHG_DET_CONNECTING: + tusb->chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + tusb->chg_det_retries = 0; + /* Power on USB controller for ulpi_read()/_write() */ + ret = pm_runtime_resume_and_get(tusb->ulpi->dev.parent); + if (ret < 0) { + dev_err(&tusb->ulpi->dev, "error %d runtime-resuming\n", ret); + /* Should never happen, skip charger detection */ + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0); + return; + } + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 0); + break; + case TUSB1210_CHG_DET_START_DET: + /* + * Use the builtin charger detection FSM to keep things simple. + * This only detects DCP / SDP. This is good enough for the few + * boards which actually rely on the phy for charger detection. + */ + mutex_lock(&tusb->phy->mutex); + ret = tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_SET, + TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET); + mutex_unlock(&tusb->phy->mutex); + if (ret) { + tusb1210_chg_det_handle_ulpi_error(tusb); + break; + } + + /* Wait 400 ms for the charger detection FSM to finish */ + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_READ_DET, 400); + break; + case TUSB1210_CHG_DET_READ_DET: + mutex_lock(&tusb->phy->mutex); + ret = tusb1210_ulpi_read(tusb, TUSB1211_POWER_CONTROL, &val); + mutex_unlock(&tusb->phy->mutex); + if (ret) { + tusb1210_chg_det_handle_ulpi_error(tusb); + break; + } + + if (val & TUSB1211_POWER_CONTROL_DET_COMP) + tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_DCP); + else + tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_SDP); + + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 0); + break; + case TUSB1210_CHG_DET_FINISH_DET: + mutex_lock(&tusb->phy->mutex); + + /* Set SW_CONTROL to stop the charger-det FSM */ + ret = tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_SET, + TUSB1211_POWER_CONTROL_SW_CONTROL); + + /* Clear DP_VSRC_EN which may have been enabled by the charger-det FSM */ + ret |= tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_CLEAR, + TUSB1211_POWER_CONTROL_DP_VSRC_EN); + + /* Clear CHGD_IDP_SRC_EN (may have been enabled by the charger-det FSM) */ + ret |= tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_CLEAR, + TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN); + + /* If any of the above fails reset the phy */ + if (ret) { + tusb1210_reset(tusb); + msleep(TUSB1210_RESET_TIME_MS); + } + + /* Restore phy-parameters and OTG_CTRL register */ + tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, tusb->otg_ctrl); + tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + mutex_unlock(&tusb->phy->mutex); + + pm_runtime_put(tusb->ulpi->dev.parent); + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0); + break; + case TUSB1210_CHG_DET_CONNECTED: + if (!vbus_present) + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING, 0); + break; + case TUSB1210_CHG_DET_DISCONNECTING: + /* + * The phy seems to take approx. 600ms longer then the charger + * chip (which is used to get vbus_present) to determine Vbus + * session end. Wait 800ms to ensure the phy has detected and + * signalled Vbus session end. + */ + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING_DONE, 800); + break; + case TUSB1210_CHG_DET_DISCONNECTING_DONE: + /* + * The phy often stops reacting to ulpi_read()/_write requests + * after a Vbus-session end. Reset it to work around this. + */ + tusb1210_reset(tusb); + tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_UNKNOWN); + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTED, 0); + break; + case TUSB1210_CHG_DET_DISCONNECTED: + if (vbus_present) + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTING, 0); + break; + } +} + +static int tusb1210_psy_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct tusb1210 *tusb = container_of(nb, struct tusb1210, psy_nb); + struct power_supply *psy = ptr; + + if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB) + queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0); + + return NOTIFY_OK; +} + +static int tusb1210_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct tusb1210 *tusb = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = tusb1210_get_online(tusb); + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = tusb->chg_type; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (tusb->chg_type == POWER_SUPPLY_USB_TYPE_DCP) + val->intval = 2000000; + else + val->intval = 500000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const enum power_supply_usb_type tusb1210_psy_usb_types[] = { + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_UNKNOWN, +}; + +static const enum power_supply_property tusb1210_psy_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static const struct power_supply_desc tusb1210_psy_desc = { + .name = "tusb1211-charger-detect", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = tusb1210_psy_usb_types, + .num_usb_types = ARRAY_SIZE(tusb1210_psy_usb_types), + .properties = tusb1210_psy_props, + .num_properties = ARRAY_SIZE(tusb1210_psy_props), + .get_property = tusb1210_psy_get_prop, +}; + +/* Setup charger detection if requested, on errors continue without chg-det */ +static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) +{ + struct power_supply_config psy_cfg = { .drv_data = tusb }; + struct device *dev = &tusb->ulpi->dev; + int ret; + + if (!device_property_read_bool(dev->parent, "linux,phy_charger_detect")) + return; + + if (tusb->ulpi->id.product != 0x1508) { + dev_err(dev, "error charger detection is only supported on the TUSB1211\n"); + return; + } + + ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &tusb->otg_ctrl); + if (ret) + return; + + tusb->psy = power_supply_register(dev, &tusb1210_psy_desc, &psy_cfg); + if (IS_ERR(tusb->psy)) + return; + + /* + * Delay initial run by 2 seconds to allow the charger driver, + * which is used to determine vbus_present, to load. + */ + tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED; + INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work); + queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ); + + tusb->psy_nb.notifier_call = tusb1210_psy_notifier; + power_supply_reg_notifier(&tusb->psy_nb); +} + +static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) +{ + + if (!IS_ERR_OR_NULL(tusb->psy)) { + power_supply_unreg_notifier(&tusb->psy_nb); + cancel_delayed_work_sync(&tusb->chg_det_work); + power_supply_unregister(tusb->psy); + } + + if (tusb->charger) + power_supply_put(tusb->charger); +} +#else +static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) { } +static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) { } +#endif + static const struct phy_ops phy_ops = { .power_on = tusb1210_power_on, .power_off = tusb1210_power_off, @@ -95,11 +485,14 @@ static int tusb1210_probe(struct ulpi *ulpi) { struct tusb1210 *tusb; u8 val, reg; + int ret; tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); if (!tusb) return -ENOMEM; + tusb->ulpi = ulpi; + tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(tusb->gpio_reset)) @@ -119,7 +512,9 @@ static int tusb1210_probe(struct ulpi *ulpi) * diagram optimization and DP/DM swap. */ - reg = ulpi_read(ulpi, TUSB1210_VENDOR_SPECIFIC2); + ret = tusb1210_ulpi_read(tusb, TUSB1210_VENDOR_SPECIFIC2, ®); + if (ret) + return ret; /* High speed output drive strength configuration */ if (!device_property_read_u8(&ulpi->dev, "ihstx", &val)) @@ -133,15 +528,18 @@ static int tusb1210_probe(struct ulpi *ulpi) if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val)) u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK); - ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); + ret = tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, reg); + if (ret) + return ret; + tusb->vendor_specific2 = reg; + tusb1210_probe_charger_detect(tusb); + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); if (IS_ERR(tusb->phy)) return PTR_ERR(tusb->phy); - tusb->ulpi = ulpi; - phy_set_drvdata(tusb->phy, tusb); ulpi_set_drvdata(ulpi, tusb); return 0; @@ -152,6 +550,7 @@ static void tusb1210_remove(struct ulpi *ulpi) struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); ulpi_phy_destroy(ulpi, tusb->phy); + tusb1210_remove_charger_detect(tusb); } #define TI_VENDOR_ID 0x0451 diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index b67539f9848c..7737d56191d7 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -896,11 +896,9 @@ static int goldfish_pipe_probe(struct platform_device *pdev) return -EINVAL; } - r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!r) - return -EINVAL; - - dev->irq = r->start; + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) + return dev->irq; /* * Exchange the versions with the host device diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 35799e6401c9..2f4b11b4dfcd 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -169,7 +169,7 @@ static int pps_gpio_probe(struct platform_device *pdev) /* GPIO setup */ ret = pps_gpio_setup(dev); if (ret) - return -EINVAL; + return ret; /* IRQ setup */ ret = gpiod_to_irq(data->gpio_pin); diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c index 6a1af7664f3b..b3e084b75c23 100644 --- a/drivers/pps/generators/pps_gen_parport.c +++ b/drivers/pps/generators/pps_gen_parport.c @@ -20,8 +20,6 @@ #include <linux/hrtimer.h> #include <linux/parport.h> -#define DRVDESC "parallel port PPS signal generator" - #define SIGNAL 0 #define NO_SIGNAL PARPORT_CONTROL_STROBE @@ -180,6 +178,11 @@ static void parport_attach(struct parport *port) { struct pardev_cb pps_cb; + if (send_delay > SEND_DELAY_MAX) { + pr_err("delay value should be not greater then %d\n", SEND_DELAY_MAX); + return; + } + if (attached) { /* we already have a port */ return; @@ -231,39 +234,8 @@ static struct parport_driver pps_gen_parport_driver = { .detach = parport_detach, .devmodel = true, }; - -/* module staff */ - -static int __init pps_gen_parport_init(void) -{ - int ret; - - pr_info(DRVDESC "\n"); - - if (send_delay > SEND_DELAY_MAX) { - pr_err("delay value should be not greater" - " then %d\n", SEND_DELAY_MAX); - return -EINVAL; - } - - ret = parport_register_driver(&pps_gen_parport_driver); - if (ret) { - pr_err("unable to register with parport\n"); - return ret; - } - - return 0; -} - -static void __exit pps_gen_parport_exit(void) -{ - parport_unregister_driver(&pps_gen_parport_driver); - pr_info("hrtimer avg error is %ldns\n", hrtimer_error); -} - -module_init(pps_gen_parport_init); -module_exit(pps_gen_parport_exit); +module_parport_driver(pps_gen_parport_driver); MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>"); -MODULE_DESCRIPTION(DRVDESC); +MODULE_DESCRIPTION("parallel port PPS signal generator"); MODULE_LICENSE("GPL"); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6f8ba0ddc05f..b496028b6bfa 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -256,6 +256,19 @@ config RESET_TI_SYSCON you wish to use the reset framework for such memory-mapped devices, say Y here. Otherwise, say N. +config RESET_TN48M_CPLD + tristate "Delta Networks TN48M switch CPLD reset controller" + depends on MFD_TN48M_CPLD || COMPILE_TEST + default MFD_TN48M_CPLD + help + This enables the reset controller driver for the Delta TN48M CPLD. + It provides reset signals for Armada 7040 and 385 SoC-s, Alleycat 3X + switch MAC-s, Alaska OOB ethernet PHY, Quad Alaska ethernet PHY-s and + Microchip PD69200 PoE PSE controller. + + This driver can also be built as a module. If so, the module will be + called reset-tn48m. + config RESET_UNIPHIER tristate "Reset controller driver for UniPhier SoCs" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index bd0a97be18b5..a80a9c4008a7 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o +obj-$(CONFIG_RESET_TN48M_CPLD) += reset-tn48m.o obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o diff --git a/drivers/reset/reset-tn48m.c b/drivers/reset/reset-tn48m.c new file mode 100644 index 000000000000..130027291b6e --- /dev/null +++ b/drivers/reset/reset-tn48m.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Delta TN48M CPLD reset driver + * + * Copyright (C) 2021 Sartura Ltd. + * + * Author: Robert Marko <robert.marko@sartura.hr> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/reset/delta,tn48m-reset.h> + +#define TN48M_RESET_REG 0x10 + +#define TN48M_RESET_TIMEOUT_US 125000 +#define TN48M_RESET_SLEEP_US 10 + +struct tn48_reset_map { + u8 bit; +}; + +struct tn48_reset_data { + struct reset_controller_dev rcdev; + struct regmap *regmap; +}; + +static const struct tn48_reset_map tn48m_resets[] = { + [CPU_88F7040_RESET] = {0}, + [CPU_88F6820_RESET] = {1}, + [MAC_98DX3265_RESET] = {2}, + [PHY_88E1680_RESET] = {4}, + [PHY_88E1512_RESET] = {6}, + [POE_RESET] = {7}, +}; + +static inline struct tn48_reset_data *to_tn48_reset_data( + struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct tn48_reset_data, rcdev); +} + +static int tn48m_control_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct tn48_reset_data *data = to_tn48_reset_data(rcdev); + unsigned int val; + + regmap_update_bits(data->regmap, TN48M_RESET_REG, + BIT(tn48m_resets[id].bit), 0); + + return regmap_read_poll_timeout(data->regmap, + TN48M_RESET_REG, + val, + val & BIT(tn48m_resets[id].bit), + TN48M_RESET_SLEEP_US, + TN48M_RESET_TIMEOUT_US); +} + +static int tn48m_control_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct tn48_reset_data *data = to_tn48_reset_data(rcdev); + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, TN48M_RESET_REG, ®val); + if (ret < 0) + return ret; + + if (BIT(tn48m_resets[id].bit) & regval) + return 0; + else + return 1; +} + +static const struct reset_control_ops tn48_reset_ops = { + .reset = tn48m_control_reset, + .status = tn48m_control_status, +}; + +static int tn48m_reset_probe(struct platform_device *pdev) +{ + struct tn48_reset_data *data; + struct regmap *regmap; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = regmap; + + data->rcdev.owner = THIS_MODULE; + data->rcdev.ops = &tn48_reset_ops; + data->rcdev.nr_resets = ARRAY_SIZE(tn48m_resets); + data->rcdev.of_node = pdev->dev.of_node; + + return devm_reset_controller_register(&pdev->dev, &data->rcdev); +} + +static const struct of_device_id tn48m_reset_of_match[] = { + { .compatible = "delta,tn48m-reset" }, + { } +}; +MODULE_DEVICE_TABLE(of, tn48m_reset_of_match); + +static struct platform_driver tn48m_reset_driver = { + .driver = { + .name = "delta-tn48m-reset", + .of_match_table = tn48m_reset_of_match, + }, + .probe = tn48m_reset_probe, +}; +module_platform_driver(tn48m_reset_driver); + +MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); +MODULE_DESCRIPTION("Delta TN48M CPLD reset driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 67369e941d0d..354d3f89366f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1749,8 +1749,11 @@ int sdw_handle_slave_status(struct sdw_bus *bus, continue; if (status[i] == SDW_SLAVE_UNATTACHED && - slave->status != SDW_SLAVE_UNATTACHED) + slave->status != SDW_SLAVE_UNATTACHED) { + dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", + i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + } } if (status[0] == SDW_SLAVE_ATTACHED) { @@ -1785,6 +1788,9 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (slave->status == SDW_SLAVE_UNATTACHED) break; + dev_warn(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n", + i, slave->status); + sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); break; diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 122f7a29d8ca..63101f1ba271 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -448,8 +448,8 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) /* Clear wake status */ wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); - wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); - intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); + wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); + intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts); } mutex_unlock(sdw->link_res->shim_lock); } diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d99807765dfe..824f4f32d4dc 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -180,7 +180,8 @@ static struct sdw_intel_ctx if (!res) return NULL; - if (acpi_bus_get_device(res->handle, &adev)) + adev = acpi_fetch_acpi_dev(res->handle); + if (!adev) return NULL; if (!res->count) @@ -294,13 +295,13 @@ err: static int sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) { - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(ctx->handle); struct sdw_intel_link_dev *ldev; u32 caps; u32 link_mask; int i; - if (acpi_bus_get_device(ctx->handle, &adev)) + if (!adev) return -EINVAL; /* Check SNDWLCAP.LCOUNT */ diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 54813417ef8e..da1ad7ebb1aa 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -11,8 +11,10 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_device.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/pm_wakeirq.h> #include <linux/slimbus.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> @@ -20,6 +22,9 @@ #include <sound/soc.h> #include "bus.h" +#define SWRM_COMP_SW_RESET 0x008 +#define SWRM_COMP_STATUS 0x014 +#define SWRM_FRM_GEN_ENABLED BIT(0) #define SWRM_COMP_HW_VERSION 0x00 #define SWRM_COMP_CFG_ADDR 0x04 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) @@ -29,6 +34,7 @@ #define SWRM_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(19, 15) #define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0) #define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) +#define SWRM_COMP_MASTER_ID 0x104 #define SWRM_INTERRUPT_STATUS 0x200 #define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0) #define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0) @@ -111,6 +117,13 @@ #define SWR_MAX_CMD_ID 14 #define MAX_FIFO_RD_RETRY 3 #define SWR_OVERFLOW_RETRY_COUNT 30 +#define SWRM_LINK_STATUS_RETRY_CNT 100 + +enum { + MASTER_ID_WSA = 1, + MASTER_ID_RX, + MASTER_ID_TX +}; struct qcom_swrm_port_config { u8 si; @@ -142,6 +155,7 @@ struct qcom_swrm_ctrl { u8 rd_cmd_id; int irq; unsigned int version; + int wake_irq; int num_din_ports; int num_dout_ports; int cols_index; @@ -159,6 +173,7 @@ struct qcom_swrm_ctrl { u32 slave_status; u32 wr_fifo_depth; u32 rd_fifo_depth; + bool clock_stop_not_supported; }; struct qcom_swrm_data { @@ -166,12 +181,12 @@ struct qcom_swrm_data { u32 default_rows; }; -static struct qcom_swrm_data swrm_v1_3_data = { +static const struct qcom_swrm_data swrm_v1_3_data = { .default_rows = 48, .default_cols = 16, }; -static struct qcom_swrm_data swrm_v1_5_data = { +static const struct qcom_swrm_data swrm_v1_5_data = { .default_rows = 50, .default_cols = 16, }; @@ -490,6 +505,30 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus) return 0; } +static irqreturn_t qcom_swrm_wake_irq_handler(int irq, void *dev_id) +{ + struct qcom_swrm_ctrl *swrm = dev_id; + int ret; + + ret = pm_runtime_get_sync(swrm->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(swrm->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(swrm->dev); + } + + if (swrm->wake_irq > 0) { + if (!irqd_irq_disabled(irq_get_irq_data(swrm->wake_irq))) + disable_irq_nosync(swrm->wake_irq); + } + + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + + return IRQ_HANDLED; +} + static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) { struct qcom_swrm_ctrl *swrm = dev_id; @@ -497,6 +536,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) u32 i; int devnum; int ret = IRQ_HANDLED; + clk_prepare_enable(swrm->hclk); swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); intr_sts_masked = intr_sts & swrm->intr_mask; @@ -604,6 +644,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) intr_sts_masked = intr_sts & swrm->intr_mask; } while (intr_sts_masked); + clk_disable_unprepare(swrm->hclk); return ret; } @@ -1017,6 +1058,15 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int ret, i; + ret = pm_runtime_get_sync(ctrl->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(ctrl->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(ctrl->dev); + return ret; + } + sruntime = sdw_alloc_stream(dai->name); if (!sruntime) return -ENOMEM; @@ -1044,6 +1094,9 @@ static void qcom_swrm_shutdown(struct snd_pcm_substream *substream, sdw_release_stream(ctrl->sruntime[dai->id]); ctrl->sruntime[dai->id] = NULL; + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + } static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = { @@ -1197,12 +1250,23 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) static int swrm_reg_show(struct seq_file *s_file, void *data) { struct qcom_swrm_ctrl *swrm = s_file->private; - int reg, reg_val; + int reg, reg_val, ret; + + ret = pm_runtime_get_sync(swrm->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(swrm->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(swrm->dev); + } for (reg = 0; reg <= SWR_MSTR_MAX_REG_ADDR; reg += 4) { swrm->reg_read(swrm, reg, ®_val); seq_printf(s_file, "0x%.3x: 0x%.2x\n", reg, reg_val); } + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + return 0; } @@ -1267,6 +1331,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) ctrl->bus.ops = &qcom_swrm_ops; ctrl->bus.port_ops = &qcom_swrm_port_ops; ctrl->bus.compute_params = &qcom_swrm_compute_params; + ctrl->bus.clk_stop_timeout = 300; ret = qcom_swrm_get_port_config(ctrl); if (ret) @@ -1301,6 +1366,18 @@ static int qcom_swrm_probe(struct platform_device *pdev) goto err_clk; } + ctrl->wake_irq = of_irq_get(dev->of_node, 1); + if (ctrl->wake_irq > 0) { + ret = devm_request_threaded_irq(dev, ctrl->wake_irq, NULL, + qcom_swrm_wake_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "swr_wake_irq", ctrl); + if (ret) { + dev_err(dev, "Failed to request soundwire wake irq\n"); + goto err_init; + } + } + ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode); if (ret) { dev_err(dev, "Failed to register Soundwire controller (%d)\n", @@ -1319,6 +1396,21 @@ static int qcom_swrm_probe(struct platform_device *pdev) (ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff, ctrl->version & 0xffff); + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* Clk stop is not supported on WSA Soundwire masters */ + if (ctrl->version <= 0x01030000) { + ctrl->clock_stop_not_supported = true; + } else { + ctrl->reg_read(ctrl, SWRM_COMP_MASTER_ID, &val); + if (val == MASTER_ID_WSA) + ctrl->clock_stop_not_supported = true; + } + #ifdef CONFIG_DEBUG_FS ctrl->debugfs = debugfs_create_dir("qualcomm-sdw", ctrl->bus.debugfs); debugfs_create_file("qualcomm-registers", 0400, ctrl->debugfs, ctrl, @@ -1345,6 +1437,115 @@ static int qcom_swrm_remove(struct platform_device *pdev) return 0; } +static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *swrm) +{ + int retry = SWRM_LINK_STATUS_RETRY_CNT; + int comp_sts; + + do { + swrm->reg_read(swrm, SWRM_COMP_STATUS, &comp_sts); + + if (comp_sts & SWRM_FRM_GEN_ENABLED) + return true; + + usleep_range(500, 510); + } while (retry--); + + dev_err(swrm->dev, "%s: link status not %s\n", __func__, + comp_sts && SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected"); + + return false; +} + +static int __maybe_unused swrm_runtime_resume(struct device *dev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); + int ret; + + if (ctrl->wake_irq > 0) { + if (!irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq))) + disable_irq_nosync(ctrl->wake_irq); + } + + clk_prepare_enable(ctrl->hclk); + + if (ctrl->clock_stop_not_supported) { + reinit_completion(&ctrl->enumeration); + ctrl->reg_write(ctrl, SWRM_COMP_SW_RESET, 0x01); + usleep_range(100, 105); + + qcom_swrm_init(ctrl); + + usleep_range(100, 105); + if (!swrm_wait_for_frame_gen_enabled(ctrl)) + dev_err(ctrl->dev, "link failed to connect\n"); + + /* wait for hw enumeration to complete */ + wait_for_completion_timeout(&ctrl->enumeration, + msecs_to_jiffies(TIMEOUT_MS)); + qcom_swrm_get_device_status(ctrl); + sdw_handle_slave_status(&ctrl->bus, ctrl->status); + } else { + ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR, + SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET); + + ctrl->intr_mask |= SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; + ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + + usleep_range(100, 105); + if (!swrm_wait_for_frame_gen_enabled(ctrl)) + dev_err(ctrl->dev, "link failed to connect\n"); + + ret = sdw_bus_exit_clk_stop(&ctrl->bus); + if (ret < 0) + dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret); + } + + return 0; +} + +static int __maybe_unused swrm_runtime_suspend(struct device *dev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); + int ret; + + if (!ctrl->clock_stop_not_supported) { + /* Mask bus clash interrupt */ + ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; + ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + /* Prepare slaves for clock stop */ + ret = sdw_bus_prep_clk_stop(&ctrl->bus); + if (ret < 0 && ret != -ENODATA) { + dev_err(dev, "prepare clock stop failed %d", ret); + return ret; + } + + ret = sdw_bus_clk_stop(&ctrl->bus); + if (ret < 0 && ret != -ENODATA) { + dev_err(dev, "bus clock stop failed %d", ret); + return ret; + } + } + + clk_disable_unprepare(ctrl->hclk); + + usleep_range(300, 305); + + if (ctrl->wake_irq > 0) { + if (irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq))) + enable_irq(ctrl->wake_irq); + } + + return 0; +} + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_RUNTIME_PM_OPS(swrm_runtime_suspend, swrm_runtime_resume, NULL) +}; + static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data }, { .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data }, @@ -1359,6 +1560,7 @@ static struct platform_driver qcom_swrm_driver = { .driver = { .name = "qcom-soundwire", .of_match_table = qcom_swrm_of_match, + .pm = &swrm_dev_pm_ops, } }; module_platform_driver(qcom_swrm_driver); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 980f26d49b66..f273459b2023 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -865,117 +865,177 @@ msg_unlock: return ret; } -/** - * sdw_release_stream() - Free the assigned stream runtime - * - * @stream: SoundWire stream runtime - * - * sdw_release_stream should be called only once per stream - */ -void sdw_release_stream(struct sdw_stream_runtime *stream) +static struct sdw_port_runtime *sdw_port_alloc(struct list_head *port_list) { - kfree(stream); + struct sdw_port_runtime *p_rt; + + p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); + if (!p_rt) + return NULL; + + list_add_tail(&p_rt->port_node, port_list); + + return p_rt; } -EXPORT_SYMBOL(sdw_release_stream); -/** - * sdw_alloc_stream() - Allocate and return stream runtime - * - * @stream_name: SoundWire stream name - * - * Allocates a SoundWire stream runtime instance. - * sdw_alloc_stream should be called only once per stream. Typically - * invoked from ALSA/ASoC machine/platform driver. - */ -struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) +static int sdw_port_config(struct sdw_port_runtime *p_rt, + struct sdw_port_config *port_config, + int port_index) { - struct sdw_stream_runtime *stream; + p_rt->ch_mask = port_config[port_index].ch_mask; + p_rt->num = port_config[port_index].num; - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) - return NULL; + /* + * TODO: Check port capabilities for requested configuration + */ - stream->name = stream_name; - INIT_LIST_HEAD(&stream->master_list); - stream->state = SDW_STREAM_ALLOCATED; - stream->m_rt_count = 0; + return 0; +} - return stream; +static void sdw_port_free(struct sdw_port_runtime *p_rt) +{ + list_del(&p_rt->port_node); + kfree(p_rt); } -EXPORT_SYMBOL(sdw_alloc_stream); -static struct sdw_master_runtime -*sdw_find_master_rt(struct sdw_bus *bus, - struct sdw_stream_runtime *stream) +static bool sdw_slave_port_allocated(struct sdw_slave_runtime *s_rt) +{ + return !list_empty(&s_rt->port_list); +} + +static void sdw_slave_port_free(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) { + struct sdw_port_runtime *p_rt, *_p_rt; struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; - /* Retrieve Bus handle if already available */ list_for_each_entry(m_rt, &stream->master_list, stream_node) { - if (m_rt->bus == bus) - return m_rt; + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + if (s_rt->slave != slave) + continue; + + list_for_each_entry_safe(p_rt, _p_rt, + &s_rt->port_list, port_node) { + sdw_port_free(p_rt); + } + } } +} - return NULL; +static int sdw_slave_port_alloc(struct sdw_slave *slave, + struct sdw_slave_runtime *s_rt, + unsigned int num_config) +{ + struct sdw_port_runtime *p_rt; + int i; + + /* Iterate for number of ports to perform initialization */ + for (i = 0; i < num_config; i++) { + p_rt = sdw_port_alloc(&s_rt->port_list); + if (!p_rt) + return -ENOMEM; + } + + return 0; } -/** - * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle - * - * @bus: SDW bus instance - * @stream_config: Stream configuration - * @stream: Stream runtime handle. - * - * This function is to be called with bus_lock held. - */ -static struct sdw_master_runtime -*sdw_alloc_master_rt(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, - struct sdw_stream_runtime *stream) +static int sdw_slave_port_is_valid_range(struct device *dev, int num) { - struct sdw_master_runtime *m_rt; + if (!SDW_VALID_PORT_RANGE(num)) { + dev_err(dev, "SoundWire: Invalid port number :%d\n", num); + return -EINVAL; + } - /* - * check if Master is already allocated (as a result of Slave adding - * it first), if so skip allocation and go to configure - */ - m_rt = sdw_find_master_rt(bus, stream); - if (m_rt) - goto stream_config; + return 0; +} - m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); - if (!m_rt) - return NULL; +static int sdw_slave_port_config(struct sdw_slave *slave, + struct sdw_slave_runtime *s_rt, + struct sdw_port_config *port_config) +{ + struct sdw_port_runtime *p_rt; + int ret; + int i; - /* Initialization of Master runtime handle */ - INIT_LIST_HEAD(&m_rt->port_list); - INIT_LIST_HEAD(&m_rt->slave_rt_list); - list_add_tail(&m_rt->stream_node, &stream->master_list); + i = 0; + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + /* + * TODO: Check valid port range as defined by DisCo/ + * slave + */ + ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num); + if (ret < 0) + return ret; - list_add_tail(&m_rt->bus_node, &bus->m_rt_list); + ret = sdw_port_config(p_rt, port_config, i); + if (ret < 0) + return ret; + i++; + } -stream_config: - m_rt->ch_count = stream_config->ch_count; - m_rt->bus = bus; - m_rt->stream = stream; - m_rt->direction = stream_config->direction; + return 0; +} - return m_rt; +static bool sdw_master_port_allocated(struct sdw_master_runtime *m_rt) +{ + return !list_empty(&m_rt->port_list); +} + +static void sdw_master_port_free(struct sdw_master_runtime *m_rt) +{ + struct sdw_port_runtime *p_rt, *_p_rt; + + list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) { + sdw_port_free(p_rt); + } +} + +static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt, + unsigned int num_ports) +{ + struct sdw_port_runtime *p_rt; + int i; + + /* Iterate for number of ports to perform initialization */ + for (i = 0; i < num_ports; i++) { + p_rt = sdw_port_alloc(&m_rt->port_list); + if (!p_rt) + return -ENOMEM; + } + + return 0; +} + +static int sdw_master_port_config(struct sdw_master_runtime *m_rt, + struct sdw_port_config *port_config) +{ + struct sdw_port_runtime *p_rt; + int ret; + int i; + + i = 0; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + ret = sdw_port_config(p_rt, port_config, i); + if (ret < 0) + return ret; + i++; + } + + return 0; } /** - * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. + * sdw_slave_rt_alloc() - Allocate a Slave runtime handle. * * @slave: Slave handle - * @stream_config: Stream configuration - * @stream: Stream runtime handle + * @m_rt: Master runtime handle * * This function is to be called with bus_lock held. */ static struct sdw_slave_runtime -*sdw_alloc_slave_rt(struct sdw_slave *slave, - struct sdw_stream_config *stream_config, - struct sdw_stream_runtime *stream) +*sdw_slave_rt_alloc(struct sdw_slave *slave, + struct sdw_master_runtime *m_rt) { struct sdw_slave_runtime *s_rt; @@ -984,154 +1044,156 @@ static struct sdw_slave_runtime return NULL; INIT_LIST_HEAD(&s_rt->port_list); - s_rt->ch_count = stream_config->ch_count; - s_rt->direction = stream_config->direction; s_rt->slave = slave; + list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); + return s_rt; } -static void sdw_master_port_release(struct sdw_bus *bus, - struct sdw_master_runtime *m_rt) +/** + * sdw_slave_rt_config() - Configure a Slave runtime handle. + * + * @s_rt: Slave runtime handle + * @stream_config: Stream configuration + * + * This function is to be called with bus_lock held. + */ +static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt, + struct sdw_stream_config *stream_config) { - struct sdw_port_runtime *p_rt, *_p_rt; + s_rt->ch_count = stream_config->ch_count; + s_rt->direction = stream_config->direction; - list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); - } + return 0; } -static void sdw_slave_port_release(struct sdw_bus *bus, - struct sdw_slave *slave, - struct sdw_stream_runtime *stream) +static struct sdw_slave_runtime *sdw_slave_rt_find(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) { - struct sdw_port_runtime *p_rt, *_p_rt; + struct sdw_slave_runtime *s_rt, *_s_rt; struct sdw_master_runtime *m_rt; - struct sdw_slave_runtime *s_rt; list_for_each_entry(m_rt, &stream->master_list, stream_node) { - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - if (s_rt->slave != slave) - continue; - - list_for_each_entry_safe(p_rt, _p_rt, - &s_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); - } + /* Retrieve Slave runtime handle */ + list_for_each_entry_safe(s_rt, _s_rt, + &m_rt->slave_rt_list, m_rt_node) { + if (s_rt->slave == slave) + return s_rt; } } + return NULL; } /** - * sdw_release_slave_stream() - Free Slave(s) runtime handle + * sdw_slave_rt_free() - Free Slave(s) runtime handle * * @slave: Slave handle. * @stream: Stream runtime handle. * * This function is to be called with bus_lock held. */ -static void sdw_release_slave_stream(struct sdw_slave *slave, - struct sdw_stream_runtime *stream) +static void sdw_slave_rt_free(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) +{ + struct sdw_slave_runtime *s_rt; + + s_rt = sdw_slave_rt_find(slave, stream); + if (s_rt) { + list_del(&s_rt->m_rt_node); + kfree(s_rt); + } +} + +static struct sdw_master_runtime +*sdw_master_rt_find(struct sdw_bus *bus, + struct sdw_stream_runtime *stream) { - struct sdw_slave_runtime *s_rt, *_s_rt; struct sdw_master_runtime *m_rt; + /* Retrieve Bus handle if already available */ list_for_each_entry(m_rt, &stream->master_list, stream_node) { - /* Retrieve Slave runtime handle */ - list_for_each_entry_safe(s_rt, _s_rt, - &m_rt->slave_rt_list, m_rt_node) { - if (s_rt->slave == slave) { - list_del(&s_rt->m_rt_node); - kfree(s_rt); - return; - } - } + if (m_rt->bus == bus) + return m_rt; } + + return NULL; } /** - * sdw_release_master_stream() - Free Master runtime handle + * sdw_master_rt_alloc() - Allocates a Master runtime handle * - * @m_rt: Master runtime node + * @bus: SDW bus instance * @stream: Stream runtime handle. * - * This function is to be called with bus_lock held - * It frees the Master runtime handle and associated Slave(s) runtime - * handle. If this is called first then sdw_release_slave_stream() will have - * no effect as Slave(s) runtime handle would already be freed up. + * This function is to be called with bus_lock held. */ -static void sdw_release_master_stream(struct sdw_master_runtime *m_rt, - struct sdw_stream_runtime *stream) +static struct sdw_master_runtime +*sdw_master_rt_alloc(struct sdw_bus *bus, + struct sdw_stream_runtime *stream) { - struct sdw_slave_runtime *s_rt, *_s_rt; + struct sdw_master_runtime *m_rt; - list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) { - sdw_slave_port_release(s_rt->slave->bus, s_rt->slave, stream); - sdw_release_slave_stream(s_rt->slave, stream); - } + m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); + if (!m_rt) + return NULL; - list_del(&m_rt->stream_node); - list_del(&m_rt->bus_node); - kfree(m_rt); + /* Initialization of Master runtime handle */ + INIT_LIST_HEAD(&m_rt->port_list); + INIT_LIST_HEAD(&m_rt->slave_rt_list); + list_add_tail(&m_rt->stream_node, &stream->master_list); + + list_add_tail(&m_rt->bus_node, &bus->m_rt_list); + + m_rt->bus = bus; + m_rt->stream = stream; + + return m_rt; } /** - * sdw_stream_remove_master() - Remove master from sdw_stream + * sdw_master_rt_config() - Configure Master runtime handle * - * @bus: SDW Bus instance - * @stream: SoundWire stream + * @m_rt: Master runtime handle + * @stream_config: Stream configuration * - * This removes and frees port_rt and master_rt from a stream + * This function is to be called with bus_lock held. */ -int sdw_stream_remove_master(struct sdw_bus *bus, - struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt, *_m_rt; - - mutex_lock(&bus->bus_lock); - - list_for_each_entry_safe(m_rt, _m_rt, - &stream->master_list, stream_node) { - if (m_rt->bus != bus) - continue; - - sdw_master_port_release(bus, m_rt); - sdw_release_master_stream(m_rt, stream); - stream->m_rt_count--; - } - - if (list_empty(&stream->master_list)) - stream->state = SDW_STREAM_RELEASED; - mutex_unlock(&bus->bus_lock); +static int sdw_master_rt_config(struct sdw_master_runtime *m_rt, + struct sdw_stream_config *stream_config) +{ + m_rt->ch_count = stream_config->ch_count; + m_rt->direction = stream_config->direction; return 0; } -EXPORT_SYMBOL(sdw_stream_remove_master); /** - * sdw_stream_remove_slave() - Remove slave from sdw_stream + * sdw_master_rt_free() - Free Master runtime handle * - * @slave: SDW Slave instance - * @stream: SoundWire stream + * @m_rt: Master runtime node + * @stream: Stream runtime handle. * - * This removes and frees port_rt and slave_rt from a stream + * This function is to be called with bus_lock held + * It frees the Master runtime handle and associated Slave(s) runtime + * handle. If this is called first then sdw_slave_rt_free() will have + * no effect as Slave(s) runtime handle would already be freed up. */ -int sdw_stream_remove_slave(struct sdw_slave *slave, - struct sdw_stream_runtime *stream) +static void sdw_master_rt_free(struct sdw_master_runtime *m_rt, + struct sdw_stream_runtime *stream) { - mutex_lock(&slave->bus->bus_lock); - - sdw_slave_port_release(slave->bus, slave, stream); - sdw_release_slave_stream(slave, stream); + struct sdw_slave_runtime *s_rt, *_s_rt; - mutex_unlock(&slave->bus->bus_lock); + list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) { + sdw_slave_port_free(s_rt->slave, stream); + sdw_slave_rt_free(s_rt->slave, stream); + } - return 0; + list_del(&m_rt->stream_node); + list_del(&m_rt->bus_node); + kfree(m_rt); } -EXPORT_SYMBOL(sdw_stream_remove_slave); /** * sdw_config_stream() - Configure the allocated stream @@ -1179,242 +1241,6 @@ static int sdw_config_stream(struct device *dev, return 0; } -static int sdw_is_valid_port_range(struct device *dev, - struct sdw_port_runtime *p_rt) -{ - if (!SDW_VALID_PORT_RANGE(p_rt->num)) { - dev_err(dev, - "SoundWire: Invalid port number :%d\n", p_rt->num); - return -EINVAL; - } - - return 0; -} - -static struct sdw_port_runtime -*sdw_port_alloc(struct device *dev, - struct sdw_port_config *port_config, - int port_index) -{ - struct sdw_port_runtime *p_rt; - - p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); - if (!p_rt) - return NULL; - - p_rt->ch_mask = port_config[port_index].ch_mask; - p_rt->num = port_config[port_index].num; - - return p_rt; -} - -static int sdw_master_port_config(struct sdw_bus *bus, - struct sdw_master_runtime *m_rt, - struct sdw_port_config *port_config, - unsigned int num_ports) -{ - struct sdw_port_runtime *p_rt; - int i; - - /* Iterate for number of ports to perform initialization */ - for (i = 0; i < num_ports; i++) { - p_rt = sdw_port_alloc(bus->dev, port_config, i); - if (!p_rt) - return -ENOMEM; - - /* - * TODO: Check port capabilities for requested - * configuration (audio mode support) - */ - - list_add_tail(&p_rt->port_node, &m_rt->port_list); - } - - return 0; -} - -static int sdw_slave_port_config(struct sdw_slave *slave, - struct sdw_slave_runtime *s_rt, - struct sdw_port_config *port_config, - unsigned int num_config) -{ - struct sdw_port_runtime *p_rt; - int i, ret; - - /* Iterate for number of ports to perform initialization */ - for (i = 0; i < num_config; i++) { - p_rt = sdw_port_alloc(&slave->dev, port_config, i); - if (!p_rt) - return -ENOMEM; - - /* - * TODO: Check valid port range as defined by DisCo/ - * slave - */ - ret = sdw_is_valid_port_range(&slave->dev, p_rt); - if (ret < 0) { - kfree(p_rt); - return ret; - } - - /* - * TODO: Check port capabilities for requested - * configuration (audio mode support) - */ - - list_add_tail(&p_rt->port_node, &s_rt->port_list); - } - - return 0; -} - -/** - * sdw_stream_add_master() - Allocate and add master runtime to a stream - * - * @bus: SDW Bus instance - * @stream_config: Stream configuration for audio stream - * @port_config: Port configuration for audio stream - * @num_ports: Number of ports - * @stream: SoundWire stream - */ -int sdw_stream_add_master(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt; - int ret; - - mutex_lock(&bus->bus_lock); - - /* - * For multi link streams, add the second master only if - * the bus supports it. - * Check if bus->multi_link is set - */ - if (!bus->multi_link && stream->m_rt_count > 0) { - dev_err(bus->dev, - "Multilink not supported, link %d\n", bus->link_id); - ret = -EINVAL; - goto unlock; - } - - m_rt = sdw_alloc_master_rt(bus, stream_config, stream); - if (!m_rt) { - dev_err(bus->dev, - "Master runtime config failed for stream:%s\n", - stream->name); - ret = -ENOMEM; - goto unlock; - } - - ret = sdw_config_stream(bus->dev, stream, stream_config, false); - if (ret) - goto stream_error; - - ret = sdw_master_port_config(bus, m_rt, port_config, num_ports); - if (ret) - goto stream_error; - - stream->m_rt_count++; - - goto unlock; - -stream_error: - sdw_release_master_stream(m_rt, stream); -unlock: - mutex_unlock(&bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_stream_add_master); - -/** - * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream - * - * @slave: SDW Slave instance - * @stream_config: Stream configuration for audio stream - * @stream: SoundWire stream - * @port_config: Port configuration for audio stream - * @num_ports: Number of ports - * - * It is expected that Slave is added before adding Master - * to the Stream. - * - */ -int sdw_stream_add_slave(struct sdw_slave *slave, - struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream) -{ - struct sdw_slave_runtime *s_rt; - struct sdw_master_runtime *m_rt; - int ret; - - mutex_lock(&slave->bus->bus_lock); - - /* - * If this API is invoked by Slave first then m_rt is not valid. - * So, allocate m_rt and add Slave to it. - */ - m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream); - if (!m_rt) { - dev_err(&slave->dev, - "alloc master runtime failed for stream:%s\n", - stream->name); - ret = -ENOMEM; - goto error; - } - - s_rt = sdw_alloc_slave_rt(slave, stream_config, stream); - if (!s_rt) { - dev_err(&slave->dev, - "Slave runtime config failed for stream:%s\n", - stream->name); - ret = -ENOMEM; - goto stream_error; - } - - ret = sdw_config_stream(&slave->dev, stream, stream_config, true); - if (ret) { - /* - * sdw_release_master_stream will release s_rt in slave_rt_list in - * stream_error case, but s_rt is only added to slave_rt_list - * when sdw_config_stream is successful, so free s_rt explicitly - * when sdw_config_stream is failed. - */ - kfree(s_rt); - goto stream_error; - } - - list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); - - ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports); - if (ret) - goto stream_error; - - /* - * Change stream state to CONFIGURED on first Slave add. - * Bus is not aware of number of Slave(s) in a stream at this - * point so cannot depend on all Slave(s) to be added in order to - * change stream state to CONFIGURED. - */ - stream->state = SDW_STREAM_CONFIGURED; - goto error; - -stream_error: - /* - * we hit error so cleanup the stream, release all Slave(s) and - * Master runtime - */ - sdw_release_master_stream(m_rt, stream); -error: - mutex_unlock(&slave->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_stream_add_slave); - /** * sdw_get_slave_dpn_prop() - Get Slave port capabilities * @@ -1679,6 +1505,11 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_ENABLED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { pr_err("%s: %s: inconsistent state state %d\n", @@ -1762,6 +1593,11 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_DISABLED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_ENABLED) { pr_err("%s: %s: inconsistent state state %d\n", __func__, stream->name, stream->state); @@ -1837,6 +1673,11 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_DEPREPARED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { pr_err("%s: %s: inconsistent state state %d\n", @@ -1874,6 +1715,32 @@ static int set_stream(struct snd_pcm_substream *substream, } /** + * sdw_alloc_stream() - Allocate and return stream runtime + * + * @stream_name: SoundWire stream name + * + * Allocates a SoundWire stream runtime instance. + * sdw_alloc_stream should be called only once per stream. Typically + * invoked from ALSA/ASoC machine/platform driver. + */ +struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) +{ + struct sdw_stream_runtime *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return NULL; + + stream->name = stream_name; + INIT_LIST_HEAD(&stream->master_list); + stream->state = SDW_STREAM_ALLOCATED; + stream->m_rt_count = 0; + + return stream; +} +EXPORT_SYMBOL(sdw_alloc_stream); + +/** * sdw_startup_stream() - Startup SoundWire stream * * @sdw_substream: Soundwire stream @@ -1949,3 +1816,270 @@ void sdw_shutdown_stream(void *sdw_substream) set_stream(substream, NULL); } EXPORT_SYMBOL(sdw_shutdown_stream); + +/** + * sdw_release_stream() - Free the assigned stream runtime + * + * @stream: SoundWire stream runtime + * + * sdw_release_stream should be called only once per stream + */ +void sdw_release_stream(struct sdw_stream_runtime *stream) +{ + kfree(stream); +} +EXPORT_SYMBOL(sdw_release_stream); + +/** + * sdw_stream_add_master() - Allocate and add master runtime to a stream + * + * @bus: SDW Bus instance + * @stream_config: Stream configuration for audio stream + * @port_config: Port configuration for audio stream + * @num_ports: Number of ports + * @stream: SoundWire stream + */ +int sdw_stream_add_master(struct sdw_bus *bus, + struct sdw_stream_config *stream_config, + struct sdw_port_config *port_config, + unsigned int num_ports, + struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt; + bool alloc_master_rt = true; + int ret; + + mutex_lock(&bus->bus_lock); + + /* + * For multi link streams, add the second master only if + * the bus supports it. + * Check if bus->multi_link is set + */ + if (!bus->multi_link && stream->m_rt_count > 0) { + dev_err(bus->dev, + "Multilink not supported, link %d\n", bus->link_id); + ret = -EINVAL; + goto unlock; + } + + /* + * check if Master is already allocated (e.g. as a result of Slave adding + * it first), if so skip allocation and go to configuration + */ + m_rt = sdw_master_rt_find(bus, stream); + if (m_rt) { + alloc_master_rt = false; + goto skip_alloc_master_rt; + } + + m_rt = sdw_master_rt_alloc(bus, stream); + if (!m_rt) { + dev_err(bus->dev, "Master runtime alloc failed for stream:%s\n", stream->name); + ret = -ENOMEM; + goto unlock; + } +skip_alloc_master_rt: + + if (sdw_master_port_allocated(m_rt)) + goto skip_alloc_master_port; + + ret = sdw_master_port_alloc(m_rt, num_ports); + if (ret) + goto alloc_error; + + stream->m_rt_count++; + +skip_alloc_master_port: + + ret = sdw_master_rt_config(m_rt, stream_config); + if (ret < 0) + goto unlock; + + ret = sdw_config_stream(bus->dev, stream, stream_config, false); + if (ret) + goto unlock; + + ret = sdw_master_port_config(m_rt, port_config); + + goto unlock; + +alloc_error: + /* + * we only cleanup what was allocated in this routine + */ + if (alloc_master_rt) + sdw_master_rt_free(m_rt, stream); +unlock: + mutex_unlock(&bus->bus_lock); + return ret; +} +EXPORT_SYMBOL(sdw_stream_add_master); + +/** + * sdw_stream_remove_master() - Remove master from sdw_stream + * + * @bus: SDW Bus instance + * @stream: SoundWire stream + * + * This removes and frees port_rt and master_rt from a stream + */ +int sdw_stream_remove_master(struct sdw_bus *bus, + struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt, *_m_rt; + + mutex_lock(&bus->bus_lock); + + list_for_each_entry_safe(m_rt, _m_rt, + &stream->master_list, stream_node) { + if (m_rt->bus != bus) + continue; + + sdw_master_port_free(m_rt); + sdw_master_rt_free(m_rt, stream); + stream->m_rt_count--; + } + + if (list_empty(&stream->master_list)) + stream->state = SDW_STREAM_RELEASED; + + mutex_unlock(&bus->bus_lock); + + return 0; +} +EXPORT_SYMBOL(sdw_stream_remove_master); + +/** + * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream + * + * @slave: SDW Slave instance + * @stream_config: Stream configuration for audio stream + * @stream: SoundWire stream + * @port_config: Port configuration for audio stream + * @num_ports: Number of ports + * + * It is expected that Slave is added before adding Master + * to the Stream. + * + */ +int sdw_stream_add_slave(struct sdw_slave *slave, + struct sdw_stream_config *stream_config, + struct sdw_port_config *port_config, + unsigned int num_ports, + struct sdw_stream_runtime *stream) +{ + struct sdw_slave_runtime *s_rt; + struct sdw_master_runtime *m_rt; + bool alloc_master_rt = true; + bool alloc_slave_rt = true; + + int ret; + + mutex_lock(&slave->bus->bus_lock); + + /* + * check if Master is already allocated, if so skip allocation + * and go to configuration + */ + m_rt = sdw_master_rt_find(slave->bus, stream); + if (m_rt) { + alloc_master_rt = false; + goto skip_alloc_master_rt; + } + + /* + * If this API is invoked by Slave first then m_rt is not valid. + * So, allocate m_rt and add Slave to it. + */ + m_rt = sdw_master_rt_alloc(slave->bus, stream); + if (!m_rt) { + dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name); + ret = -ENOMEM; + goto unlock; + } + +skip_alloc_master_rt: + s_rt = sdw_slave_rt_find(slave, stream); + if (s_rt) + goto skip_alloc_slave_rt; + + s_rt = sdw_slave_rt_alloc(slave, m_rt); + if (!s_rt) { + dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); + alloc_slave_rt = false; + ret = -ENOMEM; + goto alloc_error; + } + +skip_alloc_slave_rt: + if (sdw_slave_port_allocated(s_rt)) + goto skip_port_alloc; + + ret = sdw_slave_port_alloc(slave, s_rt, num_ports); + if (ret) + goto alloc_error; + +skip_port_alloc: + ret = sdw_master_rt_config(m_rt, stream_config); + if (ret) + goto unlock; + + ret = sdw_slave_rt_config(s_rt, stream_config); + if (ret) + goto unlock; + + ret = sdw_config_stream(&slave->dev, stream, stream_config, true); + if (ret) + goto unlock; + + ret = sdw_slave_port_config(slave, s_rt, port_config); + if (ret) + goto unlock; + + /* + * Change stream state to CONFIGURED on first Slave add. + * Bus is not aware of number of Slave(s) in a stream at this + * point so cannot depend on all Slave(s) to be added in order to + * change stream state to CONFIGURED. + */ + stream->state = SDW_STREAM_CONFIGURED; + goto unlock; + +alloc_error: + /* + * we only cleanup what was allocated in this routine. The 'else if' + * is intentional, the 'master_rt_free' will call sdw_slave_rt_free() + * internally. + */ + if (alloc_master_rt) + sdw_master_rt_free(m_rt, stream); + else if (alloc_slave_rt) + sdw_slave_rt_free(slave, stream); +unlock: + mutex_unlock(&slave->bus->bus_lock); + return ret; +} +EXPORT_SYMBOL(sdw_stream_add_slave); + +/** + * sdw_stream_remove_slave() - Remove slave from sdw_stream + * + * @slave: SDW Slave instance + * @stream: SoundWire stream + * + * This removes and frees port_rt and slave_rt from a stream + */ +int sdw_stream_remove_slave(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) +{ + mutex_lock(&slave->bus->bus_lock); + + sdw_slave_port_free(slave, stream); + sdw_slave_rt_free(slave, stream); + + mutex_unlock(&slave->bus->bus_lock); + + return 0; +} +EXPORT_SYMBOL(sdw_stream_remove_slave); diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index 1d3026dae827..62d5397ff1f9 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -312,3 +312,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16203"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 2a8aa83b8d9e..bca857eef92e 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -440,3 +440,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16240"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index b25f41053fac..2f0d6cf048d2 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -15,15 +15,4 @@ config AD7816 To compile this driver as a module, choose M here: the module will be called ad7816. -config AD7280 - tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System" - depends on SPI - select CRC8 - help - Say yes here to build support for Analog Devices AD7280A - Lithium Ion Battery Monitoring System. - - To compile this driver as a module, choose M here: the - module will be called ad7280a - endmenu diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 6436a62b6278..1e2a94c4db84 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -4,4 +4,3 @@ # obj-$(CONFIG_AD7816) += ad7816.o -obj-$(CONFIG_AD7280) += ad7280a.o diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c deleted file mode 100644 index fef0055b8990..000000000000 --- a/drivers/staging/iio/adc/ad7280a.c +++ /dev/null @@ -1,1044 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * AD7280A Lithium Ion Battery Monitoring System - * - * Copyright 2011 Analog Devices Inc. - */ - -#include <linux/crc8.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/spi/spi.h> -#include <linux/err.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/module.h> - -#include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> -#include <linux/iio/events.h> - -#include "ad7280a.h" - -/* Registers */ -#define AD7280A_CELL_VOLTAGE_1 0x0 /* D11 to D0, Read only */ -#define AD7280A_CELL_VOLTAGE_2 0x1 /* D11 to D0, Read only */ -#define AD7280A_CELL_VOLTAGE_3 0x2 /* D11 to D0, Read only */ -#define AD7280A_CELL_VOLTAGE_4 0x3 /* D11 to D0, Read only */ -#define AD7280A_CELL_VOLTAGE_5 0x4 /* D11 to D0, Read only */ -#define AD7280A_CELL_VOLTAGE_6 0x5 /* D11 to D0, Read only */ -#define AD7280A_AUX_ADC_1 0x6 /* D11 to D0, Read only */ -#define AD7280A_AUX_ADC_2 0x7 /* D11 to D0, Read only */ -#define AD7280A_AUX_ADC_3 0x8 /* D11 to D0, Read only */ -#define AD7280A_AUX_ADC_4 0x9 /* D11 to D0, Read only */ -#define AD7280A_AUX_ADC_5 0xA /* D11 to D0, Read only */ -#define AD7280A_AUX_ADC_6 0xB /* D11 to D0, Read only */ -#define AD7280A_SELF_TEST 0xC /* D11 to D0, Read only */ -#define AD7280A_CONTROL_HB 0xD /* D15 to D8, Read/write */ -#define AD7280A_CONTROL_LB 0xE /* D7 to D0, Read/write */ -#define AD7280A_CELL_OVERVOLTAGE 0xF /* D7 to D0, Read/write */ -#define AD7280A_CELL_UNDERVOLTAGE 0x10 /* D7 to D0, Read/write */ -#define AD7280A_AUX_ADC_OVERVOLTAGE 0x11 /* D7 to D0, Read/write */ -#define AD7280A_AUX_ADC_UNDERVOLTAGE 0x12 /* D7 to D0, Read/write */ -#define AD7280A_ALERT 0x13 /* D7 to D0, Read/write */ -#define AD7280A_CELL_BALANCE 0x14 /* D7 to D0, Read/write */ -#define AD7280A_CB1_TIMER 0x15 /* D7 to D0, Read/write */ -#define AD7280A_CB2_TIMER 0x16 /* D7 to D0, Read/write */ -#define AD7280A_CB3_TIMER 0x17 /* D7 to D0, Read/write */ -#define AD7280A_CB4_TIMER 0x18 /* D7 to D0, Read/write */ -#define AD7280A_CB5_TIMER 0x19 /* D7 to D0, Read/write */ -#define AD7280A_CB6_TIMER 0x1A /* D7 to D0, Read/write */ -#define AD7280A_PD_TIMER 0x1B /* D7 to D0, Read/write */ -#define AD7280A_READ 0x1C /* D7 to D0, Read/write */ -#define AD7280A_CNVST_CONTROL 0x1D /* D7 to D0, Read/write */ - -/* Bits and Masks */ -#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0 -#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_4 BIT(6) -#define AD7280A_CTRL_HB_CONV_INPUT_6CELL BIT(7) -#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST (BIT(7) | BIT(6)) -#define AD7280A_CTRL_HB_CONV_RES_READ_ALL 0 -#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL_AUX1_3_4 BIT(4) -#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL BIT(5) -#define AD7280A_CTRL_HB_CONV_RES_READ_NO (BIT(5) | BIT(4)) -#define AD7280A_CTRL_HB_CONV_START_CNVST 0 -#define AD7280A_CTRL_HB_CONV_START_CS BIT(3) -#define AD7280A_CTRL_HB_CONV_AVG_DIS 0 -#define AD7280A_CTRL_HB_CONV_AVG_2 BIT(1) -#define AD7280A_CTRL_HB_CONV_AVG_4 BIT(2) -#define AD7280A_CTRL_HB_CONV_AVG_8 (BIT(2) | BIT(1)) -#define AD7280A_CTRL_HB_CONV_AVG(x) ((x) << 1) -#define AD7280A_CTRL_HB_PWRDN_SW BIT(0) - -#define AD7280A_CTRL_LB_SWRST BIT(7) -#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0 -#define AD7280A_CTRL_LB_ACQ_TIME_800ns BIT(5) -#define AD7280A_CTRL_LB_ACQ_TIME_1200ns BIT(6) -#define AD7280A_CTRL_LB_ACQ_TIME_1600ns (BIT(6) | BIT(5)) -#define AD7280A_CTRL_LB_ACQ_TIME(x) ((x) << 5) -#define AD7280A_CTRL_LB_MUST_SET BIT(4) -#define AD7280A_CTRL_LB_THERMISTOR_EN BIT(3) -#define AD7280A_CTRL_LB_LOCK_DEV_ADDR BIT(2) -#define AD7280A_CTRL_LB_INC_DEV_ADDR BIT(1) -#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN BIT(0) - -#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6) -#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6)) - -#define AD7280A_ALL_CELLS (0xAD << 16) - -#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */ -#define AD7280A_MAX_CHAIN 8 -#define AD7280A_CELLS_PER_DEV 6 -#define AD7280A_BITS 12 -#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \ - AD7280A_CELL_VOLTAGE_1 + 1) - -#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \ - (c)) -#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \ - (c) - AD7280A_CELLS_PER_DEV) - -#define AD7280A_DEVADDR_MASTER 0 -#define AD7280A_DEVADDR_ALL 0x1F -/* 5-bit device address is sent LSB first */ -static unsigned int ad7280a_devaddr(unsigned int addr) -{ - return ((addr & 0x1) << 4) | - ((addr & 0x2) << 3) | - (addr & 0x4) | - ((addr & 0x8) >> 3) | - ((addr & 0x10) >> 4); -} - -/* During a read a valid write is mandatory. - * So writing to the highest available address (Address 0x1F) - * and setting the address all parts bit to 0 is recommended - * So the TXVAL is AD7280A_DEVADDR_ALL + CRC - */ -#define AD7280A_READ_TXVAL 0xF800030A - -/* - * AD7280 CRC - * - * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F - */ -#define POLYNOM 0x2F - -struct ad7280_state { - struct spi_device *spi; - struct iio_chan_spec *channels; - struct iio_dev_attr *iio_attr; - int slave_num; - int scan_cnt; - int readback_delay_us; - unsigned char crc_tab[CRC8_TABLE_SIZE]; - unsigned char ctrl_hb; - unsigned char ctrl_lb; - unsigned char cell_threshhigh; - unsigned char cell_threshlow; - unsigned char aux_threshhigh; - unsigned char aux_threshlow; - unsigned char cb_mask[AD7280A_MAX_CHAIN]; - struct mutex lock; /* protect sensor state */ - - __be32 buf[2] ____cacheline_aligned; -}; - -static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val) -{ - unsigned char crc; - - crc = crc_tab[val >> 16 & 0xFF]; - crc = crc_tab[crc ^ (val >> 8 & 0xFF)]; - - return crc ^ (val & 0xFF); -} - -static int ad7280_check_crc(struct ad7280_state *st, unsigned int val) -{ - unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10); - - if (crc != ((val >> 2) & 0xFF)) - return -EIO; - - return 0; -} - -/* After initiating a conversion sequence we need to wait until the - * conversion is done. The delay is typically in the range of 15..30 us - * however depending an the number of devices in the daisy chain and the - * number of averages taken, conversion delays and acquisition time options - * it may take up to 250us, in this case we better sleep instead of busy - * wait. - */ - -static void ad7280_delay(struct ad7280_state *st) -{ - if (st->readback_delay_us < 50) - udelay(st->readback_delay_us); - else - usleep_range(250, 500); -} - -static int __ad7280_read32(struct ad7280_state *st, unsigned int *val) -{ - int ret; - struct spi_transfer t = { - .tx_buf = &st->buf[0], - .rx_buf = &st->buf[1], - .len = 4, - }; - - st->buf[0] = cpu_to_be32(AD7280A_READ_TXVAL); - - ret = spi_sync_transfer(st->spi, &t, 1); - if (ret) - return ret; - - *val = be32_to_cpu(st->buf[1]); - - return 0; -} - -static int ad7280_write(struct ad7280_state *st, unsigned int devaddr, - unsigned int addr, bool all, unsigned int val) -{ - unsigned int reg = devaddr << 27 | addr << 21 | - (val & 0xFF) << 13 | all << 12; - - reg |= ad7280_calc_crc8(st->crc_tab, reg >> 11) << 3 | 0x2; - st->buf[0] = cpu_to_be32(reg); - - return spi_write(st->spi, &st->buf[0], 4); -} - -static int ad7280_read(struct ad7280_state *st, unsigned int devaddr, - unsigned int addr) -{ - int ret; - unsigned int tmp; - - /* turns off the read operation on all parts */ - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, - AD7280A_CTRL_HB_CONV_INPUT_ALL | - AD7280A_CTRL_HB_CONV_RES_READ_NO | - st->ctrl_hb); - if (ret) - return ret; - - /* turns on the read operation on the addressed part */ - ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0, - AD7280A_CTRL_HB_CONV_INPUT_ALL | - AD7280A_CTRL_HB_CONV_RES_READ_ALL | - st->ctrl_hb); - if (ret) - return ret; - - /* Set register address on the part to be read from */ - ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2); - if (ret) - return ret; - - ret = __ad7280_read32(st, &tmp); - if (ret) - return ret; - - if (ad7280_check_crc(st, tmp)) - return -EIO; - - if (((tmp >> 27) != devaddr) || (((tmp >> 21) & 0x3F) != addr)) - return -EFAULT; - - return (tmp >> 13) & 0xFF; -} - -static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr, - unsigned int addr) -{ - int ret; - unsigned int tmp; - - ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2); - if (ret) - return ret; - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, - AD7280A_CTRL_HB_CONV_INPUT_ALL | - AD7280A_CTRL_HB_CONV_RES_READ_NO | - st->ctrl_hb); - if (ret) - return ret; - - ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0, - AD7280A_CTRL_HB_CONV_INPUT_ALL | - AD7280A_CTRL_HB_CONV_RES_READ_ALL | - AD7280A_CTRL_HB_CONV_START_CS | - st->ctrl_hb); - if (ret) - return ret; - - ad7280_delay(st); - - ret = __ad7280_read32(st, &tmp); - if (ret) - return ret; - - if (ad7280_check_crc(st, tmp)) - return -EIO; - - if (((tmp >> 27) != devaddr) || (((tmp >> 23) & 0xF) != addr)) - return -EFAULT; - - return (tmp >> 11) & 0xFFF; -} - -static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt, - unsigned int *array) -{ - int i, ret; - unsigned int tmp, sum = 0; - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1, - AD7280A_CELL_VOLTAGE_1 << 2); - if (ret) - return ret; - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, - AD7280A_CTRL_HB_CONV_INPUT_ALL | - AD7280A_CTRL_HB_CONV_RES_READ_ALL | - AD7280A_CTRL_HB_CONV_START_CS | - st->ctrl_hb); - if (ret) - return ret; - - ad7280_delay(st); - - for (i = 0; i < cnt; i++) { - ret = __ad7280_read32(st, &tmp); - if (ret) - return ret; - - if (ad7280_check_crc(st, tmp)) - return -EIO; - - if (array) - array[i] = tmp; - /* only sum cell voltages */ - if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) - sum += ((tmp >> 11) & 0xFFF); - } - - return sum; -} - -static void ad7280_sw_power_down(void *data) -{ - struct ad7280_state *st = data; - - ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, - AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb); -} - -static int ad7280_chain_setup(struct ad7280_state *st) -{ - unsigned int val, n; - int ret; - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1, - AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | - AD7280A_CTRL_LB_LOCK_DEV_ADDR | - AD7280A_CTRL_LB_MUST_SET | - AD7280A_CTRL_LB_SWRST | - st->ctrl_lb); - if (ret) - return ret; - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1, - AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | - AD7280A_CTRL_LB_LOCK_DEV_ADDR | - AD7280A_CTRL_LB_MUST_SET | - st->ctrl_lb); - if (ret) - goto error_power_down; - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1, - AD7280A_CONTROL_LB << 2); - if (ret) - goto error_power_down; - - for (n = 0; n <= AD7280A_MAX_CHAIN; n++) { - ret = __ad7280_read32(st, &val); - if (ret) - goto error_power_down; - - if (val == 0) - return n - 1; - - if (ad7280_check_crc(st, val)) { - ret = -EIO; - goto error_power_down; - } - - if (n != ad7280a_devaddr(val >> 27)) { - ret = -EIO; - goto error_power_down; - } - } - ret = -EFAULT; - -error_power_down: - ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, - AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb); - - return ret; -} - -static ssize_t ad7280_show_balance_sw(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7280_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - - return sprintf(buf, "%d\n", - !!(st->cb_mask[this_attr->address >> 8] & - (1 << ((this_attr->address & 0xFF) + 2)))); -} - -static ssize_t ad7280_store_balance_sw(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7280_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - bool readin; - int ret; - unsigned int devaddr, ch; - - ret = strtobool(buf, &readin); - if (ret) - return ret; - - devaddr = this_attr->address >> 8; - ch = this_attr->address & 0xFF; - - mutex_lock(&st->lock); - if (readin) - st->cb_mask[devaddr] |= 1 << (ch + 2); - else - st->cb_mask[devaddr] &= ~(1 << (ch + 2)); - - ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE, - 0, st->cb_mask[devaddr]); - mutex_unlock(&st->lock); - - return ret ? ret : len; -} - -static ssize_t ad7280_show_balance_timer(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7280_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int ret; - unsigned int msecs; - - mutex_lock(&st->lock); - ret = ad7280_read(st, this_attr->address >> 8, - this_attr->address & 0xFF); - mutex_unlock(&st->lock); - - if (ret < 0) - return ret; - - msecs = (ret >> 3) * 71500; - - return sprintf(buf, "%u\n", msecs); -} - -static ssize_t ad7280_store_balance_timer(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7280_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 10, &val); - if (ret) - return ret; - - val /= 71500; - - if (val > 31) - return -EINVAL; - - mutex_lock(&st->lock); - ret = ad7280_write(st, this_attr->address >> 8, - this_attr->address & 0xFF, - 0, (val & 0x1F) << 3); - mutex_unlock(&st->lock); - - return ret ? ret : len; -} - -static struct attribute *ad7280_attributes[AD7280A_MAX_CHAIN * - AD7280A_CELLS_PER_DEV * 2 + 1]; - -static const struct attribute_group ad7280_attrs_group = { - .attrs = ad7280_attributes, -}; - -static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i) -{ - chan->type = IIO_VOLTAGE; - chan->differential = 1; - chan->channel = i; - chan->channel2 = chan->channel + 1; -} - -static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i) -{ - chan->type = IIO_TEMP; - chan->channel = i; -} - -static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr, - int cnt) -{ - chan->indexed = 1; - chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); - chan->address = addr; - chan->scan_index = cnt; - chan->scan_type.sign = 'u'; - chan->scan_type.realbits = 12; - chan->scan_type.storagebits = 32; -} - -static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan, - int cnt, int dev) -{ - chan->type = IIO_VOLTAGE; - chan->differential = 1; - chan->channel = 0; - chan->channel2 = dev * AD7280A_CELLS_PER_DEV; - chan->address = AD7280A_ALL_CELLS; - chan->indexed = 1; - chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); - chan->scan_index = cnt; - chan->scan_type.sign = 'u'; - chan->scan_type.realbits = 32; - chan->scan_type.storagebits = 32; -} - -static void ad7280_timestamp_channel_init(struct iio_chan_spec *chan, int cnt) -{ - chan->type = IIO_TIMESTAMP; - chan->channel = -1; - chan->scan_index = cnt; - chan->scan_type.sign = 's'; - chan->scan_type.realbits = 64; - chan->scan_type.storagebits = 64; -} - -static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt) -{ - int addr, ch, i; - struct iio_chan_spec *chan; - - for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++) { - chan = &st->channels[*cnt]; - - if (ch < AD7280A_AUX_ADC_1) { - i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch); - ad7280_voltage_channel_init(chan, i); - } else { - i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch); - ad7280_temp_channel_init(chan, i); - } - - addr = ad7280a_devaddr(dev) << 8 | ch; - ad7280_common_fields_init(chan, addr, *cnt); - - (*cnt)++; - } -} - -static int ad7280_channel_init(struct ad7280_state *st) -{ - int dev, cnt = 0; - - st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 2, - sizeof(*st->channels), GFP_KERNEL); - if (!st->channels) - return -ENOMEM; - - for (dev = 0; dev <= st->slave_num; dev++) - ad7280_init_dev_channels(st, dev, &cnt); - - ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev); - cnt++; - ad7280_timestamp_channel_init(&st->channels[cnt], cnt); - - return cnt + 1; -} - -static int ad7280_balance_switch_attr_init(struct iio_dev_attr *attr, - struct device *dev, int addr, int i) -{ - attr->address = addr; - attr->dev_attr.attr.mode = 0644; - attr->dev_attr.show = ad7280_show_balance_sw; - attr->dev_attr.store = ad7280_store_balance_sw; - attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, - "in%d-in%d_balance_switch_en", - i, i + 1); - if (!attr->dev_attr.attr.name) - return -ENOMEM; - - return 0; -} - -static int ad7280_balance_timer_attr_init(struct iio_dev_attr *attr, - struct device *dev, int addr, int i) -{ - attr->address = addr; - attr->dev_attr.attr.mode = 0644; - attr->dev_attr.show = ad7280_show_balance_timer; - attr->dev_attr.store = ad7280_store_balance_timer; - attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, - "in%d-in%d_balance_timer", - i, i + 1); - if (!attr->dev_attr.attr.name) - return -ENOMEM; - - return 0; -} - -static int ad7280_init_dev_attrs(struct ad7280_state *st, int dev, int *cnt) -{ - int addr, ch, i, ret; - struct iio_dev_attr *iio_attr; - struct device *sdev = &st->spi->dev; - - for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; ch++) { - iio_attr = &st->iio_attr[*cnt]; - addr = ad7280a_devaddr(dev) << 8 | ch; - i = dev * AD7280A_CELLS_PER_DEV + ch; - - ret = ad7280_balance_switch_attr_init(iio_attr, sdev, addr, i); - if (ret < 0) - return ret; - - ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr; - - (*cnt)++; - iio_attr = &st->iio_attr[*cnt]; - addr = ad7280a_devaddr(dev) << 8 | (AD7280A_CB1_TIMER + ch); - - ret = ad7280_balance_timer_attr_init(iio_attr, sdev, addr, i); - if (ret < 0) - return ret; - - ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr; - (*cnt)++; - } - - ad7280_attributes[*cnt] = NULL; - - return 0; -} - -static int ad7280_attr_init(struct ad7280_state *st) -{ - int dev, cnt = 0, ret; - - st->iio_attr = devm_kcalloc(&st->spi->dev, 2, sizeof(*st->iio_attr) * - (st->slave_num + 1) * AD7280A_CELLS_PER_DEV, - GFP_KERNEL); - if (!st->iio_attr) - return -ENOMEM; - - for (dev = 0; dev <= st->slave_num; dev++) { - ret = ad7280_init_dev_attrs(st, dev, &cnt); - if (ret < 0) - return ret; - } - - return 0; -} - -static ssize_t ad7280_read_channel_config(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7280_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - unsigned int val; - - switch (this_attr->address) { - case AD7280A_CELL_OVERVOLTAGE: - val = 1000 + (st->cell_threshhigh * 1568) / 100; - break; - case AD7280A_CELL_UNDERVOLTAGE: - val = 1000 + (st->cell_threshlow * 1568) / 100; - break; - case AD7280A_AUX_ADC_OVERVOLTAGE: - val = (st->aux_threshhigh * 196) / 10; - break; - case AD7280A_AUX_ADC_UNDERVOLTAGE: - val = (st->aux_threshlow * 196) / 10; - break; - default: - return -EINVAL; - } - - return sprintf(buf, "%u\n", val); -} - -static ssize_t ad7280_write_channel_config(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7280_state *st = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - - long val; - int ret; - - ret = kstrtol(buf, 10, &val); - if (ret) - return ret; - - switch (this_attr->address) { - case AD7280A_CELL_OVERVOLTAGE: - case AD7280A_CELL_UNDERVOLTAGE: - val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */ - break; - case AD7280A_AUX_ADC_OVERVOLTAGE: - case AD7280A_AUX_ADC_UNDERVOLTAGE: - val = (val * 10) / 196; /* LSB 19.6mV */ - break; - default: - return -EFAULT; - } - - val = clamp(val, 0L, 0xFFL); - - mutex_lock(&st->lock); - switch (this_attr->address) { - case AD7280A_CELL_OVERVOLTAGE: - st->cell_threshhigh = val; - break; - case AD7280A_CELL_UNDERVOLTAGE: - st->cell_threshlow = val; - break; - case AD7280A_AUX_ADC_OVERVOLTAGE: - st->aux_threshhigh = val; - break; - case AD7280A_AUX_ADC_UNDERVOLTAGE: - st->aux_threshlow = val; - break; - } - - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, - this_attr->address, 1, val); - - mutex_unlock(&st->lock); - - return ret ? ret : len; -} - -static irqreturn_t ad7280_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct ad7280_state *st = iio_priv(indio_dev); - unsigned int *channels; - int i, ret; - - channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL); - if (!channels) - return IRQ_HANDLED; - - ret = ad7280_read_all_channels(st, st->scan_cnt, channels); - if (ret < 0) - goto out; - - for (i = 0; i < st->scan_cnt; i++) { - if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) { - if (((channels[i] >> 11) & 0xFFF) >= - st->cell_threshhigh) { - u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, - 0, 0, 0); - iio_push_event(indio_dev, tmp, - iio_get_time_ns(indio_dev)); - } else if (((channels[i] >> 11) & 0xFFF) <= - st->cell_threshlow) { - u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0, - IIO_EV_DIR_FALLING, - IIO_EV_TYPE_THRESH, - 0, 0, 0); - iio_push_event(indio_dev, tmp, - iio_get_time_ns(indio_dev)); - } - } else { - if (((channels[i] >> 11) & 0xFFF) >= - st->aux_threshhigh) { - u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING); - iio_push_event(indio_dev, tmp, - iio_get_time_ns(indio_dev)); - } else if (((channels[i] >> 11) & 0xFFF) <= - st->aux_threshlow) { - u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING); - iio_push_event(indio_dev, tmp, - iio_get_time_ns(indio_dev)); - } - } - } - -out: - kfree(channels); - - return IRQ_HANDLED; -} - -/* Note: No need to fix checkpatch warning that reads: - * CHECK: spaces preferred around that '-' (ctx:VxV) - * The function argument is stringified and doesn't need a fix - */ -static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value, - in_voltage-voltage_thresh_low_value, - 0644, - ad7280_read_channel_config, - ad7280_write_channel_config, - AD7280A_CELL_UNDERVOLTAGE); - -static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value, - in_voltage-voltage_thresh_high_value, - 0644, - ad7280_read_channel_config, - ad7280_write_channel_config, - AD7280A_CELL_OVERVOLTAGE); - -static IIO_DEVICE_ATTR(in_temp_thresh_low_value, - 0644, - ad7280_read_channel_config, - ad7280_write_channel_config, - AD7280A_AUX_ADC_UNDERVOLTAGE); - -static IIO_DEVICE_ATTR(in_temp_thresh_high_value, - 0644, - ad7280_read_channel_config, - ad7280_write_channel_config, - AD7280A_AUX_ADC_OVERVOLTAGE); - -static struct attribute *ad7280_event_attributes[] = { - &iio_dev_attr_in_thresh_low_value.dev_attr.attr, - &iio_dev_attr_in_thresh_high_value.dev_attr.attr, - &iio_dev_attr_in_temp_thresh_low_value.dev_attr.attr, - &iio_dev_attr_in_temp_thresh_high_value.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad7280_event_attrs_group = { - .attrs = ad7280_event_attributes, -}; - -static int ad7280_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad7280_state *st = iio_priv(indio_dev); - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&st->lock); - if (chan->address == AD7280A_ALL_CELLS) - ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); - else - ret = ad7280_read_channel(st, chan->address >> 8, - chan->address & 0xFF); - mutex_unlock(&st->lock); - - if (ret < 0) - return ret; - - *val = ret; - - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6) - *val = 4000; - else - *val = 5000; - - *val2 = AD7280A_BITS; - return IIO_VAL_FRACTIONAL_LOG2; - } - return -EINVAL; -} - -static const struct iio_info ad7280_info = { - .read_raw = ad7280_read_raw, - .event_attrs = &ad7280_event_attrs_group, - .attrs = &ad7280_attrs_group, -}; - -static const struct ad7280_platform_data ad7793_default_pdata = { - .acquisition_time = AD7280A_ACQ_TIME_400ns, - .conversion_averaging = AD7280A_CONV_AVG_DIS, - .thermistor_term_en = true, -}; - -static int ad7280_probe(struct spi_device *spi) -{ - const struct ad7280_platform_data *pdata = dev_get_platdata(&spi->dev); - struct ad7280_state *st; - int ret; - const unsigned short t_acq_ns[4] = {465, 1010, 1460, 1890}; - const unsigned short n_avg[4] = {1, 2, 4, 8}; - 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->spi = spi; - mutex_init(&st->lock); - - if (!pdata) - pdata = &ad7793_default_pdata; - - crc8_populate_msb(st->crc_tab, POLYNOM); - - st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ; - st->spi->mode = SPI_MODE_1; - spi_setup(st->spi); - - st->ctrl_lb = AD7280A_CTRL_LB_ACQ_TIME(pdata->acquisition_time & 0x3); - st->ctrl_hb = AD7280A_CTRL_HB_CONV_AVG(pdata->conversion_averaging - & 0x3) | (pdata->thermistor_term_en ? - AD7280A_CTRL_LB_THERMISTOR_EN : 0); - - ret = ad7280_chain_setup(st); - if (ret < 0) - return ret; - - st->slave_num = ret; - st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH; - st->cell_threshhigh = 0xFF; - st->aux_threshhigh = 0xFF; - - ret = devm_add_action_or_reset(&spi->dev, ad7280_sw_power_down, st); - if (ret) - return ret; - - /* - * Total Conversion Time = ((tACQ + tCONV) * - * (Number of Conversions per Part)) − - * tACQ + ((N - 1) * tDELAY) - * - * Readback Delay = Total Conversion Time + tWAIT - */ - - st->readback_delay_us = - ((t_acq_ns[pdata->acquisition_time & 0x3] + 695) * - (AD7280A_NUM_CH * n_avg[pdata->conversion_averaging & 0x3])) - - t_acq_ns[pdata->acquisition_time & 0x3] + st->slave_num * 250; - - /* Convert to usecs */ - st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000); - st->readback_delay_us += 5; /* Add tWAIT */ - - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->modes = INDIO_DIRECT_MODE; - - ret = ad7280_channel_init(st); - if (ret < 0) - return ret; - - indio_dev->num_channels = ret; - indio_dev->channels = st->channels; - indio_dev->info = &ad7280_info; - - ret = ad7280_attr_init(st); - if (ret < 0) - return ret; - - ret = devm_iio_device_register(&spi->dev, indio_dev); - if (ret) - return ret; - - if (spi->irq > 0) { - ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, - AD7280A_ALERT, 1, - AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN); - if (ret) - return ret; - - ret = ad7280_write(st, ad7280a_devaddr(st->slave_num), - AD7280A_ALERT, 0, - AD7280A_ALERT_GEN_STATIC_HIGH | - (pdata->chain_last_alert_ignore & 0xF)); - if (ret) - return ret; - - ret = devm_request_threaded_irq(&spi->dev, spi->irq, - NULL, - ad7280_event_handler, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - indio_dev->name, - indio_dev); - if (ret) - return ret; - } - - return 0; -} - -static const struct spi_device_id ad7280_id[] = { - {"ad7280a", 0}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad7280_id); - -static struct spi_driver ad7280_driver = { - .driver = { - .name = "ad7280", - }, - .probe = ad7280_probe, - .id_table = ad7280_id, -}; -module_spi_driver(ad7280_driver); - -MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); -MODULE_DESCRIPTION("Analog Devices AD7280A"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7280a.h b/drivers/staging/iio/adc/ad7280a.h deleted file mode 100644 index 23f18bb9e279..000000000000 --- a/drivers/staging/iio/adc/ad7280a.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * AD7280A Lithium Ion Battery Monitoring System - * - * Copyright 2011 Analog Devices Inc. - */ - -#ifndef IIO_ADC_AD7280_H_ -#define IIO_ADC_AD7280_H_ - -/* - * TODO: struct ad7280_platform_data needs to go into include/linux/iio - */ - -#define AD7280A_ACQ_TIME_400ns 0 -#define AD7280A_ACQ_TIME_800ns 1 -#define AD7280A_ACQ_TIME_1200ns 2 -#define AD7280A_ACQ_TIME_1600ns 3 - -#define AD7280A_CONV_AVG_DIS 0 -#define AD7280A_CONV_AVG_2 1 -#define AD7280A_CONV_AVG_4 2 -#define AD7280A_CONV_AVG_8 3 - -#define AD7280A_ALERT_REMOVE_VIN5 BIT(2) -#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3) -#define AD7280A_ALERT_REMOVE_AUX5 BIT(0) -#define AD7280A_ALERT_REMOVE_AUX4_AUX5 BIT(1) - -struct ad7280_platform_data { - unsigned int acquisition_time; - unsigned int conversion_averaging; - unsigned int chain_last_alert_ignore; - bool thermistor_term_en; -}; - -#endif /* IIO_ADC_AD7280_H_ */ diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c index 3a5336913cca..b3f310389378 100644 --- a/drivers/thunderbolt/nvm.c +++ b/drivers/thunderbolt/nvm.c @@ -154,10 +154,8 @@ int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size, void tb_nvm_free(struct tb_nvm *nvm) { if (nvm) { - if (nvm->non_active) - nvmem_unregister(nvm->non_active); - if (nvm->active) - nvmem_unregister(nvm->active); + nvmem_unregister(nvm->non_active); + nvmem_unregister(nvm->active); vfree(nvm->buf); ida_simple_remove(&nvm_ida, nvm->id); } diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 8ca1a235d164..60651a50770f 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -8,7 +8,6 @@ #include <linux/mbus.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/phy/phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -75,47 +74,6 @@ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) return 0; } -int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct device *dev = hcd->self.controller; - struct phy *phy; - int ret; - - /* Old bindings miss the PHY handle */ - phy = of_phy_get(dev->of_node, "usb3-phy"); - if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER) - return -EPROBE_DEFER; - else if (IS_ERR(phy)) - goto phy_out; - - ret = phy_init(phy); - if (ret) - goto phy_put; - - ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS); - if (ret) - goto phy_exit; - - ret = phy_power_on(phy); - if (ret == -EOPNOTSUPP) { - /* Skip initializatin of XHCI PHY when it is unsupported by firmware */ - dev_warn(dev, "PHY unsupported by firmware\n"); - xhci->quirks |= XHCI_SKIP_PHY_INIT; - } - if (ret) - goto phy_exit; - - phy_power_off(phy); -phy_exit: - phy_exit(phy); -phy_put: - of_phy_put(phy); -phy_out: - - return 0; -} - int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h index 01bf3fcb3eca..3be021793cc8 100644 --- a/drivers/usb/host/xhci-mvebu.h +++ b/drivers/usb/host/xhci-mvebu.h @@ -12,7 +12,6 @@ struct usb_hcd; #if IS_ENABLED(CONFIG_USB_XHCI_MVEBU) int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd); -int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd); int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd); #else static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) @@ -20,11 +19,6 @@ static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) return 0; } -static inline int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd) -{ - return 0; -} - static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) { return 0; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 8094da34825e..649ffd861b44 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -44,16 +44,6 @@ static void xhci_priv_plat_start(struct usb_hcd *hcd) priv->plat_start(hcd); } -static int xhci_priv_plat_setup(struct usb_hcd *hcd) -{ - struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); - - if (!priv->plat_setup) - return 0; - - return priv->plat_setup(hcd); -} - static int xhci_priv_init_quirk(struct usb_hcd *hcd) { struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); @@ -121,7 +111,6 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada = { }; static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { - .plat_setup = xhci_mvebu_a3700_plat_setup, .init_quirk = xhci_mvebu_a3700_init_quirk, }; @@ -334,14 +323,7 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node); xhci->shared_hcd->tpl_support = hcd->tpl_support; - - if (priv) { - ret = xhci_priv_plat_setup(hcd); - if (ret) - goto disable_usb_phy; - } - - if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))) + if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) hcd->skip_phy_initialization = 1; if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK)) diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 561d0b7bce09..1fb149d1fbce 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -13,7 +13,6 @@ struct xhci_plat_priv { const char *firmware_name; unsigned long long quirks; - int (*plat_setup)(struct usb_hcd *); void (*plat_start)(struct usb_hcd *); int (*init_quirk)(struct usb_hcd *); int (*suspend_quirk)(struct usb_hcd *); diff --git a/drivers/virt/acrn/hsm.c b/drivers/virt/acrn/hsm.c index 5419794fccf1..423ea888d79a 100644 --- a/drivers/virt/acrn/hsm.c +++ b/drivers/virt/acrn/hsm.c @@ -136,8 +136,10 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd, if (IS_ERR(vm_param)) return PTR_ERR(vm_param); - if ((vm_param->reserved0 | vm_param->reserved1) != 0) + if ((vm_param->reserved0 | vm_param->reserved1) != 0) { + kfree(vm_param); return -EINVAL; + } vm = acrn_vm_create(vm, vm_param); if (!vm) { @@ -182,21 +184,29 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd, return PTR_ERR(cpu_regs); for (i = 0; i < ARRAY_SIZE(cpu_regs->reserved); i++) - if (cpu_regs->reserved[i]) + if (cpu_regs->reserved[i]) { + kfree(cpu_regs); return -EINVAL; + } for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.reserved_32); i++) - if (cpu_regs->vcpu_regs.reserved_32[i]) + if (cpu_regs->vcpu_regs.reserved_32[i]) { + kfree(cpu_regs); return -EINVAL; + } for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.reserved_64); i++) - if (cpu_regs->vcpu_regs.reserved_64[i]) + if (cpu_regs->vcpu_regs.reserved_64[i]) { + kfree(cpu_regs); return -EINVAL; + } for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.gdt.reserved); i++) if (cpu_regs->vcpu_regs.gdt.reserved[i] | - cpu_regs->vcpu_regs.idt.reserved[i]) + cpu_regs->vcpu_regs.idt.reserved[i]) { + kfree(cpu_regs); return -EINVAL; + } ret = hcall_set_vcpu_regs(vm->vmid, virt_to_phys(cpu_regs)); if (ret < 0) diff --git a/drivers/virt/acrn/irqfd.c b/drivers/virt/acrn/irqfd.c index df5184979b28..d4ad211dce7a 100644 --- a/drivers/virt/acrn/irqfd.c +++ b/drivers/virt/acrn/irqfd.c @@ -17,7 +17,6 @@ #include "acrn_drv.h" static LIST_HEAD(acrn_irqfd_clients); -static DEFINE_MUTEX(acrn_irqfds_mutex); /** * struct hsm_irqfd - Properties of HSM irqfd diff --git a/drivers/virt/acrn/mm.c b/drivers/virt/acrn/mm.c index c4f2e15c8a2b..3b1b1e7a844b 100644 --- a/drivers/virt/acrn/mm.c +++ b/drivers/virt/acrn/mm.c @@ -162,10 +162,34 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap) void *remap_vaddr; int ret, pinned; u64 user_vm_pa; + unsigned long pfn; + struct vm_area_struct *vma; if (!vm || !memmap) return -EINVAL; + mmap_read_lock(current->mm); + vma = vma_lookup(current->mm, memmap->vma_base); + if (vma && ((vma->vm_flags & VM_PFNMAP) != 0)) { + if ((memmap->vma_base + memmap->len) > vma->vm_end) { + mmap_read_unlock(current->mm); + return -EINVAL; + } + + ret = follow_pfn(vma, memmap->vma_base, &pfn); + mmap_read_unlock(current->mm); + if (ret < 0) { + dev_dbg(acrn_dev.this_device, + "Failed to lookup PFN at VMA:%pK.\n", (void *)memmap->vma_base); + return ret; + } + + return acrn_mm_region_add(vm, memmap->user_vm_pa, + PFN_PHYS(pfn), memmap->len, + ACRN_MEM_TYPE_WB, memmap->attr); + } + mmap_read_unlock(current->mm); + /* Get the page number of the map region */ nr_pages = memmap->len >> PAGE_SHIFT; pages = vzalloc(nr_pages * sizeof(struct page *)); diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index 46ee0a0998b6..e49bec8bc8a4 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -687,15 +687,13 @@ static int fsl_hv_close(struct inode *inode, struct file *filp) struct doorbell_queue *dbq = filp->private_data; unsigned long flags; - int ret = 0; - spin_lock_irqsave(&db_list_lock, flags); list_del(&dbq->list); spin_unlock_irqrestore(&db_list_lock, flags); kfree(dbq); - return ret; + return 0; } static const struct file_operations fsl_hv_fops = { diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index cd8821580f71..f6664fc9596a 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -219,10 +219,8 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) { int i; - pr_info("0x%x: count=%d, status: ", dev->ep[EP_STATUS], count); - for (i = 0; i < count; ++i) - pr_info("%02x ", buf[i]); - pr_info("\n"); + dev_info(&dev->udev->dev, "ep_status=0x%x, count=%d, status=%*phC", + dev->ep[EP_STATUS], count, count, buf); if (count >= 16) { ds_print_msg(buf, "enable flag", 0); @@ -331,7 +329,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), buf, size, &count, 1000); if (err < 0) { - pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); + dev_info(&dev->udev->dev, "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); ds_recv_status(dev, NULL, true); return err; diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 565578002d79..067692626cf0 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -574,6 +574,41 @@ static inline int w1_DS18S20_convert_time(struct w1_slave *sl) return SLAVE_CONV_TIME_OVERRIDE(sl); } +static inline int w1_DS1825_convert_time(struct w1_slave *sl) +{ + int ret; + + if (!sl->family_data) + return -ENODEV; /* device unknown */ + + if (SLAVE_CONV_TIME_OVERRIDE(sl) != CONV_TIME_DEFAULT) + return SLAVE_CONV_TIME_OVERRIDE(sl); + + /* Return the conversion time, depending on resolution, + * select maximum conversion time among all compatible devices + */ + switch (SLAVE_RESOLUTION(sl)) { + case 9: + ret = 95; + break; + case 10: + ret = 190; + break; + case 11: + ret = 375; + break; + case 12: + ret = 750; + break; + case 14: + ret = 100; /* MAX31850 only. Datasheet says 100ms */ + break; + default: + ret = 750; + } + return ret; +} + static inline int w1_DS18B20_write_data(struct w1_slave *sl, const u8 *data) { @@ -594,6 +629,7 @@ static inline int w1_DS18B20_set_resolution(struct w1_slave *sl, int val) /* DS18B20 resolution is 9 to 12 bits */ /* GX20MH01 resolution is 9 to 14 bits */ + /* MAX31850 resolution is fixed 14 bits */ if (val < W1_THERM_RESOLUTION_MIN || val > W1_THERM_RESOLUTION_MAX) return -EINVAL; @@ -649,6 +685,7 @@ static inline int w1_DS18B20_get_resolution(struct w1_slave *sl) + W1_THERM_RESOLUTION_MIN; /* GX20MH01 has one special case: * >=14 means 14 bits when getting resolution from bit value. + * MAX31850 delivers fixed 15 and has 14 bits. * Other devices have no more then 12 bits. */ if (resolution > W1_THERM_RESOLUTION_MAX) @@ -715,6 +752,34 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9]) return t; } +/** + * w1_DS1825_convert_temp() - temperature computation for DS1825 + * @rom: data read from device RAM (8 data bytes + 1 CRC byte) + * + * Can be called for any DS1825 compliant device. + * Is used by MAX31850, too + * + * Return: value in millidegrees Celsius. + */ + +static inline int w1_DS1825_convert_temp(u8 rom[9]) +{ + u16 bv; + s16 t; + + /* Signed 16-bit value to unsigned, cpu order */ + bv = le16_to_cpup((__le16 *)rom); + + /* Config register bit 7 = 1 - MA31850 found, 14 bit resolution */ + if (rom[4] & 0x80) { + /* Mask out bits 0 (Fault) and 1 (Reserved) */ + /* Avoid arithmetic shift of signed value */ + bv = (bv & 0xFFFC); /* Degrees, lowest 4 bits are 2^-1, 2^-2 and 2 zero bits */ + } + t = (s16)bv; /* Degrees, lowest bit is 2^-4 */ + return (int)t * 1000 / 16; /* Sign-extend to int; millidegrees */ +} + /* Device capability description */ /* GX20MH01 device shares family number and structure with DS18B20 */ @@ -757,9 +822,10 @@ static struct w1_therm_family_converter w1_therm_families[] = { .bulk_read = false }, { + /* Also used for MAX31850 */ .f = &w1_therm_family_DS1825, - .convert = w1_DS18B20_convert_temp, - .get_conversion_time = w1_DS18B20_convert_time, + .convert = w1_DS1825_convert_temp, + .get_conversion_time = w1_DS1825_convert_time, .set_resolution = w1_DS18B20_set_resolution, .get_resolution = w1_DS18B20_get_resolution, .write_data = w1_DS18B20_write_data, @@ -2089,16 +2155,20 @@ static ssize_t w1_seq_show(struct device *device, if (sl->reg_num.id == reg_num->id) seq = i; + if (w1_reset_bus(sl->master)) + goto error; + + /* Put the device into chain DONE state */ + w1_write_8(sl->master, W1_MATCH_ROM); + w1_write_block(sl->master, (u8 *)&rn, 8); w1_write_8(sl->master, W1_42_CHAIN); w1_write_8(sl->master, W1_42_CHAIN_DONE); w1_write_8(sl->master, W1_42_CHAIN_DONE_INV); - w1_read_block(sl->master, &ack, sizeof(ack)); /* check for acknowledgment */ ack = w1_read_8(sl->master); if (ack != W1_42_SUCCESS_CONFIRM_BYTE) goto error; - } /* Exit from CHAIN state */ |