18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Greybus interface code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2014 Google Inc.
68c2ecf20Sopenharmony_ci * Copyright 2014 Linaro Ltd.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/greybus.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "greybus_trace.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define GB_INTERFACE_MODE_SWITCH_TIMEOUT	2000
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define GB_INTERFACE_DEVICE_ID_BAD	0xff
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define GB_INTERFACE_AUTOSUSPEND_MS			3000
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* Time required for interface to enter standby before disabling REFCLK */
218c2ecf20Sopenharmony_ci#define GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS			20
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Don't-care selector index */
248c2ecf20Sopenharmony_ci#define DME_SELECTOR_INDEX_NULL		0
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* DME attributes */
278c2ecf20Sopenharmony_ci/* FIXME: remove ES2 support and DME_T_TST_SRC_INCREMENT */
288c2ecf20Sopenharmony_ci#define DME_T_TST_SRC_INCREMENT		0x4083
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define DME_DDBL1_MANUFACTURERID	0x5003
318c2ecf20Sopenharmony_ci#define DME_DDBL1_PRODUCTID		0x5004
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define DME_TOSHIBA_GMP_VID		0x6000
348c2ecf20Sopenharmony_ci#define DME_TOSHIBA_GMP_PID		0x6001
358c2ecf20Sopenharmony_ci#define DME_TOSHIBA_GMP_SN0		0x6002
368c2ecf20Sopenharmony_ci#define DME_TOSHIBA_GMP_SN1		0x6003
378c2ecf20Sopenharmony_ci#define DME_TOSHIBA_GMP_INIT_STATUS	0x6101
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* DDBL1 Manufacturer and Product ids */
408c2ecf20Sopenharmony_ci#define TOSHIBA_DMID			0x0126
418c2ecf20Sopenharmony_ci#define TOSHIBA_ES2_BRIDGE_DPID		0x1000
428c2ecf20Sopenharmony_ci#define TOSHIBA_ES3_APBRIDGE_DPID	0x1001
438c2ecf20Sopenharmony_ci#define TOSHIBA_ES3_GBPHY_DPID	0x1002
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int gb_interface_hibernate_link(struct gb_interface *intf);
468c2ecf20Sopenharmony_cistatic int gb_interface_refclk_set(struct gb_interface *intf, bool enable);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int gb_interface_dme_attr_get(struct gb_interface *intf,
498c2ecf20Sopenharmony_ci				     u16 attr, u32 *val)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return gb_svc_dme_peer_get(intf->hd->svc, intf->interface_id,
528c2ecf20Sopenharmony_ci					attr, DME_SELECTOR_INDEX_NULL, val);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int gb_interface_read_ara_dme(struct gb_interface *intf)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	u32 sn0, sn1;
588c2ecf20Sopenharmony_ci	int ret;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * Unless this is a Toshiba bridge, bail out until we have defined
628c2ecf20Sopenharmony_ci	 * standard GMP attributes.
638c2ecf20Sopenharmony_ci	 */
648c2ecf20Sopenharmony_ci	if (intf->ddbl1_manufacturer_id != TOSHIBA_DMID) {
658c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "unknown manufacturer %08x\n",
668c2ecf20Sopenharmony_ci			intf->ddbl1_manufacturer_id);
678c2ecf20Sopenharmony_ci		return -ENODEV;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_VID,
718c2ecf20Sopenharmony_ci					&intf->vendor_id);
728c2ecf20Sopenharmony_ci	if (ret)
738c2ecf20Sopenharmony_ci		return ret;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_PID,
768c2ecf20Sopenharmony_ci					&intf->product_id);
778c2ecf20Sopenharmony_ci	if (ret)
788c2ecf20Sopenharmony_ci		return ret;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN0, &sn0);
818c2ecf20Sopenharmony_ci	if (ret)
828c2ecf20Sopenharmony_ci		return ret;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN1, &sn1);
858c2ecf20Sopenharmony_ci	if (ret)
868c2ecf20Sopenharmony_ci		return ret;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	intf->serial_number = (u64)sn1 << 32 | sn0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return 0;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int gb_interface_read_dme(struct gb_interface *intf)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int ret;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* DME attributes have already been read */
988c2ecf20Sopenharmony_ci	if (intf->dme_read)
998c2ecf20Sopenharmony_ci		return 0;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = gb_interface_dme_attr_get(intf, DME_DDBL1_MANUFACTURERID,
1028c2ecf20Sopenharmony_ci					&intf->ddbl1_manufacturer_id);
1038c2ecf20Sopenharmony_ci	if (ret)
1048c2ecf20Sopenharmony_ci		return ret;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = gb_interface_dme_attr_get(intf, DME_DDBL1_PRODUCTID,
1078c2ecf20Sopenharmony_ci					&intf->ddbl1_product_id);
1088c2ecf20Sopenharmony_ci	if (ret)
1098c2ecf20Sopenharmony_ci		return ret;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (intf->ddbl1_manufacturer_id == TOSHIBA_DMID &&
1128c2ecf20Sopenharmony_ci	    intf->ddbl1_product_id == TOSHIBA_ES2_BRIDGE_DPID) {
1138c2ecf20Sopenharmony_ci		intf->quirks |= GB_INTERFACE_QUIRK_NO_GMP_IDS;
1148c2ecf20Sopenharmony_ci		intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	ret = gb_interface_read_ara_dme(intf);
1188c2ecf20Sopenharmony_ci	if (ret)
1198c2ecf20Sopenharmony_ci		return ret;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	intf->dme_read = true;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int gb_interface_route_create(struct gb_interface *intf)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
1298c2ecf20Sopenharmony_ci	u8 intf_id = intf->interface_id;
1308c2ecf20Sopenharmony_ci	u8 device_id;
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Allocate an interface device id. */
1348c2ecf20Sopenharmony_ci	ret = ida_simple_get(&svc->device_id_map,
1358c2ecf20Sopenharmony_ci			     GB_SVC_DEVICE_ID_MIN, GB_SVC_DEVICE_ID_MAX + 1,
1368c2ecf20Sopenharmony_ci			     GFP_KERNEL);
1378c2ecf20Sopenharmony_ci	if (ret < 0) {
1388c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to allocate device id: %d\n", ret);
1398c2ecf20Sopenharmony_ci		return ret;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	device_id = ret;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ret = gb_svc_intf_device_id(svc, intf_id, device_id);
1448c2ecf20Sopenharmony_ci	if (ret) {
1458c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to set device id %u: %d\n",
1468c2ecf20Sopenharmony_ci			device_id, ret);
1478c2ecf20Sopenharmony_ci		goto err_ida_remove;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* FIXME: Hard-coded AP device id. */
1518c2ecf20Sopenharmony_ci	ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_SVC_DEVICE_ID_AP,
1528c2ecf20Sopenharmony_ci				  intf_id, device_id);
1538c2ecf20Sopenharmony_ci	if (ret) {
1548c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to create route: %d\n", ret);
1558c2ecf20Sopenharmony_ci		goto err_svc_id_free;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	intf->device_id = device_id;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cierr_svc_id_free:
1638c2ecf20Sopenharmony_ci	/*
1648c2ecf20Sopenharmony_ci	 * XXX Should we tell SVC that this id doesn't belong to interface
1658c2ecf20Sopenharmony_ci	 * XXX anymore.
1668c2ecf20Sopenharmony_ci	 */
1678c2ecf20Sopenharmony_cierr_ida_remove:
1688c2ecf20Sopenharmony_ci	ida_simple_remove(&svc->device_id_map, device_id);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return ret;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void gb_interface_route_destroy(struct gb_interface *intf)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (intf->device_id == GB_INTERFACE_DEVICE_ID_BAD)
1788c2ecf20Sopenharmony_ci		return;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id);
1818c2ecf20Sopenharmony_ci	ida_simple_remove(&svc->device_id_map, intf->device_id);
1828c2ecf20Sopenharmony_ci	intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/* Locking: Caller holds the interface mutex. */
1868c2ecf20Sopenharmony_cistatic int gb_interface_legacy_mode_switch(struct gb_interface *intf)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int ret;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "legacy mode switch detected\n");
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Mark as disconnected to prevent I/O during disable. */
1938c2ecf20Sopenharmony_ci	intf->disconnected = true;
1948c2ecf20Sopenharmony_ci	gb_interface_disable(intf);
1958c2ecf20Sopenharmony_ci	intf->disconnected = false;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = gb_interface_enable(intf);
1988c2ecf20Sopenharmony_ci	if (ret) {
1998c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to re-enable interface: %d\n", ret);
2008c2ecf20Sopenharmony_ci		gb_interface_deactivate(intf);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ret;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_civoid gb_interface_mailbox_event(struct gb_interface *intf, u16 result,
2078c2ecf20Sopenharmony_ci				u32 mailbox)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (result) {
2128c2ecf20Sopenharmony_ci		dev_warn(&intf->dev,
2138c2ecf20Sopenharmony_ci			 "mailbox event with UniPro error: 0x%04x\n",
2148c2ecf20Sopenharmony_ci			 result);
2158c2ecf20Sopenharmony_ci		goto err_disable;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (mailbox != GB_SVC_INTF_MAILBOX_GREYBUS) {
2198c2ecf20Sopenharmony_ci		dev_warn(&intf->dev,
2208c2ecf20Sopenharmony_ci			 "mailbox event with unexpected value: 0x%08x\n",
2218c2ecf20Sopenharmony_ci			 mailbox);
2228c2ecf20Sopenharmony_ci		goto err_disable;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (intf->quirks & GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH) {
2268c2ecf20Sopenharmony_ci		gb_interface_legacy_mode_switch(intf);
2278c2ecf20Sopenharmony_ci		goto out_unlock;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (!intf->mode_switch) {
2318c2ecf20Sopenharmony_ci		dev_warn(&intf->dev, "unexpected mailbox event: 0x%08x\n",
2328c2ecf20Sopenharmony_ci			 mailbox);
2338c2ecf20Sopenharmony_ci		goto err_disable;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "mode switch detected\n");
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	complete(&intf->mode_switch_completion);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciout_unlock:
2418c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cierr_disable:
2468c2ecf20Sopenharmony_ci	gb_interface_disable(intf);
2478c2ecf20Sopenharmony_ci	gb_interface_deactivate(intf);
2488c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic void gb_interface_mode_switch_work(struct work_struct *work)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct gb_interface *intf;
2548c2ecf20Sopenharmony_ci	struct gb_control *control;
2558c2ecf20Sopenharmony_ci	unsigned long timeout;
2568c2ecf20Sopenharmony_ci	int ret;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	intf = container_of(work, struct gb_interface, mode_switch_work);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
2618c2ecf20Sopenharmony_ci	/* Make sure interface is still enabled. */
2628c2ecf20Sopenharmony_ci	if (!intf->enabled) {
2638c2ecf20Sopenharmony_ci		dev_dbg(&intf->dev, "mode switch aborted\n");
2648c2ecf20Sopenharmony_ci		intf->mode_switch = false;
2658c2ecf20Sopenharmony_ci		mutex_unlock(&intf->mutex);
2668c2ecf20Sopenharmony_ci		goto out_interface_put;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/*
2708c2ecf20Sopenharmony_ci	 * Prepare the control device for mode switch and make sure to get an
2718c2ecf20Sopenharmony_ci	 * extra reference before it goes away during interface disable.
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	control = gb_control_get(intf->control);
2748c2ecf20Sopenharmony_ci	gb_control_mode_switch_prepare(control);
2758c2ecf20Sopenharmony_ci	gb_interface_disable(intf);
2768c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	timeout = msecs_to_jiffies(GB_INTERFACE_MODE_SWITCH_TIMEOUT);
2798c2ecf20Sopenharmony_ci	ret = wait_for_completion_interruptible_timeout(
2808c2ecf20Sopenharmony_ci			&intf->mode_switch_completion, timeout);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* Finalise control-connection mode switch. */
2838c2ecf20Sopenharmony_ci	gb_control_mode_switch_complete(control);
2848c2ecf20Sopenharmony_ci	gb_control_put(control);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (ret < 0) {
2878c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "mode switch interrupted\n");
2888c2ecf20Sopenharmony_ci		goto err_deactivate;
2898c2ecf20Sopenharmony_ci	} else if (ret == 0) {
2908c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "mode switch timed out\n");
2918c2ecf20Sopenharmony_ci		goto err_deactivate;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* Re-enable (re-enumerate) interface if still active. */
2958c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
2968c2ecf20Sopenharmony_ci	intf->mode_switch = false;
2978c2ecf20Sopenharmony_ci	if (intf->active) {
2988c2ecf20Sopenharmony_ci		ret = gb_interface_enable(intf);
2998c2ecf20Sopenharmony_ci		if (ret) {
3008c2ecf20Sopenharmony_ci			dev_err(&intf->dev, "failed to re-enable interface: %d\n",
3018c2ecf20Sopenharmony_ci				ret);
3028c2ecf20Sopenharmony_ci			gb_interface_deactivate(intf);
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ciout_interface_put:
3088c2ecf20Sopenharmony_ci	gb_interface_put(intf);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cierr_deactivate:
3138c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
3148c2ecf20Sopenharmony_ci	intf->mode_switch = false;
3158c2ecf20Sopenharmony_ci	gb_interface_deactivate(intf);
3168c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	gb_interface_put(intf);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ciint gb_interface_request_mode_switch(struct gb_interface *intf)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	int ret = 0;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
3268c2ecf20Sopenharmony_ci	if (intf->mode_switch) {
3278c2ecf20Sopenharmony_ci		ret = -EBUSY;
3288c2ecf20Sopenharmony_ci		goto out_unlock;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	intf->mode_switch = true;
3328c2ecf20Sopenharmony_ci	reinit_completion(&intf->mode_switch_completion);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/*
3358c2ecf20Sopenharmony_ci	 * Get a reference to the interface device, which will be put once the
3368c2ecf20Sopenharmony_ci	 * mode switch is complete.
3378c2ecf20Sopenharmony_ci	 */
3388c2ecf20Sopenharmony_ci	get_device(&intf->dev);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (!queue_work(system_long_wq, &intf->mode_switch_work)) {
3418c2ecf20Sopenharmony_ci		put_device(&intf->dev);
3428c2ecf20Sopenharmony_ci		ret = -EBUSY;
3438c2ecf20Sopenharmony_ci		goto out_unlock;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ciout_unlock:
3478c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return ret;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_interface_request_mode_switch);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/*
3548c2ecf20Sopenharmony_ci * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the
3558c2ecf20Sopenharmony_ci * init-status attribute DME_TOSHIBA_INIT_STATUS. The AP needs to read and
3568c2ecf20Sopenharmony_ci * clear it after reading a non-zero value from it.
3578c2ecf20Sopenharmony_ci *
3588c2ecf20Sopenharmony_ci * FIXME: This is module-hardware dependent and needs to be extended for every
3598c2ecf20Sopenharmony_ci * type of module we want to support.
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_cistatic int gb_interface_read_and_clear_init_status(struct gb_interface *intf)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct gb_host_device *hd = intf->hd;
3648c2ecf20Sopenharmony_ci	unsigned long bootrom_quirks;
3658c2ecf20Sopenharmony_ci	unsigned long s2l_quirks;
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci	u32 value;
3688c2ecf20Sopenharmony_ci	u16 attr;
3698c2ecf20Sopenharmony_ci	u8 init_status;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/*
3728c2ecf20Sopenharmony_ci	 * ES2 bridges use T_TstSrcIncrement for the init status.
3738c2ecf20Sopenharmony_ci	 *
3748c2ecf20Sopenharmony_ci	 * FIXME: Remove ES2 support
3758c2ecf20Sopenharmony_ci	 */
3768c2ecf20Sopenharmony_ci	if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS)
3778c2ecf20Sopenharmony_ci		attr = DME_T_TST_SRC_INCREMENT;
3788c2ecf20Sopenharmony_ci	else
3798c2ecf20Sopenharmony_ci		attr = DME_TOSHIBA_GMP_INIT_STATUS;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr,
3828c2ecf20Sopenharmony_ci				  DME_SELECTOR_INDEX_NULL, &value);
3838c2ecf20Sopenharmony_ci	if (ret)
3848c2ecf20Sopenharmony_ci		return ret;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/*
3878c2ecf20Sopenharmony_ci	 * A nonzero init status indicates the module has finished
3888c2ecf20Sopenharmony_ci	 * initializing.
3898c2ecf20Sopenharmony_ci	 */
3908c2ecf20Sopenharmony_ci	if (!value) {
3918c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "invalid init status\n");
3928c2ecf20Sopenharmony_ci		return -ENODEV;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/*
3968c2ecf20Sopenharmony_ci	 * Extract the init status.
3978c2ecf20Sopenharmony_ci	 *
3988c2ecf20Sopenharmony_ci	 * For ES2: We need to check lowest 8 bits of 'value'.
3998c2ecf20Sopenharmony_ci	 * For ES3: We need to check highest 8 bits out of 32 of 'value'.
4008c2ecf20Sopenharmony_ci	 *
4018c2ecf20Sopenharmony_ci	 * FIXME: Remove ES2 support
4028c2ecf20Sopenharmony_ci	 */
4038c2ecf20Sopenharmony_ci	if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS)
4048c2ecf20Sopenharmony_ci		init_status = value & 0xff;
4058c2ecf20Sopenharmony_ci	else
4068c2ecf20Sopenharmony_ci		init_status = value >> 24;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/*
4098c2ecf20Sopenharmony_ci	 * Check if the interface is executing the quirky ES3 bootrom that,
4108c2ecf20Sopenharmony_ci	 * for example, requires E2EFC, CSD and CSV to be disabled.
4118c2ecf20Sopenharmony_ci	 */
4128c2ecf20Sopenharmony_ci	bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES |
4138c2ecf20Sopenharmony_ci				GB_INTERFACE_QUIRK_FORCED_DISABLE |
4148c2ecf20Sopenharmony_ci				GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH |
4158c2ecf20Sopenharmony_ci				GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	s2l_quirks = GB_INTERFACE_QUIRK_NO_PM;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	switch (init_status) {
4208c2ecf20Sopenharmony_ci	case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED:
4218c2ecf20Sopenharmony_ci	case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED:
4228c2ecf20Sopenharmony_ci		intf->quirks |= bootrom_quirks;
4238c2ecf20Sopenharmony_ci		break;
4248c2ecf20Sopenharmony_ci	case GB_INIT_S2_LOADER_BOOT_STARTED:
4258c2ecf20Sopenharmony_ci		/* S2 Loader doesn't support runtime PM */
4268c2ecf20Sopenharmony_ci		intf->quirks &= ~bootrom_quirks;
4278c2ecf20Sopenharmony_ci		intf->quirks |= s2l_quirks;
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	default:
4308c2ecf20Sopenharmony_ci		intf->quirks &= ~bootrom_quirks;
4318c2ecf20Sopenharmony_ci		intf->quirks &= ~s2l_quirks;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* Clear the init status. */
4358c2ecf20Sopenharmony_ci	return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr,
4368c2ecf20Sopenharmony_ci				   DME_SELECTOR_INDEX_NULL, 0);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/* interface sysfs attributes */
4408c2ecf20Sopenharmony_ci#define gb_interface_attr(field, type)					\
4418c2ecf20Sopenharmony_cistatic ssize_t field##_show(struct device *dev,				\
4428c2ecf20Sopenharmony_ci			    struct device_attribute *attr,		\
4438c2ecf20Sopenharmony_ci			    char *buf)					\
4448c2ecf20Sopenharmony_ci{									\
4458c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);		\
4468c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, type"\n", intf->field);	\
4478c2ecf20Sopenharmony_ci}									\
4488c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field)
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cigb_interface_attr(ddbl1_manufacturer_id, "0x%08x");
4518c2ecf20Sopenharmony_cigb_interface_attr(ddbl1_product_id, "0x%08x");
4528c2ecf20Sopenharmony_cigb_interface_attr(interface_id, "%u");
4538c2ecf20Sopenharmony_cigb_interface_attr(vendor_id, "0x%08x");
4548c2ecf20Sopenharmony_cigb_interface_attr(product_id, "0x%08x");
4558c2ecf20Sopenharmony_cigb_interface_attr(serial_number, "0x%016llx");
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic ssize_t voltage_now_show(struct device *dev,
4588c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
4618c2ecf20Sopenharmony_ci	int ret;
4628c2ecf20Sopenharmony_ci	u32 measurement;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id,
4658c2ecf20Sopenharmony_ci					    GB_SVC_PWRMON_TYPE_VOL,
4668c2ecf20Sopenharmony_ci					    &measurement);
4678c2ecf20Sopenharmony_ci	if (ret) {
4688c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to get voltage sample (%d)\n", ret);
4698c2ecf20Sopenharmony_ci		return ret;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", measurement);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(voltage_now);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic ssize_t current_now_show(struct device *dev,
4778c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
4808c2ecf20Sopenharmony_ci	int ret;
4818c2ecf20Sopenharmony_ci	u32 measurement;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id,
4848c2ecf20Sopenharmony_ci					    GB_SVC_PWRMON_TYPE_CURR,
4858c2ecf20Sopenharmony_ci					    &measurement);
4868c2ecf20Sopenharmony_ci	if (ret) {
4878c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to get current sample (%d)\n", ret);
4888c2ecf20Sopenharmony_ci		return ret;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", measurement);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(current_now);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic ssize_t power_now_show(struct device *dev,
4968c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buf)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
4998c2ecf20Sopenharmony_ci	int ret;
5008c2ecf20Sopenharmony_ci	u32 measurement;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id,
5038c2ecf20Sopenharmony_ci					    GB_SVC_PWRMON_TYPE_PWR,
5048c2ecf20Sopenharmony_ci					    &measurement);
5058c2ecf20Sopenharmony_ci	if (ret) {
5068c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to get power sample (%d)\n", ret);
5078c2ecf20Sopenharmony_ci		return ret;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", measurement);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power_now);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic ssize_t power_state_show(struct device *dev,
5158c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (intf->active)
5208c2ecf20Sopenharmony_ci		return scnprintf(buf, PAGE_SIZE, "on\n");
5218c2ecf20Sopenharmony_ci	else
5228c2ecf20Sopenharmony_ci		return scnprintf(buf, PAGE_SIZE, "off\n");
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic ssize_t power_state_store(struct device *dev,
5268c2ecf20Sopenharmony_ci				 struct device_attribute *attr, const char *buf,
5278c2ecf20Sopenharmony_ci				 size_t len)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
5308c2ecf20Sopenharmony_ci	bool activate;
5318c2ecf20Sopenharmony_ci	int ret = 0;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (kstrtobool(buf, &activate))
5348c2ecf20Sopenharmony_ci		return -EINVAL;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	mutex_lock(&intf->mutex);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (activate == intf->active)
5398c2ecf20Sopenharmony_ci		goto unlock;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (activate) {
5428c2ecf20Sopenharmony_ci		ret = gb_interface_activate(intf);
5438c2ecf20Sopenharmony_ci		if (ret) {
5448c2ecf20Sopenharmony_ci			dev_err(&intf->dev,
5458c2ecf20Sopenharmony_ci				"failed to activate interface: %d\n", ret);
5468c2ecf20Sopenharmony_ci			goto unlock;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		ret = gb_interface_enable(intf);
5508c2ecf20Sopenharmony_ci		if (ret) {
5518c2ecf20Sopenharmony_ci			dev_err(&intf->dev,
5528c2ecf20Sopenharmony_ci				"failed to enable interface: %d\n", ret);
5538c2ecf20Sopenharmony_ci			gb_interface_deactivate(intf);
5548c2ecf20Sopenharmony_ci			goto unlock;
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci	} else {
5578c2ecf20Sopenharmony_ci		gb_interface_disable(intf);
5588c2ecf20Sopenharmony_ci		gb_interface_deactivate(intf);
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ciunlock:
5628c2ecf20Sopenharmony_ci	mutex_unlock(&intf->mutex);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (ret)
5658c2ecf20Sopenharmony_ci		return ret;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	return len;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(power_state);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic const char *gb_interface_type_string(struct gb_interface *intf)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	static const char * const types[] = {
5748c2ecf20Sopenharmony_ci		[GB_INTERFACE_TYPE_INVALID] = "invalid",
5758c2ecf20Sopenharmony_ci		[GB_INTERFACE_TYPE_UNKNOWN] = "unknown",
5768c2ecf20Sopenharmony_ci		[GB_INTERFACE_TYPE_DUMMY] = "dummy",
5778c2ecf20Sopenharmony_ci		[GB_INTERFACE_TYPE_UNIPRO] = "unipro",
5788c2ecf20Sopenharmony_ci		[GB_INTERFACE_TYPE_GREYBUS] = "greybus",
5798c2ecf20Sopenharmony_ci	};
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	return types[intf->type];
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic ssize_t interface_type_show(struct device *dev,
5858c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", gb_interface_type_string(intf));
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(interface_type);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic struct attribute *interface_unipro_attrs[] = {
5948c2ecf20Sopenharmony_ci	&dev_attr_ddbl1_manufacturer_id.attr,
5958c2ecf20Sopenharmony_ci	&dev_attr_ddbl1_product_id.attr,
5968c2ecf20Sopenharmony_ci	NULL
5978c2ecf20Sopenharmony_ci};
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic struct attribute *interface_greybus_attrs[] = {
6008c2ecf20Sopenharmony_ci	&dev_attr_vendor_id.attr,
6018c2ecf20Sopenharmony_ci	&dev_attr_product_id.attr,
6028c2ecf20Sopenharmony_ci	&dev_attr_serial_number.attr,
6038c2ecf20Sopenharmony_ci	NULL
6048c2ecf20Sopenharmony_ci};
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic struct attribute *interface_power_attrs[] = {
6078c2ecf20Sopenharmony_ci	&dev_attr_voltage_now.attr,
6088c2ecf20Sopenharmony_ci	&dev_attr_current_now.attr,
6098c2ecf20Sopenharmony_ci	&dev_attr_power_now.attr,
6108c2ecf20Sopenharmony_ci	&dev_attr_power_state.attr,
6118c2ecf20Sopenharmony_ci	NULL
6128c2ecf20Sopenharmony_ci};
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic struct attribute *interface_common_attrs[] = {
6158c2ecf20Sopenharmony_ci	&dev_attr_interface_id.attr,
6168c2ecf20Sopenharmony_ci	&dev_attr_interface_type.attr,
6178c2ecf20Sopenharmony_ci	NULL
6188c2ecf20Sopenharmony_ci};
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic umode_t interface_unipro_is_visible(struct kobject *kobj,
6218c2ecf20Sopenharmony_ci					   struct attribute *attr, int n)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
6248c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	switch (intf->type) {
6278c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_UNIPRO:
6288c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_GREYBUS:
6298c2ecf20Sopenharmony_ci		return attr->mode;
6308c2ecf20Sopenharmony_ci	default:
6318c2ecf20Sopenharmony_ci		return 0;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic umode_t interface_greybus_is_visible(struct kobject *kobj,
6368c2ecf20Sopenharmony_ci					    struct attribute *attr, int n)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
6398c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	switch (intf->type) {
6428c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_GREYBUS:
6438c2ecf20Sopenharmony_ci		return attr->mode;
6448c2ecf20Sopenharmony_ci	default:
6458c2ecf20Sopenharmony_ci		return 0;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic umode_t interface_power_is_visible(struct kobject *kobj,
6508c2ecf20Sopenharmony_ci					  struct attribute *attr, int n)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
6538c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	switch (intf->type) {
6568c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_UNIPRO:
6578c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_GREYBUS:
6588c2ecf20Sopenharmony_ci		return attr->mode;
6598c2ecf20Sopenharmony_ci	default:
6608c2ecf20Sopenharmony_ci		return 0;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic const struct attribute_group interface_unipro_group = {
6658c2ecf20Sopenharmony_ci	.is_visible	= interface_unipro_is_visible,
6668c2ecf20Sopenharmony_ci	.attrs		= interface_unipro_attrs,
6678c2ecf20Sopenharmony_ci};
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic const struct attribute_group interface_greybus_group = {
6708c2ecf20Sopenharmony_ci	.is_visible	= interface_greybus_is_visible,
6718c2ecf20Sopenharmony_ci	.attrs		= interface_greybus_attrs,
6728c2ecf20Sopenharmony_ci};
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic const struct attribute_group interface_power_group = {
6758c2ecf20Sopenharmony_ci	.is_visible	= interface_power_is_visible,
6768c2ecf20Sopenharmony_ci	.attrs		= interface_power_attrs,
6778c2ecf20Sopenharmony_ci};
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic const struct attribute_group interface_common_group = {
6808c2ecf20Sopenharmony_ci	.attrs		= interface_common_attrs,
6818c2ecf20Sopenharmony_ci};
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic const struct attribute_group *interface_groups[] = {
6848c2ecf20Sopenharmony_ci	&interface_unipro_group,
6858c2ecf20Sopenharmony_ci	&interface_greybus_group,
6868c2ecf20Sopenharmony_ci	&interface_power_group,
6878c2ecf20Sopenharmony_ci	&interface_common_group,
6888c2ecf20Sopenharmony_ci	NULL
6898c2ecf20Sopenharmony_ci};
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic void gb_interface_release(struct device *dev)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	trace_gb_interface_release(intf);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	cancel_work_sync(&intf->mode_switch_work);
6988c2ecf20Sopenharmony_ci	kfree(intf);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
7028c2ecf20Sopenharmony_cistatic int gb_interface_suspend(struct device *dev)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
7058c2ecf20Sopenharmony_ci	int ret;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	ret = gb_control_interface_suspend_prepare(intf->control);
7088c2ecf20Sopenharmony_ci	if (ret)
7098c2ecf20Sopenharmony_ci		return ret;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	ret = gb_control_suspend(intf->control);
7128c2ecf20Sopenharmony_ci	if (ret)
7138c2ecf20Sopenharmony_ci		goto err_hibernate_abort;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	ret = gb_interface_hibernate_link(intf);
7168c2ecf20Sopenharmony_ci	if (ret)
7178c2ecf20Sopenharmony_ci		return ret;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/* Delay to allow interface to enter standby before disabling refclk */
7208c2ecf20Sopenharmony_ci	msleep(GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	ret = gb_interface_refclk_set(intf, false);
7238c2ecf20Sopenharmony_ci	if (ret)
7248c2ecf20Sopenharmony_ci		return ret;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	return 0;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cierr_hibernate_abort:
7298c2ecf20Sopenharmony_ci	gb_control_interface_hibernate_abort(intf->control);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return ret;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic int gb_interface_resume(struct device *dev)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct gb_interface *intf = to_gb_interface(dev);
7378c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
7388c2ecf20Sopenharmony_ci	int ret;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	ret = gb_interface_refclk_set(intf, true);
7418c2ecf20Sopenharmony_ci	if (ret)
7428c2ecf20Sopenharmony_ci		return ret;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	ret = gb_svc_intf_resume(svc, intf->interface_id);
7458c2ecf20Sopenharmony_ci	if (ret)
7468c2ecf20Sopenharmony_ci		return ret;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	ret = gb_control_resume(intf->control);
7498c2ecf20Sopenharmony_ci	if (ret)
7508c2ecf20Sopenharmony_ci		return ret;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return 0;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic int gb_interface_runtime_idle(struct device *dev)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
7588c2ecf20Sopenharmony_ci	pm_request_autosuspend(dev);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return 0;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci#endif
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic const struct dev_pm_ops gb_interface_pm_ops = {
7658c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(gb_interface_suspend, gb_interface_resume,
7668c2ecf20Sopenharmony_ci			   gb_interface_runtime_idle)
7678c2ecf20Sopenharmony_ci};
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistruct device_type greybus_interface_type = {
7708c2ecf20Sopenharmony_ci	.name =		"greybus_interface",
7718c2ecf20Sopenharmony_ci	.release =	gb_interface_release,
7728c2ecf20Sopenharmony_ci	.pm =		&gb_interface_pm_ops,
7738c2ecf20Sopenharmony_ci};
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci/*
7768c2ecf20Sopenharmony_ci * A Greybus module represents a user-replaceable component on a GMP
7778c2ecf20Sopenharmony_ci * phone.  An interface is the physical connection on that module.  A
7788c2ecf20Sopenharmony_ci * module may have more than one interface.
7798c2ecf20Sopenharmony_ci *
7808c2ecf20Sopenharmony_ci * Create a gb_interface structure to represent a discovered interface.
7818c2ecf20Sopenharmony_ci * The position of interface within the Endo is encoded in "interface_id"
7828c2ecf20Sopenharmony_ci * argument.
7838c2ecf20Sopenharmony_ci *
7848c2ecf20Sopenharmony_ci * Returns a pointer to the new interfce or a null pointer if a
7858c2ecf20Sopenharmony_ci * failure occurs due to memory exhaustion.
7868c2ecf20Sopenharmony_ci */
7878c2ecf20Sopenharmony_cistruct gb_interface *gb_interface_create(struct gb_module *module,
7888c2ecf20Sopenharmony_ci					 u8 interface_id)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct gb_host_device *hd = module->hd;
7918c2ecf20Sopenharmony_ci	struct gb_interface *intf;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	intf = kzalloc(sizeof(*intf), GFP_KERNEL);
7948c2ecf20Sopenharmony_ci	if (!intf)
7958c2ecf20Sopenharmony_ci		return NULL;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	intf->hd = hd;		/* XXX refcount? */
7988c2ecf20Sopenharmony_ci	intf->module = module;
7998c2ecf20Sopenharmony_ci	intf->interface_id = interface_id;
8008c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->bundles);
8018c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->manifest_descs);
8028c2ecf20Sopenharmony_ci	mutex_init(&intf->mutex);
8038c2ecf20Sopenharmony_ci	INIT_WORK(&intf->mode_switch_work, gb_interface_mode_switch_work);
8048c2ecf20Sopenharmony_ci	init_completion(&intf->mode_switch_completion);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	/* Invalid device id to start with */
8078c2ecf20Sopenharmony_ci	intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	intf->dev.parent = &module->dev;
8108c2ecf20Sopenharmony_ci	intf->dev.bus = &greybus_bus_type;
8118c2ecf20Sopenharmony_ci	intf->dev.type = &greybus_interface_type;
8128c2ecf20Sopenharmony_ci	intf->dev.groups = interface_groups;
8138c2ecf20Sopenharmony_ci	intf->dev.dma_mask = module->dev.dma_mask;
8148c2ecf20Sopenharmony_ci	device_initialize(&intf->dev);
8158c2ecf20Sopenharmony_ci	dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev),
8168c2ecf20Sopenharmony_ci		     interface_id);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&intf->dev,
8198c2ecf20Sopenharmony_ci					 GB_INTERFACE_AUTOSUSPEND_MS);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	trace_gb_interface_create(intf);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return intf;
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic int gb_interface_vsys_set(struct gb_interface *intf, bool enable)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
8298c2ecf20Sopenharmony_ci	int ret;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	dev_dbg(&intf->dev, "%s - %d\n", __func__, enable);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	ret = gb_svc_intf_vsys_set(svc, intf->interface_id, enable);
8348c2ecf20Sopenharmony_ci	if (ret) {
8358c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to set v_sys: %d\n", ret);
8368c2ecf20Sopenharmony_ci		return ret;
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	return 0;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic int gb_interface_refclk_set(struct gb_interface *intf, bool enable)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
8458c2ecf20Sopenharmony_ci	int ret;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	dev_dbg(&intf->dev, "%s - %d\n", __func__, enable);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	ret = gb_svc_intf_refclk_set(svc, intf->interface_id, enable);
8508c2ecf20Sopenharmony_ci	if (ret) {
8518c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to set refclk: %d\n", ret);
8528c2ecf20Sopenharmony_ci		return ret;
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return 0;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic int gb_interface_unipro_set(struct gb_interface *intf, bool enable)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
8618c2ecf20Sopenharmony_ci	int ret;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	dev_dbg(&intf->dev, "%s - %d\n", __func__, enable);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	ret = gb_svc_intf_unipro_set(svc, intf->interface_id, enable);
8668c2ecf20Sopenharmony_ci	if (ret) {
8678c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to set UniPro: %d\n", ret);
8688c2ecf20Sopenharmony_ci		return ret;
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return 0;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic int gb_interface_activate_operation(struct gb_interface *intf,
8758c2ecf20Sopenharmony_ci					   enum gb_interface_type *intf_type)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
8788c2ecf20Sopenharmony_ci	u8 type;
8798c2ecf20Sopenharmony_ci	int ret;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	dev_dbg(&intf->dev, "%s\n", __func__);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	ret = gb_svc_intf_activate(svc, intf->interface_id, &type);
8848c2ecf20Sopenharmony_ci	if (ret) {
8858c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to activate: %d\n", ret);
8868c2ecf20Sopenharmony_ci		return ret;
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	switch (type) {
8908c2ecf20Sopenharmony_ci	case GB_SVC_INTF_TYPE_DUMMY:
8918c2ecf20Sopenharmony_ci		*intf_type = GB_INTERFACE_TYPE_DUMMY;
8928c2ecf20Sopenharmony_ci		/* FIXME: handle as an error for now */
8938c2ecf20Sopenharmony_ci		return -ENODEV;
8948c2ecf20Sopenharmony_ci	case GB_SVC_INTF_TYPE_UNIPRO:
8958c2ecf20Sopenharmony_ci		*intf_type = GB_INTERFACE_TYPE_UNIPRO;
8968c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "interface type UniPro not supported\n");
8978c2ecf20Sopenharmony_ci		/* FIXME: handle as an error for now */
8988c2ecf20Sopenharmony_ci		return -ENODEV;
8998c2ecf20Sopenharmony_ci	case GB_SVC_INTF_TYPE_GREYBUS:
9008c2ecf20Sopenharmony_ci		*intf_type = GB_INTERFACE_TYPE_GREYBUS;
9018c2ecf20Sopenharmony_ci		break;
9028c2ecf20Sopenharmony_ci	default:
9038c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "unknown interface type: %u\n", type);
9048c2ecf20Sopenharmony_ci		*intf_type = GB_INTERFACE_TYPE_UNKNOWN;
9058c2ecf20Sopenharmony_ci		return -ENODEV;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	return 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int gb_interface_hibernate_link(struct gb_interface *intf)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct gb_svc *svc = intf->hd->svc;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int _gb_interface_activate(struct gb_interface *intf,
9198c2ecf20Sopenharmony_ci				  enum gb_interface_type *type)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	int ret;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	*type = GB_INTERFACE_TYPE_UNKNOWN;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (intf->ejected || intf->removed)
9268c2ecf20Sopenharmony_ci		return -ENODEV;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	ret = gb_interface_vsys_set(intf, true);
9298c2ecf20Sopenharmony_ci	if (ret)
9308c2ecf20Sopenharmony_ci		return ret;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	ret = gb_interface_refclk_set(intf, true);
9338c2ecf20Sopenharmony_ci	if (ret)
9348c2ecf20Sopenharmony_ci		goto err_vsys_disable;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	ret = gb_interface_unipro_set(intf, true);
9378c2ecf20Sopenharmony_ci	if (ret)
9388c2ecf20Sopenharmony_ci		goto err_refclk_disable;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	ret = gb_interface_activate_operation(intf, type);
9418c2ecf20Sopenharmony_ci	if (ret) {
9428c2ecf20Sopenharmony_ci		switch (*type) {
9438c2ecf20Sopenharmony_ci		case GB_INTERFACE_TYPE_UNIPRO:
9448c2ecf20Sopenharmony_ci		case GB_INTERFACE_TYPE_GREYBUS:
9458c2ecf20Sopenharmony_ci			goto err_hibernate_link;
9468c2ecf20Sopenharmony_ci		default:
9478c2ecf20Sopenharmony_ci			goto err_unipro_disable;
9488c2ecf20Sopenharmony_ci		}
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	ret = gb_interface_read_dme(intf);
9528c2ecf20Sopenharmony_ci	if (ret)
9538c2ecf20Sopenharmony_ci		goto err_hibernate_link;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	ret = gb_interface_route_create(intf);
9568c2ecf20Sopenharmony_ci	if (ret)
9578c2ecf20Sopenharmony_ci		goto err_hibernate_link;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	intf->active = true;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	trace_gb_interface_activate(intf);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	return 0;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cierr_hibernate_link:
9668c2ecf20Sopenharmony_ci	gb_interface_hibernate_link(intf);
9678c2ecf20Sopenharmony_cierr_unipro_disable:
9688c2ecf20Sopenharmony_ci	gb_interface_unipro_set(intf, false);
9698c2ecf20Sopenharmony_cierr_refclk_disable:
9708c2ecf20Sopenharmony_ci	gb_interface_refclk_set(intf, false);
9718c2ecf20Sopenharmony_cierr_vsys_disable:
9728c2ecf20Sopenharmony_ci	gb_interface_vsys_set(intf, false);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	return ret;
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci/*
9788c2ecf20Sopenharmony_ci * At present, we assume a UniPro-only module to be a Greybus module that
9798c2ecf20Sopenharmony_ci * failed to send its mailbox poke. There is some reason to believe that this
9808c2ecf20Sopenharmony_ci * is because of a bug in the ES3 bootrom.
9818c2ecf20Sopenharmony_ci *
9828c2ecf20Sopenharmony_ci * FIXME: Check if this is a Toshiba bridge before retrying?
9838c2ecf20Sopenharmony_ci */
9848c2ecf20Sopenharmony_cistatic int _gb_interface_activate_es3_hack(struct gb_interface *intf,
9858c2ecf20Sopenharmony_ci					   enum gb_interface_type *type)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	int retries = 3;
9888c2ecf20Sopenharmony_ci	int ret;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	while (retries--) {
9918c2ecf20Sopenharmony_ci		ret = _gb_interface_activate(intf, type);
9928c2ecf20Sopenharmony_ci		if (ret == -ENODEV && *type == GB_INTERFACE_TYPE_UNIPRO)
9938c2ecf20Sopenharmony_ci			continue;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci		break;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	return ret;
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci/*
10028c2ecf20Sopenharmony_ci * Activate an interface.
10038c2ecf20Sopenharmony_ci *
10048c2ecf20Sopenharmony_ci * Locking: Caller holds the interface mutex.
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_ciint gb_interface_activate(struct gb_interface *intf)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	enum gb_interface_type type;
10098c2ecf20Sopenharmony_ci	int ret;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	switch (intf->type) {
10128c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_INVALID:
10138c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_GREYBUS:
10148c2ecf20Sopenharmony_ci		ret = _gb_interface_activate_es3_hack(intf, &type);
10158c2ecf20Sopenharmony_ci		break;
10168c2ecf20Sopenharmony_ci	default:
10178c2ecf20Sopenharmony_ci		ret = _gb_interface_activate(intf, &type);
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	/* Make sure type is detected correctly during reactivation. */
10218c2ecf20Sopenharmony_ci	if (intf->type != GB_INTERFACE_TYPE_INVALID) {
10228c2ecf20Sopenharmony_ci		if (type != intf->type) {
10238c2ecf20Sopenharmony_ci			dev_err(&intf->dev, "failed to detect interface type\n");
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci			if (!ret)
10268c2ecf20Sopenharmony_ci				gb_interface_deactivate(intf);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci			return -EIO;
10298c2ecf20Sopenharmony_ci		}
10308c2ecf20Sopenharmony_ci	} else {
10318c2ecf20Sopenharmony_ci		intf->type = type;
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	return ret;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/*
10388c2ecf20Sopenharmony_ci * Deactivate an interface.
10398c2ecf20Sopenharmony_ci *
10408c2ecf20Sopenharmony_ci * Locking: Caller holds the interface mutex.
10418c2ecf20Sopenharmony_ci */
10428c2ecf20Sopenharmony_civoid gb_interface_deactivate(struct gb_interface *intf)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	if (!intf->active)
10458c2ecf20Sopenharmony_ci		return;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	trace_gb_interface_deactivate(intf);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	/* Abort any ongoing mode switch. */
10508c2ecf20Sopenharmony_ci	if (intf->mode_switch)
10518c2ecf20Sopenharmony_ci		complete(&intf->mode_switch_completion);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	gb_interface_route_destroy(intf);
10548c2ecf20Sopenharmony_ci	gb_interface_hibernate_link(intf);
10558c2ecf20Sopenharmony_ci	gb_interface_unipro_set(intf, false);
10568c2ecf20Sopenharmony_ci	gb_interface_refclk_set(intf, false);
10578c2ecf20Sopenharmony_ci	gb_interface_vsys_set(intf, false);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	intf->active = false;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci/*
10638c2ecf20Sopenharmony_ci * Enable an interface by enabling its control connection, fetching the
10648c2ecf20Sopenharmony_ci * manifest and other information over it, and finally registering its child
10658c2ecf20Sopenharmony_ci * devices.
10668c2ecf20Sopenharmony_ci *
10678c2ecf20Sopenharmony_ci * Locking: Caller holds the interface mutex.
10688c2ecf20Sopenharmony_ci */
10698c2ecf20Sopenharmony_ciint gb_interface_enable(struct gb_interface *intf)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	struct gb_control *control;
10728c2ecf20Sopenharmony_ci	struct gb_bundle *bundle, *tmp;
10738c2ecf20Sopenharmony_ci	int ret, size;
10748c2ecf20Sopenharmony_ci	void *manifest;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	ret = gb_interface_read_and_clear_init_status(intf);
10778c2ecf20Sopenharmony_ci	if (ret) {
10788c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to clear init status: %d\n", ret);
10798c2ecf20Sopenharmony_ci		return ret;
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	/* Establish control connection */
10838c2ecf20Sopenharmony_ci	control = gb_control_create(intf);
10848c2ecf20Sopenharmony_ci	if (IS_ERR(control)) {
10858c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to create control device: %ld\n",
10868c2ecf20Sopenharmony_ci			PTR_ERR(control));
10878c2ecf20Sopenharmony_ci		return PTR_ERR(control);
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci	intf->control = control;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	ret = gb_control_enable(intf->control);
10928c2ecf20Sopenharmony_ci	if (ret)
10938c2ecf20Sopenharmony_ci		goto err_put_control;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/* Get manifest size using control protocol on CPort */
10968c2ecf20Sopenharmony_ci	size = gb_control_get_manifest_size_operation(intf);
10978c2ecf20Sopenharmony_ci	if (size <= 0) {
10988c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to get manifest size: %d\n", size);
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci		if (size)
11018c2ecf20Sopenharmony_ci			ret = size;
11028c2ecf20Sopenharmony_ci		else
11038c2ecf20Sopenharmony_ci			ret =  -EINVAL;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		goto err_disable_control;
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	manifest = kmalloc(size, GFP_KERNEL);
11098c2ecf20Sopenharmony_ci	if (!manifest) {
11108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11118c2ecf20Sopenharmony_ci		goto err_disable_control;
11128c2ecf20Sopenharmony_ci	}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* Get manifest using control protocol on CPort */
11158c2ecf20Sopenharmony_ci	ret = gb_control_get_manifest_operation(intf, manifest, size);
11168c2ecf20Sopenharmony_ci	if (ret) {
11178c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to get manifest: %d\n", ret);
11188c2ecf20Sopenharmony_ci		goto err_free_manifest;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/*
11228c2ecf20Sopenharmony_ci	 * Parse the manifest and build up our data structures representing
11238c2ecf20Sopenharmony_ci	 * what's in it.
11248c2ecf20Sopenharmony_ci	 */
11258c2ecf20Sopenharmony_ci	if (!gb_manifest_parse(intf, manifest, size)) {
11268c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to parse manifest\n");
11278c2ecf20Sopenharmony_ci		ret = -EINVAL;
11288c2ecf20Sopenharmony_ci		goto err_destroy_bundles;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	ret = gb_control_get_bundle_versions(intf->control);
11328c2ecf20Sopenharmony_ci	if (ret)
11338c2ecf20Sopenharmony_ci		goto err_destroy_bundles;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	/* Register the control device and any bundles */
11368c2ecf20Sopenharmony_ci	ret = gb_control_add(intf->control);
11378c2ecf20Sopenharmony_ci	if (ret)
11388c2ecf20Sopenharmony_ci		goto err_destroy_bundles;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(&intf->dev);
11418c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(&intf->dev);
11428c2ecf20Sopenharmony_ci	pm_runtime_set_active(&intf->dev);
11438c2ecf20Sopenharmony_ci	pm_runtime_enable(&intf->dev);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) {
11468c2ecf20Sopenharmony_ci		ret = gb_bundle_add(bundle);
11478c2ecf20Sopenharmony_ci		if (ret) {
11488c2ecf20Sopenharmony_ci			gb_bundle_destroy(bundle);
11498c2ecf20Sopenharmony_ci			continue;
11508c2ecf20Sopenharmony_ci		}
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	kfree(manifest);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	intf->enabled = true;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	pm_runtime_put(&intf->dev);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	trace_gb_interface_enable(intf);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	return 0;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cierr_destroy_bundles:
11648c2ecf20Sopenharmony_ci	list_for_each_entry_safe(bundle, tmp, &intf->bundles, links)
11658c2ecf20Sopenharmony_ci		gb_bundle_destroy(bundle);
11668c2ecf20Sopenharmony_cierr_free_manifest:
11678c2ecf20Sopenharmony_ci	kfree(manifest);
11688c2ecf20Sopenharmony_cierr_disable_control:
11698c2ecf20Sopenharmony_ci	gb_control_disable(intf->control);
11708c2ecf20Sopenharmony_cierr_put_control:
11718c2ecf20Sopenharmony_ci	gb_control_put(intf->control);
11728c2ecf20Sopenharmony_ci	intf->control = NULL;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	return ret;
11758c2ecf20Sopenharmony_ci}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci/*
11788c2ecf20Sopenharmony_ci * Disable an interface and destroy its bundles.
11798c2ecf20Sopenharmony_ci *
11808c2ecf20Sopenharmony_ci * Locking: Caller holds the interface mutex.
11818c2ecf20Sopenharmony_ci */
11828c2ecf20Sopenharmony_civoid gb_interface_disable(struct gb_interface *intf)
11838c2ecf20Sopenharmony_ci{
11848c2ecf20Sopenharmony_ci	struct gb_bundle *bundle;
11858c2ecf20Sopenharmony_ci	struct gb_bundle *next;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	if (!intf->enabled)
11888c2ecf20Sopenharmony_ci		return;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	trace_gb_interface_disable(intf);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&intf->dev);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/* Set disconnected flag to avoid I/O during connection tear down. */
11958c2ecf20Sopenharmony_ci	if (intf->quirks & GB_INTERFACE_QUIRK_FORCED_DISABLE)
11968c2ecf20Sopenharmony_ci		intf->disconnected = true;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	list_for_each_entry_safe(bundle, next, &intf->bundles, links)
11998c2ecf20Sopenharmony_ci		gb_bundle_destroy(bundle);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	if (!intf->mode_switch && !intf->disconnected)
12028c2ecf20Sopenharmony_ci		gb_control_interface_deactivate_prepare(intf->control);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	gb_control_del(intf->control);
12058c2ecf20Sopenharmony_ci	gb_control_disable(intf->control);
12068c2ecf20Sopenharmony_ci	gb_control_put(intf->control);
12078c2ecf20Sopenharmony_ci	intf->control = NULL;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	intf->enabled = false;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	pm_runtime_disable(&intf->dev);
12128c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(&intf->dev);
12138c2ecf20Sopenharmony_ci	pm_runtime_dont_use_autosuspend(&intf->dev);
12148c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(&intf->dev);
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci/* Register an interface. */
12188c2ecf20Sopenharmony_ciint gb_interface_add(struct gb_interface *intf)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	int ret;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	ret = device_add(&intf->dev);
12238c2ecf20Sopenharmony_ci	if (ret) {
12248c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to register interface: %d\n", ret);
12258c2ecf20Sopenharmony_ci		return ret;
12268c2ecf20Sopenharmony_ci	}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	trace_gb_interface_add(intf);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "Interface added (%s)\n",
12318c2ecf20Sopenharmony_ci		 gb_interface_type_string(intf));
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	switch (intf->type) {
12348c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_GREYBUS:
12358c2ecf20Sopenharmony_ci		dev_info(&intf->dev, "GMP VID=0x%08x, PID=0x%08x\n",
12368c2ecf20Sopenharmony_ci			 intf->vendor_id, intf->product_id);
12378c2ecf20Sopenharmony_ci		fallthrough;
12388c2ecf20Sopenharmony_ci	case GB_INTERFACE_TYPE_UNIPRO:
12398c2ecf20Sopenharmony_ci		dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n",
12408c2ecf20Sopenharmony_ci			 intf->ddbl1_manufacturer_id,
12418c2ecf20Sopenharmony_ci			 intf->ddbl1_product_id);
12428c2ecf20Sopenharmony_ci		break;
12438c2ecf20Sopenharmony_ci	default:
12448c2ecf20Sopenharmony_ci		break;
12458c2ecf20Sopenharmony_ci	}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	return 0;
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci/* Deregister an interface. */
12518c2ecf20Sopenharmony_civoid gb_interface_del(struct gb_interface *intf)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	if (device_is_registered(&intf->dev)) {
12548c2ecf20Sopenharmony_ci		trace_gb_interface_del(intf);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci		device_del(&intf->dev);
12578c2ecf20Sopenharmony_ci		dev_info(&intf->dev, "Interface removed\n");
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_civoid gb_interface_put(struct gb_interface *intf)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	put_device(&intf->dev);
12648c2ecf20Sopenharmony_ci}
1265