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