18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ISHTP bus driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012-2016, Intel Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include "bus.h"
158c2ecf20Sopenharmony_ci#include "ishtp-dev.h"
168c2ecf20Sopenharmony_ci#include "client.h"
178c2ecf20Sopenharmony_ci#include "hbm.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int ishtp_use_dma;
208c2ecf20Sopenharmony_cimodule_param_named(ishtp_use_dma, ishtp_use_dma, int, 0600);
218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ishtp_use_dma, "Use DMA to send messages");
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define to_ishtp_cl_driver(d) container_of(d, struct ishtp_cl_driver, driver)
248c2ecf20Sopenharmony_ci#define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev)
258c2ecf20Sopenharmony_cistatic bool ishtp_device_ready;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/**
288c2ecf20Sopenharmony_ci * ishtp_recv() - process ishtp message
298c2ecf20Sopenharmony_ci * @dev: ishtp device
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * If a message with valid header and size is received, then
328c2ecf20Sopenharmony_ci * this function calls appropriate handler. The host or firmware
338c2ecf20Sopenharmony_ci * address is zero, then they are host bus management message,
348c2ecf20Sopenharmony_ci * otherwise they are message fo clients.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_civoid ishtp_recv(struct ishtp_device *dev)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	uint32_t	msg_hdr;
398c2ecf20Sopenharmony_ci	struct ishtp_msg_hdr	*ishtp_hdr;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Read ISHTP header dword */
428c2ecf20Sopenharmony_ci	msg_hdr = dev->ops->ishtp_read_hdr(dev);
438c2ecf20Sopenharmony_ci	if (!msg_hdr)
448c2ecf20Sopenharmony_ci		return;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	dev->ops->sync_fw_clock(dev);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	ishtp_hdr = (struct ishtp_msg_hdr *)&msg_hdr;
498c2ecf20Sopenharmony_ci	dev->ishtp_msg_hdr = msg_hdr;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* Sanity check: ISHTP frag. length in header */
528c2ecf20Sopenharmony_ci	if (ishtp_hdr->length > dev->mtu) {
538c2ecf20Sopenharmony_ci		dev_err(dev->devc,
548c2ecf20Sopenharmony_ci			"ISHTP hdr - bad length: %u; dropped [%08X]\n",
558c2ecf20Sopenharmony_ci			(unsigned int)ishtp_hdr->length, msg_hdr);
568c2ecf20Sopenharmony_ci		return;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* ISHTP bus message */
608c2ecf20Sopenharmony_ci	if (!ishtp_hdr->host_addr && !ishtp_hdr->fw_addr)
618c2ecf20Sopenharmony_ci		recv_hbm(dev, ishtp_hdr);
628c2ecf20Sopenharmony_ci	/* ISHTP fixed-client message */
638c2ecf20Sopenharmony_ci	else if (!ishtp_hdr->host_addr)
648c2ecf20Sopenharmony_ci		recv_fixed_cl_msg(dev, ishtp_hdr);
658c2ecf20Sopenharmony_ci	else
668c2ecf20Sopenharmony_ci		/* ISHTP client message */
678c2ecf20Sopenharmony_ci		recv_ishtp_cl_msg(dev, ishtp_hdr);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_recv);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/**
728c2ecf20Sopenharmony_ci * ishtp_send_msg() - Send ishtp message
738c2ecf20Sopenharmony_ci * @dev: ishtp device
748c2ecf20Sopenharmony_ci * @hdr: Message header
758c2ecf20Sopenharmony_ci * @msg: Message contents
768c2ecf20Sopenharmony_ci * @ipc_send_compl: completion callback
778c2ecf20Sopenharmony_ci * @ipc_send_compl_prm: completion callback parameter
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * Send a multi fragment message via IPC. After sending the first fragment
808c2ecf20Sopenharmony_ci * the completion callback is called to schedule transmit of next fragment.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * Return: This returns IPC send message status.
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_ciint ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
858c2ecf20Sopenharmony_ci		       void *msg, void(*ipc_send_compl)(void *),
868c2ecf20Sopenharmony_ci		       void *ipc_send_compl_prm)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	unsigned char	ipc_msg[IPC_FULL_MSG_SIZE];
898c2ecf20Sopenharmony_ci	uint32_t	drbl_val;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	drbl_val = dev->ops->ipc_get_header(dev, hdr->length +
928c2ecf20Sopenharmony_ci					    sizeof(struct ishtp_msg_hdr),
938c2ecf20Sopenharmony_ci					    1);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	memcpy(ipc_msg, &drbl_val, sizeof(uint32_t));
968c2ecf20Sopenharmony_ci	memcpy(ipc_msg + sizeof(uint32_t), hdr, sizeof(uint32_t));
978c2ecf20Sopenharmony_ci	memcpy(ipc_msg + 2 * sizeof(uint32_t), msg, hdr->length);
988c2ecf20Sopenharmony_ci	return	dev->ops->write(dev, ipc_send_compl, ipc_send_compl_prm,
998c2ecf20Sopenharmony_ci				ipc_msg, 2 * sizeof(uint32_t) + hdr->length);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/**
1038c2ecf20Sopenharmony_ci * ishtp_write_message() - Send ishtp single fragment message
1048c2ecf20Sopenharmony_ci * @dev: ishtp device
1058c2ecf20Sopenharmony_ci * @hdr: Message header
1068c2ecf20Sopenharmony_ci * @buf: message data
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * Send a single fragment message via IPC.  This returns IPC send message
1098c2ecf20Sopenharmony_ci * status.
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * Return: This returns IPC send message status.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_ciint ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
1148c2ecf20Sopenharmony_ci			void *buf)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	return ishtp_send_msg(dev, hdr, buf, NULL, NULL);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/**
1208c2ecf20Sopenharmony_ci * ishtp_fw_cl_by_uuid() - locate index of fw client
1218c2ecf20Sopenharmony_ci * @dev: ishtp device
1228c2ecf20Sopenharmony_ci * @uuid: uuid of the client to search
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Search firmware client using UUID.
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Return: fw client index or -ENOENT if not found
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_ciint ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *uuid)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	unsigned int i;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for (i = 0; i < dev->fw_clients_num; ++i) {
1338c2ecf20Sopenharmony_ci		if (guid_equal(uuid, &dev->fw_clients[i].props.protocol_name))
1348c2ecf20Sopenharmony_ci			return i;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	return -ENOENT;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/**
1418c2ecf20Sopenharmony_ci * ishtp_fw_cl_get_client() - return client information to client
1428c2ecf20Sopenharmony_ci * @dev: the ishtp device structure
1438c2ecf20Sopenharmony_ci * @uuid: uuid of the client to search
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci * Search firmware client using UUID and reture related client information.
1468c2ecf20Sopenharmony_ci *
1478c2ecf20Sopenharmony_ci * Return: pointer of client information on success, NULL on failure.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistruct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
1508c2ecf20Sopenharmony_ci					       const guid_t *uuid)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	int i;
1538c2ecf20Sopenharmony_ci	unsigned long flags;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->fw_clients_lock, flags);
1568c2ecf20Sopenharmony_ci	i = ishtp_fw_cl_by_uuid(dev, uuid);
1578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
1588c2ecf20Sopenharmony_ci	if (i < 0 || dev->fw_clients[i].props.fixed_address)
1598c2ecf20Sopenharmony_ci		return NULL;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return &dev->fw_clients[i];
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_fw_cl_get_client);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * ishtp_get_fw_client_id() - Get fw client id
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * This interface is used to reset HW get FW client id.
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * Return: firmware client id.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_ciint ishtp_get_fw_client_id(struct ishtp_fw_client *fw_client)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	return fw_client->client_id;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_fw_client_id);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * ishtp_fw_cl_by_id() - return index to fw_clients for client_id
1808c2ecf20Sopenharmony_ci * @dev: the ishtp device structure
1818c2ecf20Sopenharmony_ci * @client_id: fw client id to search
1828c2ecf20Sopenharmony_ci *
1838c2ecf20Sopenharmony_ci * Search firmware client using client id.
1848c2ecf20Sopenharmony_ci *
1858c2ecf20Sopenharmony_ci * Return: index on success, -ENOENT on failure.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_ciint ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int i, res = -ENOENT;
1908c2ecf20Sopenharmony_ci	unsigned long	flags;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->fw_clients_lock, flags);
1938c2ecf20Sopenharmony_ci	for (i = 0; i < dev->fw_clients_num; i++) {
1948c2ecf20Sopenharmony_ci		if (dev->fw_clients[i].client_id == client_id) {
1958c2ecf20Sopenharmony_ci			res = i;
1968c2ecf20Sopenharmony_ci			break;
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return res;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/**
2058c2ecf20Sopenharmony_ci * ishtp_cl_device_probe() - Bus probe() callback
2068c2ecf20Sopenharmony_ci * @dev: the device structure
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * This is a bus probe callback and calls the drive probe function.
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * Return: Return value from driver probe() call.
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistatic int ishtp_cl_device_probe(struct device *dev)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
2158c2ecf20Sopenharmony_ci	struct ishtp_cl_driver *driver;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (!device)
2188c2ecf20Sopenharmony_ci		return 0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
2218c2ecf20Sopenharmony_ci	if (!driver || !driver->probe)
2228c2ecf20Sopenharmony_ci		return -ENODEV;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return driver->probe(device);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/**
2288c2ecf20Sopenharmony_ci * ishtp_cl_bus_match() - Bus match() callback
2298c2ecf20Sopenharmony_ci * @dev: the device structure
2308c2ecf20Sopenharmony_ci * @drv: the driver structure
2318c2ecf20Sopenharmony_ci *
2328c2ecf20Sopenharmony_ci * This is a bus match callback, called when a new ishtp_cl_device is
2338c2ecf20Sopenharmony_ci * registered during ishtp bus client enumeration. Use the guid_t in
2348c2ecf20Sopenharmony_ci * drv and dev to decide whether they match or not.
2358c2ecf20Sopenharmony_ci *
2368c2ecf20Sopenharmony_ci * Return: 1 if dev & drv matches, 0 otherwise.
2378c2ecf20Sopenharmony_ci */
2388c2ecf20Sopenharmony_cistatic int ishtp_cl_bus_match(struct device *dev, struct device_driver *drv)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
2418c2ecf20Sopenharmony_ci	struct ishtp_cl_driver *driver = to_ishtp_cl_driver(drv);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return guid_equal(driver->guid,
2448c2ecf20Sopenharmony_ci			  &device->fw_client->props.protocol_name);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/**
2488c2ecf20Sopenharmony_ci * ishtp_cl_device_remove() - Bus remove() callback
2498c2ecf20Sopenharmony_ci * @dev: the device structure
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * This is a bus remove callback and calls the drive remove function.
2528c2ecf20Sopenharmony_ci * Since the ISH driver model supports only built in, this is
2538c2ecf20Sopenharmony_ci * primarily can be called during pci driver init failure.
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * Return: Return value from driver remove() call.
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_cistatic int ishtp_cl_device_remove(struct device *dev)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
2608c2ecf20Sopenharmony_ci	struct ishtp_cl_driver *driver;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (!device || !dev->driver)
2638c2ecf20Sopenharmony_ci		return 0;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (device->event_cb) {
2668c2ecf20Sopenharmony_ci		device->event_cb = NULL;
2678c2ecf20Sopenharmony_ci		cancel_work_sync(&device->event_work);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
2718c2ecf20Sopenharmony_ci	if (!driver->remove) {
2728c2ecf20Sopenharmony_ci		dev->driver = NULL;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		return 0;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return driver->remove(device);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/**
2818c2ecf20Sopenharmony_ci * ishtp_cl_device_suspend() - Bus suspend callback
2828c2ecf20Sopenharmony_ci * @dev:	device
2838c2ecf20Sopenharmony_ci *
2848c2ecf20Sopenharmony_ci * Called during device suspend process.
2858c2ecf20Sopenharmony_ci *
2868c2ecf20Sopenharmony_ci * Return: Return value from driver suspend() call.
2878c2ecf20Sopenharmony_ci */
2888c2ecf20Sopenharmony_cistatic int ishtp_cl_device_suspend(struct device *dev)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
2918c2ecf20Sopenharmony_ci	struct ishtp_cl_driver *driver;
2928c2ecf20Sopenharmony_ci	int ret = 0;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (!device)
2958c2ecf20Sopenharmony_ci		return 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
2988c2ecf20Sopenharmony_ci	if (driver && driver->driver.pm) {
2998c2ecf20Sopenharmony_ci		if (driver->driver.pm->suspend)
3008c2ecf20Sopenharmony_ci			ret = driver->driver.pm->suspend(dev);
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return ret;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/**
3078c2ecf20Sopenharmony_ci * ishtp_cl_device_resume() - Bus resume callback
3088c2ecf20Sopenharmony_ci * @dev:	device
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * Called during device resume process.
3118c2ecf20Sopenharmony_ci *
3128c2ecf20Sopenharmony_ci * Return: Return value from driver resume() call.
3138c2ecf20Sopenharmony_ci */
3148c2ecf20Sopenharmony_cistatic int ishtp_cl_device_resume(struct device *dev)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
3178c2ecf20Sopenharmony_ci	struct ishtp_cl_driver *driver;
3188c2ecf20Sopenharmony_ci	int ret = 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (!device)
3218c2ecf20Sopenharmony_ci		return 0;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/*
3248c2ecf20Sopenharmony_ci	 * When ISH needs hard reset, it is done asynchrnously, hence bus
3258c2ecf20Sopenharmony_ci	 * resume will  be called before full ISH resume
3268c2ecf20Sopenharmony_ci	 */
3278c2ecf20Sopenharmony_ci	if (device->ishtp_dev->resume_flag)
3288c2ecf20Sopenharmony_ci		return 0;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	driver = to_ishtp_cl_driver(dev->driver);
3318c2ecf20Sopenharmony_ci	if (driver && driver->driver.pm) {
3328c2ecf20Sopenharmony_ci		if (driver->driver.pm->resume)
3338c2ecf20Sopenharmony_ci			ret = driver->driver.pm->resume(dev);
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return ret;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/**
3408c2ecf20Sopenharmony_ci * ishtp_cl_device_reset() - Reset callback
3418c2ecf20Sopenharmony_ci * @device:	ishtp client device instance
3428c2ecf20Sopenharmony_ci *
3438c2ecf20Sopenharmony_ci * This is a callback when HW reset is done and the device need
3448c2ecf20Sopenharmony_ci * reinit.
3458c2ecf20Sopenharmony_ci *
3468c2ecf20Sopenharmony_ci * Return: Return value from driver reset() call.
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistatic int ishtp_cl_device_reset(struct ishtp_cl_device *device)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct ishtp_cl_driver *driver;
3518c2ecf20Sopenharmony_ci	int ret = 0;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	device->event_cb = NULL;
3548c2ecf20Sopenharmony_ci	cancel_work_sync(&device->event_work);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	driver = to_ishtp_cl_driver(device->dev.driver);
3578c2ecf20Sopenharmony_ci	if (driver && driver->reset)
3588c2ecf20Sopenharmony_ci		ret = driver->reset(device);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return ret;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *a,
3648c2ecf20Sopenharmony_ci	char *buf)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	int len;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	len = snprintf(buf, PAGE_SIZE, "ishtp:%s\n", dev_name(dev));
3698c2ecf20Sopenharmony_ci	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic struct attribute *ishtp_cl_dev_attrs[] = {
3748c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
3758c2ecf20Sopenharmony_ci	NULL,
3768c2ecf20Sopenharmony_ci};
3778c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ishtp_cl_dev);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int ishtp_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=ishtp:%s", dev_name(dev)))
3828c2ecf20Sopenharmony_ci		return -ENOMEM;
3838c2ecf20Sopenharmony_ci	return 0;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = {
3878c2ecf20Sopenharmony_ci	/* Suspend callbacks */
3888c2ecf20Sopenharmony_ci	.suspend = ishtp_cl_device_suspend,
3898c2ecf20Sopenharmony_ci	.resume = ishtp_cl_device_resume,
3908c2ecf20Sopenharmony_ci	/* Hibernate callbacks */
3918c2ecf20Sopenharmony_ci	.freeze = ishtp_cl_device_suspend,
3928c2ecf20Sopenharmony_ci	.thaw = ishtp_cl_device_resume,
3938c2ecf20Sopenharmony_ci	.restore = ishtp_cl_device_resume,
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic struct bus_type ishtp_cl_bus_type = {
3978c2ecf20Sopenharmony_ci	.name		= "ishtp",
3988c2ecf20Sopenharmony_ci	.dev_groups	= ishtp_cl_dev_groups,
3998c2ecf20Sopenharmony_ci	.probe		= ishtp_cl_device_probe,
4008c2ecf20Sopenharmony_ci	.match		= ishtp_cl_bus_match,
4018c2ecf20Sopenharmony_ci	.remove		= ishtp_cl_device_remove,
4028c2ecf20Sopenharmony_ci	.pm		= &ishtp_cl_bus_dev_pm_ops,
4038c2ecf20Sopenharmony_ci	.uevent		= ishtp_cl_uevent,
4048c2ecf20Sopenharmony_ci};
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void ishtp_cl_dev_release(struct device *dev)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	kfree(to_ishtp_cl_device(dev));
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic const struct device_type ishtp_cl_device_type = {
4128c2ecf20Sopenharmony_ci	.release	= ishtp_cl_dev_release,
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/**
4168c2ecf20Sopenharmony_ci * ishtp_bus_add_device() - Function to create device on bus
4178c2ecf20Sopenharmony_ci * @dev:	ishtp device
4188c2ecf20Sopenharmony_ci * @uuid:	uuid of the client
4198c2ecf20Sopenharmony_ci * @name:	Name of the client
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci * Allocate ISHTP bus client device, attach it to uuid
4228c2ecf20Sopenharmony_ci * and register with ISHTP bus.
4238c2ecf20Sopenharmony_ci *
4248c2ecf20Sopenharmony_ci * Return: ishtp_cl_device pointer or NULL on failure
4258c2ecf20Sopenharmony_ci */
4268c2ecf20Sopenharmony_cistatic struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev,
4278c2ecf20Sopenharmony_ci						    guid_t uuid, char *name)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device;
4308c2ecf20Sopenharmony_ci	int status;
4318c2ecf20Sopenharmony_ci	unsigned long flags;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->device_list_lock, flags);
4348c2ecf20Sopenharmony_ci	list_for_each_entry(device, &dev->device_list, device_link) {
4358c2ecf20Sopenharmony_ci		if (!strcmp(name, dev_name(&device->dev))) {
4368c2ecf20Sopenharmony_ci			device->fw_client = &dev->fw_clients[
4378c2ecf20Sopenharmony_ci				dev->fw_client_presentation_num - 1];
4388c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&dev->device_list_lock, flags);
4398c2ecf20Sopenharmony_ci			ishtp_cl_device_reset(device);
4408c2ecf20Sopenharmony_ci			return device;
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->device_list_lock, flags);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	device = kzalloc(sizeof(struct ishtp_cl_device), GFP_KERNEL);
4468c2ecf20Sopenharmony_ci	if (!device)
4478c2ecf20Sopenharmony_ci		return NULL;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	device->dev.parent = dev->devc;
4508c2ecf20Sopenharmony_ci	device->dev.bus = &ishtp_cl_bus_type;
4518c2ecf20Sopenharmony_ci	device->dev.type = &ishtp_cl_device_type;
4528c2ecf20Sopenharmony_ci	device->ishtp_dev = dev;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	device->fw_client =
4558c2ecf20Sopenharmony_ci		&dev->fw_clients[dev->fw_client_presentation_num - 1];
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	dev_set_name(&device->dev, "%s", name);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->device_list_lock, flags);
4608c2ecf20Sopenharmony_ci	list_add_tail(&device->device_link, &dev->device_list);
4618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->device_list_lock, flags);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	status = device_register(&device->dev);
4648c2ecf20Sopenharmony_ci	if (status) {
4658c2ecf20Sopenharmony_ci		spin_lock_irqsave(&dev->device_list_lock, flags);
4668c2ecf20Sopenharmony_ci		list_del(&device->device_link);
4678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->device_list_lock, flags);
4688c2ecf20Sopenharmony_ci		dev_err(dev->devc, "Failed to register ISHTP client device\n");
4698c2ecf20Sopenharmony_ci		put_device(&device->dev);
4708c2ecf20Sopenharmony_ci		return NULL;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	ishtp_device_ready = true;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return device;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci/**
4798c2ecf20Sopenharmony_ci * ishtp_bus_remove_device() - Function to relase device on bus
4808c2ecf20Sopenharmony_ci * @device:	client device instance
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * This is a counterpart of ishtp_bus_add_device.
4838c2ecf20Sopenharmony_ci * Device is unregistered.
4848c2ecf20Sopenharmony_ci * the device structure is freed in 'ishtp_cl_dev_release' function
4858c2ecf20Sopenharmony_ci * Called only during error in pci driver init path.
4868c2ecf20Sopenharmony_ci */
4878c2ecf20Sopenharmony_cistatic void ishtp_bus_remove_device(struct ishtp_cl_device *device)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	device_unregister(&device->dev);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci/**
4938c2ecf20Sopenharmony_ci * ishtp_cl_driver_register() - Client driver register
4948c2ecf20Sopenharmony_ci * @driver:	the client driver instance
4958c2ecf20Sopenharmony_ci * @owner:	Owner of this driver module
4968c2ecf20Sopenharmony_ci *
4978c2ecf20Sopenharmony_ci * Once a client driver is probed, it created a client
4988c2ecf20Sopenharmony_ci * instance and registers with the bus.
4998c2ecf20Sopenharmony_ci *
5008c2ecf20Sopenharmony_ci * Return: Return value of driver_register or -ENODEV if not ready
5018c2ecf20Sopenharmony_ci */
5028c2ecf20Sopenharmony_ciint ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
5038c2ecf20Sopenharmony_ci			     struct module *owner)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	if (!ishtp_device_ready)
5068c2ecf20Sopenharmony_ci		return -ENODEV;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	driver->driver.name = driver->name;
5098c2ecf20Sopenharmony_ci	driver->driver.owner = owner;
5108c2ecf20Sopenharmony_ci	driver->driver.bus = &ishtp_cl_bus_type;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	return driver_register(&driver->driver);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_driver_register);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci/**
5178c2ecf20Sopenharmony_ci * ishtp_cl_driver_unregister() - Client driver unregister
5188c2ecf20Sopenharmony_ci * @driver:	the client driver instance
5198c2ecf20Sopenharmony_ci *
5208c2ecf20Sopenharmony_ci * Unregister client during device removal process.
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_civoid ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	driver_unregister(&driver->driver);
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_driver_unregister);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/**
5298c2ecf20Sopenharmony_ci * ishtp_bus_event_work() - event work function
5308c2ecf20Sopenharmony_ci * @work:	work struct pointer
5318c2ecf20Sopenharmony_ci *
5328c2ecf20Sopenharmony_ci * Once an event is received for a client this work
5338c2ecf20Sopenharmony_ci * function is called. If the device has registered a
5348c2ecf20Sopenharmony_ci * callback then the callback is called.
5358c2ecf20Sopenharmony_ci */
5368c2ecf20Sopenharmony_cistatic void ishtp_bus_event_work(struct work_struct *work)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct ishtp_cl_device *device;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	device = container_of(work, struct ishtp_cl_device, event_work);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (device->event_cb)
5438c2ecf20Sopenharmony_ci		device->event_cb(device);
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci/**
5478c2ecf20Sopenharmony_ci * ishtp_cl_bus_rx_event() - schedule event work
5488c2ecf20Sopenharmony_ci * @device:	client device instance
5498c2ecf20Sopenharmony_ci *
5508c2ecf20Sopenharmony_ci * Once an event is received for a client this schedules
5518c2ecf20Sopenharmony_ci * a work function to process.
5528c2ecf20Sopenharmony_ci */
5538c2ecf20Sopenharmony_civoid ishtp_cl_bus_rx_event(struct ishtp_cl_device *device)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	if (!device || !device->event_cb)
5568c2ecf20Sopenharmony_ci		return;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (device->event_cb)
5598c2ecf20Sopenharmony_ci		schedule_work(&device->event_work);
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci/**
5638c2ecf20Sopenharmony_ci * ishtp_register_event_cb() - Register callback
5648c2ecf20Sopenharmony_ci * @device:	client device instance
5658c2ecf20Sopenharmony_ci * @event_cb:	Event processor for an client
5668c2ecf20Sopenharmony_ci *
5678c2ecf20Sopenharmony_ci * Register a callback for events, called from client driver
5688c2ecf20Sopenharmony_ci *
5698c2ecf20Sopenharmony_ci * Return: Return 0 or -EALREADY if already registered
5708c2ecf20Sopenharmony_ci */
5718c2ecf20Sopenharmony_ciint ishtp_register_event_cb(struct ishtp_cl_device *device,
5728c2ecf20Sopenharmony_ci	void (*event_cb)(struct ishtp_cl_device *))
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	if (device->event_cb)
5758c2ecf20Sopenharmony_ci		return -EALREADY;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	device->event_cb = event_cb;
5788c2ecf20Sopenharmony_ci	INIT_WORK(&device->event_work, ishtp_bus_event_work);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_register_event_cb);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci/**
5858c2ecf20Sopenharmony_ci * ishtp_get_device() - update usage count for the device
5868c2ecf20Sopenharmony_ci * @cl_device:	client device instance
5878c2ecf20Sopenharmony_ci *
5888c2ecf20Sopenharmony_ci * Increment the usage count. The device can't be deleted
5898c2ecf20Sopenharmony_ci */
5908c2ecf20Sopenharmony_civoid ishtp_get_device(struct ishtp_cl_device *cl_device)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	cl_device->reference_count++;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_device);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci/**
5978c2ecf20Sopenharmony_ci * ishtp_put_device() - decrement usage count for the device
5988c2ecf20Sopenharmony_ci * @cl_device:	client device instance
5998c2ecf20Sopenharmony_ci *
6008c2ecf20Sopenharmony_ci * Decrement the usage count. The device can be deleted is count = 0
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_civoid ishtp_put_device(struct ishtp_cl_device *cl_device)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	cl_device->reference_count--;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_put_device);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci/**
6098c2ecf20Sopenharmony_ci * ishtp_set_drvdata() - set client driver data
6108c2ecf20Sopenharmony_ci * @cl_device:	client device instance
6118c2ecf20Sopenharmony_ci * @data:	driver data need to be set
6128c2ecf20Sopenharmony_ci *
6138c2ecf20Sopenharmony_ci * Set client driver data to cl_device->driver_data.
6148c2ecf20Sopenharmony_ci */
6158c2ecf20Sopenharmony_civoid ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	cl_device->driver_data = data;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_set_drvdata);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/**
6228c2ecf20Sopenharmony_ci * ishtp_get_drvdata() - get client driver data
6238c2ecf20Sopenharmony_ci * @cl_device:	client device instance
6248c2ecf20Sopenharmony_ci *
6258c2ecf20Sopenharmony_ci * Get client driver data from cl_device->driver_data.
6268c2ecf20Sopenharmony_ci *
6278c2ecf20Sopenharmony_ci * Return: pointer of driver data
6288c2ecf20Sopenharmony_ci */
6298c2ecf20Sopenharmony_civoid *ishtp_get_drvdata(struct ishtp_cl_device *cl_device)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	return cl_device->driver_data;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_drvdata);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/**
6368c2ecf20Sopenharmony_ci * ishtp_dev_to_cl_device() - get ishtp_cl_device instance from device instance
6378c2ecf20Sopenharmony_ci * @device: device instance
6388c2ecf20Sopenharmony_ci *
6398c2ecf20Sopenharmony_ci * Get ish_cl_device instance which embeds device instance in it.
6408c2ecf20Sopenharmony_ci *
6418c2ecf20Sopenharmony_ci * Return: pointer to ishtp_cl_device instance
6428c2ecf20Sopenharmony_ci */
6438c2ecf20Sopenharmony_cistruct ishtp_cl_device *ishtp_dev_to_cl_device(struct device *device)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	return to_ishtp_cl_device(device);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_dev_to_cl_device);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci/**
6508c2ecf20Sopenharmony_ci * ishtp_bus_new_client() - Create a new client
6518c2ecf20Sopenharmony_ci * @dev:	ISHTP device instance
6528c2ecf20Sopenharmony_ci *
6538c2ecf20Sopenharmony_ci * Once bus protocol enumerates a client, this is called
6548c2ecf20Sopenharmony_ci * to add a device for the client.
6558c2ecf20Sopenharmony_ci *
6568c2ecf20Sopenharmony_ci * Return: 0 on success or error code on failure
6578c2ecf20Sopenharmony_ci */
6588c2ecf20Sopenharmony_ciint ishtp_bus_new_client(struct ishtp_device *dev)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	int	i;
6618c2ecf20Sopenharmony_ci	char	*dev_name;
6628c2ecf20Sopenharmony_ci	struct ishtp_cl_device	*cl_device;
6638c2ecf20Sopenharmony_ci	guid_t	device_uuid;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/*
6668c2ecf20Sopenharmony_ci	 * For all reported clients, create an unconnected client and add its
6678c2ecf20Sopenharmony_ci	 * device to ISHTP bus.
6688c2ecf20Sopenharmony_ci	 * If appropriate driver has loaded, this will trigger its probe().
6698c2ecf20Sopenharmony_ci	 * Otherwise, probe() will be called when driver is loaded
6708c2ecf20Sopenharmony_ci	 */
6718c2ecf20Sopenharmony_ci	i = dev->fw_client_presentation_num - 1;
6728c2ecf20Sopenharmony_ci	device_uuid = dev->fw_clients[i].props.protocol_name;
6738c2ecf20Sopenharmony_ci	dev_name = kasprintf(GFP_KERNEL, "{%pUL}", &device_uuid);
6748c2ecf20Sopenharmony_ci	if (!dev_name)
6758c2ecf20Sopenharmony_ci		return	-ENOMEM;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	cl_device = ishtp_bus_add_device(dev, device_uuid, dev_name);
6788c2ecf20Sopenharmony_ci	if (!cl_device) {
6798c2ecf20Sopenharmony_ci		kfree(dev_name);
6808c2ecf20Sopenharmony_ci		return	-ENOENT;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	kfree(dev_name);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	return	0;
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci/**
6898c2ecf20Sopenharmony_ci * ishtp_cl_device_bind() - bind a device
6908c2ecf20Sopenharmony_ci * @cl:		ishtp client device
6918c2ecf20Sopenharmony_ci *
6928c2ecf20Sopenharmony_ci * Binds connected ishtp_cl to ISHTP bus device
6938c2ecf20Sopenharmony_ci *
6948c2ecf20Sopenharmony_ci * Return: 0 on success or fault code
6958c2ecf20Sopenharmony_ci */
6968c2ecf20Sopenharmony_ciint ishtp_cl_device_bind(struct ishtp_cl *cl)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	struct ishtp_cl_device	*cl_device;
6998c2ecf20Sopenharmony_ci	unsigned long flags;
7008c2ecf20Sopenharmony_ci	int	rv;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (!cl->fw_client_id || cl->state != ISHTP_CL_CONNECTED)
7038c2ecf20Sopenharmony_ci		return	-EFAULT;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	rv = -ENOENT;
7068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cl->dev->device_list_lock, flags);
7078c2ecf20Sopenharmony_ci	list_for_each_entry(cl_device, &cl->dev->device_list,
7088c2ecf20Sopenharmony_ci			device_link) {
7098c2ecf20Sopenharmony_ci		if (cl_device->fw_client &&
7108c2ecf20Sopenharmony_ci		    cl_device->fw_client->client_id == cl->fw_client_id) {
7118c2ecf20Sopenharmony_ci			cl->device = cl_device;
7128c2ecf20Sopenharmony_ci			rv = 0;
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cl->dev->device_list_lock, flags);
7178c2ecf20Sopenharmony_ci	return	rv;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci/**
7218c2ecf20Sopenharmony_ci * ishtp_bus_remove_all_clients() - Remove all clients
7228c2ecf20Sopenharmony_ci * @ishtp_dev:		ishtp device
7238c2ecf20Sopenharmony_ci * @warm_reset:		Reset due to FW reset dure to errors or S3 suspend
7248c2ecf20Sopenharmony_ci *
7258c2ecf20Sopenharmony_ci * This is part of reset/remove flow. This function the main processing
7268c2ecf20Sopenharmony_ci * only targets error processing, if the FW has forced reset or
7278c2ecf20Sopenharmony_ci * error to remove connected clients. When warm reset the client devices are
7288c2ecf20Sopenharmony_ci * not removed.
7298c2ecf20Sopenharmony_ci */
7308c2ecf20Sopenharmony_civoid ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev,
7318c2ecf20Sopenharmony_ci				  bool warm_reset)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct ishtp_cl_device	*cl_device, *n;
7348c2ecf20Sopenharmony_ci	struct ishtp_cl	*cl;
7358c2ecf20Sopenharmony_ci	unsigned long	flags;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ishtp_dev->cl_list_lock, flags);
7388c2ecf20Sopenharmony_ci	list_for_each_entry(cl, &ishtp_dev->cl_list, link) {
7398c2ecf20Sopenharmony_ci		cl->state = ISHTP_CL_DISCONNECTED;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		/*
7428c2ecf20Sopenharmony_ci		 * Wake any pending process. The waiter would check dev->state
7438c2ecf20Sopenharmony_ci		 * and determine that it's not enabled already,
7448c2ecf20Sopenharmony_ci		 * and will return error to its caller
7458c2ecf20Sopenharmony_ci		 */
7468c2ecf20Sopenharmony_ci		wake_up_interruptible(&cl->wait_ctrl_res);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		/* Disband any pending read/write requests and free rb */
7498c2ecf20Sopenharmony_ci		ishtp_cl_flush_queues(cl);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		/* Remove all free and in_process rings, both Rx and Tx */
7528c2ecf20Sopenharmony_ci		ishtp_cl_free_rx_ring(cl);
7538c2ecf20Sopenharmony_ci		ishtp_cl_free_tx_ring(cl);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci		/*
7568c2ecf20Sopenharmony_ci		 * Free client and ISHTP bus client device structures
7578c2ecf20Sopenharmony_ci		 * don't free host client because it is part of the OS fd
7588c2ecf20Sopenharmony_ci		 * structure
7598c2ecf20Sopenharmony_ci		 */
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ishtp_dev->cl_list_lock, flags);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	/* Release DMA buffers for client messages */
7648c2ecf20Sopenharmony_ci	ishtp_cl_free_dma_buf(ishtp_dev);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* remove bus clients */
7678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
7688c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list,
7698c2ecf20Sopenharmony_ci				 device_link) {
7708c2ecf20Sopenharmony_ci		cl_device->fw_client = NULL;
7718c2ecf20Sopenharmony_ci		if (warm_reset && cl_device->reference_count)
7728c2ecf20Sopenharmony_ci			continue;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		list_del(&cl_device->device_link);
7758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags);
7768c2ecf20Sopenharmony_ci		ishtp_bus_remove_device(cl_device);
7778c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* Free all client structures */
7828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ishtp_dev->fw_clients_lock, flags);
7838c2ecf20Sopenharmony_ci	kfree(ishtp_dev->fw_clients);
7848c2ecf20Sopenharmony_ci	ishtp_dev->fw_clients = NULL;
7858c2ecf20Sopenharmony_ci	ishtp_dev->fw_clients_num = 0;
7868c2ecf20Sopenharmony_ci	ishtp_dev->fw_client_presentation_num = 0;
7878c2ecf20Sopenharmony_ci	ishtp_dev->fw_client_index = 0;
7888c2ecf20Sopenharmony_ci	bitmap_zero(ishtp_dev->fw_clients_map, ISHTP_CLIENTS_MAX);
7898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ishtp_dev->fw_clients_lock, flags);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_bus_remove_all_clients);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci/**
7948c2ecf20Sopenharmony_ci * ishtp_reset_handler() - IPC reset handler
7958c2ecf20Sopenharmony_ci * @dev:	ishtp device
7968c2ecf20Sopenharmony_ci *
7978c2ecf20Sopenharmony_ci * ISHTP Handler for IPC_RESET notification
7988c2ecf20Sopenharmony_ci */
7998c2ecf20Sopenharmony_civoid ishtp_reset_handler(struct ishtp_device *dev)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	unsigned long	flags;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* Handle FW-initiated reset */
8048c2ecf20Sopenharmony_ci	dev->dev_state = ISHTP_DEV_RESETTING;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	/* Clear BH processing queue - no further HBMs */
8078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->rd_msg_spinlock, flags);
8088c2ecf20Sopenharmony_ci	dev->rd_msg_fifo_head = dev->rd_msg_fifo_tail = 0;
8098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	/* Handle ISH FW reset against upper layers */
8128c2ecf20Sopenharmony_ci	ishtp_bus_remove_all_clients(dev, true);
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_reset_handler);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/**
8178c2ecf20Sopenharmony_ci * ishtp_reset_compl_handler() - Reset completion handler
8188c2ecf20Sopenharmony_ci * @dev:	ishtp device
8198c2ecf20Sopenharmony_ci *
8208c2ecf20Sopenharmony_ci * ISHTP handler for IPC_RESET sequence completion to start
8218c2ecf20Sopenharmony_ci * host message bus start protocol sequence.
8228c2ecf20Sopenharmony_ci */
8238c2ecf20Sopenharmony_civoid ishtp_reset_compl_handler(struct ishtp_device *dev)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	dev->dev_state = ISHTP_DEV_INIT_CLIENTS;
8268c2ecf20Sopenharmony_ci	dev->hbm_state = ISHTP_HBM_START;
8278c2ecf20Sopenharmony_ci	ishtp_hbm_start_req(dev);
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_reset_compl_handler);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci/**
8328c2ecf20Sopenharmony_ci * ishtp_use_dma_transfer() - Function to use DMA
8338c2ecf20Sopenharmony_ci *
8348c2ecf20Sopenharmony_ci * This interface is used to enable usage of DMA
8358c2ecf20Sopenharmony_ci *
8368c2ecf20Sopenharmony_ci * Return non zero if DMA can be enabled
8378c2ecf20Sopenharmony_ci */
8388c2ecf20Sopenharmony_ciint ishtp_use_dma_transfer(void)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	return ishtp_use_dma;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci/**
8448c2ecf20Sopenharmony_ci * ishtp_device() - Return device pointer
8458c2ecf20Sopenharmony_ci *
8468c2ecf20Sopenharmony_ci * This interface is used to return device pointer from ishtp_cl_device
8478c2ecf20Sopenharmony_ci * instance.
8488c2ecf20Sopenharmony_ci *
8498c2ecf20Sopenharmony_ci * Return: device *.
8508c2ecf20Sopenharmony_ci */
8518c2ecf20Sopenharmony_cistruct device *ishtp_device(struct ishtp_cl_device *device)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	return &device->dev;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_device);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci/**
8588c2ecf20Sopenharmony_ci * ishtp_get_pci_device() - Return PCI device dev pointer
8598c2ecf20Sopenharmony_ci * This interface is used to return PCI device pointer
8608c2ecf20Sopenharmony_ci * from ishtp_cl_device instance.
8618c2ecf20Sopenharmony_ci *
8628c2ecf20Sopenharmony_ci * Return: device *.
8638c2ecf20Sopenharmony_ci */
8648c2ecf20Sopenharmony_cistruct device *ishtp_get_pci_device(struct ishtp_cl_device *device)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	return device->ishtp_dev->devc;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_get_pci_device);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci/**
8718c2ecf20Sopenharmony_ci * ishtp_trace_callback() - Return trace callback
8728c2ecf20Sopenharmony_ci *
8738c2ecf20Sopenharmony_ci * This interface is used to return trace callback function pointer.
8748c2ecf20Sopenharmony_ci *
8758c2ecf20Sopenharmony_ci * Return: void *.
8768c2ecf20Sopenharmony_ci */
8778c2ecf20Sopenharmony_civoid *ishtp_trace_callback(struct ishtp_cl_device *cl_device)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	return cl_device->ishtp_dev->print_log;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_trace_callback);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci/**
8848c2ecf20Sopenharmony_ci * ish_hw_reset() - Call HW reset IPC callback
8858c2ecf20Sopenharmony_ci *
8868c2ecf20Sopenharmony_ci * This interface is used to reset HW in case of error.
8878c2ecf20Sopenharmony_ci *
8888c2ecf20Sopenharmony_ci * Return: value from IPC hw_reset callback
8898c2ecf20Sopenharmony_ci */
8908c2ecf20Sopenharmony_ciint ish_hw_reset(struct ishtp_device *dev)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	return dev->ops->hw_reset(dev);
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ish_hw_reset);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/**
8978c2ecf20Sopenharmony_ci * ishtp_bus_register() - Function to register bus
8988c2ecf20Sopenharmony_ci *
8998c2ecf20Sopenharmony_ci * This register ishtp bus
9008c2ecf20Sopenharmony_ci *
9018c2ecf20Sopenharmony_ci * Return: Return output of bus_register
9028c2ecf20Sopenharmony_ci */
9038c2ecf20Sopenharmony_cistatic int  __init ishtp_bus_register(void)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	return bus_register(&ishtp_cl_bus_type);
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci/**
9098c2ecf20Sopenharmony_ci * ishtp_bus_unregister() - Function to unregister bus
9108c2ecf20Sopenharmony_ci *
9118c2ecf20Sopenharmony_ci * This unregister ishtp bus
9128c2ecf20Sopenharmony_ci */
9138c2ecf20Sopenharmony_cistatic void __exit ishtp_bus_unregister(void)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	bus_unregister(&ishtp_cl_bus_type);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cimodule_init(ishtp_bus_register);
9198c2ecf20Sopenharmony_cimodule_exit(ishtp_bus_unregister);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
922