Commit | Line | Data |
---|---|---|
9b748e0e | 1 | // SPDX-License-Identifier: GPL-2.0 |
9a58a333 | 2 | /* |
9b748e0e | 3 | * Driver for the Intel SCU IPC mechanism |
9a58a333 | 4 | * |
7c2e3c74 | 5 | * (C) Copyright 2008-2010,2015 Intel Corporation |
9a58a333 SD |
6 | * Author: Sreedhara DS (sreedhara.ds@intel.com) |
7 | * | |
c8440336 | 8 | * SCU running in ARC processor communicates with other entity running in IA |
9a58a333 SD |
9 | * core through IPC mechanism which in turn messaging between IA core ad SCU. |
10 | * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and | |
11 | * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with | |
12 | * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) | |
13 | * along with other APIs. | |
14 | */ | |
2d0554e8 | 15 | |
9a58a333 | 16 | #include <linux/delay.h> |
2d0554e8 | 17 | #include <linux/device.h> |
9a58a333 SD |
18 | #include <linux/errno.h> |
19 | #include <linux/init.h> | |
9a58a333 | 20 | #include <linux/interrupt.h> |
54b34aa0 | 21 | #include <linux/io.h> |
e0b4ab3b | 22 | #include <linux/iopoll.h> |
54b34aa0 MW |
23 | #include <linux/module.h> |
24 | #include <linux/slab.h> | |
2d0554e8 | 25 | |
c912ac66 | 26 | #include <linux/platform_data/x86/intel_scu_ipc.h> |
9a58a333 SD |
27 | |
28 | /* IPC defines the following message types */ | |
59aa78e3 | 29 | #define IPCMSG_PCNTRL 0xff /* Power controller unit read/write */ |
9a58a333 SD |
30 | |
31 | /* Command id associated with message IPCMSG_PCNTRL */ | |
32 | #define IPC_CMD_PCNTRL_W 0 /* Register write */ | |
33 | #define IPC_CMD_PCNTRL_R 1 /* Register read */ | |
34 | #define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ | |
35 | ||
9a58a333 SD |
36 | /* |
37 | * IPC register summary | |
38 | * | |
32d0e4a3 | 39 | * IPC register blocks are memory mapped at fixed address of PCI BAR 0. |
9a58a333 | 40 | * To read or write information to the SCU, driver writes to IPC-1 memory |
32d0e4a3 | 41 | * mapped registers. The following is the IPC mechanism |
9a58a333 SD |
42 | * |
43 | * 1. IA core cDMI interface claims this transaction and converts it to a | |
44 | * Transaction Layer Packet (TLP) message which is sent across the cDMI. | |
45 | * | |
46 | * 2. South Complex cDMI block receives this message and writes it to | |
47 | * the IPC-1 register block, causing an interrupt to the SCU | |
48 | * | |
49 | * 3. SCU firmware decodes this interrupt and IPC message and the appropriate | |
50 | * message handler is called within firmware. | |
51 | */ | |
52 | ||
51cd525d AV |
53 | #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ |
54 | #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ | |
ed12f295 | 55 | #define IPC_IOC 0x100 /* IPC command register IOC bit */ |
e97a1c98 | 56 | |
9a58a333 | 57 | struct intel_scu_ipc_dev { |
54b34aa0 MW |
58 | struct device dev; |
59 | struct resource mem; | |
f57fa185 | 60 | struct module *owner; |
54b34aa0 | 61 | int irq; |
9a58a333 | 62 | void __iomem *ipc_base; |
ed12f295 | 63 | struct completion cmd_complete; |
9a58a333 SD |
64 | }; |
65 | ||
e48b72a5 MW |
66 | #define IPC_STATUS 0x04 |
67 | #define IPC_STATUS_IRQ BIT(2) | |
19e2d350 MW |
68 | #define IPC_STATUS_ERR BIT(1) |
69 | #define IPC_STATUS_BUSY BIT(0) | |
e48b72a5 | 70 | |
9a58a333 | 71 | /* |
19e2d350 MW |
72 | * IPC Write/Read Buffers: |
73 | * 16 byte buffer for sending and receiving data to and from SCU. | |
9a58a333 | 74 | */ |
19e2d350 | 75 | #define IPC_WRITE_BUFFER 0x80 |
9a58a333 SD |
76 | #define IPC_READ_BUFFER 0x90 |
77 | ||
e7b7ab38 | 78 | /* Timeout in jiffies */ |
5c02b581 | 79 | #define IPC_TIMEOUT (10 * HZ) |
e7b7ab38 | 80 | |
54b34aa0 | 81 | static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */ |
9a58a333 SD |
82 | static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ |
83 | ||
54b34aa0 MW |
84 | static struct class intel_scu_ipc_class = { |
85 | .name = "intel_scu_ipc", | |
54b34aa0 MW |
86 | }; |
87 | ||
f57fa185 MW |
88 | /** |
89 | * intel_scu_ipc_dev_get() - Get SCU IPC instance | |
90 | * | |
91 | * The recommended new API takes SCU IPC instance as parameter and this | |
92 | * function can be called by driver to get the instance. This also makes | |
93 | * sure the driver providing the IPC functionality cannot be unloaded | |
94 | * while the caller has the instance. | |
95 | * | |
96 | * Call intel_scu_ipc_dev_put() to release the instance. | |
97 | * | |
98 | * Returns %NULL if SCU IPC is not currently available. | |
99 | */ | |
100 | struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void) | |
101 | { | |
102 | struct intel_scu_ipc_dev *scu = NULL; | |
103 | ||
104 | mutex_lock(&ipclock); | |
105 | if (ipcdev) { | |
106 | get_device(&ipcdev->dev); | |
107 | /* | |
108 | * Prevent the IPC provider from being unloaded while it | |
109 | * is being used. | |
110 | */ | |
111 | if (!try_module_get(ipcdev->owner)) | |
112 | put_device(&ipcdev->dev); | |
113 | else | |
114 | scu = ipcdev; | |
115 | } | |
116 | ||
117 | mutex_unlock(&ipclock); | |
118 | return scu; | |
119 | } | |
120 | EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get); | |
121 | ||
122 | /** | |
123 | * intel_scu_ipc_dev_put() - Put SCU IPC instance | |
124 | * @scu: SCU IPC instance | |
125 | * | |
126 | * This function releases the SCU IPC instance retrieved from | |
127 | * intel_scu_ipc_dev_get() and allows the driver providing IPC to be | |
128 | * unloaded. | |
129 | */ | |
130 | void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu) | |
131 | { | |
132 | if (scu) { | |
133 | module_put(scu->owner); | |
134 | put_device(&scu->dev); | |
135 | } | |
136 | } | |
137 | EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put); | |
138 | ||
139 | struct intel_scu_ipc_devres { | |
140 | struct intel_scu_ipc_dev *scu; | |
141 | }; | |
142 | ||
143 | static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res) | |
144 | { | |
145 | struct intel_scu_ipc_devres *dr = res; | |
146 | struct intel_scu_ipc_dev *scu = dr->scu; | |
147 | ||
148 | intel_scu_ipc_dev_put(scu); | |
149 | } | |
150 | ||
151 | /** | |
152 | * devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device | |
153 | * @dev: Device requesting the SCU IPC device | |
154 | * | |
155 | * The recommended new API takes SCU IPC instance as parameter and this | |
156 | * function can be called by driver to get the instance. This also makes | |
157 | * sure the driver providing the IPC functionality cannot be unloaded | |
158 | * while the caller has the instance. | |
159 | * | |
160 | * Returns %NULL if SCU IPC is not currently available. | |
161 | */ | |
162 | struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev) | |
163 | { | |
164 | struct intel_scu_ipc_devres *dr; | |
165 | struct intel_scu_ipc_dev *scu; | |
166 | ||
167 | dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL); | |
168 | if (!dr) | |
169 | return NULL; | |
170 | ||
171 | scu = intel_scu_ipc_dev_get(); | |
172 | if (!scu) { | |
173 | devres_free(dr); | |
174 | return NULL; | |
175 | } | |
176 | ||
177 | dr->scu = scu; | |
178 | devres_add(dev, dr); | |
179 | ||
180 | return scu; | |
181 | } | |
182 | EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get); | |
183 | ||
9a58a333 | 184 | /* |
b0b3f578 | 185 | * Send ipc command |
9a58a333 SD |
186 | * Command Register (Write Only): |
187 | * A write to this register results in an interrupt to the SCU core processor | |
188 | * Format: | |
189 | * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| | |
190 | */ | |
b0b3f578 | 191 | static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd) |
9a58a333 | 192 | { |
e48b72a5 MW |
193 | reinit_completion(&scu->cmd_complete); |
194 | writel(cmd | IPC_IOC, scu->ipc_base); | |
9a58a333 SD |
195 | } |
196 | ||
197 | /* | |
b0b3f578 | 198 | * Write ipc data |
9a58a333 SD |
199 | * IPC Write Buffer (Write Only): |
200 | * 16-byte buffer for sending data associated with IPC command to | |
201 | * SCU. Size of the data is specified in the IPC_COMMAND_REG register | |
202 | */ | |
b0b3f578 | 203 | static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset) |
9a58a333 | 204 | { |
19e2d350 | 205 | writel(data, scu->ipc_base + IPC_WRITE_BUFFER + offset); |
9a58a333 SD |
206 | } |
207 | ||
9a58a333 SD |
208 | /* |
209 | * Status Register (Read Only): | |
210 | * Driver will read this register to get the ready/busy status of the IPC | |
211 | * block and error status of the IPC command that was just processed by SCU | |
212 | * Format: | |
213 | * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| | |
214 | */ | |
b0b3f578 | 215 | static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu) |
9a58a333 | 216 | { |
19e2d350 | 217 | return __raw_readl(scu->ipc_base + IPC_STATUS); |
9a58a333 SD |
218 | } |
219 | ||
b0b3f578 AS |
220 | /* Read ipc byte data */ |
221 | static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset) | |
9a58a333 | 222 | { |
b0b3f578 | 223 | return readb(scu->ipc_base + IPC_READ_BUFFER + offset); |
9a58a333 SD |
224 | } |
225 | ||
b0b3f578 AS |
226 | /* Read ipc u32 data */ |
227 | static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset) | |
9a58a333 | 228 | { |
b0b3f578 | 229 | return readl(scu->ipc_base + IPC_READ_BUFFER + offset); |
9a58a333 SD |
230 | } |
231 | ||
7c2e3c74 | 232 | /* Wait till scu status is busy */ |
b0b3f578 | 233 | static inline int busy_loop(struct intel_scu_ipc_dev *scu) |
9a58a333 | 234 | { |
e0b4ab3b SB |
235 | u8 status; |
236 | int err; | |
9a58a333 | 237 | |
e0b4ab3b SB |
238 | err = readx_poll_timeout(ipc_read_status, scu, status, !(status & IPC_STATUS_BUSY), |
239 | 100, jiffies_to_usecs(IPC_TIMEOUT)); | |
240 | if (err) | |
241 | return err; | |
f0295a36 | 242 | |
e0b4ab3b | 243 | return (status & IPC_STATUS_ERR) ? -EIO : 0; |
9a58a333 SD |
244 | } |
245 | ||
a0c5814b | 246 | /* Wait till ipc ioc interrupt is received or timeout in 10 HZ */ |
b0b3f578 | 247 | static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) |
ed12f295 KS |
248 | { |
249 | int status; | |
250 | ||
427fada6 | 251 | wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT); |
ed12f295 | 252 | |
b0b3f578 | 253 | status = ipc_read_status(scu); |
427fada6 SB |
254 | if (status & IPC_STATUS_BUSY) |
255 | return -ETIMEDOUT; | |
256 | ||
19e2d350 | 257 | if (status & IPC_STATUS_ERR) |
ed12f295 KS |
258 | return -EIO; |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
b0b3f578 | 263 | static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu) |
ed12f295 | 264 | { |
54b34aa0 | 265 | return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu); |
ed12f295 KS |
266 | } |
267 | ||
85e654c9 SB |
268 | static struct intel_scu_ipc_dev *intel_scu_ipc_get(struct intel_scu_ipc_dev *scu) |
269 | { | |
270 | u8 status; | |
271 | ||
272 | if (!scu) | |
273 | scu = ipcdev; | |
274 | if (!scu) | |
275 | return ERR_PTR(-ENODEV); | |
276 | ||
277 | status = ipc_read_status(scu); | |
278 | if (status & IPC_STATUS_BUSY) { | |
279 | dev_dbg(&scu->dev, "device is busy\n"); | |
280 | return ERR_PTR(-EBUSY); | |
281 | } | |
282 | ||
283 | return scu; | |
284 | } | |
285 | ||
9a58a333 | 286 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ |
f57fa185 MW |
287 | static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, |
288 | u32 count, u32 op, u32 id) | |
9a58a333 | 289 | { |
4707375f | 290 | int nc; |
9a58a333 | 291 | u32 offset = 0; |
ecb5646c | 292 | int err; |
8642d7f8 | 293 | u8 cbuf[IPC_WWBUF_SIZE]; |
9a58a333 SD |
294 | u32 *wbuf = (u32 *)&cbuf; |
295 | ||
ed6f2b4d AV |
296 | memset(cbuf, 0, sizeof(cbuf)); |
297 | ||
8642d7f8 | 298 | mutex_lock(&ipclock); |
85e654c9 SB |
299 | scu = intel_scu_ipc_get(scu); |
300 | if (IS_ERR(scu)) { | |
9a58a333 | 301 | mutex_unlock(&ipclock); |
85e654c9 | 302 | return PTR_ERR(scu); |
9a58a333 SD |
303 | } |
304 | ||
4707375f AC |
305 | for (nc = 0; nc < count; nc++, offset += 2) { |
306 | cbuf[offset] = addr[nc]; | |
307 | cbuf[offset + 1] = addr[nc] >> 8; | |
308 | } | |
9a58a333 | 309 | |
4707375f AC |
310 | if (id == IPC_CMD_PCNTRL_R) { |
311 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | |
b0b3f578 AS |
312 | ipc_data_writel(scu, wbuf[nc], offset); |
313 | ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op); | |
4707375f AC |
314 | } else if (id == IPC_CMD_PCNTRL_W) { |
315 | for (nc = 0; nc < count; nc++, offset += 1) | |
316 | cbuf[offset] = data[nc]; | |
317 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | |
b0b3f578 AS |
318 | ipc_data_writel(scu, wbuf[nc], offset); |
319 | ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op); | |
4707375f AC |
320 | } else if (id == IPC_CMD_PCNTRL_M) { |
321 | cbuf[offset] = data[0]; | |
322 | cbuf[offset + 1] = data[1]; | |
b0b3f578 AS |
323 | ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */ |
324 | ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op); | |
e3359fd5 | 325 | } |
9a58a333 | 326 | |
b0b3f578 | 327 | err = intel_scu_ipc_check_status(scu); |
c7094d1d | 328 | if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ |
9a58a333 | 329 | /* Workaround: values are read as 0 without memcpy_fromio */ |
b0b3f578 | 330 | memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16); |
4707375f | 331 | for (nc = 0; nc < count; nc++) |
b0b3f578 | 332 | data[nc] = ipc_data_readb(scu, nc); |
9a58a333 SD |
333 | } |
334 | mutex_unlock(&ipclock); | |
335 | return err; | |
336 | } | |
337 | ||
338 | /** | |
f57fa185 MW |
339 | * intel_scu_ipc_dev_ioread8() - Read a byte via the SCU |
340 | * @scu: Optional SCU IPC instance | |
8b236565 MW |
341 | * @addr: Register on SCU |
342 | * @data: Return pointer for read byte | |
9a58a333 | 343 | * |
8b236565 MW |
344 | * Read a single register. Returns %0 on success or an error code. All |
345 | * locking between SCU accesses is handled for the caller. | |
9a58a333 | 346 | * |
8b236565 | 347 | * This function may sleep. |
9a58a333 | 348 | */ |
f57fa185 | 349 | int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data) |
9a58a333 | 350 | { |
f57fa185 | 351 | return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); |
9a58a333 | 352 | } |
f57fa185 | 353 | EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8); |
9a58a333 | 354 | |
9a58a333 | 355 | /** |
f57fa185 MW |
356 | * intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU |
357 | * @scu: Optional SCU IPC instance | |
8b236565 MW |
358 | * @addr: Register on SCU |
359 | * @data: Byte to write | |
9a58a333 | 360 | * |
8b236565 MW |
361 | * Write a single register. Returns %0 on success or an error code. All |
362 | * locking between SCU accesses is handled for the caller. | |
9a58a333 | 363 | * |
8b236565 | 364 | * This function may sleep. |
9a58a333 | 365 | */ |
f57fa185 | 366 | int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data) |
9a58a333 | 367 | { |
f57fa185 | 368 | return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); |
9a58a333 | 369 | } |
f57fa185 | 370 | EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8); |
9a58a333 | 371 | |
9a58a333 | 372 | /** |
f57fa185 MW |
373 | * intel_scu_ipc_dev_readv() - Read a set of registers |
374 | * @scu: Optional SCU IPC instance | |
8b236565 MW |
375 | * @addr: Register list |
376 | * @data: Bytes to return | |
377 | * @len: Length of array | |
9a58a333 | 378 | * |
8b236565 MW |
379 | * Read registers. Returns %0 on success or an error code. All locking |
380 | * between SCU accesses is handled for the caller. | |
9a58a333 | 381 | * |
8b236565 | 382 | * The largest array length permitted by the hardware is 5 items. |
9a58a333 | 383 | * |
8b236565 | 384 | * This function may sleep. |
9a58a333 | 385 | */ |
f57fa185 MW |
386 | int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, |
387 | size_t len) | |
9a58a333 | 388 | { |
f57fa185 | 389 | return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); |
9a58a333 | 390 | } |
f57fa185 | 391 | EXPORT_SYMBOL(intel_scu_ipc_dev_readv); |
9a58a333 SD |
392 | |
393 | /** | |
f57fa185 MW |
394 | * intel_scu_ipc_dev_writev() - Write a set of registers |
395 | * @scu: Optional SCU IPC instance | |
8b236565 MW |
396 | * @addr: Register list |
397 | * @data: Bytes to write | |
398 | * @len: Length of array | |
9a58a333 | 399 | * |
8b236565 MW |
400 | * Write registers. Returns %0 on success or an error code. All locking |
401 | * between SCU accesses is handled for the caller. | |
9a58a333 | 402 | * |
8b236565 | 403 | * The largest array length permitted by the hardware is 5 items. |
9a58a333 | 404 | * |
8b236565 | 405 | * This function may sleep. |
9a58a333 | 406 | */ |
f57fa185 MW |
407 | int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, |
408 | size_t len) | |
9a58a333 | 409 | { |
f57fa185 | 410 | return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); |
9a58a333 | 411 | } |
f57fa185 | 412 | EXPORT_SYMBOL(intel_scu_ipc_dev_writev); |
9a58a333 | 413 | |
9a58a333 | 414 | /** |
f57fa185 MW |
415 | * intel_scu_ipc_dev_update() - Update a register |
416 | * @scu: Optional SCU IPC instance | |
8b236565 | 417 | * @addr: Register address |
f57fa185 | 418 | * @data: Bits to update |
8b236565 | 419 | * @mask: Mask of bits to update |
9a58a333 | 420 | * |
8b236565 MW |
421 | * Read-modify-write power control unit register. The first data argument |
422 | * must be register value and second is mask value mask is a bitmap that | |
423 | * indicates which bits to update. %0 = masked. Don't modify this bit, %1 = | |
424 | * modify this bit. returns %0 on success or an error code. | |
9a58a333 | 425 | * |
8b236565 MW |
426 | * This function may sleep. Locking between SCU accesses is handled |
427 | * for the caller. | |
9a58a333 | 428 | */ |
f57fa185 MW |
429 | int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data, |
430 | u8 mask) | |
9a58a333 | 431 | { |
f57fa185 MW |
432 | u8 tmp[2] = { data, mask }; |
433 | return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); | |
9a58a333 | 434 | } |
f57fa185 | 435 | EXPORT_SYMBOL(intel_scu_ipc_dev_update); |
9a58a333 | 436 | |
9a58a333 | 437 | /** |
f57fa185 MW |
438 | * intel_scu_ipc_dev_simple_command() - Send a simple command |
439 | * @scu: Optional SCU IPC instance | |
8b236565 MW |
440 | * @cmd: Command |
441 | * @sub: Sub type | |
9a58a333 | 442 | * |
8b236565 MW |
443 | * Issue a simple command to the SCU. Do not use this interface if you must |
444 | * then access data as any data values may be overwritten by another SCU | |
445 | * access by the time this function returns. | |
9a58a333 | 446 | * |
8b236565 MW |
447 | * This function may sleep. Locking for SCU accesses is handled for the |
448 | * caller. | |
9a58a333 | 449 | */ |
f57fa185 MW |
450 | int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd, |
451 | int sub) | |
9a58a333 | 452 | { |
ea608f25 | 453 | u32 cmdval; |
ecb5646c | 454 | int err; |
9a58a333 SD |
455 | |
456 | mutex_lock(&ipclock); | |
85e654c9 SB |
457 | scu = intel_scu_ipc_get(scu); |
458 | if (IS_ERR(scu)) { | |
9a58a333 | 459 | mutex_unlock(&ipclock); |
85e654c9 | 460 | return PTR_ERR(scu); |
9a58a333 | 461 | } |
85e654c9 | 462 | |
ea608f25 MW |
463 | cmdval = sub << 12 | cmd; |
464 | ipc_command(scu, cmdval); | |
b0b3f578 | 465 | err = intel_scu_ipc_check_status(scu); |
9a58a333 | 466 | mutex_unlock(&ipclock); |
ea608f25 MW |
467 | if (err) |
468 | dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); | |
9a58a333 SD |
469 | return err; |
470 | } | |
f57fa185 | 471 | EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command); |
9a58a333 SD |
472 | |
473 | /** | |
b38d4ef1 | 474 | * intel_scu_ipc_dev_command_with_size() - Command with data |
f57fa185 | 475 | * @scu: Optional SCU IPC instance |
8b236565 MW |
476 | * @cmd: Command |
477 | * @sub: Sub type | |
478 | * @in: Input data | |
f57fa185 MW |
479 | * @inlen: Input length in bytes |
480 | * @size: Input size written to the IPC command register in whatever | |
481 | * units (dword, byte) the particular firmware requires. Normally | |
482 | * should be the same as @inlen. | |
8b236565 | 483 | * @out: Output data |
f57fa185 | 484 | * @outlen: Output length in bytes |
9a58a333 | 485 | * |
8b236565 MW |
486 | * Issue a command to the SCU which involves data transfers. Do the |
487 | * data copies under the lock but leave it for the caller to interpret. | |
9a58a333 | 488 | */ |
f57fa185 MW |
489 | int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd, |
490 | int sub, const void *in, size_t inlen, | |
491 | size_t size, void *out, size_t outlen) | |
9a58a333 | 492 | { |
f57fa185 MW |
493 | size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32)); |
494 | size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32)); | |
495 | u32 cmdval, inbuf[4] = {}; | |
ecb5646c | 496 | int i, err; |
9a58a333 | 497 | |
f57fa185 MW |
498 | if (inbuflen > 4 || outbuflen > 4) |
499 | return -EINVAL; | |
500 | ||
9a58a333 | 501 | mutex_lock(&ipclock); |
85e654c9 SB |
502 | scu = intel_scu_ipc_get(scu); |
503 | if (IS_ERR(scu)) { | |
9a58a333 | 504 | mutex_unlock(&ipclock); |
85e654c9 | 505 | return PTR_ERR(scu); |
9a58a333 SD |
506 | } |
507 | ||
f57fa185 MW |
508 | memcpy(inbuf, in, inlen); |
509 | for (i = 0; i < inbuflen; i++) | |
510 | ipc_data_writel(scu, inbuf[i], 4 * i); | |
9a58a333 | 511 | |
f57fa185 | 512 | cmdval = (size << 16) | (sub << 12) | cmd; |
ea608f25 | 513 | ipc_command(scu, cmdval); |
b0b3f578 | 514 | err = intel_scu_ipc_check_status(scu); |
9a58a333 | 515 | |
c7094d1d | 516 | if (!err) { |
f57fa185 MW |
517 | u32 outbuf[4] = {}; |
518 | ||
519 | for (i = 0; i < outbuflen; i++) | |
520 | outbuf[i] = ipc_data_readl(scu, 4 * i); | |
521 | ||
522 | memcpy(out, outbuf, outlen); | |
c7094d1d | 523 | } |
9a58a333 SD |
524 | |
525 | mutex_unlock(&ipclock); | |
ea608f25 MW |
526 | if (err) |
527 | dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); | |
9a58a333 SD |
528 | return err; |
529 | } | |
f57fa185 | 530 | EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size); |
9a58a333 | 531 | |
9a58a333 SD |
532 | /* |
533 | * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 | |
534 | * When ioc bit is set to 1, caller api must wait for interrupt handler called | |
535 | * which in turn unlocks the caller api. Currently this is not used | |
536 | * | |
537 | * This is edge triggered so we need take no action to clear anything | |
538 | */ | |
539 | static irqreturn_t ioc(int irq, void *dev_id) | |
540 | { | |
b0b3f578 | 541 | struct intel_scu_ipc_dev *scu = dev_id; |
e48b72a5 | 542 | int status = ipc_read_status(scu); |
b0b3f578 | 543 | |
e48b72a5 MW |
544 | writel(status | IPC_STATUS_IRQ, scu->ipc_base + IPC_STATUS); |
545 | complete(&scu->cmd_complete); | |
ed12f295 | 546 | |
9a58a333 SD |
547 | return IRQ_HANDLED; |
548 | } | |
549 | ||
54b34aa0 MW |
550 | static void intel_scu_ipc_release(struct device *dev) |
551 | { | |
552 | struct intel_scu_ipc_dev *scu; | |
553 | ||
554 | scu = container_of(dev, struct intel_scu_ipc_dev, dev); | |
555 | if (scu->irq > 0) | |
556 | free_irq(scu->irq, scu); | |
557 | iounmap(scu->ipc_base); | |
558 | release_mem_region(scu->mem.start, resource_size(&scu->mem)); | |
559 | kfree(scu); | |
560 | } | |
561 | ||
9a58a333 | 562 | /** |
f57fa185 | 563 | * __intel_scu_ipc_register() - Register SCU IPC device |
54b34aa0 MW |
564 | * @parent: Parent device |
565 | * @scu_data: Data used to configure SCU IPC | |
f57fa185 | 566 | * @owner: Module registering the SCU IPC device |
9a58a333 | 567 | * |
54b34aa0 MW |
568 | * Call this function to register SCU IPC mechanism under @parent. |
569 | * Returns pointer to the new SCU IPC device or ERR_PTR() in case of | |
f57fa185 MW |
570 | * failure. The caller may use the returned instance if it needs to do |
571 | * SCU IPC calls itself. | |
9a58a333 | 572 | */ |
54b34aa0 | 573 | struct intel_scu_ipc_dev * |
f57fa185 MW |
574 | __intel_scu_ipc_register(struct device *parent, |
575 | const struct intel_scu_ipc_data *scu_data, | |
576 | struct module *owner) | |
9a58a333 | 577 | { |
694e523c | 578 | int err; |
54b34aa0 MW |
579 | struct intel_scu_ipc_dev *scu; |
580 | void __iomem *ipc_base; | |
9a58a333 | 581 | |
54b34aa0 MW |
582 | mutex_lock(&ipclock); |
583 | /* We support only one IPC */ | |
584 | if (ipcdev) { | |
585 | err = -EBUSY; | |
586 | goto err_unlock; | |
587 | } | |
9a58a333 | 588 | |
54b34aa0 MW |
589 | scu = kzalloc(sizeof(*scu), GFP_KERNEL); |
590 | if (!scu) { | |
591 | err = -ENOMEM; | |
592 | goto err_unlock; | |
593 | } | |
9a58a333 | 594 | |
f57fa185 | 595 | scu->owner = owner; |
54b34aa0 MW |
596 | scu->dev.parent = parent; |
597 | scu->dev.class = &intel_scu_ipc_class; | |
598 | scu->dev.release = intel_scu_ipc_release; | |
9a58a333 | 599 | |
54b34aa0 MW |
600 | if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem), |
601 | "intel_scu_ipc")) { | |
602 | err = -EBUSY; | |
603 | goto err_free; | |
604 | } | |
605 | ||
606 | ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem)); | |
607 | if (!ipc_base) { | |
608 | err = -ENOMEM; | |
609 | goto err_release; | |
610 | } | |
611 | ||
612 | scu->ipc_base = ipc_base; | |
613 | scu->mem = scu_data->mem; | |
614 | scu->irq = scu_data->irq; | |
b0b3f578 | 615 | init_completion(&scu->cmd_complete); |
ed12f295 | 616 | |
54b34aa0 MW |
617 | if (scu->irq > 0) { |
618 | err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu); | |
619 | if (err) | |
620 | goto err_unmap; | |
621 | } | |
9a58a333 | 622 | |
54b34aa0 MW |
623 | /* |
624 | * After this point intel_scu_ipc_release() takes care of | |
625 | * releasing the SCU IPC resources once refcount drops to zero. | |
626 | */ | |
0b3d0cb7 | 627 | dev_set_name(&scu->dev, "intel_scu_ipc"); |
54b34aa0 MW |
628 | err = device_register(&scu->dev); |
629 | if (err) { | |
630 | put_device(&scu->dev); | |
631 | goto err_unlock; | |
632 | } | |
822e423e AS |
633 | |
634 | /* Assign device at last */ | |
54b34aa0 MW |
635 | ipcdev = scu; |
636 | mutex_unlock(&ipclock); | |
822e423e | 637 | |
54b34aa0 | 638 | return scu; |
1da4b1c6 | 639 | |
54b34aa0 MW |
640 | err_unmap: |
641 | iounmap(ipc_base); | |
642 | err_release: | |
643 | release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem)); | |
644 | err_free: | |
645 | kfree(scu); | |
646 | err_unlock: | |
647 | mutex_unlock(&ipclock); | |
648 | ||
649 | return ERR_PTR(err); | |
9a58a333 | 650 | } |
f57fa185 | 651 | EXPORT_SYMBOL_GPL(__intel_scu_ipc_register); |
9a58a333 | 652 | |
7e18c89d MW |
653 | /** |
654 | * intel_scu_ipc_unregister() - Unregister SCU IPC | |
655 | * @scu: SCU IPC handle | |
656 | * | |
657 | * This unregisters the SCU IPC device and releases the acquired | |
658 | * resources once the refcount goes to zero. | |
659 | */ | |
660 | void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu) | |
661 | { | |
662 | mutex_lock(&ipclock); | |
663 | if (!WARN_ON(!ipcdev)) { | |
664 | ipcdev = NULL; | |
665 | device_unregister(&scu->dev); | |
666 | } | |
667 | mutex_unlock(&ipclock); | |
668 | } | |
669 | EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister); | |
670 | ||
671 | static void devm_intel_scu_ipc_unregister(struct device *dev, void *res) | |
672 | { | |
673 | struct intel_scu_ipc_devres *dr = res; | |
674 | struct intel_scu_ipc_dev *scu = dr->scu; | |
675 | ||
676 | intel_scu_ipc_unregister(scu); | |
677 | } | |
678 | ||
679 | /** | |
680 | * __devm_intel_scu_ipc_register() - Register managed SCU IPC device | |
681 | * @parent: Parent device | |
682 | * @scu_data: Data used to configure SCU IPC | |
683 | * @owner: Module registering the SCU IPC device | |
684 | * | |
685 | * Call this function to register managed SCU IPC mechanism under | |
686 | * @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in | |
687 | * case of failure. The caller may use the returned instance if it needs | |
688 | * to do SCU IPC calls itself. | |
689 | */ | |
690 | struct intel_scu_ipc_dev * | |
691 | __devm_intel_scu_ipc_register(struct device *parent, | |
692 | const struct intel_scu_ipc_data *scu_data, | |
693 | struct module *owner) | |
694 | { | |
695 | struct intel_scu_ipc_devres *dr; | |
696 | struct intel_scu_ipc_dev *scu; | |
697 | ||
698 | dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL); | |
699 | if (!dr) | |
700 | return NULL; | |
701 | ||
702 | scu = __intel_scu_ipc_register(parent, scu_data, owner); | |
703 | if (IS_ERR(scu)) { | |
704 | devres_free(dr); | |
705 | return scu; | |
706 | } | |
707 | ||
708 | dr->scu = scu; | |
709 | devres_add(parent, dr); | |
710 | ||
711 | return scu; | |
712 | } | |
713 | EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register); | |
714 | ||
54b34aa0 MW |
715 | static int __init intel_scu_ipc_init(void) |
716 | { | |
717 | return class_register(&intel_scu_ipc_class); | |
718 | } | |
719 | subsys_initcall(intel_scu_ipc_init); | |
9a58a333 | 720 | |
54b34aa0 MW |
721 | static void __exit intel_scu_ipc_exit(void) |
722 | { | |
723 | class_unregister(&intel_scu_ipc_class); | |
724 | } | |
725 | module_exit(intel_scu_ipc_exit); |