162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Connection Management Procedures (IEC 61883-1) helper functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/firewire.h>
1062306a36Sopenharmony_ci#include <linux/firewire-constants.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include "lib.h"
1462306a36Sopenharmony_ci#include "iso-resources.h"
1562306a36Sopenharmony_ci#include "cmp.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* MPR common fields */
1862306a36Sopenharmony_ci#define MPR_SPEED_MASK		0xc0000000
1962306a36Sopenharmony_ci#define MPR_SPEED_SHIFT		30
2062306a36Sopenharmony_ci#define MPR_XSPEED_MASK		0x00000060
2162306a36Sopenharmony_ci#define MPR_XSPEED_SHIFT	5
2262306a36Sopenharmony_ci#define MPR_PLUGS_MASK		0x0000001f
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* PCR common fields */
2562306a36Sopenharmony_ci#define PCR_ONLINE		0x80000000
2662306a36Sopenharmony_ci#define PCR_BCAST_CONN		0x40000000
2762306a36Sopenharmony_ci#define PCR_P2P_CONN_MASK	0x3f000000
2862306a36Sopenharmony_ci#define PCR_P2P_CONN_SHIFT	24
2962306a36Sopenharmony_ci#define PCR_CHANNEL_MASK	0x003f0000
3062306a36Sopenharmony_ci#define PCR_CHANNEL_SHIFT	16
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* oPCR specific fields */
3362306a36Sopenharmony_ci#define OPCR_XSPEED_MASK	0x00C00000
3462306a36Sopenharmony_ci#define OPCR_XSPEED_SHIFT	22
3562306a36Sopenharmony_ci#define OPCR_SPEED_MASK		0x0000C000
3662306a36Sopenharmony_ci#define OPCR_SPEED_SHIFT	14
3762306a36Sopenharmony_ci#define OPCR_OVERHEAD_ID_MASK	0x00003C00
3862306a36Sopenharmony_ci#define OPCR_OVERHEAD_ID_SHIFT	10
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cienum bus_reset_handling {
4162306a36Sopenharmony_ci	ABORT_ON_BUS_RESET,
4262306a36Sopenharmony_ci	SUCCEED_ON_BUS_RESET,
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic __printf(2, 3)
4662306a36Sopenharmony_civoid cmp_error(struct cmp_connection *c, const char *fmt, ...)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	va_list va;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	va_start(va, fmt);
5162306a36Sopenharmony_ci	dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
5262306a36Sopenharmony_ci		(c->direction == CMP_INPUT) ? 'i' : 'o',
5362306a36Sopenharmony_ci		c->pcr_index, &(struct va_format){ fmt, &va });
5462306a36Sopenharmony_ci	va_end(va);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic u64 mpr_address(struct cmp_connection *c)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	if (c->direction == CMP_INPUT)
6062306a36Sopenharmony_ci		return CSR_REGISTER_BASE + CSR_IMPR;
6162306a36Sopenharmony_ci	else
6262306a36Sopenharmony_ci		return CSR_REGISTER_BASE + CSR_OMPR;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic u64 pcr_address(struct cmp_connection *c)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	if (c->direction == CMP_INPUT)
6862306a36Sopenharmony_ci		return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
6962306a36Sopenharmony_ci	else
7062306a36Sopenharmony_ci		return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int pcr_modify(struct cmp_connection *c,
7462306a36Sopenharmony_ci		      __be32 (*modify)(struct cmp_connection *c, __be32 old),
7562306a36Sopenharmony_ci		      int (*check)(struct cmp_connection *c, __be32 pcr),
7662306a36Sopenharmony_ci		      enum bus_reset_handling bus_reset_handling)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	__be32 old_arg, buffer[2];
7962306a36Sopenharmony_ci	int err;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	buffer[0] = c->last_pcr_value;
8262306a36Sopenharmony_ci	for (;;) {
8362306a36Sopenharmony_ci		old_arg = buffer[0];
8462306a36Sopenharmony_ci		buffer[1] = modify(c, buffer[0]);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		err = snd_fw_transaction(
8762306a36Sopenharmony_ci				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
8862306a36Sopenharmony_ci				pcr_address(c), buffer, 8,
8962306a36Sopenharmony_ci				FW_FIXED_GENERATION | c->resources.generation);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		if (err < 0) {
9262306a36Sopenharmony_ci			if (err == -EAGAIN &&
9362306a36Sopenharmony_ci			    bus_reset_handling == SUCCEED_ON_BUS_RESET)
9462306a36Sopenharmony_ci				err = 0;
9562306a36Sopenharmony_ci			return err;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		if (buffer[0] == old_arg) /* success? */
9962306a36Sopenharmony_ci			break;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		if (check) {
10262306a36Sopenharmony_ci			err = check(c, buffer[0]);
10362306a36Sopenharmony_ci			if (err < 0)
10462306a36Sopenharmony_ci				return err;
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci	c->last_pcr_value = buffer[1];
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/**
11462306a36Sopenharmony_ci * cmp_connection_init - initializes a connection manager
11562306a36Sopenharmony_ci * @c: the connection manager to initialize
11662306a36Sopenharmony_ci * @unit: a unit of the target device
11762306a36Sopenharmony_ci * @direction: input or output
11862306a36Sopenharmony_ci * @pcr_index: the index of the iPCR/oPCR on the target device
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_ciint cmp_connection_init(struct cmp_connection *c,
12162306a36Sopenharmony_ci			struct fw_unit *unit,
12262306a36Sopenharmony_ci			enum cmp_direction direction,
12362306a36Sopenharmony_ci			unsigned int pcr_index)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	__be32 mpr_be;
12662306a36Sopenharmony_ci	u32 mpr;
12762306a36Sopenharmony_ci	int err;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	c->direction = direction;
13062306a36Sopenharmony_ci	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
13162306a36Sopenharmony_ci				 mpr_address(c), &mpr_be, 4, 0);
13262306a36Sopenharmony_ci	if (err < 0)
13362306a36Sopenharmony_ci		return err;
13462306a36Sopenharmony_ci	mpr = be32_to_cpu(mpr_be);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (pcr_index >= (mpr & MPR_PLUGS_MASK))
13762306a36Sopenharmony_ci		return -EINVAL;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	err = fw_iso_resources_init(&c->resources, unit);
14062306a36Sopenharmony_ci	if (err < 0)
14162306a36Sopenharmony_ci		return err;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	c->connected = false;
14462306a36Sopenharmony_ci	mutex_init(&c->mutex);
14562306a36Sopenharmony_ci	c->last_pcr_value = cpu_to_be32(0x80000000);
14662306a36Sopenharmony_ci	c->pcr_index = pcr_index;
14762306a36Sopenharmony_ci	c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
14862306a36Sopenharmony_ci	if (c->max_speed == SCODE_BETA)
14962306a36Sopenharmony_ci		c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_init);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * cmp_connection_check_used - check connection is already esablished or not
15762306a36Sopenharmony_ci * @c: the connection manager to be checked
15862306a36Sopenharmony_ci * @used: the pointer to store the result of checking the connection
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_ciint cmp_connection_check_used(struct cmp_connection *c, bool *used)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	__be32 pcr;
16362306a36Sopenharmony_ci	int err;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	err = snd_fw_transaction(
16662306a36Sopenharmony_ci			c->resources.unit, TCODE_READ_QUADLET_REQUEST,
16762306a36Sopenharmony_ci			pcr_address(c), &pcr, 4, 0);
16862306a36Sopenharmony_ci	if (err >= 0)
16962306a36Sopenharmony_ci		*used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
17062306a36Sopenharmony_ci					     PCR_P2P_CONN_MASK));
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return err;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_check_used);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/**
17762306a36Sopenharmony_ci * cmp_connection_destroy - free connection manager resources
17862306a36Sopenharmony_ci * @c: the connection manager
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_civoid cmp_connection_destroy(struct cmp_connection *c)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	WARN_ON(c->connected);
18362306a36Sopenharmony_ci	mutex_destroy(&c->mutex);
18462306a36Sopenharmony_ci	fw_iso_resources_destroy(&c->resources);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_destroy);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciint cmp_connection_reserve(struct cmp_connection *c,
18962306a36Sopenharmony_ci			   unsigned int max_payload_bytes)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	int err;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	mutex_lock(&c->mutex);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (WARN_ON(c->resources.allocated)) {
19662306a36Sopenharmony_ci		err = -EBUSY;
19762306a36Sopenharmony_ci		goto end;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	c->speed = min(c->max_speed,
20162306a36Sopenharmony_ci		       fw_parent_device(c->resources.unit)->max_speed);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
20462306a36Sopenharmony_ci					c->speed);
20562306a36Sopenharmony_ciend:
20662306a36Sopenharmony_ci	mutex_unlock(&c->mutex);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return err;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_reserve);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_civoid cmp_connection_release(struct cmp_connection *c)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	mutex_lock(&c->mutex);
21562306a36Sopenharmony_ci	fw_iso_resources_free(&c->resources);
21662306a36Sopenharmony_ci	mutex_unlock(&c->mutex);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_release);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
22362306a36Sopenharmony_ci			     PCR_P2P_CONN_MASK |
22462306a36Sopenharmony_ci			     PCR_CHANNEL_MASK);
22562306a36Sopenharmony_ci	ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
22662306a36Sopenharmony_ci	ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return ipcr;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int get_overhead_id(struct cmp_connection *c)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int id;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/*
23662306a36Sopenharmony_ci	 * apply "oPCR overhead ID encoding"
23762306a36Sopenharmony_ci	 * the encoding table can convert up to 512.
23862306a36Sopenharmony_ci	 * here the value over 512 is converted as the same way as 512.
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci	for (id = 1; id < 16; id++) {
24162306a36Sopenharmony_ci		if (c->resources.bandwidth_overhead < (id << 5))
24262306a36Sopenharmony_ci			break;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	if (id == 16)
24562306a36Sopenharmony_ci		id = 0;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return id;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	unsigned int spd, xspd;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* generate speed and extended speed field value */
25562306a36Sopenharmony_ci	if (c->speed > SCODE_400) {
25662306a36Sopenharmony_ci		spd  = SCODE_800;
25762306a36Sopenharmony_ci		xspd = c->speed - SCODE_800;
25862306a36Sopenharmony_ci	} else {
25962306a36Sopenharmony_ci		spd = c->speed;
26062306a36Sopenharmony_ci		xspd = 0;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
26462306a36Sopenharmony_ci			     PCR_P2P_CONN_MASK |
26562306a36Sopenharmony_ci			     OPCR_XSPEED_MASK |
26662306a36Sopenharmony_ci			     PCR_CHANNEL_MASK |
26762306a36Sopenharmony_ci			     OPCR_SPEED_MASK |
26862306a36Sopenharmony_ci			     OPCR_OVERHEAD_ID_MASK);
26962306a36Sopenharmony_ci	opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
27062306a36Sopenharmony_ci	opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
27162306a36Sopenharmony_ci	opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
27262306a36Sopenharmony_ci	opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
27362306a36Sopenharmony_ci	opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return opcr;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic int pcr_set_check(struct cmp_connection *c, __be32 pcr)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
28162306a36Sopenharmony_ci			      PCR_P2P_CONN_MASK)) {
28262306a36Sopenharmony_ci		cmp_error(c, "plug is already in use\n");
28362306a36Sopenharmony_ci		return -EBUSY;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci	if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
28662306a36Sopenharmony_ci		cmp_error(c, "plug is not on-line\n");
28762306a36Sopenharmony_ci		return -ECONNREFUSED;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/**
29462306a36Sopenharmony_ci * cmp_connection_establish - establish a connection to the target
29562306a36Sopenharmony_ci * @c: the connection manager
29662306a36Sopenharmony_ci *
29762306a36Sopenharmony_ci * This function establishes a point-to-point connection from the local
29862306a36Sopenharmony_ci * computer to the target by allocating isochronous resources (channel and
29962306a36Sopenharmony_ci * bandwidth) and setting the target's input/output plug control register.
30062306a36Sopenharmony_ci * When this function succeeds, the caller is responsible for starting
30162306a36Sopenharmony_ci * transmitting packets.
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_ciint cmp_connection_establish(struct cmp_connection *c)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int err;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	mutex_lock(&c->mutex);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (WARN_ON(c->connected)) {
31062306a36Sopenharmony_ci		mutex_unlock(&c->mutex);
31162306a36Sopenharmony_ci		return -EISCONN;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ciretry_after_bus_reset:
31562306a36Sopenharmony_ci	if (c->direction == CMP_OUTPUT)
31662306a36Sopenharmony_ci		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
31762306a36Sopenharmony_ci				 ABORT_ON_BUS_RESET);
31862306a36Sopenharmony_ci	else
31962306a36Sopenharmony_ci		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
32062306a36Sopenharmony_ci				 ABORT_ON_BUS_RESET);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (err == -EAGAIN) {
32362306a36Sopenharmony_ci		err = fw_iso_resources_update(&c->resources);
32462306a36Sopenharmony_ci		if (err >= 0)
32562306a36Sopenharmony_ci			goto retry_after_bus_reset;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci	if (err >= 0)
32862306a36Sopenharmony_ci		c->connected = true;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mutex_unlock(&c->mutex);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return err;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_establish);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/**
33762306a36Sopenharmony_ci * cmp_connection_update - update the connection after a bus reset
33862306a36Sopenharmony_ci * @c: the connection manager
33962306a36Sopenharmony_ci *
34062306a36Sopenharmony_ci * This function must be called from the driver's .update handler to
34162306a36Sopenharmony_ci * reestablish any connection that might have been active.
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci * Returns zero on success, or a negative error code.  On an error, the
34462306a36Sopenharmony_ci * connection is broken and the caller must stop transmitting iso packets.
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_ciint cmp_connection_update(struct cmp_connection *c)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	int err;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	mutex_lock(&c->mutex);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (!c->connected) {
35362306a36Sopenharmony_ci		mutex_unlock(&c->mutex);
35462306a36Sopenharmony_ci		return 0;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	err = fw_iso_resources_update(&c->resources);
35862306a36Sopenharmony_ci	if (err < 0)
35962306a36Sopenharmony_ci		goto err_unconnect;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (c->direction == CMP_OUTPUT)
36262306a36Sopenharmony_ci		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
36362306a36Sopenharmony_ci				 SUCCEED_ON_BUS_RESET);
36462306a36Sopenharmony_ci	else
36562306a36Sopenharmony_ci		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
36662306a36Sopenharmony_ci				 SUCCEED_ON_BUS_RESET);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (err < 0)
36962306a36Sopenharmony_ci		goto err_unconnect;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	mutex_unlock(&c->mutex);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return 0;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cierr_unconnect:
37662306a36Sopenharmony_ci	c->connected = false;
37762306a36Sopenharmony_ci	mutex_unlock(&c->mutex);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return err;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_update);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/**
38962306a36Sopenharmony_ci * cmp_connection_break - break the connection to the target
39062306a36Sopenharmony_ci * @c: the connection manager
39162306a36Sopenharmony_ci *
39262306a36Sopenharmony_ci * This function deactives the connection in the target's input/output plug
39362306a36Sopenharmony_ci * control register, and frees the isochronous resources of the connection.
39462306a36Sopenharmony_ci * Before calling this function, the caller should cease transmitting packets.
39562306a36Sopenharmony_ci */
39662306a36Sopenharmony_civoid cmp_connection_break(struct cmp_connection *c)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	int err;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	mutex_lock(&c->mutex);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (!c->connected) {
40362306a36Sopenharmony_ci		mutex_unlock(&c->mutex);
40462306a36Sopenharmony_ci		return;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
40862306a36Sopenharmony_ci	if (err < 0)
40962306a36Sopenharmony_ci		cmp_error(c, "plug is still connected\n");
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	c->connected = false;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	mutex_unlock(&c->mutex);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_break);
416