19fff0425STomas Winkler // SPDX-License-Identifier: GPL-2.0
2e5354107SSamuel Ortiz /*
3784304aeSAlexander Usyskin * Copyright (c) 2012-2023, Intel Corporation. All rights reserved.
4e5354107SSamuel Ortiz * Intel Management Engine Interface (Intel MEI) Linux driver
5e5354107SSamuel Ortiz */
6e5354107SSamuel Ortiz
7e5354107SSamuel Ortiz #include <linux/module.h>
8e5354107SSamuel Ortiz #include <linux/device.h>
9e5354107SSamuel Ortiz #include <linux/kernel.h>
10174cd4b1SIngo Molnar #include <linux/sched/signal.h>
11e5354107SSamuel Ortiz #include <linux/init.h>
12e5354107SSamuel Ortiz #include <linux/errno.h>
13e5354107SSamuel Ortiz #include <linux/slab.h>
14e5354107SSamuel Ortiz #include <linux/mutex.h>
15e5354107SSamuel Ortiz #include <linux/interrupt.h>
162266e58aSVitaly Lubart #include <linux/scatterlist.h>
17e5354107SSamuel Ortiz #include <linux/mei_cl_bus.h>
18e5354107SSamuel Ortiz
19e5354107SSamuel Ortiz #include "mei_dev.h"
203e833295SSamuel Ortiz #include "client.h"
21e5354107SSamuel Ortiz
22*d69d8048SGreg Kroah-Hartman #define to_mei_cl_driver(d) container_of_const(d, struct mei_cl_driver, driver)
23e5354107SSamuel Ortiz
2462382997STomas Winkler /**
2562382997STomas Winkler * __mei_cl_send - internal client send (write)
2662382997STomas Winkler *
2762382997STomas Winkler * @cl: host client
2862382997STomas Winkler * @buf: buffer to send
2962382997STomas Winkler * @length: buffer length
3085261c1fSAlexander Usyskin * @vtag: virtual tag
31e0cb6b2fSAlexander Usyskin * @mode: sending mode
3262382997STomas Winkler *
3362382997STomas Winkler * Return: written size bytes or < 0 on error
3462382997STomas Winkler */
__mei_cl_send(struct mei_cl * cl,const u8 * buf,size_t length,u8 vtag,unsigned int mode)350912ef48SKrzysztof Kozlowski ssize_t __mei_cl_send(struct mei_cl *cl, const u8 *buf, size_t length, u8 vtag,
36e0cb6b2fSAlexander Usyskin unsigned int mode)
3762382997STomas Winkler {
3883f47eeaSAlexander Usyskin return __mei_cl_send_timeout(cl, buf, length, vtag, mode, MAX_SCHEDULE_TIMEOUT);
3983f47eeaSAlexander Usyskin }
4083f47eeaSAlexander Usyskin
4183f47eeaSAlexander Usyskin /**
4283f47eeaSAlexander Usyskin * __mei_cl_send_timeout - internal client send (write)
4383f47eeaSAlexander Usyskin *
4483f47eeaSAlexander Usyskin * @cl: host client
4583f47eeaSAlexander Usyskin * @buf: buffer to send
4683f47eeaSAlexander Usyskin * @length: buffer length
4783f47eeaSAlexander Usyskin * @vtag: virtual tag
4883f47eeaSAlexander Usyskin * @mode: sending mode
4983f47eeaSAlexander Usyskin * @timeout: send timeout in milliseconds.
5083f47eeaSAlexander Usyskin * effective only for blocking writes: the MEI_CL_IO_TX_BLOCKING mode bit is set.
5183f47eeaSAlexander Usyskin * set timeout to the MAX_SCHEDULE_TIMEOUT to maixum allowed wait.
5283f47eeaSAlexander Usyskin *
5383f47eeaSAlexander Usyskin * Return: written size bytes or < 0 on error
5483f47eeaSAlexander Usyskin */
__mei_cl_send_timeout(struct mei_cl * cl,const u8 * buf,size_t length,u8 vtag,unsigned int mode,unsigned long timeout)5583f47eeaSAlexander Usyskin ssize_t __mei_cl_send_timeout(struct mei_cl *cl, const u8 *buf, size_t length, u8 vtag,
5683f47eeaSAlexander Usyskin unsigned int mode, unsigned long timeout)
5783f47eeaSAlexander Usyskin {
5862382997STomas Winkler struct mei_device *bus;
596cbb097fSAlexander Usyskin struct mei_cl_cb *cb;
6062382997STomas Winkler ssize_t rets;
6162382997STomas Winkler
6262382997STomas Winkler if (WARN_ON(!cl || !cl->dev))
6362382997STomas Winkler return -ENODEV;
6462382997STomas Winkler
6562382997STomas Winkler bus = cl->dev;
6662382997STomas Winkler
6762382997STomas Winkler mutex_lock(&bus->device_lock);
6836edb140SAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED &&
6936edb140SAlexander Usyskin bus->dev_state != MEI_DEV_POWERING_DOWN) {
7015c13dfcSAlexander Usyskin rets = -ENODEV;
7115c13dfcSAlexander Usyskin goto out;
7215c13dfcSAlexander Usyskin }
7315c13dfcSAlexander Usyskin
7462382997STomas Winkler if (!mei_cl_is_connected(cl)) {
7562382997STomas Winkler rets = -ENODEV;
7662382997STomas Winkler goto out;
7762382997STomas Winkler }
7862382997STomas Winkler
7962382997STomas Winkler /* Check if we have an ME client device */
8062382997STomas Winkler if (!mei_me_cl_is_active(cl->me_cl)) {
8162382997STomas Winkler rets = -ENOTTY;
8262382997STomas Winkler goto out;
8362382997STomas Winkler }
8462382997STomas Winkler
85b398d53cSAlexander Usyskin if (vtag) {
86b398d53cSAlexander Usyskin /* Check if vtag is supported by client */
87b398d53cSAlexander Usyskin rets = mei_cl_vt_support_check(cl);
88b398d53cSAlexander Usyskin if (rets)
89b398d53cSAlexander Usyskin goto out;
90b398d53cSAlexander Usyskin }
91b398d53cSAlexander Usyskin
9262382997STomas Winkler if (length > mei_cl_mtu(cl)) {
9362382997STomas Winkler rets = -EFBIG;
9462382997STomas Winkler goto out;
9562382997STomas Winkler }
9662382997STomas Winkler
97af336cabSAlexander Usyskin while (cl->tx_cb_queued >= bus->tx_queue_limit) {
98af336cabSAlexander Usyskin mutex_unlock(&bus->device_lock);
99af336cabSAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait,
100af336cabSAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE ||
101af336cabSAlexander Usyskin (!mei_cl_is_connected(cl)));
102af336cabSAlexander Usyskin mutex_lock(&bus->device_lock);
103af336cabSAlexander Usyskin if (rets) {
104af336cabSAlexander Usyskin if (signal_pending(current))
105af336cabSAlexander Usyskin rets = -EINTR;
106af336cabSAlexander Usyskin goto out;
107af336cabSAlexander Usyskin }
108af336cabSAlexander Usyskin if (!mei_cl_is_connected(cl)) {
109af336cabSAlexander Usyskin rets = -ENODEV;
110af336cabSAlexander Usyskin goto out;
111af336cabSAlexander Usyskin }
112af336cabSAlexander Usyskin }
113af336cabSAlexander Usyskin
11462382997STomas Winkler cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
11562382997STomas Winkler if (!cb) {
11662382997STomas Winkler rets = -ENOMEM;
11762382997STomas Winkler goto out;
11862382997STomas Winkler }
11985261c1fSAlexander Usyskin cb->vtag = vtag;
12062382997STomas Winkler
121e0cb6b2fSAlexander Usyskin cb->internal = !!(mode & MEI_CL_IO_TX_INTERNAL);
122e0cb6b2fSAlexander Usyskin cb->blocking = !!(mode & MEI_CL_IO_TX_BLOCKING);
12362382997STomas Winkler memcpy(cb->buf.data, buf, length);
1245d5bc189STomas Winkler /* hack we point data to header */
1255d5bc189STomas Winkler if (mode & MEI_CL_IO_SGL) {
1265d5bc189STomas Winkler cb->ext_hdr = (struct mei_ext_hdr *)cb->buf.data;
1275d5bc189STomas Winkler cb->buf.data = NULL;
1285d5bc189STomas Winkler cb->buf.size = 0;
1295d5bc189STomas Winkler }
13062382997STomas Winkler
13183f47eeaSAlexander Usyskin rets = mei_cl_write(cl, cb, timeout);
13262382997STomas Winkler
1335d5bc189STomas Winkler if (mode & MEI_CL_IO_SGL && rets == 0)
1345d5bc189STomas Winkler rets = length;
1355d5bc189STomas Winkler
13662382997STomas Winkler out:
13762382997STomas Winkler mutex_unlock(&bus->device_lock);
13862382997STomas Winkler
13962382997STomas Winkler return rets;
14062382997STomas Winkler }
14162382997STomas Winkler
14262382997STomas Winkler /**
14362382997STomas Winkler * __mei_cl_recv - internal client receive (read)
14462382997STomas Winkler *
14562382997STomas Winkler * @cl: host client
146df7f5447STomas Winkler * @buf: buffer to receive
14762382997STomas Winkler * @length: buffer length
148076802d0SAlexander Usyskin * @mode: io mode
14985261c1fSAlexander Usyskin * @vtag: virtual tag
1509a7c0b69SAlexander Usyskin * @timeout: recv timeout, 0 for infinite timeout
15162382997STomas Winkler *
15262382997STomas Winkler * Return: read size in bytes of < 0 on error
15362382997STomas Winkler */
__mei_cl_recv(struct mei_cl * cl,u8 * buf,size_t length,u8 * vtag,unsigned int mode,unsigned long timeout)15485261c1fSAlexander Usyskin ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, u8 *vtag,
1559a7c0b69SAlexander Usyskin unsigned int mode, unsigned long timeout)
15662382997STomas Winkler {
15762382997STomas Winkler struct mei_device *bus;
15862382997STomas Winkler struct mei_cl_cb *cb;
15962382997STomas Winkler size_t r_length;
16062382997STomas Winkler ssize_t rets;
161076802d0SAlexander Usyskin bool nonblock = !!(mode & MEI_CL_IO_RX_NONBLOCK);
16262382997STomas Winkler
16362382997STomas Winkler if (WARN_ON(!cl || !cl->dev))
16462382997STomas Winkler return -ENODEV;
16562382997STomas Winkler
16662382997STomas Winkler bus = cl->dev;
16762382997STomas Winkler
16862382997STomas Winkler mutex_lock(&bus->device_lock);
16936edb140SAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED &&
17036edb140SAlexander Usyskin bus->dev_state != MEI_DEV_POWERING_DOWN) {
17115c13dfcSAlexander Usyskin rets = -ENODEV;
17215c13dfcSAlexander Usyskin goto out;
17315c13dfcSAlexander Usyskin }
17462382997STomas Winkler
17562382997STomas Winkler cb = mei_cl_read_cb(cl, NULL);
17662382997STomas Winkler if (cb)
17762382997STomas Winkler goto copy;
17862382997STomas Winkler
17962382997STomas Winkler rets = mei_cl_read_start(cl, length, NULL);
18062382997STomas Winkler if (rets && rets != -EBUSY)
18162382997STomas Winkler goto out;
18262382997STomas Winkler
183076802d0SAlexander Usyskin if (nonblock) {
184076802d0SAlexander Usyskin rets = -EAGAIN;
185076802d0SAlexander Usyskin goto out;
186076802d0SAlexander Usyskin }
187076802d0SAlexander Usyskin
18862382997STomas Winkler /* wait on event only if there is no other waiter */
1891eb5bd4dSAlexander Usyskin /* synchronized under device mutex */
1901eb5bd4dSAlexander Usyskin if (!waitqueue_active(&cl->rx_wait)) {
19162382997STomas Winkler
19262382997STomas Winkler mutex_unlock(&bus->device_lock);
19362382997STomas Winkler
1949a7c0b69SAlexander Usyskin if (timeout) {
1959a7c0b69SAlexander Usyskin rets = wait_event_interruptible_timeout
1969a7c0b69SAlexander Usyskin (cl->rx_wait,
197d1376f3dSAlexander Usyskin mei_cl_read_cb(cl, NULL) ||
1989a7c0b69SAlexander Usyskin (!mei_cl_is_connected(cl)),
1999a7c0b69SAlexander Usyskin msecs_to_jiffies(timeout));
2009a7c0b69SAlexander Usyskin if (rets == 0)
2019a7c0b69SAlexander Usyskin return -ETIME;
2029a7c0b69SAlexander Usyskin if (rets < 0) {
20362382997STomas Winkler if (signal_pending(current))
20462382997STomas Winkler return -EINTR;
20562382997STomas Winkler return -ERESTARTSYS;
20662382997STomas Winkler }
2079a7c0b69SAlexander Usyskin } else {
2089a7c0b69SAlexander Usyskin if (wait_event_interruptible
2099a7c0b69SAlexander Usyskin (cl->rx_wait,
210d1376f3dSAlexander Usyskin mei_cl_read_cb(cl, NULL) ||
2119a7c0b69SAlexander Usyskin (!mei_cl_is_connected(cl)))) {
2129a7c0b69SAlexander Usyskin if (signal_pending(current))
2139a7c0b69SAlexander Usyskin return -EINTR;
2149a7c0b69SAlexander Usyskin return -ERESTARTSYS;
2159a7c0b69SAlexander Usyskin }
2169a7c0b69SAlexander Usyskin }
21762382997STomas Winkler
21862382997STomas Winkler mutex_lock(&bus->device_lock);
21962382997STomas Winkler
22062382997STomas Winkler if (!mei_cl_is_connected(cl)) {
2212d4d5481STomas Winkler rets = -ENODEV;
22262382997STomas Winkler goto out;
22362382997STomas Winkler }
22462382997STomas Winkler }
22562382997STomas Winkler
22662382997STomas Winkler cb = mei_cl_read_cb(cl, NULL);
22762382997STomas Winkler if (!cb) {
22862382997STomas Winkler rets = 0;
22962382997STomas Winkler goto out;
23062382997STomas Winkler }
23162382997STomas Winkler
23262382997STomas Winkler copy:
23362382997STomas Winkler if (cb->status) {
23462382997STomas Winkler rets = cb->status;
23562382997STomas Winkler goto free;
23662382997STomas Winkler }
23762382997STomas Winkler
2385d5bc189STomas Winkler /* for the GSC type - copy the extended header to the buffer */
2395d5bc189STomas Winkler if (cb->ext_hdr && cb->ext_hdr->type == MEI_EXT_HDR_GSC) {
2405d5bc189STomas Winkler r_length = min_t(size_t, length, cb->ext_hdr->length * sizeof(u32));
2415d5bc189STomas Winkler memcpy(buf, cb->ext_hdr, r_length);
2425d5bc189STomas Winkler } else {
24362382997STomas Winkler r_length = min_t(size_t, length, cb->buf_idx);
24462382997STomas Winkler memcpy(buf, cb->buf.data, r_length);
2455d5bc189STomas Winkler }
24662382997STomas Winkler rets = r_length;
2475d5bc189STomas Winkler
24885261c1fSAlexander Usyskin if (vtag)
24985261c1fSAlexander Usyskin *vtag = cb->vtag;
25062382997STomas Winkler
25162382997STomas Winkler free:
252d1376f3dSAlexander Usyskin mei_cl_del_rd_completed(cl, cb);
25362382997STomas Winkler out:
25462382997STomas Winkler mutex_unlock(&bus->device_lock);
25562382997STomas Winkler
25662382997STomas Winkler return rets;
25762382997STomas Winkler }
25862382997STomas Winkler
25962382997STomas Winkler /**
26085261c1fSAlexander Usyskin * mei_cldev_send_vtag - me device send with vtag (write)
26185261c1fSAlexander Usyskin *
26285261c1fSAlexander Usyskin * @cldev: me client device
26385261c1fSAlexander Usyskin * @buf: buffer to send
26485261c1fSAlexander Usyskin * @length: buffer length
26585261c1fSAlexander Usyskin * @vtag: virtual tag
26685261c1fSAlexander Usyskin *
26785261c1fSAlexander Usyskin * Return:
26885261c1fSAlexander Usyskin * * written size in bytes
26985261c1fSAlexander Usyskin * * < 0 on error
27085261c1fSAlexander Usyskin */
27185261c1fSAlexander Usyskin
mei_cldev_send_vtag(struct mei_cl_device * cldev,const u8 * buf,size_t length,u8 vtag)2720912ef48SKrzysztof Kozlowski ssize_t mei_cldev_send_vtag(struct mei_cl_device *cldev, const u8 *buf,
2730912ef48SKrzysztof Kozlowski size_t length, u8 vtag)
27485261c1fSAlexander Usyskin {
27585261c1fSAlexander Usyskin struct mei_cl *cl = cldev->cl;
27685261c1fSAlexander Usyskin
27785261c1fSAlexander Usyskin return __mei_cl_send(cl, buf, length, vtag, MEI_CL_IO_TX_BLOCKING);
27885261c1fSAlexander Usyskin }
27985261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_send_vtag);
28085261c1fSAlexander Usyskin
28185261c1fSAlexander Usyskin /**
282cf439721SAlexander Usyskin * mei_cldev_send_vtag_timeout - me device send with vtag and timeout (write)
283cf439721SAlexander Usyskin *
284cf439721SAlexander Usyskin * @cldev: me client device
285cf439721SAlexander Usyskin * @buf: buffer to send
286cf439721SAlexander Usyskin * @length: buffer length
287cf439721SAlexander Usyskin * @vtag: virtual tag
288cf439721SAlexander Usyskin * @timeout: send timeout in milliseconds, 0 for infinite timeout
289cf439721SAlexander Usyskin *
290cf439721SAlexander Usyskin * Return:
291cf439721SAlexander Usyskin * * written size in bytes
292cf439721SAlexander Usyskin * * < 0 on error
293cf439721SAlexander Usyskin */
294cf439721SAlexander Usyskin
mei_cldev_send_vtag_timeout(struct mei_cl_device * cldev,const u8 * buf,size_t length,u8 vtag,unsigned long timeout)295cf439721SAlexander Usyskin ssize_t mei_cldev_send_vtag_timeout(struct mei_cl_device *cldev, const u8 *buf,
296cf439721SAlexander Usyskin size_t length, u8 vtag, unsigned long timeout)
297cf439721SAlexander Usyskin {
298cf439721SAlexander Usyskin struct mei_cl *cl = cldev->cl;
299cf439721SAlexander Usyskin
300cf439721SAlexander Usyskin return __mei_cl_send_timeout(cl, buf, length, vtag, MEI_CL_IO_TX_BLOCKING, timeout);
301cf439721SAlexander Usyskin }
302cf439721SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_send_vtag_timeout);
303cf439721SAlexander Usyskin
304cf439721SAlexander Usyskin /**
30585261c1fSAlexander Usyskin * mei_cldev_recv_vtag - client receive with vtag (read)
30685261c1fSAlexander Usyskin *
30785261c1fSAlexander Usyskin * @cldev: me client device
30885261c1fSAlexander Usyskin * @buf: buffer to receive
30985261c1fSAlexander Usyskin * @length: buffer length
31085261c1fSAlexander Usyskin * @vtag: virtual tag
31185261c1fSAlexander Usyskin *
31285261c1fSAlexander Usyskin * Return:
31385261c1fSAlexander Usyskin * * read size in bytes
31485261c1fSAlexander Usyskin * * < 0 on error
31585261c1fSAlexander Usyskin */
31685261c1fSAlexander Usyskin
mei_cldev_recv_vtag(struct mei_cl_device * cldev,u8 * buf,size_t length,u8 * vtag)31785261c1fSAlexander Usyskin ssize_t mei_cldev_recv_vtag(struct mei_cl_device *cldev, u8 *buf, size_t length,
31885261c1fSAlexander Usyskin u8 *vtag)
31985261c1fSAlexander Usyskin {
32085261c1fSAlexander Usyskin struct mei_cl *cl = cldev->cl;
32185261c1fSAlexander Usyskin
32285261c1fSAlexander Usyskin return __mei_cl_recv(cl, buf, length, vtag, 0, 0);
32385261c1fSAlexander Usyskin }
32485261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_vtag);
32585261c1fSAlexander Usyskin
32685261c1fSAlexander Usyskin /**
32785261c1fSAlexander Usyskin * mei_cldev_recv_nonblock_vtag - non block client receive with vtag (read)
32885261c1fSAlexander Usyskin *
32985261c1fSAlexander Usyskin * @cldev: me client device
33085261c1fSAlexander Usyskin * @buf: buffer to receive
33185261c1fSAlexander Usyskin * @length: buffer length
33285261c1fSAlexander Usyskin * @vtag: virtual tag
33385261c1fSAlexander Usyskin *
33485261c1fSAlexander Usyskin * Return:
33585261c1fSAlexander Usyskin * * read size in bytes
33685261c1fSAlexander Usyskin * * -EAGAIN if function will block.
33785261c1fSAlexander Usyskin * * < 0 on other error
33885261c1fSAlexander Usyskin */
mei_cldev_recv_nonblock_vtag(struct mei_cl_device * cldev,u8 * buf,size_t length,u8 * vtag)33985261c1fSAlexander Usyskin ssize_t mei_cldev_recv_nonblock_vtag(struct mei_cl_device *cldev, u8 *buf,
34085261c1fSAlexander Usyskin size_t length, u8 *vtag)
34185261c1fSAlexander Usyskin {
34285261c1fSAlexander Usyskin struct mei_cl *cl = cldev->cl;
34385261c1fSAlexander Usyskin
34485261c1fSAlexander Usyskin return __mei_cl_recv(cl, buf, length, vtag, MEI_CL_IO_RX_NONBLOCK, 0);
34585261c1fSAlexander Usyskin }
34685261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock_vtag);
34785261c1fSAlexander Usyskin
34885261c1fSAlexander Usyskin /**
349cf439721SAlexander Usyskin * mei_cldev_recv_timeout - client receive with timeout (read)
350cf439721SAlexander Usyskin *
351cf439721SAlexander Usyskin * @cldev: me client device
352cf439721SAlexander Usyskin * @buf: buffer to receive
353cf439721SAlexander Usyskin * @length: buffer length
354cf439721SAlexander Usyskin * @timeout: send timeout in milliseconds, 0 for infinite timeout
355cf439721SAlexander Usyskin *
356cf439721SAlexander Usyskin * Return:
357cf439721SAlexander Usyskin * * read size in bytes
358cf439721SAlexander Usyskin * * < 0 on error
359cf439721SAlexander Usyskin */
mei_cldev_recv_timeout(struct mei_cl_device * cldev,u8 * buf,size_t length,unsigned long timeout)360cf439721SAlexander Usyskin ssize_t mei_cldev_recv_timeout(struct mei_cl_device *cldev, u8 *buf, size_t length,
361cf439721SAlexander Usyskin unsigned long timeout)
362cf439721SAlexander Usyskin {
363cf439721SAlexander Usyskin return mei_cldev_recv_vtag_timeout(cldev, buf, length, NULL, timeout);
364cf439721SAlexander Usyskin }
365cf439721SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_timeout);
366cf439721SAlexander Usyskin
367cf439721SAlexander Usyskin /**
368cf439721SAlexander Usyskin * mei_cldev_recv_vtag_timeout - client receive with vtag (read)
369cf439721SAlexander Usyskin *
370cf439721SAlexander Usyskin * @cldev: me client device
371cf439721SAlexander Usyskin * @buf: buffer to receive
372cf439721SAlexander Usyskin * @length: buffer length
373cf439721SAlexander Usyskin * @vtag: virtual tag
374cf439721SAlexander Usyskin * @timeout: recv timeout in milliseconds, 0 for infinite timeout
375cf439721SAlexander Usyskin *
376cf439721SAlexander Usyskin * Return:
377cf439721SAlexander Usyskin * * read size in bytes
378cf439721SAlexander Usyskin * * < 0 on error
379cf439721SAlexander Usyskin */
380cf439721SAlexander Usyskin
mei_cldev_recv_vtag_timeout(struct mei_cl_device * cldev,u8 * buf,size_t length,u8 * vtag,unsigned long timeout)381cf439721SAlexander Usyskin ssize_t mei_cldev_recv_vtag_timeout(struct mei_cl_device *cldev, u8 *buf, size_t length,
382cf439721SAlexander Usyskin u8 *vtag, unsigned long timeout)
383cf439721SAlexander Usyskin {
384cf439721SAlexander Usyskin struct mei_cl *cl = cldev->cl;
385cf439721SAlexander Usyskin
386cf439721SAlexander Usyskin return __mei_cl_recv(cl, buf, length, vtag, 0, timeout);
387cf439721SAlexander Usyskin }
388cf439721SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_vtag_timeout);
389cf439721SAlexander Usyskin
390cf439721SAlexander Usyskin /**
391d49dc5e7STomas Winkler * mei_cldev_send - me device send (write)
39262382997STomas Winkler *
39362382997STomas Winkler * @cldev: me client device
39462382997STomas Winkler * @buf: buffer to send
39562382997STomas Winkler * @length: buffer length
39662382997STomas Winkler *
39785261c1fSAlexander Usyskin * Return:
39885261c1fSAlexander Usyskin * * written size in bytes
39985261c1fSAlexander Usyskin * * < 0 on error
40062382997STomas Winkler */
mei_cldev_send(struct mei_cl_device * cldev,const u8 * buf,size_t length)4010912ef48SKrzysztof Kozlowski ssize_t mei_cldev_send(struct mei_cl_device *cldev, const u8 *buf, size_t length)
40262382997STomas Winkler {
40385261c1fSAlexander Usyskin return mei_cldev_send_vtag(cldev, buf, length, 0);
40462382997STomas Winkler }
405d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_send);
40662382997STomas Winkler
40762382997STomas Winkler /**
408cf439721SAlexander Usyskin * mei_cldev_send_timeout - me device send with timeout (write)
409cf439721SAlexander Usyskin *
410cf439721SAlexander Usyskin * @cldev: me client device
411cf439721SAlexander Usyskin * @buf: buffer to send
412cf439721SAlexander Usyskin * @length: buffer length
413cf439721SAlexander Usyskin * @timeout: send timeout in milliseconds, 0 for infinite timeout
414cf439721SAlexander Usyskin *
415cf439721SAlexander Usyskin * Return:
416cf439721SAlexander Usyskin * * written size in bytes
417cf439721SAlexander Usyskin * * < 0 on error
418cf439721SAlexander Usyskin */
mei_cldev_send_timeout(struct mei_cl_device * cldev,const u8 * buf,size_t length,unsigned long timeout)419cf439721SAlexander Usyskin ssize_t mei_cldev_send_timeout(struct mei_cl_device *cldev, const u8 *buf, size_t length,
420cf439721SAlexander Usyskin unsigned long timeout)
421cf439721SAlexander Usyskin {
422cf439721SAlexander Usyskin return mei_cldev_send_vtag_timeout(cldev, buf, length, 0, timeout);
423cf439721SAlexander Usyskin }
424cf439721SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_send_timeout);
425cf439721SAlexander Usyskin
426cf439721SAlexander Usyskin /**
42785261c1fSAlexander Usyskin * mei_cldev_recv - client receive (read)
42885261c1fSAlexander Usyskin *
42985261c1fSAlexander Usyskin * @cldev: me client device
43085261c1fSAlexander Usyskin * @buf: buffer to receive
43185261c1fSAlexander Usyskin * @length: buffer length
43285261c1fSAlexander Usyskin *
43385261c1fSAlexander Usyskin * Return: read size in bytes of < 0 on error
43485261c1fSAlexander Usyskin */
mei_cldev_recv(struct mei_cl_device * cldev,u8 * buf,size_t length)43585261c1fSAlexander Usyskin ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
43685261c1fSAlexander Usyskin {
43785261c1fSAlexander Usyskin return mei_cldev_recv_vtag(cldev, buf, length, NULL);
43885261c1fSAlexander Usyskin }
43985261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv);
44085261c1fSAlexander Usyskin
44185261c1fSAlexander Usyskin /**
442076802d0SAlexander Usyskin * mei_cldev_recv_nonblock - non block client receive (read)
443076802d0SAlexander Usyskin *
444076802d0SAlexander Usyskin * @cldev: me client device
445076802d0SAlexander Usyskin * @buf: buffer to receive
446076802d0SAlexander Usyskin * @length: buffer length
447076802d0SAlexander Usyskin *
448076802d0SAlexander Usyskin * Return: read size in bytes of < 0 on error
449076802d0SAlexander Usyskin * -EAGAIN if function will block.
450076802d0SAlexander Usyskin */
mei_cldev_recv_nonblock(struct mei_cl_device * cldev,u8 * buf,size_t length)451076802d0SAlexander Usyskin ssize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf,
452076802d0SAlexander Usyskin size_t length)
453076802d0SAlexander Usyskin {
45485261c1fSAlexander Usyskin return mei_cldev_recv_nonblock_vtag(cldev, buf, length, NULL);
455076802d0SAlexander Usyskin }
456076802d0SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock);
457076802d0SAlexander Usyskin
458076802d0SAlexander Usyskin /**
4597c7a6077SAlexander Usyskin * mei_cl_bus_rx_work - dispatch rx event for a bus device
46062382997STomas Winkler *
46162382997STomas Winkler * @work: work
46262382997STomas Winkler */
mei_cl_bus_rx_work(struct work_struct * work)4637c7a6077SAlexander Usyskin static void mei_cl_bus_rx_work(struct work_struct *work)
46462382997STomas Winkler {
46562382997STomas Winkler struct mei_cl_device *cldev;
466bc46b45aSAlexander Usyskin struct mei_device *bus;
46762382997STomas Winkler
4687c7a6077SAlexander Usyskin cldev = container_of(work, struct mei_cl_device, rx_work);
46962382997STomas Winkler
470bc46b45aSAlexander Usyskin bus = cldev->bus;
471bc46b45aSAlexander Usyskin
4727c7a6077SAlexander Usyskin if (cldev->rx_cb)
4737c7a6077SAlexander Usyskin cldev->rx_cb(cldev);
47462382997STomas Winkler
475bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock);
476c7a6252bSAlexander Usyskin if (mei_cl_is_connected(cldev->cl))
4773030dc05STomas Winkler mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
478bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock);
479bc46b45aSAlexander Usyskin }
4807c7a6077SAlexander Usyskin
4817c7a6077SAlexander Usyskin /**
4827c7a6077SAlexander Usyskin * mei_cl_bus_notif_work - dispatch FW notif event for a bus device
4837c7a6077SAlexander Usyskin *
4847c7a6077SAlexander Usyskin * @work: work
4857c7a6077SAlexander Usyskin */
mei_cl_bus_notif_work(struct work_struct * work)4867c7a6077SAlexander Usyskin static void mei_cl_bus_notif_work(struct work_struct *work)
4877c7a6077SAlexander Usyskin {
4887c7a6077SAlexander Usyskin struct mei_cl_device *cldev;
4897c7a6077SAlexander Usyskin
4907c7a6077SAlexander Usyskin cldev = container_of(work, struct mei_cl_device, notif_work);
4917c7a6077SAlexander Usyskin
4927c7a6077SAlexander Usyskin if (cldev->notif_cb)
4937c7a6077SAlexander Usyskin cldev->notif_cb(cldev);
49462382997STomas Winkler }
49562382997STomas Winkler
49662382997STomas Winkler /**
497bb2ef9c3SAlexander Usyskin * mei_cl_bus_notify_event - schedule notify cb on bus client
498bb2ef9c3SAlexander Usyskin *
499bb2ef9c3SAlexander Usyskin * @cl: host client
500850f8940STomas Winkler *
501850f8940STomas Winkler * Return: true if event was scheduled
502850f8940STomas Winkler * false if the client is not waiting for event
503bb2ef9c3SAlexander Usyskin */
mei_cl_bus_notify_event(struct mei_cl * cl)504850f8940STomas Winkler bool mei_cl_bus_notify_event(struct mei_cl *cl)
505bb2ef9c3SAlexander Usyskin {
506bb2ef9c3SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev;
507bb2ef9c3SAlexander Usyskin
5087c7a6077SAlexander Usyskin if (!cldev || !cldev->notif_cb)
509850f8940STomas Winkler return false;
510bb2ef9c3SAlexander Usyskin
511bb2ef9c3SAlexander Usyskin if (!cl->notify_ev)
512850f8940STomas Winkler return false;
513bb2ef9c3SAlexander Usyskin
5147c7a6077SAlexander Usyskin schedule_work(&cldev->notif_work);
515bb2ef9c3SAlexander Usyskin
516bb2ef9c3SAlexander Usyskin cl->notify_ev = false;
517850f8940STomas Winkler
518850f8940STomas Winkler return true;
519bb2ef9c3SAlexander Usyskin }
520bb2ef9c3SAlexander Usyskin
521bb2ef9c3SAlexander Usyskin /**
522a1f9ae2bSTomas Winkler * mei_cl_bus_rx_event - schedule rx event
52362382997STomas Winkler *
52462382997STomas Winkler * @cl: host client
525a1f9ae2bSTomas Winkler *
526a1f9ae2bSTomas Winkler * Return: true if event was scheduled
527a1f9ae2bSTomas Winkler * false if the client is not waiting for event
52862382997STomas Winkler */
mei_cl_bus_rx_event(struct mei_cl * cl)529a1f9ae2bSTomas Winkler bool mei_cl_bus_rx_event(struct mei_cl *cl)
53062382997STomas Winkler {
53162382997STomas Winkler struct mei_cl_device *cldev = cl->cldev;
53262382997STomas Winkler
5337c7a6077SAlexander Usyskin if (!cldev || !cldev->rx_cb)
534a1f9ae2bSTomas Winkler return false;
53562382997STomas Winkler
5367c7a6077SAlexander Usyskin schedule_work(&cldev->rx_work);
537a1f9ae2bSTomas Winkler
538a1f9ae2bSTomas Winkler return true;
53962382997STomas Winkler }
54062382997STomas Winkler
54162382997STomas Winkler /**
5427c7a6077SAlexander Usyskin * mei_cldev_register_rx_cb - register Rx event callback
54362382997STomas Winkler *
54462382997STomas Winkler * @cldev: me client devices
5457c7a6077SAlexander Usyskin * @rx_cb: callback function
54662382997STomas Winkler *
54762382997STomas Winkler * Return: 0 on success
54862382997STomas Winkler * -EALREADY if an callback is already registered
54962382997STomas Winkler * <0 on other errors
55062382997STomas Winkler */
mei_cldev_register_rx_cb(struct mei_cl_device * cldev,mei_cldev_cb_t rx_cb)5517c7a6077SAlexander Usyskin int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb)
55262382997STomas Winkler {
553bc46b45aSAlexander Usyskin struct mei_device *bus = cldev->bus;
55448168f45STomas Winkler int ret;
55548168f45STomas Winkler
5567c7a6077SAlexander Usyskin if (!rx_cb)
5577c7a6077SAlexander Usyskin return -EINVAL;
5587c7a6077SAlexander Usyskin if (cldev->rx_cb)
55962382997STomas Winkler return -EALREADY;
56062382997STomas Winkler
5617c7a6077SAlexander Usyskin cldev->rx_cb = rx_cb;
5627c7a6077SAlexander Usyskin INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work);
56362382997STomas Winkler
564bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock);
565c7a6252bSAlexander Usyskin if (mei_cl_is_connected(cldev->cl))
5663030dc05STomas Winkler ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
567c7a6252bSAlexander Usyskin else
568c7a6252bSAlexander Usyskin ret = -ENODEV;
569bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock);
570c2192bbcSAlexander Usyskin if (ret && ret != -EBUSY) {
571c2192bbcSAlexander Usyskin cancel_work_sync(&cldev->rx_work);
572c2192bbcSAlexander Usyskin cldev->rx_cb = NULL;
57348168f45STomas Winkler return ret;
574c2192bbcSAlexander Usyskin }
57562382997STomas Winkler
57662382997STomas Winkler return 0;
57762382997STomas Winkler }
5787c7a6077SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb);
5797c7a6077SAlexander Usyskin
5807c7a6077SAlexander Usyskin /**
5817c7a6077SAlexander Usyskin * mei_cldev_register_notif_cb - register FW notification event callback
5827c7a6077SAlexander Usyskin *
5837c7a6077SAlexander Usyskin * @cldev: me client devices
5847c7a6077SAlexander Usyskin * @notif_cb: callback function
5857c7a6077SAlexander Usyskin *
5867c7a6077SAlexander Usyskin * Return: 0 on success
5877c7a6077SAlexander Usyskin * -EALREADY if an callback is already registered
5887c7a6077SAlexander Usyskin * <0 on other errors
5897c7a6077SAlexander Usyskin */
mei_cldev_register_notif_cb(struct mei_cl_device * cldev,mei_cldev_cb_t notif_cb)5907c7a6077SAlexander Usyskin int mei_cldev_register_notif_cb(struct mei_cl_device *cldev,
5917c7a6077SAlexander Usyskin mei_cldev_cb_t notif_cb)
5927c7a6077SAlexander Usyskin {
5937c7a6077SAlexander Usyskin struct mei_device *bus = cldev->bus;
5947c7a6077SAlexander Usyskin int ret;
5957c7a6077SAlexander Usyskin
5967c7a6077SAlexander Usyskin if (!notif_cb)
5977c7a6077SAlexander Usyskin return -EINVAL;
5987c7a6077SAlexander Usyskin
5997c7a6077SAlexander Usyskin if (cldev->notif_cb)
6007c7a6077SAlexander Usyskin return -EALREADY;
6017c7a6077SAlexander Usyskin
6027c7a6077SAlexander Usyskin cldev->notif_cb = notif_cb;
6037c7a6077SAlexander Usyskin INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work);
6047c7a6077SAlexander Usyskin
6057c7a6077SAlexander Usyskin mutex_lock(&bus->device_lock);
6067c7a6077SAlexander Usyskin ret = mei_cl_notify_request(cldev->cl, NULL, 1);
6077c7a6077SAlexander Usyskin mutex_unlock(&bus->device_lock);
608c2192bbcSAlexander Usyskin if (ret) {
609c2192bbcSAlexander Usyskin cancel_work_sync(&cldev->notif_work);
610c2192bbcSAlexander Usyskin cldev->notif_cb = NULL;
6117c7a6077SAlexander Usyskin return ret;
612c2192bbcSAlexander Usyskin }
6137c7a6077SAlexander Usyskin
6147c7a6077SAlexander Usyskin return 0;
6157c7a6077SAlexander Usyskin }
6167c7a6077SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb);
61762382997STomas Winkler
61862382997STomas Winkler /**
619d49dc5e7STomas Winkler * mei_cldev_get_drvdata - driver data getter
62062382997STomas Winkler *
62162382997STomas Winkler * @cldev: mei client device
62262382997STomas Winkler *
62362382997STomas Winkler * Return: driver private data
62462382997STomas Winkler */
mei_cldev_get_drvdata(const struct mei_cl_device * cldev)625d49dc5e7STomas Winkler void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev)
62662382997STomas Winkler {
62762382997STomas Winkler return dev_get_drvdata(&cldev->dev);
62862382997STomas Winkler }
629d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata);
63062382997STomas Winkler
63162382997STomas Winkler /**
632d49dc5e7STomas Winkler * mei_cldev_set_drvdata - driver data setter
63362382997STomas Winkler *
63462382997STomas Winkler * @cldev: mei client device
63562382997STomas Winkler * @data: data to store
63662382997STomas Winkler */
mei_cldev_set_drvdata(struct mei_cl_device * cldev,void * data)637d49dc5e7STomas Winkler void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data)
63862382997STomas Winkler {
63962382997STomas Winkler dev_set_drvdata(&cldev->dev, data);
64062382997STomas Winkler }
641d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata);
64262382997STomas Winkler
64362382997STomas Winkler /**
644baeacd03STomas Winkler * mei_cldev_uuid - return uuid of the underlying me client
645baeacd03STomas Winkler *
646baeacd03STomas Winkler * @cldev: mei client device
647baeacd03STomas Winkler *
648baeacd03STomas Winkler * Return: me client uuid
649baeacd03STomas Winkler */
mei_cldev_uuid(const struct mei_cl_device * cldev)650baeacd03STomas Winkler const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev)
651baeacd03STomas Winkler {
652baeacd03STomas Winkler return mei_me_cl_uuid(cldev->me_cl);
653baeacd03STomas Winkler }
654baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_uuid);
655baeacd03STomas Winkler
656baeacd03STomas Winkler /**
657baeacd03STomas Winkler * mei_cldev_ver - return protocol version of the underlying me client
658baeacd03STomas Winkler *
659baeacd03STomas Winkler * @cldev: mei client device
660baeacd03STomas Winkler *
661baeacd03STomas Winkler * Return: me client protocol version
662baeacd03STomas Winkler */
mei_cldev_ver(const struct mei_cl_device * cldev)663baeacd03STomas Winkler u8 mei_cldev_ver(const struct mei_cl_device *cldev)
664baeacd03STomas Winkler {
665baeacd03STomas Winkler return mei_me_cl_ver(cldev->me_cl);
666baeacd03STomas Winkler }
667baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_ver);
668baeacd03STomas Winkler
669baeacd03STomas Winkler /**
67001a14edeSTomas Winkler * mei_cldev_enabled - check whether the device is enabled
67101a14edeSTomas Winkler *
67201a14edeSTomas Winkler * @cldev: mei client device
67301a14edeSTomas Winkler *
67401a14edeSTomas Winkler * Return: true if me client is initialized and connected
67501a14edeSTomas Winkler */
mei_cldev_enabled(const struct mei_cl_device * cldev)6760912ef48SKrzysztof Kozlowski bool mei_cldev_enabled(const struct mei_cl_device *cldev)
67701a14edeSTomas Winkler {
678c110cdb1SAlexander Usyskin return mei_cl_is_connected(cldev->cl);
67901a14edeSTomas Winkler }
68001a14edeSTomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enabled);
68101a14edeSTomas Winkler
68201a14edeSTomas Winkler /**
683257355a4STomas Winkler * mei_cl_bus_module_get - acquire module of the underlying
684257355a4STomas Winkler * hw driver.
685257355a4STomas Winkler *
686257355a4STomas Winkler * @cldev: mei client device
687257355a4STomas Winkler *
688257355a4STomas Winkler * Return: true on success; false if the module was removed.
689257355a4STomas Winkler */
mei_cl_bus_module_get(struct mei_cl_device * cldev)690257355a4STomas Winkler static bool mei_cl_bus_module_get(struct mei_cl_device *cldev)
691257355a4STomas Winkler {
692257355a4STomas Winkler return try_module_get(cldev->bus->dev->driver->owner);
693257355a4STomas Winkler }
694257355a4STomas Winkler
695257355a4STomas Winkler /**
696257355a4STomas Winkler * mei_cl_bus_module_put - release the underlying hw module.
697257355a4STomas Winkler *
698257355a4STomas Winkler * @cldev: mei client device
699257355a4STomas Winkler */
mei_cl_bus_module_put(struct mei_cl_device * cldev)700257355a4STomas Winkler static void mei_cl_bus_module_put(struct mei_cl_device *cldev)
701257355a4STomas Winkler {
702257355a4STomas Winkler module_put(cldev->bus->dev->driver->owner);
703257355a4STomas Winkler }
704257355a4STomas Winkler
705257355a4STomas Winkler /**
706e5617d2bSAlexander Usyskin * mei_cl_bus_vtag - get bus vtag entry wrapper
707e5617d2bSAlexander Usyskin * The tag for bus client is always first.
708e5617d2bSAlexander Usyskin *
709e5617d2bSAlexander Usyskin * @cl: host client
710e5617d2bSAlexander Usyskin *
711e5617d2bSAlexander Usyskin * Return: bus vtag or NULL
712e5617d2bSAlexander Usyskin */
mei_cl_bus_vtag(struct mei_cl * cl)713e5617d2bSAlexander Usyskin static inline struct mei_cl_vtag *mei_cl_bus_vtag(struct mei_cl *cl)
714e5617d2bSAlexander Usyskin {
715e5617d2bSAlexander Usyskin return list_first_entry_or_null(&cl->vtag_map,
716e5617d2bSAlexander Usyskin struct mei_cl_vtag, list);
717e5617d2bSAlexander Usyskin }
718e5617d2bSAlexander Usyskin
719e5617d2bSAlexander Usyskin /**
720e5617d2bSAlexander Usyskin * mei_cl_bus_vtag_alloc - add bus client entry to vtag map
721e5617d2bSAlexander Usyskin *
722e5617d2bSAlexander Usyskin * @cldev: me client device
723e5617d2bSAlexander Usyskin *
724e5617d2bSAlexander Usyskin * Return:
725e5617d2bSAlexander Usyskin * * 0 on success
726e5617d2bSAlexander Usyskin * * -ENOMEM if memory allocation failed
727e5617d2bSAlexander Usyskin */
mei_cl_bus_vtag_alloc(struct mei_cl_device * cldev)728e5617d2bSAlexander Usyskin static int mei_cl_bus_vtag_alloc(struct mei_cl_device *cldev)
729e5617d2bSAlexander Usyskin {
730e5617d2bSAlexander Usyskin struct mei_cl *cl = cldev->cl;
731e5617d2bSAlexander Usyskin struct mei_cl_vtag *cl_vtag;
732e5617d2bSAlexander Usyskin
733e5617d2bSAlexander Usyskin /*
734e5617d2bSAlexander Usyskin * Bail out if the client does not supports vtags
735e5617d2bSAlexander Usyskin * or has already allocated one
736e5617d2bSAlexander Usyskin */
737e5617d2bSAlexander Usyskin if (mei_cl_vt_support_check(cl) || mei_cl_bus_vtag(cl))
738e5617d2bSAlexander Usyskin return 0;
739e5617d2bSAlexander Usyskin
740e5617d2bSAlexander Usyskin cl_vtag = mei_cl_vtag_alloc(NULL, 0);
741e5617d2bSAlexander Usyskin if (IS_ERR(cl_vtag))
742e5617d2bSAlexander Usyskin return -ENOMEM;
743e5617d2bSAlexander Usyskin
744e5617d2bSAlexander Usyskin list_add_tail(&cl_vtag->list, &cl->vtag_map);
745e5617d2bSAlexander Usyskin
746e5617d2bSAlexander Usyskin return 0;
747e5617d2bSAlexander Usyskin }
748e5617d2bSAlexander Usyskin
749e5617d2bSAlexander Usyskin /**
750e5617d2bSAlexander Usyskin * mei_cl_bus_vtag_free - remove the bus entry from vtag map
751e5617d2bSAlexander Usyskin *
752e5617d2bSAlexander Usyskin * @cldev: me client device
753e5617d2bSAlexander Usyskin */
mei_cl_bus_vtag_free(struct mei_cl_device * cldev)754e5617d2bSAlexander Usyskin static void mei_cl_bus_vtag_free(struct mei_cl_device *cldev)
755e5617d2bSAlexander Usyskin {
756e5617d2bSAlexander Usyskin struct mei_cl *cl = cldev->cl;
757e5617d2bSAlexander Usyskin struct mei_cl_vtag *cl_vtag;
758e5617d2bSAlexander Usyskin
759e5617d2bSAlexander Usyskin cl_vtag = mei_cl_bus_vtag(cl);
760e5617d2bSAlexander Usyskin if (!cl_vtag)
761e5617d2bSAlexander Usyskin return;
762e5617d2bSAlexander Usyskin
763e5617d2bSAlexander Usyskin list_del(&cl_vtag->list);
764e5617d2bSAlexander Usyskin kfree(cl_vtag);
765e5617d2bSAlexander Usyskin }
766e5617d2bSAlexander Usyskin
mei_cldev_dma_map(struct mei_cl_device * cldev,u8 buffer_id,size_t size)7672cca3465SAlexander Usyskin void *mei_cldev_dma_map(struct mei_cl_device *cldev, u8 buffer_id, size_t size)
7682cca3465SAlexander Usyskin {
7692cca3465SAlexander Usyskin struct mei_device *bus;
7702cca3465SAlexander Usyskin struct mei_cl *cl;
7712cca3465SAlexander Usyskin int ret;
7722cca3465SAlexander Usyskin
7732cca3465SAlexander Usyskin if (!cldev || !buffer_id || !size)
7742cca3465SAlexander Usyskin return ERR_PTR(-EINVAL);
7752cca3465SAlexander Usyskin
7762cca3465SAlexander Usyskin if (!IS_ALIGNED(size, MEI_FW_PAGE_SIZE)) {
7772cca3465SAlexander Usyskin dev_err(&cldev->dev, "Map size should be aligned to %lu\n",
7782cca3465SAlexander Usyskin MEI_FW_PAGE_SIZE);
7792cca3465SAlexander Usyskin return ERR_PTR(-EINVAL);
7802cca3465SAlexander Usyskin }
7812cca3465SAlexander Usyskin
7822cca3465SAlexander Usyskin cl = cldev->cl;
7832cca3465SAlexander Usyskin bus = cldev->bus;
7842cca3465SAlexander Usyskin
7852cca3465SAlexander Usyskin mutex_lock(&bus->device_lock);
7862cca3465SAlexander Usyskin if (cl->state == MEI_FILE_UNINITIALIZED) {
7872cca3465SAlexander Usyskin ret = mei_cl_link(cl);
7882cca3465SAlexander Usyskin if (ret)
789a4386685SAlexander Usyskin goto notlinked;
7902cca3465SAlexander Usyskin /* update pointers */
7912cca3465SAlexander Usyskin cl->cldev = cldev;
7922cca3465SAlexander Usyskin }
7932cca3465SAlexander Usyskin
7942cca3465SAlexander Usyskin ret = mei_cl_dma_alloc_and_map(cl, NULL, buffer_id, size);
795a4386685SAlexander Usyskin if (ret)
796a4386685SAlexander Usyskin mei_cl_unlink(cl);
797a4386685SAlexander Usyskin notlinked:
7982cca3465SAlexander Usyskin mutex_unlock(&bus->device_lock);
7992cca3465SAlexander Usyskin if (ret)
8002cca3465SAlexander Usyskin return ERR_PTR(ret);
8012cca3465SAlexander Usyskin return cl->dma.vaddr;
8022cca3465SAlexander Usyskin }
8032cca3465SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_dma_map);
8042cca3465SAlexander Usyskin
mei_cldev_dma_unmap(struct mei_cl_device * cldev)8052cca3465SAlexander Usyskin int mei_cldev_dma_unmap(struct mei_cl_device *cldev)
8062cca3465SAlexander Usyskin {
8072cca3465SAlexander Usyskin struct mei_device *bus;
8082cca3465SAlexander Usyskin struct mei_cl *cl;
8092cca3465SAlexander Usyskin int ret;
8102cca3465SAlexander Usyskin
8112cca3465SAlexander Usyskin if (!cldev)
8122cca3465SAlexander Usyskin return -EINVAL;
8132cca3465SAlexander Usyskin
8142cca3465SAlexander Usyskin cl = cldev->cl;
8152cca3465SAlexander Usyskin bus = cldev->bus;
8162cca3465SAlexander Usyskin
8172cca3465SAlexander Usyskin mutex_lock(&bus->device_lock);
8182cca3465SAlexander Usyskin ret = mei_cl_dma_unmap(cl, NULL);
8192cca3465SAlexander Usyskin
8202cca3465SAlexander Usyskin mei_cl_flush_queues(cl, NULL);
8212cca3465SAlexander Usyskin mei_cl_unlink(cl);
8222cca3465SAlexander Usyskin mutex_unlock(&bus->device_lock);
8232cca3465SAlexander Usyskin return ret;
8242cca3465SAlexander Usyskin }
8252cca3465SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_dma_unmap);
8262cca3465SAlexander Usyskin
827e5617d2bSAlexander Usyskin /**
8285026c9cbSAlexander Usyskin * mei_cldev_enable - enable me client device
82962382997STomas Winkler * create connection with me client
83062382997STomas Winkler *
83162382997STomas Winkler * @cldev: me client device
83262382997STomas Winkler *
83362382997STomas Winkler * Return: 0 on success and < 0 on error
83462382997STomas Winkler */
mei_cldev_enable(struct mei_cl_device * cldev)835d49dc5e7STomas Winkler int mei_cldev_enable(struct mei_cl_device *cldev)
83662382997STomas Winkler {
8376009595aSTomas Winkler struct mei_device *bus = cldev->bus;
8386009595aSTomas Winkler struct mei_cl *cl;
8396009595aSTomas Winkler int ret;
84062382997STomas Winkler
8416009595aSTomas Winkler cl = cldev->cl;
84262382997STomas Winkler
8436009595aSTomas Winkler mutex_lock(&bus->device_lock);
84434f1166aSTomas Winkler if (cl->state == MEI_FILE_UNINITIALIZED) {
845c110cdb1SAlexander Usyskin ret = mei_cl_link(cl);
846c110cdb1SAlexander Usyskin if (ret)
847a4386685SAlexander Usyskin goto notlinked;
8486009595aSTomas Winkler /* update pointers */
8496009595aSTomas Winkler cl->cldev = cldev;
8506009595aSTomas Winkler }
85162382997STomas Winkler
85262382997STomas Winkler if (mei_cl_is_connected(cl)) {
8536009595aSTomas Winkler ret = 0;
8546009595aSTomas Winkler goto out;
85562382997STomas Winkler }
85662382997STomas Winkler
8576009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) {
8586009595aSTomas Winkler dev_err(&cldev->dev, "me client is not active\n");
8596009595aSTomas Winkler ret = -ENOTTY;
8606009595aSTomas Winkler goto out;
86162382997STomas Winkler }
86262382997STomas Winkler
863e5617d2bSAlexander Usyskin ret = mei_cl_bus_vtag_alloc(cldev);
864e5617d2bSAlexander Usyskin if (ret)
865e5617d2bSAlexander Usyskin goto out;
866e5617d2bSAlexander Usyskin
8676009595aSTomas Winkler ret = mei_cl_connect(cl, cldev->me_cl, NULL);
868e5617d2bSAlexander Usyskin if (ret < 0) {
8696009595aSTomas Winkler dev_err(&cldev->dev, "cannot connect\n");
870e5617d2bSAlexander Usyskin mei_cl_bus_vtag_free(cldev);
871e5617d2bSAlexander Usyskin }
8726009595aSTomas Winkler
8736009595aSTomas Winkler out:
874a4386685SAlexander Usyskin if (ret)
875a4386685SAlexander Usyskin mei_cl_unlink(cl);
876a4386685SAlexander Usyskin notlinked:
87762382997STomas Winkler mutex_unlock(&bus->device_lock);
87862382997STomas Winkler
8796009595aSTomas Winkler return ret;
88062382997STomas Winkler }
881d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enable);
88262382997STomas Winkler
88362382997STomas Winkler /**
88457080e88SAlexander Usyskin * mei_cldev_unregister_callbacks - internal wrapper for unregistering
88557080e88SAlexander Usyskin * callbacks.
88657080e88SAlexander Usyskin *
88757080e88SAlexander Usyskin * @cldev: client device
88857080e88SAlexander Usyskin */
mei_cldev_unregister_callbacks(struct mei_cl_device * cldev)88957080e88SAlexander Usyskin static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev)
89057080e88SAlexander Usyskin {
89157080e88SAlexander Usyskin if (cldev->rx_cb) {
89257080e88SAlexander Usyskin cancel_work_sync(&cldev->rx_work);
89357080e88SAlexander Usyskin cldev->rx_cb = NULL;
89457080e88SAlexander Usyskin }
89557080e88SAlexander Usyskin
89657080e88SAlexander Usyskin if (cldev->notif_cb) {
89757080e88SAlexander Usyskin cancel_work_sync(&cldev->notif_work);
89857080e88SAlexander Usyskin cldev->notif_cb = NULL;
89957080e88SAlexander Usyskin }
90057080e88SAlexander Usyskin }
90157080e88SAlexander Usyskin
90257080e88SAlexander Usyskin /**
903d49dc5e7STomas Winkler * mei_cldev_disable - disable me client device
90462382997STomas Winkler * disconnect form the me client
90562382997STomas Winkler *
90662382997STomas Winkler * @cldev: me client device
90762382997STomas Winkler *
90862382997STomas Winkler * Return: 0 on success and < 0 on error
90962382997STomas Winkler */
mei_cldev_disable(struct mei_cl_device * cldev)910d49dc5e7STomas Winkler int mei_cldev_disable(struct mei_cl_device *cldev)
91162382997STomas Winkler {
91262382997STomas Winkler struct mei_device *bus;
9136009595aSTomas Winkler struct mei_cl *cl;
9146009595aSTomas Winkler int err;
91562382997STomas Winkler
916c110cdb1SAlexander Usyskin if (!cldev)
91762382997STomas Winkler return -ENODEV;
91862382997STomas Winkler
9196009595aSTomas Winkler cl = cldev->cl;
9206009595aSTomas Winkler
9216009595aSTomas Winkler bus = cldev->bus;
92262382997STomas Winkler
92357080e88SAlexander Usyskin mei_cldev_unregister_callbacks(cldev);
92457080e88SAlexander Usyskin
92562382997STomas Winkler mutex_lock(&bus->device_lock);
92662382997STomas Winkler
927e5617d2bSAlexander Usyskin mei_cl_bus_vtag_free(cldev);
928e5617d2bSAlexander Usyskin
92962382997STomas Winkler if (!mei_cl_is_connected(cl)) {
9308d52af67STomas Winkler dev_dbg(bus->dev, "Already disconnected\n");
9318d52af67STomas Winkler err = 0;
9328d52af67STomas Winkler goto out;
9338d52af67STomas Winkler }
9348d52af67STomas Winkler
93562382997STomas Winkler err = mei_cl_disconnect(cl);
9366009595aSTomas Winkler if (err < 0)
9378d52af67STomas Winkler dev_err(bus->dev, "Could not disconnect from the ME client\n");
93862382997STomas Winkler
93969bf5313STomas Winkler out:
9402cca3465SAlexander Usyskin /* Flush queues and remove any pending read unless we have mapped DMA */
9412cca3465SAlexander Usyskin if (!cl->dma_mapped) {
9426009595aSTomas Winkler mei_cl_flush_queues(cl, NULL);
9436009595aSTomas Winkler mei_cl_unlink(cl);
9442cca3465SAlexander Usyskin }
9456009595aSTomas Winkler
94662382997STomas Winkler mutex_unlock(&bus->device_lock);
94762382997STomas Winkler return err;
94862382997STomas Winkler }
949d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_disable);
95062382997STomas Winkler
951688a9cceSTomas Winkler /**
9522266e58aSVitaly Lubart * mei_cldev_send_gsc_command - sends a gsc command, by sending
9532266e58aSVitaly Lubart * a gsl mei message to gsc and receiving reply from gsc
9542266e58aSVitaly Lubart *
9552266e58aSVitaly Lubart * @cldev: me client device
9562266e58aSVitaly Lubart * @client_id: client id to send the command to
9572266e58aSVitaly Lubart * @fence_id: fence id to send the command to
9582266e58aSVitaly Lubart * @sg_in: scatter gather list containing addresses for rx message buffer
9592266e58aSVitaly Lubart * @total_in_len: total length of data in 'in' sg, can be less than the sum of buffers sizes
9602266e58aSVitaly Lubart * @sg_out: scatter gather list containing addresses for tx message buffer
9612266e58aSVitaly Lubart *
9622266e58aSVitaly Lubart * Return:
9632266e58aSVitaly Lubart * * written size in bytes
9642266e58aSVitaly Lubart * * < 0 on error
9652266e58aSVitaly Lubart */
mei_cldev_send_gsc_command(struct mei_cl_device * cldev,u8 client_id,u32 fence_id,struct scatterlist * sg_in,size_t total_in_len,struct scatterlist * sg_out)9662266e58aSVitaly Lubart ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev,
9672266e58aSVitaly Lubart u8 client_id, u32 fence_id,
9682266e58aSVitaly Lubart struct scatterlist *sg_in,
9692266e58aSVitaly Lubart size_t total_in_len,
9702266e58aSVitaly Lubart struct scatterlist *sg_out)
9712266e58aSVitaly Lubart {
9722266e58aSVitaly Lubart struct mei_cl *cl;
9732266e58aSVitaly Lubart struct mei_device *bus;
9742266e58aSVitaly Lubart ssize_t ret = 0;
9752266e58aSVitaly Lubart
9762266e58aSVitaly Lubart struct mei_ext_hdr_gsc_h2f *ext_hdr;
9772266e58aSVitaly Lubart size_t buf_sz = sizeof(struct mei_ext_hdr_gsc_h2f);
9782266e58aSVitaly Lubart int sg_out_nents, sg_in_nents;
9792266e58aSVitaly Lubart int i;
9802266e58aSVitaly Lubart struct scatterlist *sg;
9812266e58aSVitaly Lubart struct mei_ext_hdr_gsc_f2h rx_msg;
9822266e58aSVitaly Lubart unsigned int sg_len;
9832266e58aSVitaly Lubart
9842266e58aSVitaly Lubart if (!cldev || !sg_in || !sg_out)
9852266e58aSVitaly Lubart return -EINVAL;
9862266e58aSVitaly Lubart
9872266e58aSVitaly Lubart cl = cldev->cl;
9882266e58aSVitaly Lubart bus = cldev->bus;
9892266e58aSVitaly Lubart
9902266e58aSVitaly Lubart dev_dbg(bus->dev, "client_id %u, fence_id %u\n", client_id, fence_id);
9912266e58aSVitaly Lubart
9922266e58aSVitaly Lubart if (!bus->hbm_f_gsc_supported)
9932266e58aSVitaly Lubart return -EOPNOTSUPP;
9942266e58aSVitaly Lubart
9952266e58aSVitaly Lubart sg_out_nents = sg_nents(sg_out);
9962266e58aSVitaly Lubart sg_in_nents = sg_nents(sg_in);
9972266e58aSVitaly Lubart /* at least one entry in tx and rx sgls must be present */
9982266e58aSVitaly Lubart if (sg_out_nents <= 0 || sg_in_nents <= 0)
9992266e58aSVitaly Lubart return -EINVAL;
10002266e58aSVitaly Lubart
10012266e58aSVitaly Lubart buf_sz += (sg_out_nents + sg_in_nents) * sizeof(struct mei_gsc_sgl);
10022266e58aSVitaly Lubart ext_hdr = kzalloc(buf_sz, GFP_KERNEL);
10032266e58aSVitaly Lubart if (!ext_hdr)
10042266e58aSVitaly Lubart return -ENOMEM;
10052266e58aSVitaly Lubart
10062266e58aSVitaly Lubart /* construct the GSC message */
10072266e58aSVitaly Lubart ext_hdr->hdr.type = MEI_EXT_HDR_GSC;
10082266e58aSVitaly Lubart ext_hdr->hdr.length = buf_sz / sizeof(u32); /* length is in dw */
10092266e58aSVitaly Lubart
10102266e58aSVitaly Lubart ext_hdr->client_id = client_id;
10112266e58aSVitaly Lubart ext_hdr->addr_type = GSC_ADDRESS_TYPE_PHYSICAL_SGL;
10122266e58aSVitaly Lubart ext_hdr->fence_id = fence_id;
10132266e58aSVitaly Lubart ext_hdr->input_address_count = sg_in_nents;
10142266e58aSVitaly Lubart ext_hdr->output_address_count = sg_out_nents;
10152266e58aSVitaly Lubart ext_hdr->reserved[0] = 0;
10162266e58aSVitaly Lubart ext_hdr->reserved[1] = 0;
10172266e58aSVitaly Lubart
10182266e58aSVitaly Lubart /* copy in-sgl to the message */
10192266e58aSVitaly Lubart for (i = 0, sg = sg_in; i < sg_in_nents; i++, sg++) {
10202266e58aSVitaly Lubart ext_hdr->sgl[i].low = lower_32_bits(sg_dma_address(sg));
10212266e58aSVitaly Lubart ext_hdr->sgl[i].high = upper_32_bits(sg_dma_address(sg));
10222266e58aSVitaly Lubart sg_len = min_t(unsigned int, sg_dma_len(sg), PAGE_SIZE);
10232266e58aSVitaly Lubart ext_hdr->sgl[i].length = (sg_len <= total_in_len) ? sg_len : total_in_len;
10242266e58aSVitaly Lubart total_in_len -= ext_hdr->sgl[i].length;
10252266e58aSVitaly Lubart }
10262266e58aSVitaly Lubart
10272266e58aSVitaly Lubart /* copy out-sgl to the message */
10282266e58aSVitaly Lubart for (i = sg_in_nents, sg = sg_out; i < sg_in_nents + sg_out_nents; i++, sg++) {
10292266e58aSVitaly Lubart ext_hdr->sgl[i].low = lower_32_bits(sg_dma_address(sg));
10302266e58aSVitaly Lubart ext_hdr->sgl[i].high = upper_32_bits(sg_dma_address(sg));
10312266e58aSVitaly Lubart sg_len = min_t(unsigned int, sg_dma_len(sg), PAGE_SIZE);
10322266e58aSVitaly Lubart ext_hdr->sgl[i].length = sg_len;
10332266e58aSVitaly Lubart }
10342266e58aSVitaly Lubart
10352266e58aSVitaly Lubart /* send the message to GSC */
10362266e58aSVitaly Lubart ret = __mei_cl_send(cl, (u8 *)ext_hdr, buf_sz, 0, MEI_CL_IO_SGL);
10372266e58aSVitaly Lubart if (ret < 0) {
10382266e58aSVitaly Lubart dev_err(bus->dev, "__mei_cl_send failed, returned %zd\n", ret);
10392266e58aSVitaly Lubart goto end;
10402266e58aSVitaly Lubart }
10412266e58aSVitaly Lubart if (ret != buf_sz) {
10422266e58aSVitaly Lubart dev_err(bus->dev, "__mei_cl_send returned %zd instead of expected %zd\n",
10432266e58aSVitaly Lubart ret, buf_sz);
10442266e58aSVitaly Lubart ret = -EIO;
10452266e58aSVitaly Lubart goto end;
10462266e58aSVitaly Lubart }
10472266e58aSVitaly Lubart
10482266e58aSVitaly Lubart /* receive the reply from GSC, note that at this point sg_in should contain the reply */
10492266e58aSVitaly Lubart ret = __mei_cl_recv(cl, (u8 *)&rx_msg, sizeof(rx_msg), NULL, MEI_CL_IO_SGL, 0);
10502266e58aSVitaly Lubart
10512266e58aSVitaly Lubart if (ret != sizeof(rx_msg)) {
10522266e58aSVitaly Lubart dev_err(bus->dev, "__mei_cl_recv returned %zd instead of expected %zd\n",
10532266e58aSVitaly Lubart ret, sizeof(rx_msg));
10542266e58aSVitaly Lubart if (ret >= 0)
10552266e58aSVitaly Lubart ret = -EIO;
10562266e58aSVitaly Lubart goto end;
10572266e58aSVitaly Lubart }
10582266e58aSVitaly Lubart
10592266e58aSVitaly Lubart /* check rx_msg.client_id and rx_msg.fence_id match the ones we send */
10602266e58aSVitaly Lubart if (rx_msg.client_id != client_id || rx_msg.fence_id != fence_id) {
10612266e58aSVitaly Lubart dev_err(bus->dev, "received client_id/fence_id %u/%u instead of %u/%u sent\n",
10622266e58aSVitaly Lubart rx_msg.client_id, rx_msg.fence_id, client_id, fence_id);
10632266e58aSVitaly Lubart ret = -EFAULT;
10642266e58aSVitaly Lubart goto end;
10652266e58aSVitaly Lubart }
10662266e58aSVitaly Lubart
10672266e58aSVitaly Lubart dev_dbg(bus->dev, "gsc command: successfully written %u bytes\n", rx_msg.written);
10682266e58aSVitaly Lubart ret = rx_msg.written;
10692266e58aSVitaly Lubart
10702266e58aSVitaly Lubart end:
10712266e58aSVitaly Lubart kfree(ext_hdr);
10722266e58aSVitaly Lubart return ret;
10732266e58aSVitaly Lubart }
10742266e58aSVitaly Lubart EXPORT_SYMBOL_GPL(mei_cldev_send_gsc_command);
10752266e58aSVitaly Lubart
10762266e58aSVitaly Lubart /**
1077688a9cceSTomas Winkler * mei_cl_device_find - find matching entry in the driver id table
1078688a9cceSTomas Winkler *
1079688a9cceSTomas Winkler * @cldev: me client device
1080688a9cceSTomas Winkler * @cldrv: me client driver
1081688a9cceSTomas Winkler *
1082688a9cceSTomas Winkler * Return: id on success; NULL if no id is matching
1083688a9cceSTomas Winkler */
1084688a9cceSTomas Winkler static const
mei_cl_device_find(const struct mei_cl_device * cldev,const struct mei_cl_driver * cldrv)10850912ef48SKrzysztof Kozlowski struct mei_cl_device_id *mei_cl_device_find(const struct mei_cl_device *cldev,
10860912ef48SKrzysztof Kozlowski const struct mei_cl_driver *cldrv)
1087e5354107SSamuel Ortiz {
1088e5354107SSamuel Ortiz const struct mei_cl_device_id *id;
1089c93b76b3STomas Winkler const uuid_le *uuid;
1090b26864caSTomas Winkler u8 version;
1091b26864caSTomas Winkler bool match;
1092e5354107SSamuel Ortiz
1093b37719c3STomas Winkler uuid = mei_me_cl_uuid(cldev->me_cl);
1094b26864caSTomas Winkler version = mei_me_cl_ver(cldev->me_cl);
1095e5354107SSamuel Ortiz
1096b37719c3STomas Winkler id = cldrv->id_table;
1097b144ce2dSGreg Kroah-Hartman while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) {
1098b144ce2dSGreg Kroah-Hartman if (!uuid_le_cmp(*uuid, id->uuid)) {
1099b26864caSTomas Winkler match = true;
1100688a9cceSTomas Winkler
1101b26864caSTomas Winkler if (cldev->name[0])
1102b26864caSTomas Winkler if (strncmp(cldev->name, id->name,
1103b26864caSTomas Winkler sizeof(id->name)))
1104b26864caSTomas Winkler match = false;
1105688a9cceSTomas Winkler
1106b26864caSTomas Winkler if (id->version != MEI_CL_VERSION_ANY)
1107b26864caSTomas Winkler if (id->version != version)
1108b26864caSTomas Winkler match = false;
1109b26864caSTomas Winkler if (match)
1110688a9cceSTomas Winkler return id;
1111c93b76b3STomas Winkler }
1112e5354107SSamuel Ortiz
1113e5354107SSamuel Ortiz id++;
1114e5354107SSamuel Ortiz }
1115e5354107SSamuel Ortiz
1116688a9cceSTomas Winkler return NULL;
1117688a9cceSTomas Winkler }
1118688a9cceSTomas Winkler
1119688a9cceSTomas Winkler /**
1120688a9cceSTomas Winkler * mei_cl_device_match - device match function
1121688a9cceSTomas Winkler *
1122688a9cceSTomas Winkler * @dev: device
1123688a9cceSTomas Winkler * @drv: driver
1124688a9cceSTomas Winkler *
1125688a9cceSTomas Winkler * Return: 1 if matching device was found 0 otherwise
1126688a9cceSTomas Winkler */
mei_cl_device_match(struct device * dev,const struct device_driver * drv)1127*d69d8048SGreg Kroah-Hartman static int mei_cl_device_match(struct device *dev, const struct device_driver *drv)
1128688a9cceSTomas Winkler {
11290912ef48SKrzysztof Kozlowski const struct mei_cl_device *cldev = to_mei_cl_device(dev);
11300912ef48SKrzysztof Kozlowski const struct mei_cl_driver *cldrv = to_mei_cl_driver(drv);
1131688a9cceSTomas Winkler const struct mei_cl_device_id *found_id;
1132688a9cceSTomas Winkler
113371ce7891STomas Winkler if (!cldev->do_match)
113471ce7891STomas Winkler return 0;
113571ce7891STomas Winkler
1136688a9cceSTomas Winkler if (!cldrv || !cldrv->id_table)
1137688a9cceSTomas Winkler return 0;
1138688a9cceSTomas Winkler
1139688a9cceSTomas Winkler found_id = mei_cl_device_find(cldev, cldrv);
1140688a9cceSTomas Winkler if (found_id)
1141688a9cceSTomas Winkler return 1;
1142688a9cceSTomas Winkler
1143e5354107SSamuel Ortiz return 0;
1144e5354107SSamuel Ortiz }
1145e5354107SSamuel Ortiz
1146feb8cd0fSTomas Winkler /**
1147feb8cd0fSTomas Winkler * mei_cl_device_probe - bus probe function
1148feb8cd0fSTomas Winkler *
1149feb8cd0fSTomas Winkler * @dev: device
1150feb8cd0fSTomas Winkler *
1151feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise
1152feb8cd0fSTomas Winkler */
mei_cl_device_probe(struct device * dev)1153e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev)
1154e5354107SSamuel Ortiz {
1155feb8cd0fSTomas Winkler struct mei_cl_device *cldev;
1156b37719c3STomas Winkler struct mei_cl_driver *cldrv;
1157feb8cd0fSTomas Winkler const struct mei_cl_device_id *id;
1158b9c79543SAlexey Khoroshilov int ret;
1159feb8cd0fSTomas Winkler
1160feb8cd0fSTomas Winkler cldev = to_mei_cl_device(dev);
1161feb8cd0fSTomas Winkler cldrv = to_mei_cl_driver(dev->driver);
1162e5354107SSamuel Ortiz
1163b37719c3STomas Winkler if (!cldrv || !cldrv->probe)
1164e5354107SSamuel Ortiz return -ENODEV;
1165e5354107SSamuel Ortiz
1166feb8cd0fSTomas Winkler id = mei_cl_device_find(cldev, cldrv);
1167feb8cd0fSTomas Winkler if (!id)
1168feb8cd0fSTomas Winkler return -ENODEV;
1169e5354107SSamuel Ortiz
1170b5958faaSAlexander Usyskin if (!mei_cl_bus_module_get(cldev)) {
1171b5958faaSAlexander Usyskin dev_err(&cldev->dev, "get hw module failed");
1172b5958faaSAlexander Usyskin return -ENODEV;
1173b5958faaSAlexander Usyskin }
1174b5958faaSAlexander Usyskin
1175b9c79543SAlexey Khoroshilov ret = cldrv->probe(cldev, id);
1176b5958faaSAlexander Usyskin if (ret) {
1177b5958faaSAlexander Usyskin mei_cl_bus_module_put(cldev);
1178b9c79543SAlexey Khoroshilov return ret;
1179b5958faaSAlexander Usyskin }
1180e5354107SSamuel Ortiz
1181b9c79543SAlexey Khoroshilov __module_get(THIS_MODULE);
1182b9c79543SAlexey Khoroshilov return 0;
1183e5354107SSamuel Ortiz }
1184e5354107SSamuel Ortiz
1185feb8cd0fSTomas Winkler /**
1186feb8cd0fSTomas Winkler * mei_cl_device_remove - remove device from the bus
1187feb8cd0fSTomas Winkler *
1188feb8cd0fSTomas Winkler * @dev: device
1189feb8cd0fSTomas Winkler *
1190feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise
1191feb8cd0fSTomas Winkler */
mei_cl_device_remove(struct device * dev)1192fc7a6209SUwe Kleine-König static void mei_cl_device_remove(struct device *dev)
1193e5354107SSamuel Ortiz {
1194b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev);
1195f320ff03SUwe Kleine-König struct mei_cl_driver *cldrv = to_mei_cl_driver(dev->driver);
1196e5354107SSamuel Ortiz
11979ecb839fSAlexander Usyskin if (cldrv->remove)
1198bf5c9cc8SUwe Kleine-König cldrv->remove(cldev);
11999ecb839fSAlexander Usyskin
120057080e88SAlexander Usyskin mei_cldev_unregister_callbacks(cldev);
12013e833295SSamuel Ortiz
1202b5958faaSAlexander Usyskin mei_cl_bus_module_put(cldev);
1203feb8cd0fSTomas Winkler module_put(THIS_MODULE);
1204e5354107SSamuel Ortiz }
1205e5354107SSamuel Ortiz
name_show(struct device * dev,struct device_attribute * a,char * buf)1206007d64ebSTomas Winkler static ssize_t name_show(struct device *dev, struct device_attribute *a,
1207007d64ebSTomas Winkler char *buf)
1208007d64ebSTomas Winkler {
1209b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev);
1210007d64ebSTomas Winkler
1211423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%s", cldev->name);
1212007d64ebSTomas Winkler }
1213007d64ebSTomas Winkler static DEVICE_ATTR_RO(name);
1214007d64ebSTomas Winkler
uuid_show(struct device * dev,struct device_attribute * a,char * buf)1215007d64ebSTomas Winkler static ssize_t uuid_show(struct device *dev, struct device_attribute *a,
1216007d64ebSTomas Winkler char *buf)
1217007d64ebSTomas Winkler {
1218b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev);
1219b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
1220007d64ebSTomas Winkler
122149ef431dSTomas Winkler return sprintf(buf, "%pUl", uuid);
1222007d64ebSTomas Winkler }
1223007d64ebSTomas Winkler static DEVICE_ATTR_RO(uuid);
1224007d64ebSTomas Winkler
version_show(struct device * dev,struct device_attribute * a,char * buf)122540b7320eSTomas Winkler static ssize_t version_show(struct device *dev, struct device_attribute *a,
122640b7320eSTomas Winkler char *buf)
122740b7320eSTomas Winkler {
122840b7320eSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev);
122940b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl);
123040b7320eSTomas Winkler
123149ef431dSTomas Winkler return sprintf(buf, "%02X", version);
123240b7320eSTomas Winkler }
123340b7320eSTomas Winkler static DEVICE_ATTR_RO(version);
123440b7320eSTomas Winkler
modalias_show(struct device * dev,struct device_attribute * a,char * buf)1235e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
1236e5354107SSamuel Ortiz char *buf)
1237e5354107SSamuel Ortiz {
1238b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev);
1239b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
12406f9193ecSPratyush Anand u8 version = mei_me_cl_ver(cldev->me_cl);
1241e5354107SSamuel Ortiz
12426f9193ecSPratyush Anand return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:%02X:",
12436f9193ecSPratyush Anand cldev->name, uuid, version);
1244e5354107SSamuel Ortiz }
124532f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias);
1246e5354107SSamuel Ortiz
max_conn_show(struct device * dev,struct device_attribute * a,char * buf)124764498695SAlexander Usyskin static ssize_t max_conn_show(struct device *dev, struct device_attribute *a,
124864498695SAlexander Usyskin char *buf)
124964498695SAlexander Usyskin {
125064498695SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev);
125164498695SAlexander Usyskin u8 maxconn = mei_me_cl_max_conn(cldev->me_cl);
125264498695SAlexander Usyskin
125349ef431dSTomas Winkler return sprintf(buf, "%d", maxconn);
125464498695SAlexander Usyskin }
125564498695SAlexander Usyskin static DEVICE_ATTR_RO(max_conn);
125664498695SAlexander Usyskin
fixed_show(struct device * dev,struct device_attribute * a,char * buf)125764498695SAlexander Usyskin static ssize_t fixed_show(struct device *dev, struct device_attribute *a,
125864498695SAlexander Usyskin char *buf)
125964498695SAlexander Usyskin {
126064498695SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev);
126164498695SAlexander Usyskin u8 fixed = mei_me_cl_fixed(cldev->me_cl);
126264498695SAlexander Usyskin
126349ef431dSTomas Winkler return sprintf(buf, "%d", fixed);
126464498695SAlexander Usyskin }
126564498695SAlexander Usyskin static DEVICE_ATTR_RO(fixed);
126664498695SAlexander Usyskin
vtag_show(struct device * dev,struct device_attribute * a,char * buf)12672dd1e5aeSAlexander Usyskin static ssize_t vtag_show(struct device *dev, struct device_attribute *a,
12682dd1e5aeSAlexander Usyskin char *buf)
12692dd1e5aeSAlexander Usyskin {
12702dd1e5aeSAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev);
12712dd1e5aeSAlexander Usyskin bool vt = mei_me_cl_vt(cldev->me_cl);
12722dd1e5aeSAlexander Usyskin
12732dd1e5aeSAlexander Usyskin return sprintf(buf, "%d", vt);
12742dd1e5aeSAlexander Usyskin }
12752dd1e5aeSAlexander Usyskin static DEVICE_ATTR_RO(vtag);
12762dd1e5aeSAlexander Usyskin
max_len_show(struct device * dev,struct device_attribute * a,char * buf)127764498695SAlexander Usyskin static ssize_t max_len_show(struct device *dev, struct device_attribute *a,
127864498695SAlexander Usyskin char *buf)
127964498695SAlexander Usyskin {
128064498695SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev);
128164498695SAlexander Usyskin u32 maxlen = mei_me_cl_max_len(cldev->me_cl);
128264498695SAlexander Usyskin
128349ef431dSTomas Winkler return sprintf(buf, "%u", maxlen);
128464498695SAlexander Usyskin }
128564498695SAlexander Usyskin static DEVICE_ATTR_RO(max_len);
128664498695SAlexander Usyskin
1287d49dc5e7STomas Winkler static struct attribute *mei_cldev_attrs[] = {
1288007d64ebSTomas Winkler &dev_attr_name.attr,
1289007d64ebSTomas Winkler &dev_attr_uuid.attr,
129040b7320eSTomas Winkler &dev_attr_version.attr,
129132f389ecSGreg Kroah-Hartman &dev_attr_modalias.attr,
129264498695SAlexander Usyskin &dev_attr_max_conn.attr,
129364498695SAlexander Usyskin &dev_attr_fixed.attr,
12942dd1e5aeSAlexander Usyskin &dev_attr_vtag.attr,
129564498695SAlexander Usyskin &dev_attr_max_len.attr,
129632f389ecSGreg Kroah-Hartman NULL,
1297e5354107SSamuel Ortiz };
1298d49dc5e7STomas Winkler ATTRIBUTE_GROUPS(mei_cldev);
1299e5354107SSamuel Ortiz
130038d3c00dSTomas Winkler /**
130138d3c00dSTomas Winkler * mei_cl_device_uevent - me client bus uevent handler
130238d3c00dSTomas Winkler *
130338d3c00dSTomas Winkler * @dev: device
130438d3c00dSTomas Winkler * @env: uevent kobject
130538d3c00dSTomas Winkler *
130638d3c00dSTomas Winkler * Return: 0 on success -ENOMEM on when add_uevent_var fails
130738d3c00dSTomas Winkler */
mei_cl_device_uevent(const struct device * dev,struct kobj_uevent_env * env)13082a81ada3SGreg Kroah-Hartman static int mei_cl_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
1309e5354107SSamuel Ortiz {
13102a81ada3SGreg Kroah-Hartman const struct mei_cl_device *cldev = to_mei_cl_device(dev);
1311b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
131240b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl);
131340b7320eSTomas Winkler
131440b7320eSTomas Winkler if (add_uevent_var(env, "MEI_CL_VERSION=%d", version))
131540b7320eSTomas Winkler return -ENOMEM;
1316c93b76b3STomas Winkler
1317007d64ebSTomas Winkler if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid))
1318007d64ebSTomas Winkler return -ENOMEM;
1319007d64ebSTomas Winkler
1320b37719c3STomas Winkler if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name))
1321007d64ebSTomas Winkler return -ENOMEM;
1322007d64ebSTomas Winkler
1323b26864caSTomas Winkler if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:",
1324b26864caSTomas Winkler cldev->name, uuid, version))
1325e5354107SSamuel Ortiz return -ENOMEM;
1326e5354107SSamuel Ortiz
1327e5354107SSamuel Ortiz return 0;
1328e5354107SSamuel Ortiz }
1329e5354107SSamuel Ortiz
1330e22810abSKunwu Chan static const struct bus_type mei_cl_bus_type = {
1331e5354107SSamuel Ortiz .name = "mei",
1332d49dc5e7STomas Winkler .dev_groups = mei_cldev_groups,
1333e5354107SSamuel Ortiz .match = mei_cl_device_match,
1334e5354107SSamuel Ortiz .probe = mei_cl_device_probe,
1335e5354107SSamuel Ortiz .remove = mei_cl_device_remove,
133638d3c00dSTomas Winkler .uevent = mei_cl_device_uevent,
1337e5354107SSamuel Ortiz };
1338e5354107SSamuel Ortiz
mei_dev_bus_get(struct mei_device * bus)1339512f64d9STomas Winkler static struct mei_device *mei_dev_bus_get(struct mei_device *bus)
1340512f64d9STomas Winkler {
1341512f64d9STomas Winkler if (bus)
1342512f64d9STomas Winkler get_device(bus->dev);
1343512f64d9STomas Winkler
1344512f64d9STomas Winkler return bus;
1345512f64d9STomas Winkler }
1346512f64d9STomas Winkler
mei_dev_bus_put(struct mei_device * bus)1347512f64d9STomas Winkler static void mei_dev_bus_put(struct mei_device *bus)
1348512f64d9STomas Winkler {
1349512f64d9STomas Winkler if (bus)
1350512f64d9STomas Winkler put_device(bus->dev);
1351512f64d9STomas Winkler }
1352512f64d9STomas Winkler
mei_cl_bus_dev_release(struct device * dev)1353ae48d74dSTomas Winkler static void mei_cl_bus_dev_release(struct device *dev)
1354e5354107SSamuel Ortiz {
1355b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev);
1356d49ed64aSAlexander Usyskin
13572cca3465SAlexander Usyskin mei_cl_flush_queues(cldev->cl, NULL);
1358b37719c3STomas Winkler mei_me_cl_put(cldev->me_cl);
1359512f64d9STomas Winkler mei_dev_bus_put(cldev->bus);
1360c110cdb1SAlexander Usyskin kfree(cldev->cl);
1361b37719c3STomas Winkler kfree(cldev);
1362e5354107SSamuel Ortiz }
1363e5354107SSamuel Ortiz
1364b8d01b7fSBhumika Goyal static const struct device_type mei_cl_device_type = {
1365ae48d74dSTomas Winkler .release = mei_cl_bus_dev_release,
1366e5354107SSamuel Ortiz };
1367e5354107SSamuel Ortiz
136871ce7891STomas Winkler /**
1369213dd193STomas Winkler * mei_cl_bus_set_name - set device name for me client device
13707a2b9e6eSAlexander Usyskin * <controller>-<client device>
13717a2b9e6eSAlexander Usyskin * Example: 0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb
1372213dd193STomas Winkler *
1373213dd193STomas Winkler * @cldev: me client device
1374213dd193STomas Winkler */
mei_cl_bus_set_name(struct mei_cl_device * cldev)1375213dd193STomas Winkler static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev)
1376213dd193STomas Winkler {
13777a2b9e6eSAlexander Usyskin dev_set_name(&cldev->dev, "%s-%pUl",
13787a2b9e6eSAlexander Usyskin dev_name(cldev->bus->dev),
13797a2b9e6eSAlexander Usyskin mei_me_cl_uuid(cldev->me_cl));
1380213dd193STomas Winkler }
1381213dd193STomas Winkler
1382213dd193STomas Winkler /**
1383ae48d74dSTomas Winkler * mei_cl_bus_dev_alloc - initialize and allocate mei client device
138471ce7891STomas Winkler *
138571ce7891STomas Winkler * @bus: mei device
138671ce7891STomas Winkler * @me_cl: me client
138771ce7891STomas Winkler *
1388ae4cb6bdSTomas Winkler * Return: allocated device structure or NULL on allocation failure
138971ce7891STomas Winkler */
mei_cl_bus_dev_alloc(struct mei_device * bus,struct mei_me_client * me_cl)1390ae48d74dSTomas Winkler static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus,
139171ce7891STomas Winkler struct mei_me_client *me_cl)
139271ce7891STomas Winkler {
139371ce7891STomas Winkler struct mei_cl_device *cldev;
1394c110cdb1SAlexander Usyskin struct mei_cl *cl;
139571ce7891STomas Winkler
139671ae5255STomas Winkler cldev = kzalloc(sizeof(*cldev), GFP_KERNEL);
139771ce7891STomas Winkler if (!cldev)
139871ce7891STomas Winkler return NULL;
139971ce7891STomas Winkler
1400c110cdb1SAlexander Usyskin cl = mei_cl_allocate(bus);
1401c110cdb1SAlexander Usyskin if (!cl) {
1402c110cdb1SAlexander Usyskin kfree(cldev);
1403c110cdb1SAlexander Usyskin return NULL;
1404c110cdb1SAlexander Usyskin }
1405c110cdb1SAlexander Usyskin
140671ce7891STomas Winkler device_initialize(&cldev->dev);
140771ce7891STomas Winkler cldev->dev.parent = bus->dev;
140871ce7891STomas Winkler cldev->dev.bus = &mei_cl_bus_type;
140971ce7891STomas Winkler cldev->dev.type = &mei_cl_device_type;
141071ce7891STomas Winkler cldev->bus = mei_dev_bus_get(bus);
141171ce7891STomas Winkler cldev->me_cl = mei_me_cl_get(me_cl);
1412c110cdb1SAlexander Usyskin cldev->cl = cl;
1413213dd193STomas Winkler mei_cl_bus_set_name(cldev);
141471ce7891STomas Winkler cldev->is_added = 0;
141571ce7891STomas Winkler INIT_LIST_HEAD(&cldev->bus_list);
14165fc22748SAlexander Usyskin device_enable_async_suspend(&cldev->dev);
141771ce7891STomas Winkler
141871ce7891STomas Winkler return cldev;
141971ce7891STomas Winkler }
142071ce7891STomas Winkler
142171ce7891STomas Winkler /**
14227bbd2584SMauro Carvalho Chehab * mei_cl_bus_dev_setup - setup me client device
142371ce7891STomas Winkler * run fix up routines and set the device name
142471ce7891STomas Winkler *
142571ce7891STomas Winkler * @bus: mei device
142671ce7891STomas Winkler * @cldev: me client device
142771ce7891STomas Winkler *
142871ce7891STomas Winkler * Return: true if the device is eligible for enumeration
142971ce7891STomas Winkler */
mei_cl_bus_dev_setup(struct mei_device * bus,struct mei_cl_device * cldev)1430ae48d74dSTomas Winkler static bool mei_cl_bus_dev_setup(struct mei_device *bus,
143171ce7891STomas Winkler struct mei_cl_device *cldev)
143271ce7891STomas Winkler {
143371ce7891STomas Winkler cldev->do_match = 1;
1434ae48d74dSTomas Winkler mei_cl_bus_dev_fixup(cldev);
143571ce7891STomas Winkler
1436213dd193STomas Winkler /* the device name can change during fix up */
143771ce7891STomas Winkler if (cldev->do_match)
1438213dd193STomas Winkler mei_cl_bus_set_name(cldev);
143971ce7891STomas Winkler
144071ce7891STomas Winkler return cldev->do_match == 1;
144171ce7891STomas Winkler }
144271ce7891STomas Winkler
144371ce7891STomas Winkler /**
144471ce7891STomas Winkler * mei_cl_bus_dev_add - add me client devices
144571ce7891STomas Winkler *
144671ce7891STomas Winkler * @cldev: me client device
144771ce7891STomas Winkler *
1448ae4cb6bdSTomas Winkler * Return: 0 on success; < 0 on failure
144971ce7891STomas Winkler */
mei_cl_bus_dev_add(struct mei_cl_device * cldev)145071ce7891STomas Winkler static int mei_cl_bus_dev_add(struct mei_cl_device *cldev)
145171ce7891STomas Winkler {
145271ce7891STomas Winkler int ret;
145371ce7891STomas Winkler
1454b26864caSTomas Winkler dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n",
1455b26864caSTomas Winkler mei_me_cl_uuid(cldev->me_cl),
1456b26864caSTomas Winkler mei_me_cl_ver(cldev->me_cl));
145771ce7891STomas Winkler ret = device_add(&cldev->dev);
145871ce7891STomas Winkler if (!ret)
145971ce7891STomas Winkler cldev->is_added = 1;
146071ce7891STomas Winkler
146171ce7891STomas Winkler return ret;
146271ce7891STomas Winkler }
146371ce7891STomas Winkler
14646009595aSTomas Winkler /**
14656009595aSTomas Winkler * mei_cl_bus_dev_stop - stop the driver
14666009595aSTomas Winkler *
14676009595aSTomas Winkler * @cldev: me client device
14686009595aSTomas Winkler */
mei_cl_bus_dev_stop(struct mei_cl_device * cldev)14696009595aSTomas Winkler static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev)
14706009595aSTomas Winkler {
1471784304aeSAlexander Usyskin cldev->do_match = 0;
14726009595aSTomas Winkler if (cldev->is_added)
14736009595aSTomas Winkler device_release_driver(&cldev->dev);
14746009595aSTomas Winkler }
14756009595aSTomas Winkler
14766009595aSTomas Winkler /**
14776009595aSTomas Winkler * mei_cl_bus_dev_destroy - destroy me client devices object
14786009595aSTomas Winkler *
14796009595aSTomas Winkler * @cldev: me client device
14802da55cfdSTomas Winkler *
14812da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock
14826009595aSTomas Winkler */
mei_cl_bus_dev_destroy(struct mei_cl_device * cldev)14836009595aSTomas Winkler static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev)
14846009595aSTomas Winkler {
14852da55cfdSTomas Winkler
14862da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock));
14872da55cfdSTomas Winkler
14886009595aSTomas Winkler if (!cldev->is_added)
14896009595aSTomas Winkler return;
14906009595aSTomas Winkler
14916009595aSTomas Winkler device_del(&cldev->dev);
14926009595aSTomas Winkler
14936009595aSTomas Winkler list_del_init(&cldev->bus_list);
14946009595aSTomas Winkler
14956009595aSTomas Winkler cldev->is_added = 0;
14966009595aSTomas Winkler put_device(&cldev->dev);
14976009595aSTomas Winkler }
14986009595aSTomas Winkler
14996009595aSTomas Winkler /**
15006009595aSTomas Winkler * mei_cl_bus_remove_device - remove a devices form the bus
15016009595aSTomas Winkler *
15026009595aSTomas Winkler * @cldev: me client device
15036009595aSTomas Winkler */
mei_cl_bus_remove_device(struct mei_cl_device * cldev)15046009595aSTomas Winkler static void mei_cl_bus_remove_device(struct mei_cl_device *cldev)
15056009595aSTomas Winkler {
15066009595aSTomas Winkler mei_cl_bus_dev_stop(cldev);
15076009595aSTomas Winkler mei_cl_bus_dev_destroy(cldev);
15086009595aSTomas Winkler }
15096009595aSTomas Winkler
15106009595aSTomas Winkler /**
15116009595aSTomas Winkler * mei_cl_bus_remove_devices - remove all devices form the bus
15126009595aSTomas Winkler *
15136009595aSTomas Winkler * @bus: mei device
15146009595aSTomas Winkler */
mei_cl_bus_remove_devices(struct mei_device * bus)15156009595aSTomas Winkler void mei_cl_bus_remove_devices(struct mei_device *bus)
15166009595aSTomas Winkler {
15176009595aSTomas Winkler struct mei_cl_device *cldev, *next;
15186009595aSTomas Winkler
15192da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock);
15206009595aSTomas Winkler list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list)
15216009595aSTomas Winkler mei_cl_bus_remove_device(cldev);
15222da55cfdSTomas Winkler mutex_unlock(&bus->cl_bus_lock);
15236009595aSTomas Winkler }
15246009595aSTomas Winkler
15256009595aSTomas Winkler
15266009595aSTomas Winkler /**
1527ae48d74dSTomas Winkler * mei_cl_bus_dev_init - allocate and initializes an mei client devices
15286009595aSTomas Winkler * based on me client
15296009595aSTomas Winkler *
15306009595aSTomas Winkler * @bus: mei device
15316009595aSTomas Winkler * @me_cl: me client
15322da55cfdSTomas Winkler *
15332da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock
15346009595aSTomas Winkler */
mei_cl_bus_dev_init(struct mei_device * bus,struct mei_me_client * me_cl)1535ae48d74dSTomas Winkler static void mei_cl_bus_dev_init(struct mei_device *bus,
1536ae48d74dSTomas Winkler struct mei_me_client *me_cl)
1537e5354107SSamuel Ortiz {
1538b37719c3STomas Winkler struct mei_cl_device *cldev;
15396009595aSTomas Winkler
15402da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&bus->cl_bus_lock));
15412da55cfdSTomas Winkler
15426009595aSTomas Winkler dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl));
15436009595aSTomas Winkler
15446009595aSTomas Winkler if (me_cl->bus_added)
15456009595aSTomas Winkler return;
1546e5354107SSamuel Ortiz
1547ae48d74dSTomas Winkler cldev = mei_cl_bus_dev_alloc(bus, me_cl);
1548b37719c3STomas Winkler if (!cldev)
15496009595aSTomas Winkler return;
1550e5354107SSamuel Ortiz
15516009595aSTomas Winkler me_cl->bus_added = true;
15526009595aSTomas Winkler list_add_tail(&cldev->bus_list, &bus->device_list);
1553c93b76b3STomas Winkler
1554a7b71bc0SSamuel Ortiz }
1555a7b71bc0SSamuel Ortiz
15566009595aSTomas Winkler /**
15576009595aSTomas Winkler * mei_cl_bus_rescan - scan me clients list and add create
15586009595aSTomas Winkler * devices for eligible clients
15596009595aSTomas Winkler *
15606009595aSTomas Winkler * @bus: mei device
15616009595aSTomas Winkler */
mei_cl_bus_rescan(struct mei_device * bus)15628bb2d27fSAlexander Usyskin static void mei_cl_bus_rescan(struct mei_device *bus)
1563e5354107SSamuel Ortiz {
15646009595aSTomas Winkler struct mei_cl_device *cldev, *n;
15656009595aSTomas Winkler struct mei_me_client *me_cl;
15666009595aSTomas Winkler
15672da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock);
15682da55cfdSTomas Winkler
15696009595aSTomas Winkler down_read(&bus->me_clients_rwsem);
15706009595aSTomas Winkler list_for_each_entry(me_cl, &bus->me_clients, list)
1571ae48d74dSTomas Winkler mei_cl_bus_dev_init(bus, me_cl);
15726009595aSTomas Winkler up_read(&bus->me_clients_rwsem);
15736009595aSTomas Winkler
15746009595aSTomas Winkler list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) {
15756009595aSTomas Winkler
15766009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) {
15776009595aSTomas Winkler mei_cl_bus_remove_device(cldev);
15786009595aSTomas Winkler continue;
1579e5354107SSamuel Ortiz }
15806009595aSTomas Winkler
15816009595aSTomas Winkler if (cldev->is_added)
15826009595aSTomas Winkler continue;
15836009595aSTomas Winkler
1584ae48d74dSTomas Winkler if (mei_cl_bus_dev_setup(bus, cldev))
15856009595aSTomas Winkler mei_cl_bus_dev_add(cldev);
15866009595aSTomas Winkler else {
15876009595aSTomas Winkler list_del_init(&cldev->bus_list);
15886009595aSTomas Winkler put_device(&cldev->dev);
15896009595aSTomas Winkler }
15906009595aSTomas Winkler }
15916009595aSTomas Winkler mutex_unlock(&bus->cl_bus_lock);
15926009595aSTomas Winkler
15936009595aSTomas Winkler dev_dbg(bus->dev, "rescan end");
15946009595aSTomas Winkler }
1595333e4ee0SSamuel Ortiz
mei_cl_bus_rescan_work(struct work_struct * work)1596a816a00eSAlexander Usyskin void mei_cl_bus_rescan_work(struct work_struct *work)
1597a816a00eSAlexander Usyskin {
1598a816a00eSAlexander Usyskin struct mei_device *bus =
1599a816a00eSAlexander Usyskin container_of(work, struct mei_device, bus_rescan_work);
1600a816a00eSAlexander Usyskin
1601a816a00eSAlexander Usyskin mei_cl_bus_rescan(bus);
1602a816a00eSAlexander Usyskin }
1603a816a00eSAlexander Usyskin
__mei_cldev_driver_register(struct mei_cl_driver * cldrv,struct module * owner)1604d49dc5e7STomas Winkler int __mei_cldev_driver_register(struct mei_cl_driver *cldrv,
1605d49dc5e7STomas Winkler struct module *owner)
1606333e4ee0SSamuel Ortiz {
1607333e4ee0SSamuel Ortiz int err;
1608333e4ee0SSamuel Ortiz
1609b37719c3STomas Winkler cldrv->driver.name = cldrv->name;
1610b37719c3STomas Winkler cldrv->driver.owner = owner;
1611b37719c3STomas Winkler cldrv->driver.bus = &mei_cl_bus_type;
1612333e4ee0SSamuel Ortiz
1613b37719c3STomas Winkler err = driver_register(&cldrv->driver);
1614333e4ee0SSamuel Ortiz if (err)
1615333e4ee0SSamuel Ortiz return err;
1616333e4ee0SSamuel Ortiz
1617b37719c3STomas Winkler pr_debug("mei: driver [%s] registered\n", cldrv->driver.name);
1618333e4ee0SSamuel Ortiz
1619333e4ee0SSamuel Ortiz return 0;
1620333e4ee0SSamuel Ortiz }
1621d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(__mei_cldev_driver_register);
1622333e4ee0SSamuel Ortiz
mei_cldev_driver_unregister(struct mei_cl_driver * cldrv)1623d49dc5e7STomas Winkler void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv)
1624333e4ee0SSamuel Ortiz {
1625b37719c3STomas Winkler driver_unregister(&cldrv->driver);
1626333e4ee0SSamuel Ortiz
1627b37719c3STomas Winkler pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name);
1628333e4ee0SSamuel Ortiz }
1629d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister);
16303e833295SSamuel Ortiz
16316009595aSTomas Winkler
mei_cl_bus_init(void)1632cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void)
1633cf3baefbSSamuel Ortiz {
1634cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type);
1635cf3baefbSSamuel Ortiz }
1636cf3baefbSSamuel Ortiz
mei_cl_bus_exit(void)1637cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void)
1638cf3baefbSSamuel Ortiz {
1639cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type);
1640cf3baefbSSamuel Ortiz }
1641