xref: /kernel/linux/linux-6.6/drivers/misc/mei/bus.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2023, Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/sched/signal.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/scatterlist.h>
1762306a36Sopenharmony_ci#include <linux/mei_cl_bus.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "mei_dev.h"
2062306a36Sopenharmony_ci#include "client.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * __mei_cl_send - internal client send (write)
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * @cl: host client
2862306a36Sopenharmony_ci * @buf: buffer to send
2962306a36Sopenharmony_ci * @length: buffer length
3062306a36Sopenharmony_ci * @vtag: virtual tag
3162306a36Sopenharmony_ci * @mode: sending mode
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Return: written size bytes or < 0 on error
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cissize_t __mei_cl_send(struct mei_cl *cl, const u8 *buf, size_t length, u8 vtag,
3662306a36Sopenharmony_ci		      unsigned int mode)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return __mei_cl_send_timeout(cl, buf, length, vtag, mode, MAX_SCHEDULE_TIMEOUT);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * __mei_cl_send_timeout - internal client send (write)
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * @cl: host client
4562306a36Sopenharmony_ci * @buf: buffer to send
4662306a36Sopenharmony_ci * @length: buffer length
4762306a36Sopenharmony_ci * @vtag: virtual tag
4862306a36Sopenharmony_ci * @mode: sending mode
4962306a36Sopenharmony_ci * @timeout: send timeout in milliseconds.
5062306a36Sopenharmony_ci *           effective only for blocking writes: the MEI_CL_IO_TX_BLOCKING mode bit is set.
5162306a36Sopenharmony_ci *           set timeout to the MAX_SCHEDULE_TIMEOUT to maixum allowed wait.
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * Return: written size bytes or < 0 on error
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_cissize_t __mei_cl_send_timeout(struct mei_cl *cl, const u8 *buf, size_t length, u8 vtag,
5662306a36Sopenharmony_ci			      unsigned int mode, unsigned long timeout)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct mei_device *bus;
5962306a36Sopenharmony_ci	struct mei_cl_cb *cb;
6062306a36Sopenharmony_ci	ssize_t rets;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (WARN_ON(!cl || !cl->dev))
6362306a36Sopenharmony_ci		return -ENODEV;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	bus = cl->dev;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
6862306a36Sopenharmony_ci	if (bus->dev_state != MEI_DEV_ENABLED &&
6962306a36Sopenharmony_ci	    bus->dev_state != MEI_DEV_POWERING_DOWN) {
7062306a36Sopenharmony_ci		rets = -ENODEV;
7162306a36Sopenharmony_ci		goto out;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (!mei_cl_is_connected(cl)) {
7562306a36Sopenharmony_ci		rets = -ENODEV;
7662306a36Sopenharmony_ci		goto out;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* Check if we have an ME client device */
8062306a36Sopenharmony_ci	if (!mei_me_cl_is_active(cl->me_cl)) {
8162306a36Sopenharmony_ci		rets = -ENOTTY;
8262306a36Sopenharmony_ci		goto out;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (vtag) {
8662306a36Sopenharmony_ci		/* Check if vtag is supported by client */
8762306a36Sopenharmony_ci		rets = mei_cl_vt_support_check(cl);
8862306a36Sopenharmony_ci		if (rets)
8962306a36Sopenharmony_ci			goto out;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (length > mei_cl_mtu(cl)) {
9362306a36Sopenharmony_ci		rets = -EFBIG;
9462306a36Sopenharmony_ci		goto out;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	while (cl->tx_cb_queued >= bus->tx_queue_limit) {
9862306a36Sopenharmony_ci		mutex_unlock(&bus->device_lock);
9962306a36Sopenharmony_ci		rets = wait_event_interruptible(cl->tx_wait,
10062306a36Sopenharmony_ci				cl->writing_state == MEI_WRITE_COMPLETE ||
10162306a36Sopenharmony_ci				(!mei_cl_is_connected(cl)));
10262306a36Sopenharmony_ci		mutex_lock(&bus->device_lock);
10362306a36Sopenharmony_ci		if (rets) {
10462306a36Sopenharmony_ci			if (signal_pending(current))
10562306a36Sopenharmony_ci				rets = -EINTR;
10662306a36Sopenharmony_ci			goto out;
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci		if (!mei_cl_is_connected(cl)) {
10962306a36Sopenharmony_ci			rets = -ENODEV;
11062306a36Sopenharmony_ci			goto out;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
11562306a36Sopenharmony_ci	if (!cb) {
11662306a36Sopenharmony_ci		rets = -ENOMEM;
11762306a36Sopenharmony_ci		goto out;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	cb->vtag = vtag;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	cb->internal = !!(mode & MEI_CL_IO_TX_INTERNAL);
12262306a36Sopenharmony_ci	cb->blocking = !!(mode & MEI_CL_IO_TX_BLOCKING);
12362306a36Sopenharmony_ci	memcpy(cb->buf.data, buf, length);
12462306a36Sopenharmony_ci	/* hack we point data to header */
12562306a36Sopenharmony_ci	if (mode & MEI_CL_IO_SGL) {
12662306a36Sopenharmony_ci		cb->ext_hdr = (struct mei_ext_hdr *)cb->buf.data;
12762306a36Sopenharmony_ci		cb->buf.data = NULL;
12862306a36Sopenharmony_ci		cb->buf.size = 0;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	rets = mei_cl_write(cl, cb, timeout);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (mode & MEI_CL_IO_SGL && rets == 0)
13462306a36Sopenharmony_ci		rets = length;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciout:
13762306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return rets;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/**
14362306a36Sopenharmony_ci * __mei_cl_recv - internal client receive (read)
14462306a36Sopenharmony_ci *
14562306a36Sopenharmony_ci * @cl: host client
14662306a36Sopenharmony_ci * @buf: buffer to receive
14762306a36Sopenharmony_ci * @length: buffer length
14862306a36Sopenharmony_ci * @mode: io mode
14962306a36Sopenharmony_ci * @vtag: virtual tag
15062306a36Sopenharmony_ci * @timeout: recv timeout, 0 for infinite timeout
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * Return: read size in bytes of < 0 on error
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_cissize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, u8 *vtag,
15562306a36Sopenharmony_ci		      unsigned int mode, unsigned long timeout)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct mei_device *bus;
15862306a36Sopenharmony_ci	struct mei_cl_cb *cb;
15962306a36Sopenharmony_ci	size_t r_length;
16062306a36Sopenharmony_ci	ssize_t rets;
16162306a36Sopenharmony_ci	bool nonblock = !!(mode & MEI_CL_IO_RX_NONBLOCK);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (WARN_ON(!cl || !cl->dev))
16462306a36Sopenharmony_ci		return -ENODEV;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	bus = cl->dev;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
16962306a36Sopenharmony_ci	if (bus->dev_state != MEI_DEV_ENABLED &&
17062306a36Sopenharmony_ci	    bus->dev_state != MEI_DEV_POWERING_DOWN) {
17162306a36Sopenharmony_ci		rets = -ENODEV;
17262306a36Sopenharmony_ci		goto out;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	cb = mei_cl_read_cb(cl, NULL);
17662306a36Sopenharmony_ci	if (cb)
17762306a36Sopenharmony_ci		goto copy;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	rets = mei_cl_read_start(cl, length, NULL);
18062306a36Sopenharmony_ci	if (rets && rets != -EBUSY)
18162306a36Sopenharmony_ci		goto out;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (nonblock) {
18462306a36Sopenharmony_ci		rets = -EAGAIN;
18562306a36Sopenharmony_ci		goto out;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* wait on event only if there is no other waiter */
18962306a36Sopenharmony_ci	/* synchronized under device mutex */
19062306a36Sopenharmony_ci	if (!waitqueue_active(&cl->rx_wait)) {
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		mutex_unlock(&bus->device_lock);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (timeout) {
19562306a36Sopenharmony_ci			rets = wait_event_interruptible_timeout
19662306a36Sopenharmony_ci					(cl->rx_wait,
19762306a36Sopenharmony_ci					mei_cl_read_cb(cl, NULL) ||
19862306a36Sopenharmony_ci					(!mei_cl_is_connected(cl)),
19962306a36Sopenharmony_ci					msecs_to_jiffies(timeout));
20062306a36Sopenharmony_ci			if (rets == 0)
20162306a36Sopenharmony_ci				return -ETIME;
20262306a36Sopenharmony_ci			if (rets < 0) {
20362306a36Sopenharmony_ci				if (signal_pending(current))
20462306a36Sopenharmony_ci					return -EINTR;
20562306a36Sopenharmony_ci				return -ERESTARTSYS;
20662306a36Sopenharmony_ci			}
20762306a36Sopenharmony_ci		} else {
20862306a36Sopenharmony_ci			if (wait_event_interruptible
20962306a36Sopenharmony_ci					(cl->rx_wait,
21062306a36Sopenharmony_ci					mei_cl_read_cb(cl, NULL) ||
21162306a36Sopenharmony_ci					(!mei_cl_is_connected(cl)))) {
21262306a36Sopenharmony_ci				if (signal_pending(current))
21362306a36Sopenharmony_ci					return -EINTR;
21462306a36Sopenharmony_ci				return -ERESTARTSYS;
21562306a36Sopenharmony_ci			}
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		mutex_lock(&bus->device_lock);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		if (!mei_cl_is_connected(cl)) {
22162306a36Sopenharmony_ci			rets = -ENODEV;
22262306a36Sopenharmony_ci			goto out;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	cb = mei_cl_read_cb(cl, NULL);
22762306a36Sopenharmony_ci	if (!cb) {
22862306a36Sopenharmony_ci		rets = 0;
22962306a36Sopenharmony_ci		goto out;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cicopy:
23362306a36Sopenharmony_ci	if (cb->status) {
23462306a36Sopenharmony_ci		rets = cb->status;
23562306a36Sopenharmony_ci		goto free;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* for the GSC type - copy the extended header to the buffer */
23962306a36Sopenharmony_ci	if (cb->ext_hdr && cb->ext_hdr->type == MEI_EXT_HDR_GSC) {
24062306a36Sopenharmony_ci		r_length = min_t(size_t, length, cb->ext_hdr->length * sizeof(u32));
24162306a36Sopenharmony_ci		memcpy(buf, cb->ext_hdr, r_length);
24262306a36Sopenharmony_ci	} else {
24362306a36Sopenharmony_ci		r_length = min_t(size_t, length, cb->buf_idx);
24462306a36Sopenharmony_ci		memcpy(buf, cb->buf.data, r_length);
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci	rets = r_length;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (vtag)
24962306a36Sopenharmony_ci		*vtag = cb->vtag;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cifree:
25262306a36Sopenharmony_ci	mei_cl_del_rd_completed(cl, cb);
25362306a36Sopenharmony_ciout:
25462306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return rets;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/**
26062306a36Sopenharmony_ci * mei_cldev_send_vtag - me device send with vtag  (write)
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * @cldev: me client device
26362306a36Sopenharmony_ci * @buf: buffer to send
26462306a36Sopenharmony_ci * @length: buffer length
26562306a36Sopenharmony_ci * @vtag: virtual tag
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * Return:
26862306a36Sopenharmony_ci *  * written size in bytes
26962306a36Sopenharmony_ci *  * < 0 on error
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cissize_t mei_cldev_send_vtag(struct mei_cl_device *cldev, const u8 *buf,
27362306a36Sopenharmony_ci			    size_t length, u8 vtag)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct mei_cl *cl = cldev->cl;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return __mei_cl_send(cl, buf, length, vtag, MEI_CL_IO_TX_BLOCKING);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_send_vtag);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/**
28262306a36Sopenharmony_ci * mei_cldev_recv_vtag - client receive with vtag (read)
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * @cldev: me client device
28562306a36Sopenharmony_ci * @buf: buffer to receive
28662306a36Sopenharmony_ci * @length: buffer length
28762306a36Sopenharmony_ci * @vtag: virtual tag
28862306a36Sopenharmony_ci *
28962306a36Sopenharmony_ci * Return:
29062306a36Sopenharmony_ci * * read size in bytes
29162306a36Sopenharmony_ci * *  < 0 on error
29262306a36Sopenharmony_ci */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cissize_t mei_cldev_recv_vtag(struct mei_cl_device *cldev, u8 *buf, size_t length,
29562306a36Sopenharmony_ci			    u8 *vtag)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct mei_cl *cl = cldev->cl;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return __mei_cl_recv(cl, buf, length, vtag, 0, 0);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_recv_vtag);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/**
30462306a36Sopenharmony_ci * mei_cldev_recv_nonblock_vtag - non block client receive with vtag (read)
30562306a36Sopenharmony_ci *
30662306a36Sopenharmony_ci * @cldev: me client device
30762306a36Sopenharmony_ci * @buf: buffer to receive
30862306a36Sopenharmony_ci * @length: buffer length
30962306a36Sopenharmony_ci * @vtag: virtual tag
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * Return:
31262306a36Sopenharmony_ci * * read size in bytes
31362306a36Sopenharmony_ci * * -EAGAIN if function will block.
31462306a36Sopenharmony_ci * * < 0 on other error
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cissize_t mei_cldev_recv_nonblock_vtag(struct mei_cl_device *cldev, u8 *buf,
31762306a36Sopenharmony_ci				     size_t length, u8 *vtag)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct mei_cl *cl = cldev->cl;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return __mei_cl_recv(cl, buf, length, vtag, MEI_CL_IO_RX_NONBLOCK, 0);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock_vtag);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/**
32662306a36Sopenharmony_ci * mei_cldev_send - me device send  (write)
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * @cldev: me client device
32962306a36Sopenharmony_ci * @buf: buffer to send
33062306a36Sopenharmony_ci * @length: buffer length
33162306a36Sopenharmony_ci *
33262306a36Sopenharmony_ci * Return:
33362306a36Sopenharmony_ci *  * written size in bytes
33462306a36Sopenharmony_ci *  * < 0 on error
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_cissize_t mei_cldev_send(struct mei_cl_device *cldev, const u8 *buf, size_t length)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	return mei_cldev_send_vtag(cldev, buf, length, 0);
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_send);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * mei_cldev_recv - client receive (read)
34462306a36Sopenharmony_ci *
34562306a36Sopenharmony_ci * @cldev: me client device
34662306a36Sopenharmony_ci * @buf: buffer to receive
34762306a36Sopenharmony_ci * @length: buffer length
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * Return: read size in bytes of < 0 on error
35062306a36Sopenharmony_ci */
35162306a36Sopenharmony_cissize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	return mei_cldev_recv_vtag(cldev, buf, length, NULL);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_recv);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/**
35862306a36Sopenharmony_ci * mei_cldev_recv_nonblock - non block client receive (read)
35962306a36Sopenharmony_ci *
36062306a36Sopenharmony_ci * @cldev: me client device
36162306a36Sopenharmony_ci * @buf: buffer to receive
36262306a36Sopenharmony_ci * @length: buffer length
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci * Return: read size in bytes of < 0 on error
36562306a36Sopenharmony_ci *         -EAGAIN if function will block.
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_cissize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf,
36862306a36Sopenharmony_ci				size_t length)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	return mei_cldev_recv_nonblock_vtag(cldev, buf, length, NULL);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/**
37562306a36Sopenharmony_ci * mei_cl_bus_rx_work - dispatch rx event for a bus device
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * @work: work
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic void mei_cl_bus_rx_work(struct work_struct *work)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct mei_cl_device *cldev;
38262306a36Sopenharmony_ci	struct mei_device *bus;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	cldev = container_of(work, struct mei_cl_device, rx_work);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	bus = cldev->bus;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (cldev->rx_cb)
38962306a36Sopenharmony_ci		cldev->rx_cb(cldev);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
39262306a36Sopenharmony_ci	if (mei_cl_is_connected(cldev->cl))
39362306a36Sopenharmony_ci		mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
39462306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/**
39862306a36Sopenharmony_ci * mei_cl_bus_notif_work - dispatch FW notif event for a bus device
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * @work: work
40162306a36Sopenharmony_ci */
40262306a36Sopenharmony_cistatic void mei_cl_bus_notif_work(struct work_struct *work)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct mei_cl_device *cldev;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	cldev = container_of(work, struct mei_cl_device, notif_work);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (cldev->notif_cb)
40962306a36Sopenharmony_ci		cldev->notif_cb(cldev);
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/**
41362306a36Sopenharmony_ci * mei_cl_bus_notify_event - schedule notify cb on bus client
41462306a36Sopenharmony_ci *
41562306a36Sopenharmony_ci * @cl: host client
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * Return: true if event was scheduled
41862306a36Sopenharmony_ci *         false if the client is not waiting for event
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_cibool mei_cl_bus_notify_event(struct mei_cl *cl)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct mei_cl_device *cldev = cl->cldev;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (!cldev || !cldev->notif_cb)
42562306a36Sopenharmony_ci		return false;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (!cl->notify_ev)
42862306a36Sopenharmony_ci		return false;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	schedule_work(&cldev->notif_work);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	cl->notify_ev = false;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return true;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci/**
43862306a36Sopenharmony_ci * mei_cl_bus_rx_event - schedule rx event
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * @cl: host client
44162306a36Sopenharmony_ci *
44262306a36Sopenharmony_ci * Return: true if event was scheduled
44362306a36Sopenharmony_ci *         false if the client is not waiting for event
44462306a36Sopenharmony_ci */
44562306a36Sopenharmony_cibool mei_cl_bus_rx_event(struct mei_cl *cl)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct mei_cl_device *cldev = cl->cldev;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (!cldev || !cldev->rx_cb)
45062306a36Sopenharmony_ci		return false;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	schedule_work(&cldev->rx_work);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return true;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/**
45862306a36Sopenharmony_ci * mei_cldev_register_rx_cb - register Rx event callback
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci * @cldev: me client devices
46162306a36Sopenharmony_ci * @rx_cb: callback function
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * Return: 0 on success
46462306a36Sopenharmony_ci *         -EALREADY if an callback is already registered
46562306a36Sopenharmony_ci *         <0 on other errors
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_ciint mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct mei_device *bus = cldev->bus;
47062306a36Sopenharmony_ci	int ret;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!rx_cb)
47362306a36Sopenharmony_ci		return -EINVAL;
47462306a36Sopenharmony_ci	if (cldev->rx_cb)
47562306a36Sopenharmony_ci		return -EALREADY;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	cldev->rx_cb = rx_cb;
47862306a36Sopenharmony_ci	INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
48162306a36Sopenharmony_ci	if (mei_cl_is_connected(cldev->cl))
48262306a36Sopenharmony_ci		ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
48362306a36Sopenharmony_ci	else
48462306a36Sopenharmony_ci		ret = -ENODEV;
48562306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
48662306a36Sopenharmony_ci	if (ret && ret != -EBUSY) {
48762306a36Sopenharmony_ci		cancel_work_sync(&cldev->rx_work);
48862306a36Sopenharmony_ci		cldev->rx_cb = NULL;
48962306a36Sopenharmony_ci		return ret;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return 0;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/**
49762306a36Sopenharmony_ci * mei_cldev_register_notif_cb - register FW notification event callback
49862306a36Sopenharmony_ci *
49962306a36Sopenharmony_ci * @cldev: me client devices
50062306a36Sopenharmony_ci * @notif_cb: callback function
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * Return: 0 on success
50362306a36Sopenharmony_ci *         -EALREADY if an callback is already registered
50462306a36Sopenharmony_ci *         <0 on other errors
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_ciint mei_cldev_register_notif_cb(struct mei_cl_device *cldev,
50762306a36Sopenharmony_ci				mei_cldev_cb_t notif_cb)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct mei_device *bus = cldev->bus;
51062306a36Sopenharmony_ci	int ret;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (!notif_cb)
51362306a36Sopenharmony_ci		return -EINVAL;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (cldev->notif_cb)
51662306a36Sopenharmony_ci		return -EALREADY;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	cldev->notif_cb = notif_cb;
51962306a36Sopenharmony_ci	INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
52262306a36Sopenharmony_ci	ret = mei_cl_notify_request(cldev->cl, NULL, 1);
52362306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
52462306a36Sopenharmony_ci	if (ret) {
52562306a36Sopenharmony_ci		cancel_work_sync(&cldev->notif_work);
52662306a36Sopenharmony_ci		cldev->notif_cb = NULL;
52762306a36Sopenharmony_ci		return ret;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/**
53562306a36Sopenharmony_ci * mei_cldev_get_drvdata - driver data getter
53662306a36Sopenharmony_ci *
53762306a36Sopenharmony_ci * @cldev: mei client device
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * Return: driver private data
54062306a36Sopenharmony_ci */
54162306a36Sopenharmony_civoid *mei_cldev_get_drvdata(const struct mei_cl_device *cldev)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	return dev_get_drvdata(&cldev->dev);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_get_drvdata);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/**
54862306a36Sopenharmony_ci * mei_cldev_set_drvdata - driver data setter
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * @cldev: mei client device
55162306a36Sopenharmony_ci * @data: data to store
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_civoid mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	dev_set_drvdata(&cldev->dev, data);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_set_drvdata);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/**
56062306a36Sopenharmony_ci * mei_cldev_uuid - return uuid of the underlying me client
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * @cldev: mei client device
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * Return: me client uuid
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_ciconst uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	return mei_me_cl_uuid(cldev->me_cl);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_uuid);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/**
57362306a36Sopenharmony_ci * mei_cldev_ver - return protocol version of the underlying me client
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * @cldev: mei client device
57662306a36Sopenharmony_ci *
57762306a36Sopenharmony_ci * Return: me client protocol version
57862306a36Sopenharmony_ci */
57962306a36Sopenharmony_ciu8 mei_cldev_ver(const struct mei_cl_device *cldev)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	return mei_me_cl_ver(cldev->me_cl);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_ver);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci * mei_cldev_enabled - check whether the device is enabled
58762306a36Sopenharmony_ci *
58862306a36Sopenharmony_ci * @cldev: mei client device
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * Return: true if me client is initialized and connected
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_cibool mei_cldev_enabled(const struct mei_cl_device *cldev)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	return mei_cl_is_connected(cldev->cl);
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_enabled);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/**
59962306a36Sopenharmony_ci * mei_cl_bus_module_get - acquire module of the underlying
60062306a36Sopenharmony_ci *    hw driver.
60162306a36Sopenharmony_ci *
60262306a36Sopenharmony_ci * @cldev: mei client device
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * Return: true on success; false if the module was removed.
60562306a36Sopenharmony_ci */
60662306a36Sopenharmony_cistatic bool mei_cl_bus_module_get(struct mei_cl_device *cldev)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	return try_module_get(cldev->bus->dev->driver->owner);
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/**
61262306a36Sopenharmony_ci * mei_cl_bus_module_put -  release the underlying hw module.
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci * @cldev: mei client device
61562306a36Sopenharmony_ci */
61662306a36Sopenharmony_cistatic void mei_cl_bus_module_put(struct mei_cl_device *cldev)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	module_put(cldev->bus->dev->driver->owner);
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/**
62262306a36Sopenharmony_ci * mei_cl_bus_vtag - get bus vtag entry wrapper
62362306a36Sopenharmony_ci *     The tag for bus client is always first.
62462306a36Sopenharmony_ci *
62562306a36Sopenharmony_ci * @cl: host client
62662306a36Sopenharmony_ci *
62762306a36Sopenharmony_ci * Return: bus vtag or NULL
62862306a36Sopenharmony_ci */
62962306a36Sopenharmony_cistatic inline struct mei_cl_vtag *mei_cl_bus_vtag(struct mei_cl *cl)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	return list_first_entry_or_null(&cl->vtag_map,
63262306a36Sopenharmony_ci					struct mei_cl_vtag, list);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/**
63662306a36Sopenharmony_ci * mei_cl_bus_vtag_alloc - add bus client entry to vtag map
63762306a36Sopenharmony_ci *
63862306a36Sopenharmony_ci * @cldev: me client device
63962306a36Sopenharmony_ci *
64062306a36Sopenharmony_ci * Return:
64162306a36Sopenharmony_ci * * 0 on success
64262306a36Sopenharmony_ci * * -ENOMEM if memory allocation failed
64362306a36Sopenharmony_ci */
64462306a36Sopenharmony_cistatic int mei_cl_bus_vtag_alloc(struct mei_cl_device *cldev)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct mei_cl *cl = cldev->cl;
64762306a36Sopenharmony_ci	struct mei_cl_vtag *cl_vtag;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/*
65062306a36Sopenharmony_ci	 * Bail out if the client does not supports vtags
65162306a36Sopenharmony_ci	 * or has already allocated one
65262306a36Sopenharmony_ci	 */
65362306a36Sopenharmony_ci	if (mei_cl_vt_support_check(cl) || mei_cl_bus_vtag(cl))
65462306a36Sopenharmony_ci		return 0;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	cl_vtag = mei_cl_vtag_alloc(NULL, 0);
65762306a36Sopenharmony_ci	if (IS_ERR(cl_vtag))
65862306a36Sopenharmony_ci		return -ENOMEM;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	list_add_tail(&cl_vtag->list, &cl->vtag_map);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci/**
66662306a36Sopenharmony_ci * mei_cl_bus_vtag_free - remove the bus entry from vtag map
66762306a36Sopenharmony_ci *
66862306a36Sopenharmony_ci * @cldev: me client device
66962306a36Sopenharmony_ci */
67062306a36Sopenharmony_cistatic void mei_cl_bus_vtag_free(struct mei_cl_device *cldev)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct mei_cl *cl = cldev->cl;
67362306a36Sopenharmony_ci	struct mei_cl_vtag *cl_vtag;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	cl_vtag = mei_cl_bus_vtag(cl);
67662306a36Sopenharmony_ci	if (!cl_vtag)
67762306a36Sopenharmony_ci		return;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	list_del(&cl_vtag->list);
68062306a36Sopenharmony_ci	kfree(cl_vtag);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_civoid *mei_cldev_dma_map(struct mei_cl_device *cldev, u8 buffer_id, size_t size)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct mei_device *bus;
68662306a36Sopenharmony_ci	struct mei_cl *cl;
68762306a36Sopenharmony_ci	int ret;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (!cldev || !buffer_id || !size)
69062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (!IS_ALIGNED(size, MEI_FW_PAGE_SIZE)) {
69362306a36Sopenharmony_ci		dev_err(&cldev->dev, "Map size should be aligned to %lu\n",
69462306a36Sopenharmony_ci			MEI_FW_PAGE_SIZE);
69562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	cl = cldev->cl;
69962306a36Sopenharmony_ci	bus = cldev->bus;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
70262306a36Sopenharmony_ci	if (cl->state == MEI_FILE_UNINITIALIZED) {
70362306a36Sopenharmony_ci		ret = mei_cl_link(cl);
70462306a36Sopenharmony_ci		if (ret)
70562306a36Sopenharmony_ci			goto notlinked;
70662306a36Sopenharmony_ci		/* update pointers */
70762306a36Sopenharmony_ci		cl->cldev = cldev;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	ret = mei_cl_dma_alloc_and_map(cl, NULL, buffer_id, size);
71162306a36Sopenharmony_ci	if (ret)
71262306a36Sopenharmony_ci		mei_cl_unlink(cl);
71362306a36Sopenharmony_cinotlinked:
71462306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
71562306a36Sopenharmony_ci	if (ret)
71662306a36Sopenharmony_ci		return ERR_PTR(ret);
71762306a36Sopenharmony_ci	return cl->dma.vaddr;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_dma_map);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ciint mei_cldev_dma_unmap(struct mei_cl_device *cldev)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct mei_device *bus;
72462306a36Sopenharmony_ci	struct mei_cl *cl;
72562306a36Sopenharmony_ci	int ret;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (!cldev)
72862306a36Sopenharmony_ci		return -EINVAL;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	cl = cldev->cl;
73162306a36Sopenharmony_ci	bus = cldev->bus;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
73462306a36Sopenharmony_ci	ret = mei_cl_dma_unmap(cl, NULL);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	mei_cl_flush_queues(cl, NULL);
73762306a36Sopenharmony_ci	mei_cl_unlink(cl);
73862306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
73962306a36Sopenharmony_ci	return ret;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_dma_unmap);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/**
74462306a36Sopenharmony_ci * mei_cldev_enable - enable me client device
74562306a36Sopenharmony_ci *     create connection with me client
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * @cldev: me client device
74862306a36Sopenharmony_ci *
74962306a36Sopenharmony_ci * Return: 0 on success and < 0 on error
75062306a36Sopenharmony_ci */
75162306a36Sopenharmony_ciint mei_cldev_enable(struct mei_cl_device *cldev)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct mei_device *bus = cldev->bus;
75462306a36Sopenharmony_ci	struct mei_cl *cl;
75562306a36Sopenharmony_ci	int ret;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	cl = cldev->cl;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
76062306a36Sopenharmony_ci	if (cl->state == MEI_FILE_UNINITIALIZED) {
76162306a36Sopenharmony_ci		ret = mei_cl_link(cl);
76262306a36Sopenharmony_ci		if (ret)
76362306a36Sopenharmony_ci			goto notlinked;
76462306a36Sopenharmony_ci		/* update pointers */
76562306a36Sopenharmony_ci		cl->cldev = cldev;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (mei_cl_is_connected(cl)) {
76962306a36Sopenharmony_ci		ret = 0;
77062306a36Sopenharmony_ci		goto out;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (!mei_me_cl_is_active(cldev->me_cl)) {
77462306a36Sopenharmony_ci		dev_err(&cldev->dev, "me client is not active\n");
77562306a36Sopenharmony_ci		ret = -ENOTTY;
77662306a36Sopenharmony_ci		goto out;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ret = mei_cl_bus_vtag_alloc(cldev);
78062306a36Sopenharmony_ci	if (ret)
78162306a36Sopenharmony_ci		goto out;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	ret = mei_cl_connect(cl, cldev->me_cl, NULL);
78462306a36Sopenharmony_ci	if (ret < 0) {
78562306a36Sopenharmony_ci		dev_err(&cldev->dev, "cannot connect\n");
78662306a36Sopenharmony_ci		mei_cl_bus_vtag_free(cldev);
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ciout:
79062306a36Sopenharmony_ci	if (ret)
79162306a36Sopenharmony_ci		mei_cl_unlink(cl);
79262306a36Sopenharmony_cinotlinked:
79362306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return ret;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_enable);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci/**
80062306a36Sopenharmony_ci * mei_cldev_unregister_callbacks - internal wrapper for unregistering
80162306a36Sopenharmony_ci *  callbacks.
80262306a36Sopenharmony_ci *
80362306a36Sopenharmony_ci * @cldev: client device
80462306a36Sopenharmony_ci */
80562306a36Sopenharmony_cistatic void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	if (cldev->rx_cb) {
80862306a36Sopenharmony_ci		cancel_work_sync(&cldev->rx_work);
80962306a36Sopenharmony_ci		cldev->rx_cb = NULL;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (cldev->notif_cb) {
81362306a36Sopenharmony_ci		cancel_work_sync(&cldev->notif_work);
81462306a36Sopenharmony_ci		cldev->notif_cb = NULL;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci/**
81962306a36Sopenharmony_ci * mei_cldev_disable - disable me client device
82062306a36Sopenharmony_ci *     disconnect form the me client
82162306a36Sopenharmony_ci *
82262306a36Sopenharmony_ci * @cldev: me client device
82362306a36Sopenharmony_ci *
82462306a36Sopenharmony_ci * Return: 0 on success and < 0 on error
82562306a36Sopenharmony_ci */
82662306a36Sopenharmony_ciint mei_cldev_disable(struct mei_cl_device *cldev)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct mei_device *bus;
82962306a36Sopenharmony_ci	struct mei_cl *cl;
83062306a36Sopenharmony_ci	int err;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (!cldev)
83362306a36Sopenharmony_ci		return -ENODEV;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	cl = cldev->cl;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	bus = cldev->bus;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	mei_cldev_unregister_callbacks(cldev);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	mutex_lock(&bus->device_lock);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	mei_cl_bus_vtag_free(cldev);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (!mei_cl_is_connected(cl)) {
84662306a36Sopenharmony_ci		dev_dbg(bus->dev, "Already disconnected\n");
84762306a36Sopenharmony_ci		err = 0;
84862306a36Sopenharmony_ci		goto out;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	err = mei_cl_disconnect(cl);
85262306a36Sopenharmony_ci	if (err < 0)
85362306a36Sopenharmony_ci		dev_err(bus->dev, "Could not disconnect from the ME client\n");
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ciout:
85662306a36Sopenharmony_ci	/* Flush queues and remove any pending read unless we have mapped DMA */
85762306a36Sopenharmony_ci	if (!cl->dma_mapped) {
85862306a36Sopenharmony_ci		mei_cl_flush_queues(cl, NULL);
85962306a36Sopenharmony_ci		mei_cl_unlink(cl);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	mutex_unlock(&bus->device_lock);
86362306a36Sopenharmony_ci	return err;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_disable);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/**
86862306a36Sopenharmony_ci * mei_cldev_send_gsc_command - sends a gsc command, by sending
86962306a36Sopenharmony_ci * a gsl mei message to gsc and receiving reply from gsc
87062306a36Sopenharmony_ci *
87162306a36Sopenharmony_ci * @cldev: me client device
87262306a36Sopenharmony_ci * @client_id: client id to send the command to
87362306a36Sopenharmony_ci * @fence_id: fence id to send the command to
87462306a36Sopenharmony_ci * @sg_in: scatter gather list containing addresses for rx message buffer
87562306a36Sopenharmony_ci * @total_in_len: total length of data in 'in' sg, can be less than the sum of buffers sizes
87662306a36Sopenharmony_ci * @sg_out: scatter gather list containing addresses for tx message buffer
87762306a36Sopenharmony_ci *
87862306a36Sopenharmony_ci * Return:
87962306a36Sopenharmony_ci *  * written size in bytes
88062306a36Sopenharmony_ci *  * < 0 on error
88162306a36Sopenharmony_ci */
88262306a36Sopenharmony_cissize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev,
88362306a36Sopenharmony_ci				   u8 client_id, u32 fence_id,
88462306a36Sopenharmony_ci				   struct scatterlist *sg_in,
88562306a36Sopenharmony_ci				   size_t total_in_len,
88662306a36Sopenharmony_ci				   struct scatterlist *sg_out)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct mei_cl *cl;
88962306a36Sopenharmony_ci	struct mei_device *bus;
89062306a36Sopenharmony_ci	ssize_t ret = 0;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	struct mei_ext_hdr_gsc_h2f *ext_hdr;
89362306a36Sopenharmony_ci	size_t buf_sz = sizeof(struct mei_ext_hdr_gsc_h2f);
89462306a36Sopenharmony_ci	int sg_out_nents, sg_in_nents;
89562306a36Sopenharmony_ci	int i;
89662306a36Sopenharmony_ci	struct scatterlist *sg;
89762306a36Sopenharmony_ci	struct mei_ext_hdr_gsc_f2h rx_msg;
89862306a36Sopenharmony_ci	unsigned int sg_len;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (!cldev || !sg_in || !sg_out)
90162306a36Sopenharmony_ci		return -EINVAL;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	cl = cldev->cl;
90462306a36Sopenharmony_ci	bus = cldev->bus;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	dev_dbg(bus->dev, "client_id %u, fence_id %u\n", client_id, fence_id);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (!bus->hbm_f_gsc_supported)
90962306a36Sopenharmony_ci		return -EOPNOTSUPP;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	sg_out_nents = sg_nents(sg_out);
91262306a36Sopenharmony_ci	sg_in_nents = sg_nents(sg_in);
91362306a36Sopenharmony_ci	/* at least one entry in tx and rx sgls must be present */
91462306a36Sopenharmony_ci	if (sg_out_nents <= 0 || sg_in_nents <= 0)
91562306a36Sopenharmony_ci		return -EINVAL;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	buf_sz += (sg_out_nents + sg_in_nents) * sizeof(struct mei_gsc_sgl);
91862306a36Sopenharmony_ci	ext_hdr = kzalloc(buf_sz, GFP_KERNEL);
91962306a36Sopenharmony_ci	if (!ext_hdr)
92062306a36Sopenharmony_ci		return -ENOMEM;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* construct the GSC message */
92362306a36Sopenharmony_ci	ext_hdr->hdr.type = MEI_EXT_HDR_GSC;
92462306a36Sopenharmony_ci	ext_hdr->hdr.length = buf_sz / sizeof(u32); /* length is in dw */
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	ext_hdr->client_id = client_id;
92762306a36Sopenharmony_ci	ext_hdr->addr_type = GSC_ADDRESS_TYPE_PHYSICAL_SGL;
92862306a36Sopenharmony_ci	ext_hdr->fence_id = fence_id;
92962306a36Sopenharmony_ci	ext_hdr->input_address_count = sg_in_nents;
93062306a36Sopenharmony_ci	ext_hdr->output_address_count = sg_out_nents;
93162306a36Sopenharmony_ci	ext_hdr->reserved[0] = 0;
93262306a36Sopenharmony_ci	ext_hdr->reserved[1] = 0;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* copy in-sgl to the message */
93562306a36Sopenharmony_ci	for (i = 0, sg = sg_in; i < sg_in_nents; i++, sg++) {
93662306a36Sopenharmony_ci		ext_hdr->sgl[i].low = lower_32_bits(sg_dma_address(sg));
93762306a36Sopenharmony_ci		ext_hdr->sgl[i].high = upper_32_bits(sg_dma_address(sg));
93862306a36Sopenharmony_ci		sg_len = min_t(unsigned int, sg_dma_len(sg), PAGE_SIZE);
93962306a36Sopenharmony_ci		ext_hdr->sgl[i].length = (sg_len <= total_in_len) ? sg_len : total_in_len;
94062306a36Sopenharmony_ci		total_in_len -= ext_hdr->sgl[i].length;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* copy out-sgl to the message */
94462306a36Sopenharmony_ci	for (i = sg_in_nents, sg = sg_out; i < sg_in_nents + sg_out_nents; i++, sg++) {
94562306a36Sopenharmony_ci		ext_hdr->sgl[i].low = lower_32_bits(sg_dma_address(sg));
94662306a36Sopenharmony_ci		ext_hdr->sgl[i].high = upper_32_bits(sg_dma_address(sg));
94762306a36Sopenharmony_ci		sg_len = min_t(unsigned int, sg_dma_len(sg), PAGE_SIZE);
94862306a36Sopenharmony_ci		ext_hdr->sgl[i].length = sg_len;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/* send the message to GSC */
95262306a36Sopenharmony_ci	ret = __mei_cl_send(cl, (u8 *)ext_hdr, buf_sz, 0, MEI_CL_IO_SGL);
95362306a36Sopenharmony_ci	if (ret < 0) {
95462306a36Sopenharmony_ci		dev_err(bus->dev, "__mei_cl_send failed, returned %zd\n", ret);
95562306a36Sopenharmony_ci		goto end;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	if (ret != buf_sz) {
95862306a36Sopenharmony_ci		dev_err(bus->dev, "__mei_cl_send returned %zd instead of expected %zd\n",
95962306a36Sopenharmony_ci			ret, buf_sz);
96062306a36Sopenharmony_ci		ret = -EIO;
96162306a36Sopenharmony_ci		goto end;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* receive the reply from GSC, note that at this point sg_in should contain the reply */
96562306a36Sopenharmony_ci	ret = __mei_cl_recv(cl, (u8 *)&rx_msg, sizeof(rx_msg), NULL, MEI_CL_IO_SGL, 0);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (ret != sizeof(rx_msg)) {
96862306a36Sopenharmony_ci		dev_err(bus->dev, "__mei_cl_recv returned %zd instead of expected %zd\n",
96962306a36Sopenharmony_ci			ret, sizeof(rx_msg));
97062306a36Sopenharmony_ci		if (ret >= 0)
97162306a36Sopenharmony_ci			ret = -EIO;
97262306a36Sopenharmony_ci		goto end;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/* check rx_msg.client_id and rx_msg.fence_id match the ones we send */
97662306a36Sopenharmony_ci	if (rx_msg.client_id != client_id || rx_msg.fence_id != fence_id) {
97762306a36Sopenharmony_ci		dev_err(bus->dev, "received client_id/fence_id  %u/%u  instead of %u/%u sent\n",
97862306a36Sopenharmony_ci			rx_msg.client_id, rx_msg.fence_id, client_id, fence_id);
97962306a36Sopenharmony_ci		ret = -EFAULT;
98062306a36Sopenharmony_ci		goto end;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	dev_dbg(bus->dev, "gsc command: successfully written %u bytes\n",  rx_msg.written);
98462306a36Sopenharmony_ci	ret = rx_msg.written;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ciend:
98762306a36Sopenharmony_ci	kfree(ext_hdr);
98862306a36Sopenharmony_ci	return ret;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_send_gsc_command);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci/**
99362306a36Sopenharmony_ci * mei_cl_device_find - find matching entry in the driver id table
99462306a36Sopenharmony_ci *
99562306a36Sopenharmony_ci * @cldev: me client device
99662306a36Sopenharmony_ci * @cldrv: me client driver
99762306a36Sopenharmony_ci *
99862306a36Sopenharmony_ci * Return: id on success; NULL if no id is matching
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_cistatic const
100162306a36Sopenharmony_cistruct mei_cl_device_id *mei_cl_device_find(const struct mei_cl_device *cldev,
100262306a36Sopenharmony_ci					    const struct mei_cl_driver *cldrv)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	const struct mei_cl_device_id *id;
100562306a36Sopenharmony_ci	const uuid_le *uuid;
100662306a36Sopenharmony_ci	u8 version;
100762306a36Sopenharmony_ci	bool match;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	uuid = mei_me_cl_uuid(cldev->me_cl);
101062306a36Sopenharmony_ci	version = mei_me_cl_ver(cldev->me_cl);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	id = cldrv->id_table;
101362306a36Sopenharmony_ci	while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) {
101462306a36Sopenharmony_ci		if (!uuid_le_cmp(*uuid, id->uuid)) {
101562306a36Sopenharmony_ci			match = true;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci			if (cldev->name[0])
101862306a36Sopenharmony_ci				if (strncmp(cldev->name, id->name,
101962306a36Sopenharmony_ci					    sizeof(id->name)))
102062306a36Sopenharmony_ci					match = false;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci			if (id->version != MEI_CL_VERSION_ANY)
102362306a36Sopenharmony_ci				if (id->version != version)
102462306a36Sopenharmony_ci					match = false;
102562306a36Sopenharmony_ci			if (match)
102662306a36Sopenharmony_ci				return id;
102762306a36Sopenharmony_ci		}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci		id++;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	return NULL;
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci/**
103662306a36Sopenharmony_ci * mei_cl_device_match  - device match function
103762306a36Sopenharmony_ci *
103862306a36Sopenharmony_ci * @dev: device
103962306a36Sopenharmony_ci * @drv: driver
104062306a36Sopenharmony_ci *
104162306a36Sopenharmony_ci * Return:  1 if matching device was found 0 otherwise
104262306a36Sopenharmony_ci */
104362306a36Sopenharmony_cistatic int mei_cl_device_match(struct device *dev, struct device_driver *drv)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	const struct mei_cl_device *cldev = to_mei_cl_device(dev);
104662306a36Sopenharmony_ci	const struct mei_cl_driver *cldrv = to_mei_cl_driver(drv);
104762306a36Sopenharmony_ci	const struct mei_cl_device_id *found_id;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (!cldev->do_match)
105062306a36Sopenharmony_ci		return 0;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (!cldrv || !cldrv->id_table)
105362306a36Sopenharmony_ci		return 0;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	found_id = mei_cl_device_find(cldev, cldrv);
105662306a36Sopenharmony_ci	if (found_id)
105762306a36Sopenharmony_ci		return 1;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return 0;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci/**
106362306a36Sopenharmony_ci * mei_cl_device_probe - bus probe function
106462306a36Sopenharmony_ci *
106562306a36Sopenharmony_ci * @dev: device
106662306a36Sopenharmony_ci *
106762306a36Sopenharmony_ci * Return:  0 on success; < 0 otherwise
106862306a36Sopenharmony_ci */
106962306a36Sopenharmony_cistatic int mei_cl_device_probe(struct device *dev)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	struct mei_cl_device *cldev;
107262306a36Sopenharmony_ci	struct mei_cl_driver *cldrv;
107362306a36Sopenharmony_ci	const struct mei_cl_device_id *id;
107462306a36Sopenharmony_ci	int ret;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	cldev = to_mei_cl_device(dev);
107762306a36Sopenharmony_ci	cldrv = to_mei_cl_driver(dev->driver);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if (!cldrv || !cldrv->probe)
108062306a36Sopenharmony_ci		return -ENODEV;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	id = mei_cl_device_find(cldev, cldrv);
108362306a36Sopenharmony_ci	if (!id)
108462306a36Sopenharmony_ci		return -ENODEV;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (!mei_cl_bus_module_get(cldev)) {
108762306a36Sopenharmony_ci		dev_err(&cldev->dev, "get hw module failed");
108862306a36Sopenharmony_ci		return -ENODEV;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	ret = cldrv->probe(cldev, id);
109262306a36Sopenharmony_ci	if (ret) {
109362306a36Sopenharmony_ci		mei_cl_bus_module_put(cldev);
109462306a36Sopenharmony_ci		return ret;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	__module_get(THIS_MODULE);
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci/**
110262306a36Sopenharmony_ci * mei_cl_device_remove - remove device from the bus
110362306a36Sopenharmony_ci *
110462306a36Sopenharmony_ci * @dev: device
110562306a36Sopenharmony_ci *
110662306a36Sopenharmony_ci * Return:  0 on success; < 0 otherwise
110762306a36Sopenharmony_ci */
110862306a36Sopenharmony_cistatic void mei_cl_device_remove(struct device *dev)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
111162306a36Sopenharmony_ci	struct mei_cl_driver *cldrv = to_mei_cl_driver(dev->driver);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (cldrv->remove)
111462306a36Sopenharmony_ci		cldrv->remove(cldev);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	mei_cldev_unregister_callbacks(cldev);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	mei_cl_bus_module_put(cldev);
111962306a36Sopenharmony_ci	module_put(THIS_MODULE);
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *a,
112362306a36Sopenharmony_ci			     char *buf)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%s", cldev->name);
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic ssize_t uuid_show(struct device *dev, struct device_attribute *a,
113262306a36Sopenharmony_ci			     char *buf)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
113562306a36Sopenharmony_ci	const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	return sprintf(buf, "%pUl", uuid);
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(uuid);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic ssize_t version_show(struct device *dev, struct device_attribute *a,
114262306a36Sopenharmony_ci			     char *buf)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
114562306a36Sopenharmony_ci	u8 version = mei_me_cl_ver(cldev->me_cl);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	return sprintf(buf, "%02X", version);
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(version);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *a,
115262306a36Sopenharmony_ci			     char *buf)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
115562306a36Sopenharmony_ci	const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
115662306a36Sopenharmony_ci	u8 version = mei_me_cl_ver(cldev->me_cl);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:%02X:",
115962306a36Sopenharmony_ci			 cldev->name, uuid, version);
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic ssize_t max_conn_show(struct device *dev, struct device_attribute *a,
116462306a36Sopenharmony_ci			     char *buf)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
116762306a36Sopenharmony_ci	u8 maxconn = mei_me_cl_max_conn(cldev->me_cl);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	return sprintf(buf, "%d", maxconn);
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(max_conn);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic ssize_t fixed_show(struct device *dev, struct device_attribute *a,
117462306a36Sopenharmony_ci			  char *buf)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
117762306a36Sopenharmony_ci	u8 fixed = mei_me_cl_fixed(cldev->me_cl);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	return sprintf(buf, "%d", fixed);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fixed);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic ssize_t vtag_show(struct device *dev, struct device_attribute *a,
118462306a36Sopenharmony_ci			 char *buf)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
118762306a36Sopenharmony_ci	bool vt = mei_me_cl_vt(cldev->me_cl);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	return sprintf(buf, "%d", vt);
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vtag);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic ssize_t max_len_show(struct device *dev, struct device_attribute *a,
119462306a36Sopenharmony_ci			    char *buf)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
119762306a36Sopenharmony_ci	u32 maxlen = mei_me_cl_max_len(cldev->me_cl);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return sprintf(buf, "%u", maxlen);
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(max_len);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cistatic struct attribute *mei_cldev_attrs[] = {
120462306a36Sopenharmony_ci	&dev_attr_name.attr,
120562306a36Sopenharmony_ci	&dev_attr_uuid.attr,
120662306a36Sopenharmony_ci	&dev_attr_version.attr,
120762306a36Sopenharmony_ci	&dev_attr_modalias.attr,
120862306a36Sopenharmony_ci	&dev_attr_max_conn.attr,
120962306a36Sopenharmony_ci	&dev_attr_fixed.attr,
121062306a36Sopenharmony_ci	&dev_attr_vtag.attr,
121162306a36Sopenharmony_ci	&dev_attr_max_len.attr,
121262306a36Sopenharmony_ci	NULL,
121362306a36Sopenharmony_ci};
121462306a36Sopenharmony_ciATTRIBUTE_GROUPS(mei_cldev);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci/**
121762306a36Sopenharmony_ci * mei_cl_device_uevent - me client bus uevent handler
121862306a36Sopenharmony_ci *
121962306a36Sopenharmony_ci * @dev: device
122062306a36Sopenharmony_ci * @env: uevent kobject
122162306a36Sopenharmony_ci *
122262306a36Sopenharmony_ci * Return: 0 on success -ENOMEM on when add_uevent_var fails
122362306a36Sopenharmony_ci */
122462306a36Sopenharmony_cistatic int mei_cl_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	const struct mei_cl_device *cldev = to_mei_cl_device(dev);
122762306a36Sopenharmony_ci	const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
122862306a36Sopenharmony_ci	u8 version = mei_me_cl_ver(cldev->me_cl);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (add_uevent_var(env, "MEI_CL_VERSION=%d", version))
123162306a36Sopenharmony_ci		return -ENOMEM;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid))
123462306a36Sopenharmony_ci		return -ENOMEM;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name))
123762306a36Sopenharmony_ci		return -ENOMEM;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:",
124062306a36Sopenharmony_ci			   cldev->name, uuid, version))
124162306a36Sopenharmony_ci		return -ENOMEM;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return 0;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic struct bus_type mei_cl_bus_type = {
124762306a36Sopenharmony_ci	.name		= "mei",
124862306a36Sopenharmony_ci	.dev_groups	= mei_cldev_groups,
124962306a36Sopenharmony_ci	.match		= mei_cl_device_match,
125062306a36Sopenharmony_ci	.probe		= mei_cl_device_probe,
125162306a36Sopenharmony_ci	.remove		= mei_cl_device_remove,
125262306a36Sopenharmony_ci	.uevent		= mei_cl_device_uevent,
125362306a36Sopenharmony_ci};
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cistatic struct mei_device *mei_dev_bus_get(struct mei_device *bus)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	if (bus)
125862306a36Sopenharmony_ci		get_device(bus->dev);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	return bus;
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic void mei_dev_bus_put(struct mei_device *bus)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	if (bus)
126662306a36Sopenharmony_ci		put_device(bus->dev);
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic void mei_cl_bus_dev_release(struct device *dev)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct mei_cl_device *cldev = to_mei_cl_device(dev);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	mei_cl_flush_queues(cldev->cl, NULL);
127462306a36Sopenharmony_ci	mei_me_cl_put(cldev->me_cl);
127562306a36Sopenharmony_ci	mei_dev_bus_put(cldev->bus);
127662306a36Sopenharmony_ci	kfree(cldev->cl);
127762306a36Sopenharmony_ci	kfree(cldev);
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic const struct device_type mei_cl_device_type = {
128162306a36Sopenharmony_ci	.release = mei_cl_bus_dev_release,
128262306a36Sopenharmony_ci};
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci/**
128562306a36Sopenharmony_ci * mei_cl_bus_set_name - set device name for me client device
128662306a36Sopenharmony_ci *  <controller>-<client device>
128762306a36Sopenharmony_ci *  Example: 0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb
128862306a36Sopenharmony_ci *
128962306a36Sopenharmony_ci * @cldev: me client device
129062306a36Sopenharmony_ci */
129162306a36Sopenharmony_cistatic inline void mei_cl_bus_set_name(struct mei_cl_device *cldev)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	dev_set_name(&cldev->dev, "%s-%pUl",
129462306a36Sopenharmony_ci		     dev_name(cldev->bus->dev),
129562306a36Sopenharmony_ci		     mei_me_cl_uuid(cldev->me_cl));
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci/**
129962306a36Sopenharmony_ci * mei_cl_bus_dev_alloc - initialize and allocate mei client device
130062306a36Sopenharmony_ci *
130162306a36Sopenharmony_ci * @bus: mei device
130262306a36Sopenharmony_ci * @me_cl: me client
130362306a36Sopenharmony_ci *
130462306a36Sopenharmony_ci * Return: allocated device structur or NULL on allocation failure
130562306a36Sopenharmony_ci */
130662306a36Sopenharmony_cistatic struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus,
130762306a36Sopenharmony_ci						  struct mei_me_client *me_cl)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct mei_cl_device *cldev;
131062306a36Sopenharmony_ci	struct mei_cl *cl;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	cldev = kzalloc(sizeof(*cldev), GFP_KERNEL);
131362306a36Sopenharmony_ci	if (!cldev)
131462306a36Sopenharmony_ci		return NULL;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	cl = mei_cl_allocate(bus);
131762306a36Sopenharmony_ci	if (!cl) {
131862306a36Sopenharmony_ci		kfree(cldev);
131962306a36Sopenharmony_ci		return NULL;
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	device_initialize(&cldev->dev);
132362306a36Sopenharmony_ci	cldev->dev.parent = bus->dev;
132462306a36Sopenharmony_ci	cldev->dev.bus    = &mei_cl_bus_type;
132562306a36Sopenharmony_ci	cldev->dev.type   = &mei_cl_device_type;
132662306a36Sopenharmony_ci	cldev->bus        = mei_dev_bus_get(bus);
132762306a36Sopenharmony_ci	cldev->me_cl      = mei_me_cl_get(me_cl);
132862306a36Sopenharmony_ci	cldev->cl         = cl;
132962306a36Sopenharmony_ci	mei_cl_bus_set_name(cldev);
133062306a36Sopenharmony_ci	cldev->is_added   = 0;
133162306a36Sopenharmony_ci	INIT_LIST_HEAD(&cldev->bus_list);
133262306a36Sopenharmony_ci	device_enable_async_suspend(&cldev->dev);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	return cldev;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci/**
133862306a36Sopenharmony_ci * mei_cl_bus_dev_setup - setup me client device
133962306a36Sopenharmony_ci *    run fix up routines and set the device name
134062306a36Sopenharmony_ci *
134162306a36Sopenharmony_ci * @bus: mei device
134262306a36Sopenharmony_ci * @cldev: me client device
134362306a36Sopenharmony_ci *
134462306a36Sopenharmony_ci * Return: true if the device is eligible for enumeration
134562306a36Sopenharmony_ci */
134662306a36Sopenharmony_cistatic bool mei_cl_bus_dev_setup(struct mei_device *bus,
134762306a36Sopenharmony_ci				 struct mei_cl_device *cldev)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	cldev->do_match = 1;
135062306a36Sopenharmony_ci	mei_cl_bus_dev_fixup(cldev);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/* the device name can change during fix up */
135362306a36Sopenharmony_ci	if (cldev->do_match)
135462306a36Sopenharmony_ci		mei_cl_bus_set_name(cldev);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	return cldev->do_match == 1;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci/**
136062306a36Sopenharmony_ci * mei_cl_bus_dev_add - add me client devices
136162306a36Sopenharmony_ci *
136262306a36Sopenharmony_ci * @cldev: me client device
136362306a36Sopenharmony_ci *
136462306a36Sopenharmony_ci * Return: 0 on success; < 0 on failre
136562306a36Sopenharmony_ci */
136662306a36Sopenharmony_cistatic int mei_cl_bus_dev_add(struct mei_cl_device *cldev)
136762306a36Sopenharmony_ci{
136862306a36Sopenharmony_ci	int ret;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n",
137162306a36Sopenharmony_ci		mei_me_cl_uuid(cldev->me_cl),
137262306a36Sopenharmony_ci		mei_me_cl_ver(cldev->me_cl));
137362306a36Sopenharmony_ci	ret = device_add(&cldev->dev);
137462306a36Sopenharmony_ci	if (!ret)
137562306a36Sopenharmony_ci		cldev->is_added = 1;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	return ret;
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci/**
138162306a36Sopenharmony_ci * mei_cl_bus_dev_stop - stop the driver
138262306a36Sopenharmony_ci *
138362306a36Sopenharmony_ci * @cldev: me client device
138462306a36Sopenharmony_ci */
138562306a36Sopenharmony_cistatic void mei_cl_bus_dev_stop(struct mei_cl_device *cldev)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	cldev->do_match = 0;
138862306a36Sopenharmony_ci	if (cldev->is_added)
138962306a36Sopenharmony_ci		device_release_driver(&cldev->dev);
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci/**
139362306a36Sopenharmony_ci * mei_cl_bus_dev_destroy - destroy me client devices object
139462306a36Sopenharmony_ci *
139562306a36Sopenharmony_ci * @cldev: me client device
139662306a36Sopenharmony_ci *
139762306a36Sopenharmony_ci * Locking: called under "dev->cl_bus_lock" lock
139862306a36Sopenharmony_ci */
139962306a36Sopenharmony_cistatic void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock));
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (!cldev->is_added)
140562306a36Sopenharmony_ci		return;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	device_del(&cldev->dev);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	list_del_init(&cldev->bus_list);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	cldev->is_added = 0;
141262306a36Sopenharmony_ci	put_device(&cldev->dev);
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci/**
141662306a36Sopenharmony_ci * mei_cl_bus_remove_device - remove a devices form the bus
141762306a36Sopenharmony_ci *
141862306a36Sopenharmony_ci * @cldev: me client device
141962306a36Sopenharmony_ci */
142062306a36Sopenharmony_cistatic void mei_cl_bus_remove_device(struct mei_cl_device *cldev)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	mei_cl_bus_dev_stop(cldev);
142362306a36Sopenharmony_ci	mei_cl_bus_dev_destroy(cldev);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci/**
142762306a36Sopenharmony_ci * mei_cl_bus_remove_devices - remove all devices form the bus
142862306a36Sopenharmony_ci *
142962306a36Sopenharmony_ci * @bus: mei device
143062306a36Sopenharmony_ci */
143162306a36Sopenharmony_civoid mei_cl_bus_remove_devices(struct mei_device *bus)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct mei_cl_device *cldev, *next;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	mutex_lock(&bus->cl_bus_lock);
143662306a36Sopenharmony_ci	list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list)
143762306a36Sopenharmony_ci		mei_cl_bus_remove_device(cldev);
143862306a36Sopenharmony_ci	mutex_unlock(&bus->cl_bus_lock);
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci/**
144362306a36Sopenharmony_ci * mei_cl_bus_dev_init - allocate and initializes an mei client devices
144462306a36Sopenharmony_ci *     based on me client
144562306a36Sopenharmony_ci *
144662306a36Sopenharmony_ci * @bus: mei device
144762306a36Sopenharmony_ci * @me_cl: me client
144862306a36Sopenharmony_ci *
144962306a36Sopenharmony_ci * Locking: called under "dev->cl_bus_lock" lock
145062306a36Sopenharmony_ci */
145162306a36Sopenharmony_cistatic void mei_cl_bus_dev_init(struct mei_device *bus,
145262306a36Sopenharmony_ci				struct mei_me_client *me_cl)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct mei_cl_device *cldev;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&bus->cl_bus_lock));
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl));
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (me_cl->bus_added)
146162306a36Sopenharmony_ci		return;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	cldev = mei_cl_bus_dev_alloc(bus, me_cl);
146462306a36Sopenharmony_ci	if (!cldev)
146562306a36Sopenharmony_ci		return;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	me_cl->bus_added = true;
146862306a36Sopenharmony_ci	list_add_tail(&cldev->bus_list, &bus->device_list);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci/**
147362306a36Sopenharmony_ci * mei_cl_bus_rescan - scan me clients list and add create
147462306a36Sopenharmony_ci *    devices for eligible clients
147562306a36Sopenharmony_ci *
147662306a36Sopenharmony_ci * @bus: mei device
147762306a36Sopenharmony_ci */
147862306a36Sopenharmony_cistatic void mei_cl_bus_rescan(struct mei_device *bus)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	struct mei_cl_device *cldev, *n;
148162306a36Sopenharmony_ci	struct mei_me_client *me_cl;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	mutex_lock(&bus->cl_bus_lock);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	down_read(&bus->me_clients_rwsem);
148662306a36Sopenharmony_ci	list_for_each_entry(me_cl, &bus->me_clients, list)
148762306a36Sopenharmony_ci		mei_cl_bus_dev_init(bus, me_cl);
148862306a36Sopenharmony_ci	up_read(&bus->me_clients_rwsem);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) {
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		if (!mei_me_cl_is_active(cldev->me_cl)) {
149362306a36Sopenharmony_ci			mei_cl_bus_remove_device(cldev);
149462306a36Sopenharmony_ci			continue;
149562306a36Sopenharmony_ci		}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci		if (cldev->is_added)
149862306a36Sopenharmony_ci			continue;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci		if (mei_cl_bus_dev_setup(bus, cldev))
150162306a36Sopenharmony_ci			mei_cl_bus_dev_add(cldev);
150262306a36Sopenharmony_ci		else {
150362306a36Sopenharmony_ci			list_del_init(&cldev->bus_list);
150462306a36Sopenharmony_ci			put_device(&cldev->dev);
150562306a36Sopenharmony_ci		}
150662306a36Sopenharmony_ci	}
150762306a36Sopenharmony_ci	mutex_unlock(&bus->cl_bus_lock);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	dev_dbg(bus->dev, "rescan end");
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_civoid mei_cl_bus_rescan_work(struct work_struct *work)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct mei_device *bus =
151562306a36Sopenharmony_ci		container_of(work, struct mei_device, bus_rescan_work);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	mei_cl_bus_rescan(bus);
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ciint __mei_cldev_driver_register(struct mei_cl_driver *cldrv,
152162306a36Sopenharmony_ci				struct module *owner)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	int err;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	cldrv->driver.name = cldrv->name;
152662306a36Sopenharmony_ci	cldrv->driver.owner = owner;
152762306a36Sopenharmony_ci	cldrv->driver.bus = &mei_cl_bus_type;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	err = driver_register(&cldrv->driver);
153062306a36Sopenharmony_ci	if (err)
153162306a36Sopenharmony_ci		return err;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	pr_debug("mei: driver [%s] registered\n", cldrv->driver.name);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	return 0;
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__mei_cldev_driver_register);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_civoid mei_cldev_driver_unregister(struct mei_cl_driver *cldrv)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	driver_unregister(&cldrv->driver);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name);
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cldev_driver_unregister);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ciint __init mei_cl_bus_init(void)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	return bus_register(&mei_cl_bus_type);
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_civoid __exit mei_cl_bus_exit(void)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	bus_unregister(&mei_cl_bus_type);
155662306a36Sopenharmony_ci}
1557