1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
231ef9134SClemens Ladisch /*
331ef9134SClemens Ladisch * Connection Management Procedures (IEC 61883-1) helper functions
431ef9134SClemens Ladisch *
531ef9134SClemens Ladisch * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
631ef9134SClemens Ladisch */
731ef9134SClemens Ladisch
831ef9134SClemens Ladisch #include <linux/device.h>
931ef9134SClemens Ladisch #include <linux/firewire.h>
1031ef9134SClemens Ladisch #include <linux/firewire-constants.h>
1131ef9134SClemens Ladisch #include <linux/module.h>
1231ef9134SClemens Ladisch #include <linux/sched.h>
1331ef9134SClemens Ladisch #include "lib.h"
1431ef9134SClemens Ladisch #include "iso-resources.h"
1531ef9134SClemens Ladisch #include "cmp.h"
1631ef9134SClemens Ladisch
17a7fa0d04STakashi Sakamoto /* MPR common fields */
18a7fa0d04STakashi Sakamoto #define MPR_SPEED_MASK 0xc0000000
19a7fa0d04STakashi Sakamoto #define MPR_SPEED_SHIFT 30
20a7fa0d04STakashi Sakamoto #define MPR_XSPEED_MASK 0x00000060
21a7fa0d04STakashi Sakamoto #define MPR_XSPEED_SHIFT 5
22a7fa0d04STakashi Sakamoto #define MPR_PLUGS_MASK 0x0000001f
2331ef9134SClemens Ladisch
24a7fa0d04STakashi Sakamoto /* PCR common fields */
25a7fa0d04STakashi Sakamoto #define PCR_ONLINE 0x80000000
26a7fa0d04STakashi Sakamoto #define PCR_BCAST_CONN 0x40000000
27a7fa0d04STakashi Sakamoto #define PCR_P2P_CONN_MASK 0x3f000000
28a7fa0d04STakashi Sakamoto #define PCR_P2P_CONN_SHIFT 24
29a7fa0d04STakashi Sakamoto #define PCR_CHANNEL_MASK 0x003f0000
30a7fa0d04STakashi Sakamoto #define PCR_CHANNEL_SHIFT 16
3131ef9134SClemens Ladisch
3244aff698STakashi Sakamoto /* oPCR specific fields */
3344aff698STakashi Sakamoto #define OPCR_XSPEED_MASK 0x00C00000
3444aff698STakashi Sakamoto #define OPCR_XSPEED_SHIFT 22
3544aff698STakashi Sakamoto #define OPCR_SPEED_MASK 0x0000C000
3644aff698STakashi Sakamoto #define OPCR_SPEED_SHIFT 14
3744aff698STakashi Sakamoto #define OPCR_OVERHEAD_ID_MASK 0x00003C00
3844aff698STakashi Sakamoto #define OPCR_OVERHEAD_ID_SHIFT 10
3944aff698STakashi Sakamoto
4031ef9134SClemens Ladisch enum bus_reset_handling {
4131ef9134SClemens Ladisch ABORT_ON_BUS_RESET,
4231ef9134SClemens Ladisch SUCCEED_ON_BUS_RESET,
4331ef9134SClemens Ladisch };
4431ef9134SClemens Ladisch
45b9075fa9SJoe Perches static __printf(2, 3)
cmp_error(struct cmp_connection * c,const char * fmt,...)4631ef9134SClemens Ladisch void cmp_error(struct cmp_connection *c, const char *fmt, ...)
4731ef9134SClemens Ladisch {
4831ef9134SClemens Ladisch va_list va;
4931ef9134SClemens Ladisch
5031ef9134SClemens Ladisch va_start(va, fmt);
5131ef9134SClemens Ladisch dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
5244aff698STakashi Sakamoto (c->direction == CMP_INPUT) ? 'i' : 'o',
5344aff698STakashi Sakamoto c->pcr_index, &(struct va_format){ fmt, &va });
5431ef9134SClemens Ladisch va_end(va);
5531ef9134SClemens Ladisch }
5631ef9134SClemens Ladisch
mpr_address(struct cmp_connection * c)5744aff698STakashi Sakamoto static u64 mpr_address(struct cmp_connection *c)
5844aff698STakashi Sakamoto {
5944aff698STakashi Sakamoto if (c->direction == CMP_INPUT)
6044aff698STakashi Sakamoto return CSR_REGISTER_BASE + CSR_IMPR;
6144aff698STakashi Sakamoto else
6244aff698STakashi Sakamoto return CSR_REGISTER_BASE + CSR_OMPR;
6344aff698STakashi Sakamoto }
6444aff698STakashi Sakamoto
pcr_address(struct cmp_connection * c)6544aff698STakashi Sakamoto static u64 pcr_address(struct cmp_connection *c)
6644aff698STakashi Sakamoto {
6744aff698STakashi Sakamoto if (c->direction == CMP_INPUT)
6844aff698STakashi Sakamoto return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
6944aff698STakashi Sakamoto else
7044aff698STakashi Sakamoto return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
7144aff698STakashi Sakamoto }
7244aff698STakashi Sakamoto
pcr_modify(struct cmp_connection * c,__be32 (* modify)(struct cmp_connection * c,__be32 old),int (* check)(struct cmp_connection * c,__be32 pcr),enum bus_reset_handling bus_reset_handling)7331ef9134SClemens Ladisch static int pcr_modify(struct cmp_connection *c,
7431ef9134SClemens Ladisch __be32 (*modify)(struct cmp_connection *c, __be32 old),
7531ef9134SClemens Ladisch int (*check)(struct cmp_connection *c, __be32 pcr),
7631ef9134SClemens Ladisch enum bus_reset_handling bus_reset_handling)
7731ef9134SClemens Ladisch {
78f30e6d3eSStefan Richter __be32 old_arg, buffer[2];
7931ef9134SClemens Ladisch int err;
8031ef9134SClemens Ladisch
8131ef9134SClemens Ladisch buffer[0] = c->last_pcr_value;
8231ef9134SClemens Ladisch for (;;) {
8331ef9134SClemens Ladisch old_arg = buffer[0];
8431ef9134SClemens Ladisch buffer[1] = modify(c, buffer[0]);
8531ef9134SClemens Ladisch
861b70485fSClemens Ladisch err = snd_fw_transaction(
871b70485fSClemens Ladisch c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
8844aff698STakashi Sakamoto pcr_address(c), buffer, 8,
891b70485fSClemens Ladisch FW_FIXED_GENERATION | c->resources.generation);
9031ef9134SClemens Ladisch
911b70485fSClemens Ladisch if (err < 0) {
921b70485fSClemens Ladisch if (err == -EAGAIN &&
931b70485fSClemens Ladisch bus_reset_handling == SUCCEED_ON_BUS_RESET)
941b70485fSClemens Ladisch err = 0;
951b70485fSClemens Ladisch return err;
961b70485fSClemens Ladisch }
971b70485fSClemens Ladisch
9831ef9134SClemens Ladisch if (buffer[0] == old_arg) /* success? */
9931ef9134SClemens Ladisch break;
10031ef9134SClemens Ladisch
10131ef9134SClemens Ladisch if (check) {
10231ef9134SClemens Ladisch err = check(c, buffer[0]);
10331ef9134SClemens Ladisch if (err < 0)
10431ef9134SClemens Ladisch return err;
10531ef9134SClemens Ladisch }
10631ef9134SClemens Ladisch }
10731ef9134SClemens Ladisch c->last_pcr_value = buffer[1];
10831ef9134SClemens Ladisch
10931ef9134SClemens Ladisch return 0;
11031ef9134SClemens Ladisch }
11131ef9134SClemens Ladisch
11231ef9134SClemens Ladisch
11331ef9134SClemens Ladisch /**
11431ef9134SClemens Ladisch * cmp_connection_init - initializes a connection manager
11531ef9134SClemens Ladisch * @c: the connection manager to initialize
11631ef9134SClemens Ladisch * @unit: a unit of the target device
1175f217f90STakashi Sakamoto * @direction: input or output
118a7fa0d04STakashi Sakamoto * @pcr_index: the index of the iPCR/oPCR on the target device
11931ef9134SClemens Ladisch */
cmp_connection_init(struct cmp_connection * c,struct fw_unit * unit,enum cmp_direction direction,unsigned int pcr_index)12031ef9134SClemens Ladisch int cmp_connection_init(struct cmp_connection *c,
12131ef9134SClemens Ladisch struct fw_unit *unit,
122c68a1c65STakashi Sakamoto enum cmp_direction direction,
123a7fa0d04STakashi Sakamoto unsigned int pcr_index)
12431ef9134SClemens Ladisch {
125a7fa0d04STakashi Sakamoto __be32 mpr_be;
126a7fa0d04STakashi Sakamoto u32 mpr;
12731ef9134SClemens Ladisch int err;
12831ef9134SClemens Ladisch
12944aff698STakashi Sakamoto c->direction = direction;
13031ef9134SClemens Ladisch err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
13144aff698STakashi Sakamoto mpr_address(c), &mpr_be, 4, 0);
13231ef9134SClemens Ladisch if (err < 0)
13331ef9134SClemens Ladisch return err;
134a7fa0d04STakashi Sakamoto mpr = be32_to_cpu(mpr_be);
13531ef9134SClemens Ladisch
136a7fa0d04STakashi Sakamoto if (pcr_index >= (mpr & MPR_PLUGS_MASK))
13731ef9134SClemens Ladisch return -EINVAL;
13831ef9134SClemens Ladisch
1395b2599a0SClemens Ladisch err = fw_iso_resources_init(&c->resources, unit);
1405b2599a0SClemens Ladisch if (err < 0)
1415b2599a0SClemens Ladisch return err;
1425b2599a0SClemens Ladisch
14331ef9134SClemens Ladisch c->connected = false;
14431ef9134SClemens Ladisch mutex_init(&c->mutex);
14531ef9134SClemens Ladisch c->last_pcr_value = cpu_to_be32(0x80000000);
146a7fa0d04STakashi Sakamoto c->pcr_index = pcr_index;
147a7fa0d04STakashi Sakamoto c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
14831ef9134SClemens Ladisch if (c->max_speed == SCODE_BETA)
149a7fa0d04STakashi Sakamoto c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
15031ef9134SClemens Ladisch
15131ef9134SClemens Ladisch return 0;
15231ef9134SClemens Ladisch }
15331ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_init);
15431ef9134SClemens Ladisch
15531ef9134SClemens Ladisch /**
156b04479fbSTakashi Sakamoto * cmp_connection_check_used - check connection is already esablished or not
157b04479fbSTakashi Sakamoto * @c: the connection manager to be checked
1585f217f90STakashi Sakamoto * @used: the pointer to store the result of checking the connection
159b04479fbSTakashi Sakamoto */
cmp_connection_check_used(struct cmp_connection * c,bool * used)160b04479fbSTakashi Sakamoto int cmp_connection_check_used(struct cmp_connection *c, bool *used)
161b04479fbSTakashi Sakamoto {
162b04479fbSTakashi Sakamoto __be32 pcr;
163b04479fbSTakashi Sakamoto int err;
164b04479fbSTakashi Sakamoto
165b04479fbSTakashi Sakamoto err = snd_fw_transaction(
166b04479fbSTakashi Sakamoto c->resources.unit, TCODE_READ_QUADLET_REQUEST,
167b04479fbSTakashi Sakamoto pcr_address(c), &pcr, 4, 0);
168b04479fbSTakashi Sakamoto if (err >= 0)
16951212eeaSTakashi Sakamoto *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
17051212eeaSTakashi Sakamoto PCR_P2P_CONN_MASK));
17151212eeaSTakashi Sakamoto
172b04479fbSTakashi Sakamoto return err;
173b04479fbSTakashi Sakamoto }
174b04479fbSTakashi Sakamoto EXPORT_SYMBOL(cmp_connection_check_used);
175b04479fbSTakashi Sakamoto
176b04479fbSTakashi Sakamoto /**
17731ef9134SClemens Ladisch * cmp_connection_destroy - free connection manager resources
17831ef9134SClemens Ladisch * @c: the connection manager
17931ef9134SClemens Ladisch */
cmp_connection_destroy(struct cmp_connection * c)18031ef9134SClemens Ladisch void cmp_connection_destroy(struct cmp_connection *c)
18131ef9134SClemens Ladisch {
18231ef9134SClemens Ladisch WARN_ON(c->connected);
18331ef9134SClemens Ladisch mutex_destroy(&c->mutex);
18431ef9134SClemens Ladisch fw_iso_resources_destroy(&c->resources);
18531ef9134SClemens Ladisch }
18631ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_destroy);
18731ef9134SClemens Ladisch
cmp_connection_reserve(struct cmp_connection * c,unsigned int max_payload_bytes)188*7bc93821STakashi Sakamoto int cmp_connection_reserve(struct cmp_connection *c,
189*7bc93821STakashi Sakamoto unsigned int max_payload_bytes)
190*7bc93821STakashi Sakamoto {
191*7bc93821STakashi Sakamoto int err;
192*7bc93821STakashi Sakamoto
193*7bc93821STakashi Sakamoto mutex_lock(&c->mutex);
194*7bc93821STakashi Sakamoto
195*7bc93821STakashi Sakamoto if (WARN_ON(c->resources.allocated)) {
196*7bc93821STakashi Sakamoto err = -EBUSY;
197*7bc93821STakashi Sakamoto goto end;
198*7bc93821STakashi Sakamoto }
199*7bc93821STakashi Sakamoto
200*7bc93821STakashi Sakamoto c->speed = min(c->max_speed,
201*7bc93821STakashi Sakamoto fw_parent_device(c->resources.unit)->max_speed);
202*7bc93821STakashi Sakamoto
203*7bc93821STakashi Sakamoto err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
204*7bc93821STakashi Sakamoto c->speed);
205*7bc93821STakashi Sakamoto end:
206*7bc93821STakashi Sakamoto mutex_unlock(&c->mutex);
207*7bc93821STakashi Sakamoto
208*7bc93821STakashi Sakamoto return err;
209*7bc93821STakashi Sakamoto }
210*7bc93821STakashi Sakamoto EXPORT_SYMBOL(cmp_connection_reserve);
211*7bc93821STakashi Sakamoto
cmp_connection_release(struct cmp_connection * c)212*7bc93821STakashi Sakamoto void cmp_connection_release(struct cmp_connection *c)
213*7bc93821STakashi Sakamoto {
214*7bc93821STakashi Sakamoto mutex_lock(&c->mutex);
215*7bc93821STakashi Sakamoto fw_iso_resources_free(&c->resources);
216*7bc93821STakashi Sakamoto mutex_unlock(&c->mutex);
217*7bc93821STakashi Sakamoto }
218*7bc93821STakashi Sakamoto EXPORT_SYMBOL(cmp_connection_release);
21931ef9134SClemens Ladisch
ipcr_set_modify(struct cmp_connection * c,__be32 ipcr)22031ef9134SClemens Ladisch static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
22131ef9134SClemens Ladisch {
222a7fa0d04STakashi Sakamoto ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
223a7fa0d04STakashi Sakamoto PCR_P2P_CONN_MASK |
224a7fa0d04STakashi Sakamoto PCR_CHANNEL_MASK);
225a7fa0d04STakashi Sakamoto ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
226a7fa0d04STakashi Sakamoto ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
22731ef9134SClemens Ladisch
22831ef9134SClemens Ladisch return ipcr;
22931ef9134SClemens Ladisch }
23031ef9134SClemens Ladisch
get_overhead_id(struct cmp_connection * c)23144aff698STakashi Sakamoto static int get_overhead_id(struct cmp_connection *c)
23244aff698STakashi Sakamoto {
23344aff698STakashi Sakamoto int id;
23444aff698STakashi Sakamoto
23544aff698STakashi Sakamoto /*
23644aff698STakashi Sakamoto * apply "oPCR overhead ID encoding"
23744aff698STakashi Sakamoto * the encoding table can convert up to 512.
23844aff698STakashi Sakamoto * here the value over 512 is converted as the same way as 512.
23944aff698STakashi Sakamoto */
24044aff698STakashi Sakamoto for (id = 1; id < 16; id++) {
24144aff698STakashi Sakamoto if (c->resources.bandwidth_overhead < (id << 5))
24244aff698STakashi Sakamoto break;
24344aff698STakashi Sakamoto }
24444aff698STakashi Sakamoto if (id == 16)
24544aff698STakashi Sakamoto id = 0;
24644aff698STakashi Sakamoto
24744aff698STakashi Sakamoto return id;
24844aff698STakashi Sakamoto }
24944aff698STakashi Sakamoto
opcr_set_modify(struct cmp_connection * c,__be32 opcr)25044aff698STakashi Sakamoto static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
25144aff698STakashi Sakamoto {
25244aff698STakashi Sakamoto unsigned int spd, xspd;
25344aff698STakashi Sakamoto
25444aff698STakashi Sakamoto /* generate speed and extended speed field value */
25544aff698STakashi Sakamoto if (c->speed > SCODE_400) {
25644aff698STakashi Sakamoto spd = SCODE_800;
25744aff698STakashi Sakamoto xspd = c->speed - SCODE_800;
25844aff698STakashi Sakamoto } else {
25944aff698STakashi Sakamoto spd = c->speed;
26044aff698STakashi Sakamoto xspd = 0;
26144aff698STakashi Sakamoto }
26244aff698STakashi Sakamoto
26344aff698STakashi Sakamoto opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
26444aff698STakashi Sakamoto PCR_P2P_CONN_MASK |
26544aff698STakashi Sakamoto OPCR_XSPEED_MASK |
26644aff698STakashi Sakamoto PCR_CHANNEL_MASK |
26744aff698STakashi Sakamoto OPCR_SPEED_MASK |
26844aff698STakashi Sakamoto OPCR_OVERHEAD_ID_MASK);
26944aff698STakashi Sakamoto opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
27044aff698STakashi Sakamoto opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
27144aff698STakashi Sakamoto opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
27244aff698STakashi Sakamoto opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
27344aff698STakashi Sakamoto opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
27444aff698STakashi Sakamoto
27544aff698STakashi Sakamoto return opcr;
27644aff698STakashi Sakamoto }
27744aff698STakashi Sakamoto
pcr_set_check(struct cmp_connection * c,__be32 pcr)278a7fa0d04STakashi Sakamoto static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
27931ef9134SClemens Ladisch {
280a7fa0d04STakashi Sakamoto if (pcr & cpu_to_be32(PCR_BCAST_CONN |
281a7fa0d04STakashi Sakamoto PCR_P2P_CONN_MASK)) {
28231ef9134SClemens Ladisch cmp_error(c, "plug is already in use\n");
28331ef9134SClemens Ladisch return -EBUSY;
28431ef9134SClemens Ladisch }
285a7fa0d04STakashi Sakamoto if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
28631ef9134SClemens Ladisch cmp_error(c, "plug is not on-line\n");
28731ef9134SClemens Ladisch return -ECONNREFUSED;
28831ef9134SClemens Ladisch }
28931ef9134SClemens Ladisch
29031ef9134SClemens Ladisch return 0;
29131ef9134SClemens Ladisch }
29231ef9134SClemens Ladisch
29331ef9134SClemens Ladisch /**
29431ef9134SClemens Ladisch * cmp_connection_establish - establish a connection to the target
29531ef9134SClemens Ladisch * @c: the connection manager
29631ef9134SClemens Ladisch *
29731ef9134SClemens Ladisch * This function establishes a point-to-point connection from the local
29831ef9134SClemens Ladisch * computer to the target by allocating isochronous resources (channel and
299a7fa0d04STakashi Sakamoto * bandwidth) and setting the target's input/output plug control register.
300a7fa0d04STakashi Sakamoto * When this function succeeds, the caller is responsible for starting
301a7fa0d04STakashi Sakamoto * transmitting packets.
30231ef9134SClemens Ladisch */
cmp_connection_establish(struct cmp_connection * c)303*7bc93821STakashi Sakamoto int cmp_connection_establish(struct cmp_connection *c)
30431ef9134SClemens Ladisch {
30531ef9134SClemens Ladisch int err;
30631ef9134SClemens Ladisch
30731ef9134SClemens Ladisch mutex_lock(&c->mutex);
30831ef9134SClemens Ladisch
309*7bc93821STakashi Sakamoto if (WARN_ON(c->connected)) {
310*7bc93821STakashi Sakamoto mutex_unlock(&c->mutex);
311*7bc93821STakashi Sakamoto return -EISCONN;
312*7bc93821STakashi Sakamoto }
31331ef9134SClemens Ladisch
314*7bc93821STakashi Sakamoto retry_after_bus_reset:
31544aff698STakashi Sakamoto if (c->direction == CMP_OUTPUT)
31644aff698STakashi Sakamoto err = pcr_modify(c, opcr_set_modify, pcr_set_check,
31744aff698STakashi Sakamoto ABORT_ON_BUS_RESET);
31844aff698STakashi Sakamoto else
319a7fa0d04STakashi Sakamoto err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
32031ef9134SClemens Ladisch ABORT_ON_BUS_RESET);
32144aff698STakashi Sakamoto
32231ef9134SClemens Ladisch if (err == -EAGAIN) {
323*7bc93821STakashi Sakamoto err = fw_iso_resources_update(&c->resources);
324*7bc93821STakashi Sakamoto if (err >= 0)
32531ef9134SClemens Ladisch goto retry_after_bus_reset;
32631ef9134SClemens Ladisch }
327*7bc93821STakashi Sakamoto if (err >= 0)
32831ef9134SClemens Ladisch c->connected = true;
32931ef9134SClemens Ladisch
33031ef9134SClemens Ladisch mutex_unlock(&c->mutex);
33131ef9134SClemens Ladisch
33231ef9134SClemens Ladisch return err;
33331ef9134SClemens Ladisch }
33431ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_establish);
33531ef9134SClemens Ladisch
33631ef9134SClemens Ladisch /**
33731ef9134SClemens Ladisch * cmp_connection_update - update the connection after a bus reset
33831ef9134SClemens Ladisch * @c: the connection manager
33931ef9134SClemens Ladisch *
340a7fa0d04STakashi Sakamoto * This function must be called from the driver's .update handler to
341a7fa0d04STakashi Sakamoto * reestablish any connection that might have been active.
34231ef9134SClemens Ladisch *
34331ef9134SClemens Ladisch * Returns zero on success, or a negative error code. On an error, the
34431ef9134SClemens Ladisch * connection is broken and the caller must stop transmitting iso packets.
34531ef9134SClemens Ladisch */
cmp_connection_update(struct cmp_connection * c)34631ef9134SClemens Ladisch int cmp_connection_update(struct cmp_connection *c)
34731ef9134SClemens Ladisch {
34831ef9134SClemens Ladisch int err;
34931ef9134SClemens Ladisch
35031ef9134SClemens Ladisch mutex_lock(&c->mutex);
35131ef9134SClemens Ladisch
35231ef9134SClemens Ladisch if (!c->connected) {
35331ef9134SClemens Ladisch mutex_unlock(&c->mutex);
35431ef9134SClemens Ladisch return 0;
35531ef9134SClemens Ladisch }
35631ef9134SClemens Ladisch
35731ef9134SClemens Ladisch err = fw_iso_resources_update(&c->resources);
35831ef9134SClemens Ladisch if (err < 0)
35931ef9134SClemens Ladisch goto err_unconnect;
36031ef9134SClemens Ladisch
36144aff698STakashi Sakamoto if (c->direction == CMP_OUTPUT)
36244aff698STakashi Sakamoto err = pcr_modify(c, opcr_set_modify, pcr_set_check,
36344aff698STakashi Sakamoto SUCCEED_ON_BUS_RESET);
36444aff698STakashi Sakamoto else
365a7fa0d04STakashi Sakamoto err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
36631ef9134SClemens Ladisch SUCCEED_ON_BUS_RESET);
36744aff698STakashi Sakamoto
36831ef9134SClemens Ladisch if (err < 0)
369*7bc93821STakashi Sakamoto goto err_unconnect;
37031ef9134SClemens Ladisch
37131ef9134SClemens Ladisch mutex_unlock(&c->mutex);
37231ef9134SClemens Ladisch
37331ef9134SClemens Ladisch return 0;
37431ef9134SClemens Ladisch
37531ef9134SClemens Ladisch err_unconnect:
37631ef9134SClemens Ladisch c->connected = false;
37731ef9134SClemens Ladisch mutex_unlock(&c->mutex);
37831ef9134SClemens Ladisch
37931ef9134SClemens Ladisch return err;
38031ef9134SClemens Ladisch }
38131ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_update);
38231ef9134SClemens Ladisch
pcr_break_modify(struct cmp_connection * c,__be32 pcr)383a7fa0d04STakashi Sakamoto static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
38431ef9134SClemens Ladisch {
385a7fa0d04STakashi Sakamoto return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
38631ef9134SClemens Ladisch }
38731ef9134SClemens Ladisch
38831ef9134SClemens Ladisch /**
38931ef9134SClemens Ladisch * cmp_connection_break - break the connection to the target
39031ef9134SClemens Ladisch * @c: the connection manager
39131ef9134SClemens Ladisch *
392a7fa0d04STakashi Sakamoto * This function deactives the connection in the target's input/output plug
393a7fa0d04STakashi Sakamoto * control register, and frees the isochronous resources of the connection.
394a7fa0d04STakashi Sakamoto * Before calling this function, the caller should cease transmitting packets.
39531ef9134SClemens Ladisch */
cmp_connection_break(struct cmp_connection * c)39631ef9134SClemens Ladisch void cmp_connection_break(struct cmp_connection *c)
39731ef9134SClemens Ladisch {
39831ef9134SClemens Ladisch int err;
39931ef9134SClemens Ladisch
40031ef9134SClemens Ladisch mutex_lock(&c->mutex);
40131ef9134SClemens Ladisch
40231ef9134SClemens Ladisch if (!c->connected) {
40331ef9134SClemens Ladisch mutex_unlock(&c->mutex);
40431ef9134SClemens Ladisch return;
40531ef9134SClemens Ladisch }
40631ef9134SClemens Ladisch
407a7fa0d04STakashi Sakamoto err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
40831ef9134SClemens Ladisch if (err < 0)
40931ef9134SClemens Ladisch cmp_error(c, "plug is still connected\n");
41031ef9134SClemens Ladisch
41131ef9134SClemens Ladisch c->connected = false;
41231ef9134SClemens Ladisch
41331ef9134SClemens Ladisch mutex_unlock(&c->mutex);
41431ef9134SClemens Ladisch }
41531ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_break);
416