diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index fa4c2745dba2..c358c074f925 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "nhi.h" #include "nhi_regs.h" @@ -28,6 +29,8 @@ #define MSIX_MIN_VECS 6 #define MSIX_MAX_VECS 16 +#define NHI_MAILBOX_TIMEOUT 500 /* ms */ + static int ring_interrupt_index(struct tb_ring *ring) { int bit = ring->hop; @@ -525,6 +528,61 @@ void ring_free(struct tb_ring *ring) kfree(ring); } +/** + * nhi_mailbox_cmd() - Send a command through NHI mailbox + * @nhi: Pointer to the NHI structure + * @cmd: Command to send + * @data: Data to be send with the command + * + * Sends mailbox command to the firmware running on NHI. Returns %0 in + * case of success and negative errno in case of failure. + */ +int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data) +{ + ktime_t timeout; + u32 val; + + iowrite32(data, nhi->iobase + REG_INMAIL_DATA); + + val = ioread32(nhi->iobase + REG_INMAIL_CMD); + val &= ~(REG_INMAIL_CMD_MASK | REG_INMAIL_ERROR); + val |= REG_INMAIL_OP_REQUEST | cmd; + iowrite32(val, nhi->iobase + REG_INMAIL_CMD); + + timeout = ktime_add_ms(ktime_get(), NHI_MAILBOX_TIMEOUT); + do { + val = ioread32(nhi->iobase + REG_INMAIL_CMD); + if (!(val & REG_INMAIL_OP_REQUEST)) + break; + usleep_range(10, 20); + } while (ktime_before(ktime_get(), timeout)); + + if (val & REG_INMAIL_OP_REQUEST) + return -ETIMEDOUT; + if (val & REG_INMAIL_ERROR) + return -EIO; + + return 0; +} + +/** + * nhi_mailbox_mode() - Return current firmware operation mode + * @nhi: Pointer to the NHI structure + * + * The function reads current firmware operation mode using NHI mailbox + * registers and returns it to the caller. + */ +enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi) +{ + u32 val; + + val = ioread32(nhi->iobase + REG_OUTMAIL_CMD); + val &= REG_OUTMAIL_CMD_OPMODE_MASK; + val >>= REG_OUTMAIL_CMD_OPMODE_SHIFT; + + return (enum nhi_fw_mode)val; +} + static void nhi_interrupt_work(struct work_struct *work) { struct tb_nhi *nhi = container_of(work, typeof(*nhi), interrupt_work); diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 8bd9b4e5a0b1..446ff6dac91d 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -143,6 +143,22 @@ static inline int ring_tx(struct tb_ring *ring, struct ring_frame *frame) return __ring_enqueue(ring, frame); } +enum nhi_fw_mode { + NHI_FW_SAFE_MODE, + NHI_FW_AUTH_MODE, + NHI_FW_EP_MODE, + NHI_FW_CM_MODE, +}; + +enum nhi_mailbox_cmd { + NHI_MAILBOX_SAVE_DEVS = 0x05, + NHI_MAILBOX_DRV_UNLOADS = 0x07, + NHI_MAILBOX_ALLOW_ALL_DEVS = 0x23, +}; + +int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data); +enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi); + /* * PCI IDs used in this driver from Win Ridge forward. There is no * need for the PCI quirk anymore as we will use ICM also on Apple diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index 48b98d3c7e6a..322fe1fa3a3c 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -107,4 +107,15 @@ struct ring_desc { #define REG_DMA_MISC 0x39864 #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) +#define REG_INMAIL_DATA 0x39900 + +#define REG_INMAIL_CMD 0x39904 +#define REG_INMAIL_CMD_MASK GENMASK(7, 0) +#define REG_INMAIL_ERROR BIT(30) +#define REG_INMAIL_OP_REQUEST BIT(31) + +#define REG_OUTMAIL_CMD 0x3990c +#define REG_OUTMAIL_CMD_OPMODE_SHIFT 8 +#define REG_OUTMAIL_CMD_OPMODE_MASK GENMASK(11, 8) + #endif