162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ISHTP bus driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2012-2016, Intel Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include "bus.h"
1562306a36Sopenharmony_ci#include "ishtp-dev.h"
1662306a36Sopenharmony_ci#include "client.h"
1762306a36Sopenharmony_ci#include "hbm.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int ishtp_use_dma;
2062306a36Sopenharmony_cimodule_param_named(ishtp_use_dma, ishtp_use_dma, int, 0600);
2162306a36Sopenharmony_ciMODULE_PARM_DESC(ishtp_use_dma, "Use DMA to send messages");
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define to_ishtp_cl_driver(d) container_of(d, struct ishtp_cl_driver, driver)
2462306a36Sopenharmony_ci#define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev)
2562306a36Sopenharmony_cistatic bool ishtp_device_ready;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/**
2862306a36Sopenharmony_ci * ishtp_recv() - process ishtp message
2962306a36Sopenharmony_ci * @dev: ishtp device
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * If a message with valid header and size is received, then
3262306a36Sopenharmony_ci * this function calls appropriate handler. The host or firmware
3362306a36Sopenharmony_ci * address is zero, then they are host bus management message,
3462306a36Sopenharmony_ci * otherwise they are message fo clients.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_civoid ishtp_recv(struct ishtp_device *dev)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	uint32_t	msg_hdr;
3962306a36Sopenharmony_ci	struct ishtp_msg_hdr	*ishtp_hdr;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Read ISHTP header dword */
4262306a36Sopenharmony_ci	msg_hdr = dev->ops->ishtp_read_hdr(dev);
4362306a36Sopenharmony_ci	if (!msg_hdr)
4462306a36Sopenharmony_ci		return;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	dev->ops->sync_fw_clock(dev);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	ishtp_hdr = (struct ishtp_msg_hdr *)&msg_hdr;
4962306a36Sopenharmony_ci	dev->ishtp_msg_hdr = msg_hdr;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* Sanity check: ISHTP frag. length in header */
5262306a36Sopenharmony_ci	if (ishtp_hdr->length > dev->mtu) {
5362306a36Sopenharmony_ci		dev_err(dev->devc,
5462306a36Sopenharmony_ci			"ISHTP hdr - bad length: %u; dropped [%08X]\n",
5562306a36Sopenharmony_ci			(unsigned int)ishtp_hdr->length, msg_hdr);
5662306a36Sopenharmony_ci		return;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* ISHTP bus message */
6062306a36Sopenharmony_ci	if (!ishtp_hdr->host_addr && !ishtp_hdr->fw_addr)
6162306a36Sopenharmony_ci		recv_hbm(dev, ishtp_hdr);
6262306a36Sopenharmony_ci	/* ISHTP fixed-client message */
6362306a36Sopenharmony_ci	else if (!ishtp_hdr->host_addr)
6462306a36Sopenharmony_ci		recv_fixed_cl_msg(dev, ishtp_hdr);
6562306a36Sopenharmony_ci	else
6662306a36Sopenharmony_ci		/* ISHTP client message */
6762306a36Sopenharmony_ci		recv_ishtp_cl_msg(dev, ishtp_hdr);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_recv);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/**
7262306a36Sopenharmony_ci * ishtp_send_msg() - Send ishtp message
7362306a36Sopenharmony_ci * @dev: ishtp device
7462306a36Sopenharmony_ci * @hdr: Message header
7562306a36Sopenharmony_ci * @msg: Message contents
7662306a36Sopenharmony_ci * @ipc_send_compl: completion callback
7762306a36Sopenharmony_ci * @ipc_send_compl_prm: completion callback parameter
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * Send a multi fragment message via IPC. After sending the first fragment
8062306a36Sopenharmony_ci * the completion callback is called to schedule transmit of next fragment.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Return: This returns IPC send message status.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ciint ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
8562306a36Sopenharmony_ci		       void *msg, void(*ipc_send_compl)(void *),
8662306a36Sopenharmony_ci		       void *ipc_send_compl_prm)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	unsigned char	ipc_msg[IPC_FULL_MSG_SIZE];
8962306a36Sopenharmony_ci	uint32_t	drbl_val;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	drbl_val = dev->ops->ipc_get_header(dev, hdr->length +
9262306a36Sopenharmony_ci					    sizeof(struct ishtp_msg_hdr),
9362306a36Sopenharmony_ci					    1);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	memcpy(ipc_msg, &drbl_val, sizeof(uint32_t));
9662306a36Sopenharmony_ci	memcpy(ipc_msg + sizeof(uint32_t), hdr, sizeof(uint32_t));
9762306a36Sopenharmony_ci	memcpy(ipc_msg + 2 * sizeof(uint32_t), msg, hdr->length);
9862306a36Sopenharmony_ci	return	dev->ops->write(dev, ipc_send_compl, ipc_send_compl_prm,
9962306a36Sopenharmony_ci				ipc_msg, 2 * sizeof(uint32_t) + hdr->length);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**
10362306a36Sopenharmony_ci * ishtp_write_message() - Send ishtp single fragment message
10462306a36Sopenharmony_ci * @dev: ishtp device
10562306a36Sopenharmony_ci * @hdr: Message header
10662306a36Sopenharmony_ci * @buf: message data
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Send a single fragment message via IPC.  This returns IPC send message
10962306a36Sopenharmony_ci * status.
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Return: This returns IPC send message status.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ciint ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
11462306a36Sopenharmony_ci			void *buf)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	return ishtp_send_msg(dev, hdr, buf, NULL, NULL);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * ishtp_fw_cl_by_uuid() - locate index of fw client
12162306a36Sopenharmony_ci * @dev: ishtp device
12262306a36Sopenharmony_ci * @uuid: uuid of the client to search
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * Search firmware client using UUID.
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * Return: fw client index or -ENOENT if not found
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_ciint ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *uuid)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	unsigned int i;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	for (i = 0; i < dev->fw_clients_num; ++i) {
13362306a36Sopenharmony_ci		if (guid_equal(uuid, &dev->fw_clients[i].props.protocol_name))
13462306a36Sopenharmony_ci			return i;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	return -ENOENT;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci * ishtp_fw_cl_get_client() - return client information to client
14262306a36Sopenharmony_ci * @dev: the ishtp device structure
14362306a36Sopenharmony_ci * @uuid: uuid of the client to search
14462306a36Sopenharmony_ci *
14562306a36Sopenharmony_ci * Search firmware client using UUID and reture related client information.
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * Return: pointer of client information on success, NULL on failure.
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistruct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
15062306a36Sopenharmony_ci					       const guid_t *uuid)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int i;
15362306a36Sopenharmony_ci	unsigned long flags;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	spin_lock_irqsave(&dev->fw_clients_lock, flags);
15662306a36Sopenharmony_ci	i = ishtp_fw_cl_by_uuid(dev, uuid);
15762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
15862306a36Sopenharmony_ci	if (i < 0 || dev->fw_clients[i].props.fixed_address)
15962306a36Sopenharmony_ci		return NULL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return &dev->fw_clients[i];
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_fw_cl_get_client);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/**
16662306a36Sopenharmony_ci * ishtp_get_fw_client_id() - Get fw client id
16762306a36Sopenharmony_ci * @fw_client:	firmware client used to fetch the ID
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * This interface is used to reset HW get FW client id.
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * Return: firmware client id.
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_ciint ishtp_get_fw_client_id(struct ishtp_fw_client *fw_client)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	return fw_client->client_id;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_fw_client_id);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/**
18062306a36Sopenharmony_ci * ishtp_fw_cl_by_id() - return index to fw_clients for client_id
18162306a36Sopenharmony_ci * @dev: the ishtp device structure
18262306a36Sopenharmony_ci * @client_id: fw client id to search
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci * Search firmware client using client id.
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * Return: index on success, -ENOENT on failure.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_ciint ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int i, res = -ENOENT;
19162306a36Sopenharmony_ci	unsigned long	flags;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_lock_irqsave(&dev->fw_clients_lock, flags);
19462306a36Sopenharmony_ci	for (i = 0; i < dev->fw_clients_num; i++) {
19562306a36Sopenharmony_ci		if (dev->fw_clients[i].client_id == client_id) {
19662306a36Sopenharmony_ci			res = i;
19762306a36Sopenharmony_ci			break;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return res;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/**
20662306a36Sopenharmony_ci * ishtp_cl_device_probe() - Bus probe() callback
20762306a36Sopenharmony_ci * @dev: the device structure
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * This is a bus probe callback and calls the drive probe function.
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * Return: Return value from driver probe() call.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic int ishtp_cl_device_probe(struct device *dev)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
21662306a36Sopenharmony_ci	struct ishtp_cl_driver *driver;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (!device)
21962306a36Sopenharmony_ci		return 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
22262306a36Sopenharmony_ci	if (!driver || !driver->probe)
22362306a36Sopenharmony_ci		return -ENODEV;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return driver->probe(device);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/**
22962306a36Sopenharmony_ci * ishtp_cl_bus_match() - Bus match() callback
23062306a36Sopenharmony_ci * @dev: the device structure
23162306a36Sopenharmony_ci * @drv: the driver structure
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * This is a bus match callback, called when a new ishtp_cl_device is
23462306a36Sopenharmony_ci * registered during ishtp bus client enumeration. Use the guid_t in
23562306a36Sopenharmony_ci * drv and dev to decide whether they match or not.
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * Return: 1 if dev & drv matches, 0 otherwise.
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_cistatic int ishtp_cl_bus_match(struct device *dev, struct device_driver *drv)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
24262306a36Sopenharmony_ci	struct ishtp_cl_driver *driver = to_ishtp_cl_driver(drv);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return(device->fw_client ? guid_equal(&driver->id[0].guid,
24562306a36Sopenharmony_ci	       &device->fw_client->props.protocol_name) : 0);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/**
24962306a36Sopenharmony_ci * ishtp_cl_device_remove() - Bus remove() callback
25062306a36Sopenharmony_ci * @dev: the device structure
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * This is a bus remove callback and calls the drive remove function.
25362306a36Sopenharmony_ci * Since the ISH driver model supports only built in, this is
25462306a36Sopenharmony_ci * primarily can be called during pci driver init failure.
25562306a36Sopenharmony_ci *
25662306a36Sopenharmony_ci * Return: Return value from driver remove() call.
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic void ishtp_cl_device_remove(struct device *dev)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
26162306a36Sopenharmony_ci	struct ishtp_cl_driver *driver = to_ishtp_cl_driver(dev->driver);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (device->event_cb) {
26462306a36Sopenharmony_ci		device->event_cb = NULL;
26562306a36Sopenharmony_ci		cancel_work_sync(&device->event_work);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (driver->remove)
26962306a36Sopenharmony_ci		driver->remove(device);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * ishtp_cl_device_suspend() - Bus suspend callback
27462306a36Sopenharmony_ci * @dev:	device
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * Called during device suspend process.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * Return: Return value from driver suspend() call.
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic int ishtp_cl_device_suspend(struct device *dev)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
28362306a36Sopenharmony_ci	struct ishtp_cl_driver *driver;
28462306a36Sopenharmony_ci	int ret = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!device)
28762306a36Sopenharmony_ci		return 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
29062306a36Sopenharmony_ci	if (driver && driver->driver.pm) {
29162306a36Sopenharmony_ci		if (driver->driver.pm->suspend)
29262306a36Sopenharmony_ci			ret = driver->driver.pm->suspend(dev);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return ret;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/**
29962306a36Sopenharmony_ci * ishtp_cl_device_resume() - Bus resume callback
30062306a36Sopenharmony_ci * @dev:	device
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * Called during device resume process.
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * Return: Return value from driver resume() call.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_cistatic int ishtp_cl_device_resume(struct device *dev)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
30962306a36Sopenharmony_ci	struct ishtp_cl_driver *driver;
31062306a36Sopenharmony_ci	int ret = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (!device)
31362306a36Sopenharmony_ci		return 0;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
31662306a36Sopenharmony_ci	if (driver && driver->driver.pm) {
31762306a36Sopenharmony_ci		if (driver->driver.pm->resume)
31862306a36Sopenharmony_ci			ret = driver->driver.pm->resume(dev);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return ret;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/**
32562306a36Sopenharmony_ci * ishtp_cl_device_reset() - Reset callback
32662306a36Sopenharmony_ci * @device:	ishtp client device instance
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * This is a callback when HW reset is done and the device need
32962306a36Sopenharmony_ci * reinit.
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci * Return: Return value from driver reset() call.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cistatic int ishtp_cl_device_reset(struct ishtp_cl_device *device)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct ishtp_cl_driver *driver;
33662306a36Sopenharmony_ci	int ret = 0;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	device->event_cb = NULL;
33962306a36Sopenharmony_ci	cancel_work_sync(&device->event_work);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	driver = to_ishtp_cl_driver(device->dev.driver);
34262306a36Sopenharmony_ci	if (driver && driver->reset)
34362306a36Sopenharmony_ci		ret = driver->reset(device);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return ret;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *a,
34962306a36Sopenharmony_ci	char *buf)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	int len;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	len = snprintf(buf, PAGE_SIZE, ISHTP_MODULE_PREFIX "%s\n", dev_name(dev));
35462306a36Sopenharmony_ci	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic struct attribute *ishtp_cl_dev_attrs[] = {
35962306a36Sopenharmony_ci	&dev_attr_modalias.attr,
36062306a36Sopenharmony_ci	NULL,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ciATTRIBUTE_GROUPS(ishtp_cl_dev);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int ishtp_cl_uevent(const struct device *dev, struct kobj_uevent_env *env)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=" ISHTP_MODULE_PREFIX "%s", dev_name(dev)))
36762306a36Sopenharmony_ci		return -ENOMEM;
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = {
37262306a36Sopenharmony_ci	/* Suspend callbacks */
37362306a36Sopenharmony_ci	.suspend = ishtp_cl_device_suspend,
37462306a36Sopenharmony_ci	.resume = ishtp_cl_device_resume,
37562306a36Sopenharmony_ci	/* Hibernate callbacks */
37662306a36Sopenharmony_ci	.freeze = ishtp_cl_device_suspend,
37762306a36Sopenharmony_ci	.thaw = ishtp_cl_device_resume,
37862306a36Sopenharmony_ci	.restore = ishtp_cl_device_resume,
37962306a36Sopenharmony_ci};
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic struct bus_type ishtp_cl_bus_type = {
38262306a36Sopenharmony_ci	.name		= "ishtp",
38362306a36Sopenharmony_ci	.dev_groups	= ishtp_cl_dev_groups,
38462306a36Sopenharmony_ci	.probe		= ishtp_cl_device_probe,
38562306a36Sopenharmony_ci	.match		= ishtp_cl_bus_match,
38662306a36Sopenharmony_ci	.remove		= ishtp_cl_device_remove,
38762306a36Sopenharmony_ci	.pm		= &ishtp_cl_bus_dev_pm_ops,
38862306a36Sopenharmony_ci	.uevent		= ishtp_cl_uevent,
38962306a36Sopenharmony_ci};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void ishtp_cl_dev_release(struct device *dev)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	kfree(to_ishtp_cl_device(dev));
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic const struct device_type ishtp_cl_device_type = {
39762306a36Sopenharmony_ci	.release	= ishtp_cl_dev_release,
39862306a36Sopenharmony_ci};
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/**
40162306a36Sopenharmony_ci * ishtp_bus_add_device() - Function to create device on bus
40262306a36Sopenharmony_ci * @dev:	ishtp device
40362306a36Sopenharmony_ci * @uuid:	uuid of the client
40462306a36Sopenharmony_ci * @name:	Name of the client
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * Allocate ISHTP bus client device, attach it to uuid
40762306a36Sopenharmony_ci * and register with ISHTP bus.
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * Return: ishtp_cl_device pointer or NULL on failure
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_cistatic struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev,
41262306a36Sopenharmony_ci						    guid_t uuid, char *name)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct ishtp_cl_device *device;
41562306a36Sopenharmony_ci	int status;
41662306a36Sopenharmony_ci	unsigned long flags;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->device_list_lock, flags);
41962306a36Sopenharmony_ci	list_for_each_entry(device, &dev->device_list, device_link) {
42062306a36Sopenharmony_ci		if (!strcmp(name, dev_name(&device->dev))) {
42162306a36Sopenharmony_ci			device->fw_client = &dev->fw_clients[
42262306a36Sopenharmony_ci				dev->fw_client_presentation_num - 1];
42362306a36Sopenharmony_ci			spin_unlock_irqrestore(&dev->device_list_lock, flags);
42462306a36Sopenharmony_ci			ishtp_cl_device_reset(device);
42562306a36Sopenharmony_ci			return device;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->device_list_lock, flags);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	device = kzalloc(sizeof(struct ishtp_cl_device), GFP_KERNEL);
43162306a36Sopenharmony_ci	if (!device)
43262306a36Sopenharmony_ci		return NULL;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	device->dev.parent = dev->devc;
43562306a36Sopenharmony_ci	device->dev.bus = &ishtp_cl_bus_type;
43662306a36Sopenharmony_ci	device->dev.type = &ishtp_cl_device_type;
43762306a36Sopenharmony_ci	device->ishtp_dev = dev;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	device->fw_client =
44062306a36Sopenharmony_ci		&dev->fw_clients[dev->fw_client_presentation_num - 1];
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	dev_set_name(&device->dev, "%s", name);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->device_list_lock, flags);
44562306a36Sopenharmony_ci	list_add_tail(&device->device_link, &dev->device_list);
44662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->device_list_lock, flags);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	status = device_register(&device->dev);
44962306a36Sopenharmony_ci	if (status) {
45062306a36Sopenharmony_ci		spin_lock_irqsave(&dev->device_list_lock, flags);
45162306a36Sopenharmony_ci		list_del(&device->device_link);
45262306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->device_list_lock, flags);
45362306a36Sopenharmony_ci		dev_err(dev->devc, "Failed to register ISHTP client device\n");
45462306a36Sopenharmony_ci		put_device(&device->dev);
45562306a36Sopenharmony_ci		return NULL;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ishtp_device_ready = true;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return device;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci/**
46462306a36Sopenharmony_ci * ishtp_bus_remove_device() - Function to relase device on bus
46562306a36Sopenharmony_ci * @device:	client device instance
46662306a36Sopenharmony_ci *
46762306a36Sopenharmony_ci * This is a counterpart of ishtp_bus_add_device.
46862306a36Sopenharmony_ci * Device is unregistered.
46962306a36Sopenharmony_ci * the device structure is freed in 'ishtp_cl_dev_release' function
47062306a36Sopenharmony_ci * Called only during error in pci driver init path.
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_cistatic void ishtp_bus_remove_device(struct ishtp_cl_device *device)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	device_unregister(&device->dev);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci/**
47862306a36Sopenharmony_ci * ishtp_cl_driver_register() - Client driver register
47962306a36Sopenharmony_ci * @driver:	the client driver instance
48062306a36Sopenharmony_ci * @owner:	Owner of this driver module
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * Once a client driver is probed, it created a client
48362306a36Sopenharmony_ci * instance and registers with the bus.
48462306a36Sopenharmony_ci *
48562306a36Sopenharmony_ci * Return: Return value of driver_register or -ENODEV if not ready
48662306a36Sopenharmony_ci */
48762306a36Sopenharmony_ciint ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
48862306a36Sopenharmony_ci			     struct module *owner)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	if (!ishtp_device_ready)
49162306a36Sopenharmony_ci		return -ENODEV;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	driver->driver.name = driver->name;
49462306a36Sopenharmony_ci	driver->driver.owner = owner;
49562306a36Sopenharmony_ci	driver->driver.bus = &ishtp_cl_bus_type;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return driver_register(&driver->driver);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_driver_register);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/**
50262306a36Sopenharmony_ci * ishtp_cl_driver_unregister() - Client driver unregister
50362306a36Sopenharmony_ci * @driver:	the client driver instance
50462306a36Sopenharmony_ci *
50562306a36Sopenharmony_ci * Unregister client during device removal process.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_civoid ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	driver_unregister(&driver->driver);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_driver_unregister);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/**
51462306a36Sopenharmony_ci * ishtp_bus_event_work() - event work function
51562306a36Sopenharmony_ci * @work:	work struct pointer
51662306a36Sopenharmony_ci *
51762306a36Sopenharmony_ci * Once an event is received for a client this work
51862306a36Sopenharmony_ci * function is called. If the device has registered a
51962306a36Sopenharmony_ci * callback then the callback is called.
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_cistatic void ishtp_bus_event_work(struct work_struct *work)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct ishtp_cl_device *device;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	device = container_of(work, struct ishtp_cl_device, event_work);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (device->event_cb)
52862306a36Sopenharmony_ci		device->event_cb(device);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/**
53262306a36Sopenharmony_ci * ishtp_cl_bus_rx_event() - schedule event work
53362306a36Sopenharmony_ci * @device:	client device instance
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * Once an event is received for a client this schedules
53662306a36Sopenharmony_ci * a work function to process.
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_civoid ishtp_cl_bus_rx_event(struct ishtp_cl_device *device)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	if (!device || !device->event_cb)
54162306a36Sopenharmony_ci		return;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (device->event_cb)
54462306a36Sopenharmony_ci		schedule_work(&device->event_work);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/**
54862306a36Sopenharmony_ci * ishtp_register_event_cb() - Register callback
54962306a36Sopenharmony_ci * @device:	client device instance
55062306a36Sopenharmony_ci * @event_cb:	Event processor for an client
55162306a36Sopenharmony_ci *
55262306a36Sopenharmony_ci * Register a callback for events, called from client driver
55362306a36Sopenharmony_ci *
55462306a36Sopenharmony_ci * Return: Return 0 or -EALREADY if already registered
55562306a36Sopenharmony_ci */
55662306a36Sopenharmony_ciint ishtp_register_event_cb(struct ishtp_cl_device *device,
55762306a36Sopenharmony_ci	void (*event_cb)(struct ishtp_cl_device *))
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	if (device->event_cb)
56062306a36Sopenharmony_ci		return -EALREADY;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	device->event_cb = event_cb;
56362306a36Sopenharmony_ci	INIT_WORK(&device->event_work, ishtp_bus_event_work);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_register_event_cb);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/**
57062306a36Sopenharmony_ci * ishtp_get_device() - update usage count for the device
57162306a36Sopenharmony_ci * @cl_device:	client device instance
57262306a36Sopenharmony_ci *
57362306a36Sopenharmony_ci * Increment the usage count. The device can't be deleted
57462306a36Sopenharmony_ci */
57562306a36Sopenharmony_civoid ishtp_get_device(struct ishtp_cl_device *cl_device)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	cl_device->reference_count++;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_device);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/**
58262306a36Sopenharmony_ci * ishtp_put_device() - decrement usage count for the device
58362306a36Sopenharmony_ci * @cl_device:	client device instance
58462306a36Sopenharmony_ci *
58562306a36Sopenharmony_ci * Decrement the usage count. The device can be deleted is count = 0
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_civoid ishtp_put_device(struct ishtp_cl_device *cl_device)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	cl_device->reference_count--;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_put_device);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/**
59462306a36Sopenharmony_ci * ishtp_set_drvdata() - set client driver data
59562306a36Sopenharmony_ci * @cl_device:	client device instance
59662306a36Sopenharmony_ci * @data:	driver data need to be set
59762306a36Sopenharmony_ci *
59862306a36Sopenharmony_ci * Set client driver data to cl_device->driver_data.
59962306a36Sopenharmony_ci */
60062306a36Sopenharmony_civoid ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	cl_device->driver_data = data;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_set_drvdata);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci/**
60762306a36Sopenharmony_ci * ishtp_get_drvdata() - get client driver data
60862306a36Sopenharmony_ci * @cl_device:	client device instance
60962306a36Sopenharmony_ci *
61062306a36Sopenharmony_ci * Get client driver data from cl_device->driver_data.
61162306a36Sopenharmony_ci *
61262306a36Sopenharmony_ci * Return: pointer of driver data
61362306a36Sopenharmony_ci */
61462306a36Sopenharmony_civoid *ishtp_get_drvdata(struct ishtp_cl_device *cl_device)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	return cl_device->driver_data;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_drvdata);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/**
62162306a36Sopenharmony_ci * ishtp_dev_to_cl_device() - get ishtp_cl_device instance from device instance
62262306a36Sopenharmony_ci * @device: device instance
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Get ish_cl_device instance which embeds device instance in it.
62562306a36Sopenharmony_ci *
62662306a36Sopenharmony_ci * Return: pointer to ishtp_cl_device instance
62762306a36Sopenharmony_ci */
62862306a36Sopenharmony_cistruct ishtp_cl_device *ishtp_dev_to_cl_device(struct device *device)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	return to_ishtp_cl_device(device);
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_dev_to_cl_device);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci/**
63562306a36Sopenharmony_ci * ishtp_bus_new_client() - Create a new client
63662306a36Sopenharmony_ci * @dev:	ISHTP device instance
63762306a36Sopenharmony_ci *
63862306a36Sopenharmony_ci * Once bus protocol enumerates a client, this is called
63962306a36Sopenharmony_ci * to add a device for the client.
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci * Return: 0 on success or error code on failure
64262306a36Sopenharmony_ci */
64362306a36Sopenharmony_ciint ishtp_bus_new_client(struct ishtp_device *dev)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	int	i;
64662306a36Sopenharmony_ci	char	*dev_name;
64762306a36Sopenharmony_ci	struct ishtp_cl_device	*cl_device;
64862306a36Sopenharmony_ci	guid_t	device_uuid;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/*
65162306a36Sopenharmony_ci	 * For all reported clients, create an unconnected client and add its
65262306a36Sopenharmony_ci	 * device to ISHTP bus.
65362306a36Sopenharmony_ci	 * If appropriate driver has loaded, this will trigger its probe().
65462306a36Sopenharmony_ci	 * Otherwise, probe() will be called when driver is loaded
65562306a36Sopenharmony_ci	 */
65662306a36Sopenharmony_ci	i = dev->fw_client_presentation_num - 1;
65762306a36Sopenharmony_ci	device_uuid = dev->fw_clients[i].props.protocol_name;
65862306a36Sopenharmony_ci	dev_name = kasprintf(GFP_KERNEL, "{%pUL}", &device_uuid);
65962306a36Sopenharmony_ci	if (!dev_name)
66062306a36Sopenharmony_ci		return	-ENOMEM;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	cl_device = ishtp_bus_add_device(dev, device_uuid, dev_name);
66362306a36Sopenharmony_ci	if (!cl_device) {
66462306a36Sopenharmony_ci		kfree(dev_name);
66562306a36Sopenharmony_ci		return	-ENOENT;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	kfree(dev_name);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	return	0;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/**
67462306a36Sopenharmony_ci * ishtp_cl_device_bind() - bind a device
67562306a36Sopenharmony_ci * @cl:		ishtp client device
67662306a36Sopenharmony_ci *
67762306a36Sopenharmony_ci * Binds connected ishtp_cl to ISHTP bus device
67862306a36Sopenharmony_ci *
67962306a36Sopenharmony_ci * Return: 0 on success or fault code
68062306a36Sopenharmony_ci */
68162306a36Sopenharmony_ciint ishtp_cl_device_bind(struct ishtp_cl *cl)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct ishtp_cl_device	*cl_device;
68462306a36Sopenharmony_ci	unsigned long flags;
68562306a36Sopenharmony_ci	int	rv;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (!cl->fw_client_id || cl->state != ISHTP_CL_CONNECTED)
68862306a36Sopenharmony_ci		return	-EFAULT;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	rv = -ENOENT;
69162306a36Sopenharmony_ci	spin_lock_irqsave(&cl->dev->device_list_lock, flags);
69262306a36Sopenharmony_ci	list_for_each_entry(cl_device, &cl->dev->device_list,
69362306a36Sopenharmony_ci			device_link) {
69462306a36Sopenharmony_ci		if (cl_device->fw_client &&
69562306a36Sopenharmony_ci		    cl_device->fw_client->client_id == cl->fw_client_id) {
69662306a36Sopenharmony_ci			cl->device = cl_device;
69762306a36Sopenharmony_ci			rv = 0;
69862306a36Sopenharmony_ci			break;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	spin_unlock_irqrestore(&cl->dev->device_list_lock, flags);
70262306a36Sopenharmony_ci	return	rv;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci/**
70662306a36Sopenharmony_ci * ishtp_bus_remove_all_clients() - Remove all clients
70762306a36Sopenharmony_ci * @ishtp_dev:		ishtp device
70862306a36Sopenharmony_ci * @warm_reset:		Reset due to FW reset dure to errors or S3 suspend
70962306a36Sopenharmony_ci *
71062306a36Sopenharmony_ci * This is part of reset/remove flow. This function the main processing
71162306a36Sopenharmony_ci * only targets error processing, if the FW has forced reset or
71262306a36Sopenharmony_ci * error to remove connected clients. When warm reset the client devices are
71362306a36Sopenharmony_ci * not removed.
71462306a36Sopenharmony_ci */
71562306a36Sopenharmony_civoid ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev,
71662306a36Sopenharmony_ci				  bool warm_reset)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct ishtp_cl_device	*cl_device, *n;
71962306a36Sopenharmony_ci	struct ishtp_cl	*cl;
72062306a36Sopenharmony_ci	unsigned long	flags;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	spin_lock_irqsave(&ishtp_dev->cl_list_lock, flags);
72362306a36Sopenharmony_ci	list_for_each_entry(cl, &ishtp_dev->cl_list, link) {
72462306a36Sopenharmony_ci		cl->state = ISHTP_CL_DISCONNECTED;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		/*
72762306a36Sopenharmony_ci		 * Wake any pending process. The waiter would check dev->state
72862306a36Sopenharmony_ci		 * and determine that it's not enabled already,
72962306a36Sopenharmony_ci		 * and will return error to its caller
73062306a36Sopenharmony_ci		 */
73162306a36Sopenharmony_ci		wake_up_interruptible(&cl->wait_ctrl_res);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		/* Disband any pending read/write requests and free rb */
73462306a36Sopenharmony_ci		ishtp_cl_flush_queues(cl);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		/* Remove all free and in_process rings, both Rx and Tx */
73762306a36Sopenharmony_ci		ishtp_cl_free_rx_ring(cl);
73862306a36Sopenharmony_ci		ishtp_cl_free_tx_ring(cl);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		/*
74162306a36Sopenharmony_ci		 * Free client and ISHTP bus client device structures
74262306a36Sopenharmony_ci		 * don't free host client because it is part of the OS fd
74362306a36Sopenharmony_ci		 * structure
74462306a36Sopenharmony_ci		 */
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ishtp_dev->cl_list_lock, flags);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	/* Release DMA buffers for client messages */
74962306a36Sopenharmony_ci	ishtp_cl_free_dma_buf(ishtp_dev);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* remove bus clients */
75262306a36Sopenharmony_ci	spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
75362306a36Sopenharmony_ci	list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list,
75462306a36Sopenharmony_ci				 device_link) {
75562306a36Sopenharmony_ci		cl_device->fw_client = NULL;
75662306a36Sopenharmony_ci		if (warm_reset && cl_device->reference_count)
75762306a36Sopenharmony_ci			continue;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci		list_del(&cl_device->device_link);
76062306a36Sopenharmony_ci		spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags);
76162306a36Sopenharmony_ci		ishtp_bus_remove_device(cl_device);
76262306a36Sopenharmony_ci		spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* Free all client structures */
76762306a36Sopenharmony_ci	spin_lock_irqsave(&ishtp_dev->fw_clients_lock, flags);
76862306a36Sopenharmony_ci	kfree(ishtp_dev->fw_clients);
76962306a36Sopenharmony_ci	ishtp_dev->fw_clients = NULL;
77062306a36Sopenharmony_ci	ishtp_dev->fw_clients_num = 0;
77162306a36Sopenharmony_ci	ishtp_dev->fw_client_presentation_num = 0;
77262306a36Sopenharmony_ci	ishtp_dev->fw_client_index = 0;
77362306a36Sopenharmony_ci	bitmap_zero(ishtp_dev->fw_clients_map, ISHTP_CLIENTS_MAX);
77462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ishtp_dev->fw_clients_lock, flags);
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_bus_remove_all_clients);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci/**
77962306a36Sopenharmony_ci * ishtp_reset_handler() - IPC reset handler
78062306a36Sopenharmony_ci * @dev:	ishtp device
78162306a36Sopenharmony_ci *
78262306a36Sopenharmony_ci * ISHTP Handler for IPC_RESET notification
78362306a36Sopenharmony_ci */
78462306a36Sopenharmony_civoid ishtp_reset_handler(struct ishtp_device *dev)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	unsigned long	flags;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/* Handle FW-initiated reset */
78962306a36Sopenharmony_ci	dev->dev_state = ISHTP_DEV_RESETTING;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* Clear BH processing queue - no further HBMs */
79262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rd_msg_spinlock, flags);
79362306a36Sopenharmony_ci	dev->rd_msg_fifo_head = dev->rd_msg_fifo_tail = 0;
79462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* Handle ISH FW reset against upper layers */
79762306a36Sopenharmony_ci	ishtp_bus_remove_all_clients(dev, true);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_reset_handler);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci/**
80262306a36Sopenharmony_ci * ishtp_reset_compl_handler() - Reset completion handler
80362306a36Sopenharmony_ci * @dev:	ishtp device
80462306a36Sopenharmony_ci *
80562306a36Sopenharmony_ci * ISHTP handler for IPC_RESET sequence completion to start
80662306a36Sopenharmony_ci * host message bus start protocol sequence.
80762306a36Sopenharmony_ci */
80862306a36Sopenharmony_civoid ishtp_reset_compl_handler(struct ishtp_device *dev)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	dev->dev_state = ISHTP_DEV_INIT_CLIENTS;
81162306a36Sopenharmony_ci	dev->hbm_state = ISHTP_HBM_START;
81262306a36Sopenharmony_ci	ishtp_hbm_start_req(dev);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_reset_compl_handler);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci/**
81762306a36Sopenharmony_ci * ishtp_use_dma_transfer() - Function to use DMA
81862306a36Sopenharmony_ci *
81962306a36Sopenharmony_ci * This interface is used to enable usage of DMA
82062306a36Sopenharmony_ci *
82162306a36Sopenharmony_ci * Return non zero if DMA can be enabled
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_ciint ishtp_use_dma_transfer(void)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	return ishtp_use_dma;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci/**
82962306a36Sopenharmony_ci * ishtp_device() - Return device pointer
83062306a36Sopenharmony_ci * @device: ISH-TP client device instance
83162306a36Sopenharmony_ci *
83262306a36Sopenharmony_ci * This interface is used to return device pointer from ishtp_cl_device
83362306a36Sopenharmony_ci * instance.
83462306a36Sopenharmony_ci *
83562306a36Sopenharmony_ci * Return: device *.
83662306a36Sopenharmony_ci */
83762306a36Sopenharmony_cistruct device *ishtp_device(struct ishtp_cl_device *device)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	return &device->dev;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_device);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci/**
84462306a36Sopenharmony_ci * ishtp_wait_resume() - Wait for IPC resume
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * Wait for IPC resume
84762306a36Sopenharmony_ci *
84862306a36Sopenharmony_ci * Return: resume complete or not
84962306a36Sopenharmony_ci */
85062306a36Sopenharmony_cibool ishtp_wait_resume(struct ishtp_device *dev)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	/* 50ms to get resume response */
85362306a36Sopenharmony_ci	#define WAIT_FOR_RESUME_ACK_MS		50
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	/* Waiting to get resume response */
85662306a36Sopenharmony_ci	if (dev->resume_flag)
85762306a36Sopenharmony_ci		wait_event_interruptible_timeout(dev->resume_wait,
85862306a36Sopenharmony_ci						 !dev->resume_flag,
85962306a36Sopenharmony_ci						 msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	return (!dev->resume_flag);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ishtp_wait_resume);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci/**
86662306a36Sopenharmony_ci * ishtp_get_pci_device() - Return PCI device dev pointer
86762306a36Sopenharmony_ci * This interface is used to return PCI device pointer
86862306a36Sopenharmony_ci * from ishtp_cl_device instance.
86962306a36Sopenharmony_ci * @device: ISH-TP client device instance
87062306a36Sopenharmony_ci *
87162306a36Sopenharmony_ci * Return: device *.
87262306a36Sopenharmony_ci */
87362306a36Sopenharmony_cistruct device *ishtp_get_pci_device(struct ishtp_cl_device *device)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	return device->ishtp_dev->devc;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_pci_device);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci/**
88062306a36Sopenharmony_ci * ishtp_trace_callback() - Return trace callback
88162306a36Sopenharmony_ci * @cl_device: ISH-TP client device instance
88262306a36Sopenharmony_ci *
88362306a36Sopenharmony_ci * This interface is used to return trace callback function pointer.
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * Return: *ishtp_print_log()
88662306a36Sopenharmony_ci */
88762306a36Sopenharmony_ciishtp_print_log ishtp_trace_callback(struct ishtp_cl_device *cl_device)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	return cl_device->ishtp_dev->print_log;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_trace_callback);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci/**
89462306a36Sopenharmony_ci * ish_hw_reset() - Call HW reset IPC callback
89562306a36Sopenharmony_ci * @dev:	ISHTP device instance
89662306a36Sopenharmony_ci *
89762306a36Sopenharmony_ci * This interface is used to reset HW in case of error.
89862306a36Sopenharmony_ci *
89962306a36Sopenharmony_ci * Return: value from IPC hw_reset callback
90062306a36Sopenharmony_ci */
90162306a36Sopenharmony_ciint ish_hw_reset(struct ishtp_device *dev)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	return dev->ops->hw_reset(dev);
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ciEXPORT_SYMBOL(ish_hw_reset);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci/**
90862306a36Sopenharmony_ci * ishtp_bus_register() - Function to register bus
90962306a36Sopenharmony_ci *
91062306a36Sopenharmony_ci * This register ishtp bus
91162306a36Sopenharmony_ci *
91262306a36Sopenharmony_ci * Return: Return output of bus_register
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_cistatic int  __init ishtp_bus_register(void)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	return bus_register(&ishtp_cl_bus_type);
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci/**
92062306a36Sopenharmony_ci * ishtp_bus_unregister() - Function to unregister bus
92162306a36Sopenharmony_ci *
92262306a36Sopenharmony_ci * This unregister ishtp bus
92362306a36Sopenharmony_ci */
92462306a36Sopenharmony_cistatic void __exit ishtp_bus_unregister(void)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	bus_unregister(&ishtp_cl_bus_type);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cimodule_init(ishtp_bus_register);
93062306a36Sopenharmony_cimodule_exit(ishtp_bus_unregister);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
933