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