1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
/* 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
|