xref: /linux/drivers/misc/mei/bus.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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