mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 09:44:18 +08:00
529766e0a0
MP2 controllers have two separate busses, so may accommodate up to two I2C adapters. Those adapters are listed in the ACPI namespace with the "AMDI0011" HID, and probed by a platform driver. Communication with the MP2 takes place through MMIO registers, or through DMA for more than 32 bytes transfers. This is major rework of the patch submitted by Nehal-bakulchandra Shah from AMD (https://patchwork.kernel.org/patch/10597369/). Most of the event handling of v3 was rewritten to make it work with more than one bus (e.g on Ryzen-based Lenovo Yoga 530), and this version contains many other improvements. Signed-off-by: Elie Morisse <syniurge@gmail.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
220 lines
5.7 KiB
C
220 lines
5.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
|
/*
|
|
* AMD MP2 I2C adapter driver
|
|
*
|
|
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
|
* Elie Morisse <syniurge@gmail.com>
|
|
*/
|
|
|
|
#ifndef I2C_AMD_PCI_MP2_H
|
|
#define I2C_AMD_PCI_MP2_H
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#define PCI_DEVICE_ID_AMD_MP2 0x15E6
|
|
|
|
struct amd_i2c_common;
|
|
struct amd_mp2_dev;
|
|
|
|
enum {
|
|
/* MP2 C2P Message Registers */
|
|
AMD_C2P_MSG0 = 0x10500, /* MP2 Message for I2C0 */
|
|
AMD_C2P_MSG1 = 0x10504, /* MP2 Message for I2C1 */
|
|
AMD_C2P_MSG2 = 0x10508, /* DRAM Address Lo / Data 0 */
|
|
AMD_C2P_MSG3 = 0x1050c, /* DRAM Address HI / Data 1 */
|
|
AMD_C2P_MSG4 = 0x10510, /* Data 2 */
|
|
AMD_C2P_MSG5 = 0x10514, /* Data 3 */
|
|
AMD_C2P_MSG6 = 0x10518, /* Data 4 */
|
|
AMD_C2P_MSG7 = 0x1051c, /* Data 5 */
|
|
AMD_C2P_MSG8 = 0x10520, /* Data 6 */
|
|
AMD_C2P_MSG9 = 0x10524, /* Data 7 */
|
|
|
|
/* MP2 P2C Message Registers */
|
|
AMD_P2C_MSG0 = 0x10680, /* Do not use */
|
|
AMD_P2C_MSG1 = 0x10684, /* I2C0 interrupt register */
|
|
AMD_P2C_MSG2 = 0x10688, /* I2C1 interrupt register */
|
|
AMD_P2C_MSG3 = 0x1068C, /* MP2 debug info */
|
|
AMD_P2C_MSG_INTEN = 0x10690, /* MP2 interrupt gen register */
|
|
AMD_P2C_MSG_INTSTS = 0x10694, /* Interrupt status */
|
|
};
|
|
|
|
/* Command register data structures */
|
|
|
|
#define i2c_none (-1)
|
|
enum i2c_cmd {
|
|
i2c_read = 0,
|
|
i2c_write,
|
|
i2c_enable,
|
|
i2c_disable,
|
|
number_of_sensor_discovered,
|
|
is_mp2_active,
|
|
invalid_cmd = 0xF,
|
|
};
|
|
|
|
enum speed_enum {
|
|
speed100k = 0,
|
|
speed400k = 1,
|
|
speed1000k = 2,
|
|
speed1400k = 3,
|
|
speed3400k = 4
|
|
};
|
|
|
|
enum mem_type {
|
|
use_dram = 0,
|
|
use_c2pmsg = 1,
|
|
};
|
|
|
|
/**
|
|
* union i2c_cmd_base : bit access of C2P commands
|
|
* @i2c_cmd: bit 0..3 i2c R/W command
|
|
* @bus_id: bit 4..7 i2c bus index
|
|
* @slave_addr: bit 8..15 slave address
|
|
* @length: bit 16..27 read/write length
|
|
* @i2c_speed: bit 28..30 bus speed
|
|
* @mem_type: bit 31 0-DRAM; 1-C2P msg o/p
|
|
*/
|
|
union i2c_cmd_base {
|
|
u32 ul;
|
|
struct {
|
|
enum i2c_cmd i2c_cmd : 4;
|
|
u8 bus_id : 4;
|
|
u32 slave_addr : 8;
|
|
u32 length : 12;
|
|
enum speed_enum i2c_speed : 3;
|
|
enum mem_type mem_type : 1;
|
|
} s;
|
|
};
|
|
|
|
enum response_type {
|
|
invalid_response = 0,
|
|
command_success = 1,
|
|
command_failed = 2,
|
|
};
|
|
|
|
enum status_type {
|
|
i2c_readcomplete_event = 0,
|
|
i2c_readfail_event = 1,
|
|
i2c_writecomplete_event = 2,
|
|
i2c_writefail_event = 3,
|
|
i2c_busenable_complete = 4,
|
|
i2c_busenable_failed = 5,
|
|
i2c_busdisable_complete = 6,
|
|
i2c_busdisable_failed = 7,
|
|
invalid_data_length = 8,
|
|
invalid_slave_address = 9,
|
|
invalid_i2cbus_id = 10,
|
|
invalid_dram_addr = 11,
|
|
invalid_command = 12,
|
|
mp2_active = 13,
|
|
numberof_sensors_discovered_resp = 14,
|
|
i2c_bus_notinitialized
|
|
};
|
|
|
|
/**
|
|
* union i2c_event : bit access of P2C events
|
|
* @response: bit 0..1 i2c response type
|
|
* @status: bit 2..6 status_type
|
|
* @mem_type: bit 7 0-DRAM; 1-C2P msg o/p
|
|
* @bus_id: bit 8..11 i2c bus id
|
|
* @length: bit 12..23 message length
|
|
* @slave_addr: bit 24-31 slave address
|
|
*/
|
|
union i2c_event {
|
|
u32 ul;
|
|
struct {
|
|
enum response_type response : 2;
|
|
enum status_type status : 5;
|
|
enum mem_type mem_type : 1;
|
|
u8 bus_id : 4;
|
|
u32 length : 12;
|
|
u32 slave_addr : 8;
|
|
} r;
|
|
};
|
|
|
|
/**
|
|
* struct amd_i2c_common - per bus/i2c adapter context, shared
|
|
* between the pci and the platform driver
|
|
* @eventval: MP2 event value set by the IRQ handler
|
|
* @mp2_dev: MP2 pci device this adapter is part of
|
|
* @msg: i2c message
|
|
* @cmd_completion: function called by the IRQ handler to signal
|
|
* the platform driver
|
|
* @reqcmd: requested i2c command type
|
|
* @cmd_success: set to true if the MP2 responded to a command with
|
|
* the expected status and response type
|
|
* @bus_id: bus index
|
|
* @i2c_speed: i2c bus speed determined by the slowest slave
|
|
* @dma_buf: if msg length > 32, holds the DMA buffer virtual address
|
|
* @dma_addr: if msg length > 32, holds the DMA buffer address
|
|
*/
|
|
struct amd_i2c_common {
|
|
union i2c_event eventval;
|
|
struct amd_mp2_dev *mp2_dev;
|
|
struct i2c_msg *msg;
|
|
void (*cmd_completion)(struct amd_i2c_common *i2c_common);
|
|
enum i2c_cmd reqcmd;
|
|
u8 cmd_success;
|
|
u8 bus_id;
|
|
enum speed_enum i2c_speed;
|
|
u8 *dma_buf;
|
|
dma_addr_t dma_addr;
|
|
#ifdef CONFIG_PM
|
|
int (*suspend)(struct amd_i2c_common *i2c_common);
|
|
int (*resume)(struct amd_i2c_common *i2c_common);
|
|
#endif /* CONFIG_PM */
|
|
};
|
|
|
|
/**
|
|
* struct amd_mp2_dev - per PCI device context
|
|
* @pci_dev: PCI driver node
|
|
* @busses: MP2 devices may have up to two busses,
|
|
* each bus corresponding to an i2c adapter
|
|
* @mmio: iommapped registers
|
|
* @c2p_lock: controls access to the C2P mailbox shared between
|
|
* the two adapters
|
|
* @c2p_lock_busid: id of the adapter which locked c2p_lock
|
|
*/
|
|
struct amd_mp2_dev {
|
|
struct pci_dev *pci_dev;
|
|
struct amd_i2c_common *busses[2];
|
|
void __iomem *mmio;
|
|
struct mutex c2p_lock;
|
|
u8 c2p_lock_busid;
|
|
unsigned int probed;
|
|
};
|
|
|
|
#define ndev_pdev(ndev) ((ndev)->pci_dev)
|
|
#define ndev_name(ndev) pci_name(ndev_pdev(ndev))
|
|
#define ndev_dev(ndev) (&ndev_pdev(ndev)->dev)
|
|
#define work_amd_i2c_common(__work) \
|
|
container_of(__work, struct amd_i2c_common, work.work)
|
|
|
|
/* PCIe communication driver */
|
|
|
|
int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd);
|
|
int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable);
|
|
|
|
void amd_mp2_process_event(struct amd_i2c_common *i2c_common);
|
|
|
|
void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common);
|
|
|
|
int amd_mp2_register_cb(struct amd_i2c_common *i2c_common);
|
|
int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common);
|
|
|
|
struct amd_mp2_dev *amd_mp2_find_device(void);
|
|
|
|
static inline void amd_mp2_pm_runtime_get(struct amd_mp2_dev *mp2_dev)
|
|
{
|
|
pm_runtime_get_sync(&mp2_dev->pci_dev->dev);
|
|
}
|
|
|
|
static inline void amd_mp2_pm_runtime_put(struct amd_mp2_dev *mp2_dev)
|
|
{
|
|
pm_runtime_mark_last_busy(&mp2_dev->pci_dev->dev);
|
|
pm_runtime_put_autosuspend(&mp2_dev->pci_dev->dev);
|
|
}
|
|
|
|
#endif
|