2106 lines · 50.9 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)// Copyright(c) 2015-17 Intel Corporation. #include <linux/acpi.h>#include <linux/delay.h>#include <linux/pm_runtime.h>#include <linux/soundwire/sdw_registers.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_type.h>#include <linux/string_choices.h>#include "bus.h"#include "irq.h"#include "sysfs_local.h" static DEFINE_IDA(sdw_bus_ida); static int sdw_get_id(struct sdw_bus *bus){	int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL); 	if (rc < 0)		return rc; 	bus->id = rc; 	if (bus->controller_id == -1)		bus->controller_id = rc; 	return 0;} /** * sdw_bus_master_add() - add a bus Master instance * @bus: bus instance * @parent: parent device * @fwnode: firmware node handle * * Initializes the bus instance, read properties and create child * devices. */int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,		       struct fwnode_handle *fwnode){	struct sdw_master_prop *prop = NULL;	int ret; 	if (!parent) {		pr_err("SoundWire parent device is not set\n");		return -ENODEV;	} 	ret = sdw_get_id(bus);	if (ret < 0) {		dev_err(parent, "Failed to get bus id\n");		return ret;	} 	ida_init(&bus->slave_ida); 	ret = sdw_master_device_add(bus, parent, fwnode);	if (ret < 0) {		dev_err(parent, "Failed to add master device at link %d\n",			bus->link_id);		return ret;	} 	if (!bus->ops) {		dev_err(bus->dev, "SoundWire Bus ops are not set\n");		return -EINVAL;	} 	if (!bus->compute_params) {		dev_err(bus->dev,			"Bandwidth allocation not configured, compute_params no set\n");		return -EINVAL;	} 	/*	 * Give each bus_lock and msg_lock a unique key so that lockdep won't	 * trigger a deadlock warning when the locks of several buses are	 * grabbed during configuration of a multi-bus stream.	 */	lockdep_register_key(&bus->msg_lock_key);	__mutex_init(&bus->msg_lock, "msg_lock", &bus->msg_lock_key); 	lockdep_register_key(&bus->bus_lock_key);	__mutex_init(&bus->bus_lock, "bus_lock", &bus->bus_lock_key); 	INIT_LIST_HEAD(&bus->slaves);	INIT_LIST_HEAD(&bus->m_rt_list); 	/*	 * Initialize multi_link flag	 */	bus->multi_link = false;	if (bus->ops->read_prop) {		ret = bus->ops->read_prop(bus);		if (ret < 0) {			dev_err(bus->dev,				"Bus read properties failed:%d\n", ret);			return ret;		}	} 	sdw_bus_debugfs_init(bus); 	/*	 * Device numbers in SoundWire are 0 through 15. Enumeration device	 * number (0), Broadcast device number (15), Group numbers (12 and	 * 13) and Master device number (14) are not used for assignment so	 * mask these and other higher bits.	 */ 	/* Set higher order bits */	*bus->assigned = ~GENMASK(SDW_BROADCAST_DEV_NUM, SDW_ENUM_DEV_NUM); 	/* Set enumeration device number and broadcast device number */	set_bit(SDW_ENUM_DEV_NUM, bus->assigned);	set_bit(SDW_BROADCAST_DEV_NUM, bus->assigned); 	/* Set group device numbers and master device number */	set_bit(SDW_GROUP12_DEV_NUM, bus->assigned);	set_bit(SDW_GROUP13_DEV_NUM, bus->assigned);	set_bit(SDW_MASTER_DEV_NUM, bus->assigned); 	ret = sdw_irq_create(bus, fwnode);	if (ret)		return ret; 	/*	 * SDW is an enumerable bus, but devices can be powered off. So,	 * they won't be able to report as present.	 *	 * Create Slave devices based on Slaves described in	 * the respective firmware (ACPI/DT)	 */	if (IS_ENABLED(CONFIG_ACPI) && ACPI_HANDLE(bus->dev))		ret = sdw_acpi_find_slaves(bus);	else if (IS_ENABLED(CONFIG_OF) && bus->dev->of_node)		ret = sdw_of_find_slaves(bus);	else		ret = -ENOTSUPP; /* No ACPI/DT so error out */ 	if (ret < 0) {		dev_err(bus->dev, "Finding slaves failed:%d\n", ret);		sdw_irq_delete(bus);		return ret;	} 	/*	 * Initialize clock values based on Master properties. The max	 * frequency is read from max_clk_freq property. Current assumption	 * is that the bus will start at highest clock frequency when	 * powered on.	 *	 * Default active bank will be 0 as out of reset the Slaves have	 * to start with bank 0 (Table 40 of Spec)	 */	prop = &bus->prop;	bus->params.max_dr_freq = prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR;	bus->params.curr_dr_freq = bus->params.max_dr_freq;	bus->params.curr_bank = SDW_BANK0;	bus->params.next_bank = SDW_BANK1; 	return 0;}EXPORT_SYMBOL(sdw_bus_master_add); static int sdw_delete_slave(struct device *dev, void *data){	struct sdw_slave *slave = dev_to_sdw_dev(dev);	struct sdw_bus *bus = slave->bus; 	pm_runtime_disable(dev); 	sdw_slave_debugfs_exit(slave); 	mutex_lock(&bus->bus_lock); 	if (slave->dev_num) { /* clear dev_num if assigned */		clear_bit(slave->dev_num, bus->assigned);		if (bus->ops && bus->ops->put_device_num)			bus->ops->put_device_num(bus, slave);	}	list_del_init(&slave->node);	mutex_unlock(&bus->bus_lock); 	device_unregister(dev);	return 0;} /** * sdw_bus_master_delete() - delete the bus master instance * @bus: bus to be deleted * * Remove the instance, delete the child devices. */void sdw_bus_master_delete(struct sdw_bus *bus){	device_for_each_child(bus->dev, NULL, sdw_delete_slave); 	sdw_irq_delete(bus); 	sdw_master_device_del(bus); 	sdw_bus_debugfs_exit(bus);	lockdep_unregister_key(&bus->bus_lock_key);	lockdep_unregister_key(&bus->msg_lock_key);	ida_free(&sdw_bus_ida, bus->id);}EXPORT_SYMBOL(sdw_bus_master_delete); /* * SDW IO Calls */ static inline int find_response_code(enum sdw_command_response resp){	switch (resp) {	case SDW_CMD_OK:		return 0; 	case SDW_CMD_IGNORED:		return -ENODATA; 	case SDW_CMD_TIMEOUT:		return -ETIMEDOUT; 	default:		return -EIO;	}} static inline int do_transfer(struct sdw_bus *bus, struct sdw_msg *msg){	int retry = bus->prop.err_threshold;	enum sdw_command_response resp;	int ret = 0, i; 	for (i = 0; i <= retry; i++) {		resp = bus->ops->xfer_msg(bus, msg);		ret = find_response_code(resp); 		/* if cmd is ok or ignored return */		if (ret == 0 || ret == -ENODATA)			return ret;	} 	return ret;} static inline int do_transfer_defer(struct sdw_bus *bus,				    struct sdw_msg *msg){	struct sdw_defer *defer = &bus->defer_msg;	int retry = bus->prop.err_threshold;	enum sdw_command_response resp;	int ret = 0, i; 	defer->msg = msg;	defer->length = msg->len;	init_completion(&defer->complete); 	for (i = 0; i <= retry; i++) {		resp = bus->ops->xfer_msg_defer(bus);		ret = find_response_code(resp);		/* if cmd is ok or ignored return */		if (ret == 0 || ret == -ENODATA)			return ret;	} 	return ret;} static int sdw_transfer_unlocked(struct sdw_bus *bus, struct sdw_msg *msg){	int ret; 	ret = do_transfer(bus, msg);	if (ret != 0 && ret != -ENODATA)		dev_err(bus->dev, "trf on Slave %d failed:%d %s addr %x count %d\n",			msg->dev_num, ret,			str_write_read(msg->flags & SDW_MSG_FLAG_WRITE),			msg->addr, msg->len); 	return ret;} /** * sdw_transfer() - Synchronous transfer message to a SDW Slave device * @bus: SDW bus * @msg: SDW message to be xfered */int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg){	int ret; 	mutex_lock(&bus->msg_lock); 	ret = sdw_transfer_unlocked(bus, msg); 	mutex_unlock(&bus->msg_lock); 	return ret;} /** * sdw_show_ping_status() - Direct report of PING status, to be used by Peripheral drivers * @bus: SDW bus * @sync_delay: Delay before reading status */void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay){	u32 status; 	if (!bus->ops->read_ping_status)		return; 	/*	 * wait for peripheral to sync if desired. 10-15ms should be more than	 * enough in most cases.	 */	if (sync_delay)		usleep_range(10000, 15000); 	mutex_lock(&bus->msg_lock); 	status = bus->ops->read_ping_status(bus); 	mutex_unlock(&bus->msg_lock); 	if (!status)		dev_warn(bus->dev, "%s: no peripherals attached\n", __func__);	else		dev_dbg(bus->dev, "PING status: %#x\n", status);}EXPORT_SYMBOL(sdw_show_ping_status); /** * sdw_transfer_defer() - Asynchronously transfer message to a SDW Slave device * @bus: SDW bus * @msg: SDW message to be xfered * * Caller needs to hold the msg_lock lock while calling this */int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg){	int ret; 	if (!bus->ops->xfer_msg_defer)		return -ENOTSUPP; 	ret = do_transfer_defer(bus, msg);	if (ret != 0 && ret != -ENODATA)		dev_err(bus->dev, "Defer trf on Slave %d failed:%d\n",			msg->dev_num, ret); 	return ret;} int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,		 u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf){	memset(msg, 0, sizeof(*msg));	msg->addr = addr; /* addr is 16 bit and truncated here */	msg->len = count;	msg->dev_num = dev_num;	msg->flags = flags;	msg->buf = buf; 	if (addr < SDW_REG_NO_PAGE) /* no paging area */		return 0; 	if (addr >= SDW_REG_MAX) { /* illegal addr */		pr_err("SDW: Invalid address %x passed\n", addr);		return -EINVAL;	} 	if (addr < SDW_REG_OPTIONAL_PAGE) { /* 32k but no page */		if (slave && !slave->prop.paging_support)			return 0;		/* no need for else as that will fall-through to paging */	} 	/* paging mandatory */	if (dev_num == SDW_ENUM_DEV_NUM || dev_num == SDW_BROADCAST_DEV_NUM) {		pr_err("SDW: Invalid device for paging :%d\n", dev_num);		return -EINVAL;	} 	if (!slave) {		pr_err("SDW: No slave for paging addr\n");		return -EINVAL;	} 	if (!slave->prop.paging_support) {		dev_err(&slave->dev,			"address %x needs paging but no support\n", addr);		return -EINVAL;	} 	msg->addr_page1 = FIELD_GET(SDW_SCP_ADDRPAGE1_MASK, addr);	msg->addr_page2 = FIELD_GET(SDW_SCP_ADDRPAGE2_MASK, addr);	msg->addr |= BIT(15);	msg->page = true; 	return 0;} /* * Read/Write IO functions. */ static int sdw_ntransfer_no_pm(struct sdw_slave *slave, u32 addr, u8 flags,			       size_t count, u8 *val){	struct sdw_msg msg;	size_t size;	int ret; 	while (count) {		// Only handle bytes up to next page boundary		size = min_t(size_t, count, (SDW_REGADDR + 1) - (addr & SDW_REGADDR)); 		ret = sdw_fill_msg(&msg, slave, addr, size, slave->dev_num, flags, val);		if (ret < 0)			return ret; 		ret = sdw_transfer(slave->bus, &msg);		if (ret < 0 && !slave->is_mockup_device)			return ret; 		addr += size;		val += size;		count -= size;	} 	return 0;} /** * sdw_nread_no_pm() - Read "n" contiguous SDW Slave registers with no PM * @slave: SDW Slave * @addr: Register address * @count: length * @val: Buffer for values to be read * * Note that if the message crosses a page boundary each page will be * transferred under a separate invocation of the msg_lock. */int sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val){	return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_READ, count, val);}EXPORT_SYMBOL(sdw_nread_no_pm); /** * sdw_nwrite_no_pm() - Write "n" contiguous SDW Slave registers with no PM * @slave: SDW Slave * @addr: Register address * @count: length * @val: Buffer for values to be written * * Note that if the message crosses a page boundary each page will be * transferred under a separate invocation of the msg_lock. */int sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val){	return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_WRITE, count, (u8 *)val);}EXPORT_SYMBOL(sdw_nwrite_no_pm); /** * sdw_write_no_pm() - Write a SDW Slave register with no PM * @slave: SDW Slave * @addr: Register address * @value: Register value */int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value){	return sdw_nwrite_no_pm(slave, addr, 1, &value);}EXPORT_SYMBOL(sdw_write_no_pm); static intsdw_bread_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr){	struct sdw_msg msg;	u8 buf;	int ret; 	ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num,			   SDW_MSG_FLAG_READ, &buf);	if (ret < 0)		return ret; 	ret = sdw_transfer(bus, &msg);	if (ret < 0)		return ret; 

Lines 1–500 of 2106.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t