xref: /linux/drivers/greybus/interface.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
18465def4SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28465def4SGreg Kroah-Hartman /*
38465def4SGreg Kroah-Hartman  * Greybus interface code
48465def4SGreg Kroah-Hartman  *
58465def4SGreg Kroah-Hartman  * Copyright 2014 Google Inc.
68465def4SGreg Kroah-Hartman  * Copyright 2014 Linaro Ltd.
78465def4SGreg Kroah-Hartman  */
88465def4SGreg Kroah-Hartman 
98465def4SGreg Kroah-Hartman #include <linux/delay.h>
108465def4SGreg Kroah-Hartman #include <linux/greybus.h>
118465def4SGreg Kroah-Hartman 
128465def4SGreg Kroah-Hartman #include "greybus_trace.h"
138465def4SGreg Kroah-Hartman 
148465def4SGreg Kroah-Hartman #define GB_INTERFACE_MODE_SWITCH_TIMEOUT	2000
158465def4SGreg Kroah-Hartman 
168465def4SGreg Kroah-Hartman #define GB_INTERFACE_DEVICE_ID_BAD	0xff
178465def4SGreg Kroah-Hartman 
188465def4SGreg Kroah-Hartman #define GB_INTERFACE_AUTOSUSPEND_MS			3000
198465def4SGreg Kroah-Hartman 
208465def4SGreg Kroah-Hartman /* Time required for interface to enter standby before disabling REFCLK */
218465def4SGreg Kroah-Hartman #define GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS			20
228465def4SGreg Kroah-Hartman 
238465def4SGreg Kroah-Hartman /* Don't-care selector index */
248465def4SGreg Kroah-Hartman #define DME_SELECTOR_INDEX_NULL		0
258465def4SGreg Kroah-Hartman 
268465def4SGreg Kroah-Hartman /* DME attributes */
278465def4SGreg Kroah-Hartman /* FIXME: remove ES2 support and DME_T_TST_SRC_INCREMENT */
288465def4SGreg Kroah-Hartman #define DME_T_TST_SRC_INCREMENT		0x4083
298465def4SGreg Kroah-Hartman 
308465def4SGreg Kroah-Hartman #define DME_DDBL1_MANUFACTURERID	0x5003
318465def4SGreg Kroah-Hartman #define DME_DDBL1_PRODUCTID		0x5004
328465def4SGreg Kroah-Hartman 
338465def4SGreg Kroah-Hartman #define DME_TOSHIBA_GMP_VID		0x6000
348465def4SGreg Kroah-Hartman #define DME_TOSHIBA_GMP_PID		0x6001
358465def4SGreg Kroah-Hartman #define DME_TOSHIBA_GMP_SN0		0x6002
368465def4SGreg Kroah-Hartman #define DME_TOSHIBA_GMP_SN1		0x6003
378465def4SGreg Kroah-Hartman #define DME_TOSHIBA_GMP_INIT_STATUS	0x6101
388465def4SGreg Kroah-Hartman 
398465def4SGreg Kroah-Hartman /* DDBL1 Manufacturer and Product ids */
408465def4SGreg Kroah-Hartman #define TOSHIBA_DMID			0x0126
418465def4SGreg Kroah-Hartman #define TOSHIBA_ES2_BRIDGE_DPID		0x1000
428465def4SGreg Kroah-Hartman #define TOSHIBA_ES3_APBRIDGE_DPID	0x1001
438465def4SGreg Kroah-Hartman #define TOSHIBA_ES3_GBPHY_DPID	0x1002
448465def4SGreg Kroah-Hartman 
458465def4SGreg Kroah-Hartman static int gb_interface_hibernate_link(struct gb_interface *intf);
468465def4SGreg Kroah-Hartman static int gb_interface_refclk_set(struct gb_interface *intf, bool enable);
478465def4SGreg Kroah-Hartman 
488465def4SGreg Kroah-Hartman static int gb_interface_dme_attr_get(struct gb_interface *intf,
498465def4SGreg Kroah-Hartman 				     u16 attr, u32 *val)
508465def4SGreg Kroah-Hartman {
518465def4SGreg Kroah-Hartman 	return gb_svc_dme_peer_get(intf->hd->svc, intf->interface_id,
528465def4SGreg Kroah-Hartman 					attr, DME_SELECTOR_INDEX_NULL, val);
538465def4SGreg Kroah-Hartman }
548465def4SGreg Kroah-Hartman 
558465def4SGreg Kroah-Hartman static int gb_interface_read_ara_dme(struct gb_interface *intf)
568465def4SGreg Kroah-Hartman {
578465def4SGreg Kroah-Hartman 	u32 sn0, sn1;
588465def4SGreg Kroah-Hartman 	int ret;
598465def4SGreg Kroah-Hartman 
608465def4SGreg Kroah-Hartman 	/*
618465def4SGreg Kroah-Hartman 	 * Unless this is a Toshiba bridge, bail out until we have defined
628465def4SGreg Kroah-Hartman 	 * standard GMP attributes.
638465def4SGreg Kroah-Hartman 	 */
648465def4SGreg Kroah-Hartman 	if (intf->ddbl1_manufacturer_id != TOSHIBA_DMID) {
658465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "unknown manufacturer %08x\n",
668465def4SGreg Kroah-Hartman 			intf->ddbl1_manufacturer_id);
678465def4SGreg Kroah-Hartman 		return -ENODEV;
688465def4SGreg Kroah-Hartman 	}
698465def4SGreg Kroah-Hartman 
708465def4SGreg Kroah-Hartman 	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_VID,
718465def4SGreg Kroah-Hartman 					&intf->vendor_id);
728465def4SGreg Kroah-Hartman 	if (ret)
738465def4SGreg Kroah-Hartman 		return ret;
748465def4SGreg Kroah-Hartman 
758465def4SGreg Kroah-Hartman 	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_PID,
768465def4SGreg Kroah-Hartman 					&intf->product_id);
778465def4SGreg Kroah-Hartman 	if (ret)
788465def4SGreg Kroah-Hartman 		return ret;
798465def4SGreg Kroah-Hartman 
808465def4SGreg Kroah-Hartman 	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN0, &sn0);
818465def4SGreg Kroah-Hartman 	if (ret)
828465def4SGreg Kroah-Hartman 		return ret;
838465def4SGreg Kroah-Hartman 
848465def4SGreg Kroah-Hartman 	ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN1, &sn1);
858465def4SGreg Kroah-Hartman 	if (ret)
868465def4SGreg Kroah-Hartman 		return ret;
878465def4SGreg Kroah-Hartman 
888465def4SGreg Kroah-Hartman 	intf->serial_number = (u64)sn1 << 32 | sn0;
898465def4SGreg Kroah-Hartman 
908465def4SGreg Kroah-Hartman 	return 0;
918465def4SGreg Kroah-Hartman }
928465def4SGreg Kroah-Hartman 
938465def4SGreg Kroah-Hartman static int gb_interface_read_dme(struct gb_interface *intf)
948465def4SGreg Kroah-Hartman {
958465def4SGreg Kroah-Hartman 	int ret;
968465def4SGreg Kroah-Hartman 
978465def4SGreg Kroah-Hartman 	/* DME attributes have already been read */
988465def4SGreg Kroah-Hartman 	if (intf->dme_read)
998465def4SGreg Kroah-Hartman 		return 0;
1008465def4SGreg Kroah-Hartman 
1018465def4SGreg Kroah-Hartman 	ret = gb_interface_dme_attr_get(intf, DME_DDBL1_MANUFACTURERID,
1028465def4SGreg Kroah-Hartman 					&intf->ddbl1_manufacturer_id);
1038465def4SGreg Kroah-Hartman 	if (ret)
1048465def4SGreg Kroah-Hartman 		return ret;
1058465def4SGreg Kroah-Hartman 
1068465def4SGreg Kroah-Hartman 	ret = gb_interface_dme_attr_get(intf, DME_DDBL1_PRODUCTID,
1078465def4SGreg Kroah-Hartman 					&intf->ddbl1_product_id);
1088465def4SGreg Kroah-Hartman 	if (ret)
1098465def4SGreg Kroah-Hartman 		return ret;
1108465def4SGreg Kroah-Hartman 
1118465def4SGreg Kroah-Hartman 	if (intf->ddbl1_manufacturer_id == TOSHIBA_DMID &&
1128465def4SGreg Kroah-Hartman 	    intf->ddbl1_product_id == TOSHIBA_ES2_BRIDGE_DPID) {
1138465def4SGreg Kroah-Hartman 		intf->quirks |= GB_INTERFACE_QUIRK_NO_GMP_IDS;
1148465def4SGreg Kroah-Hartman 		intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS;
1158465def4SGreg Kroah-Hartman 	}
1168465def4SGreg Kroah-Hartman 
1178465def4SGreg Kroah-Hartman 	ret = gb_interface_read_ara_dme(intf);
1188465def4SGreg Kroah-Hartman 	if (ret)
1198465def4SGreg Kroah-Hartman 		return ret;
1208465def4SGreg Kroah-Hartman 
1218465def4SGreg Kroah-Hartman 	intf->dme_read = true;
1228465def4SGreg Kroah-Hartman 
1238465def4SGreg Kroah-Hartman 	return 0;
1248465def4SGreg Kroah-Hartman }
1258465def4SGreg Kroah-Hartman 
1268465def4SGreg Kroah-Hartman static int gb_interface_route_create(struct gb_interface *intf)
1278465def4SGreg Kroah-Hartman {
1288465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
1298465def4SGreg Kroah-Hartman 	u8 intf_id = intf->interface_id;
1308465def4SGreg Kroah-Hartman 	u8 device_id;
1318465def4SGreg Kroah-Hartman 	int ret;
1328465def4SGreg Kroah-Hartman 
1338465def4SGreg Kroah-Hartman 	/* Allocate an interface device id. */
1343bd29138SChristophe JAILLET 	ret = ida_alloc_range(&svc->device_id_map, GB_SVC_DEVICE_ID_MIN,
1353bd29138SChristophe JAILLET 			      GB_SVC_DEVICE_ID_MAX, GFP_KERNEL);
1368465def4SGreg Kroah-Hartman 	if (ret < 0) {
1378465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to allocate device id: %d\n", ret);
1388465def4SGreg Kroah-Hartman 		return ret;
1398465def4SGreg Kroah-Hartman 	}
1408465def4SGreg Kroah-Hartman 	device_id = ret;
1418465def4SGreg Kroah-Hartman 
1428465def4SGreg Kroah-Hartman 	ret = gb_svc_intf_device_id(svc, intf_id, device_id);
1438465def4SGreg Kroah-Hartman 	if (ret) {
1448465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to set device id %u: %d\n",
1458465def4SGreg Kroah-Hartman 			device_id, ret);
1468465def4SGreg Kroah-Hartman 		goto err_ida_remove;
1478465def4SGreg Kroah-Hartman 	}
1488465def4SGreg Kroah-Hartman 
1498465def4SGreg Kroah-Hartman 	/* FIXME: Hard-coded AP device id. */
1508465def4SGreg Kroah-Hartman 	ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_SVC_DEVICE_ID_AP,
1518465def4SGreg Kroah-Hartman 				  intf_id, device_id);
1528465def4SGreg Kroah-Hartman 	if (ret) {
1538465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to create route: %d\n", ret);
1548465def4SGreg Kroah-Hartman 		goto err_svc_id_free;
1558465def4SGreg Kroah-Hartman 	}
1568465def4SGreg Kroah-Hartman 
1578465def4SGreg Kroah-Hartman 	intf->device_id = device_id;
1588465def4SGreg Kroah-Hartman 
1598465def4SGreg Kroah-Hartman 	return 0;
1608465def4SGreg Kroah-Hartman 
1618465def4SGreg Kroah-Hartman err_svc_id_free:
1628465def4SGreg Kroah-Hartman 	/*
1638465def4SGreg Kroah-Hartman 	 * XXX Should we tell SVC that this id doesn't belong to interface
1648465def4SGreg Kroah-Hartman 	 * XXX anymore.
1658465def4SGreg Kroah-Hartman 	 */
1668465def4SGreg Kroah-Hartman err_ida_remove:
1673bd29138SChristophe JAILLET 	ida_free(&svc->device_id_map, device_id);
1688465def4SGreg Kroah-Hartman 
1698465def4SGreg Kroah-Hartman 	return ret;
1708465def4SGreg Kroah-Hartman }
1718465def4SGreg Kroah-Hartman 
1728465def4SGreg Kroah-Hartman static void gb_interface_route_destroy(struct gb_interface *intf)
1738465def4SGreg Kroah-Hartman {
1748465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
1758465def4SGreg Kroah-Hartman 
1768465def4SGreg Kroah-Hartman 	if (intf->device_id == GB_INTERFACE_DEVICE_ID_BAD)
1778465def4SGreg Kroah-Hartman 		return;
1788465def4SGreg Kroah-Hartman 
1798465def4SGreg Kroah-Hartman 	gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id);
1803bd29138SChristophe JAILLET 	ida_free(&svc->device_id_map, intf->device_id);
1818465def4SGreg Kroah-Hartman 	intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
1828465def4SGreg Kroah-Hartman }
1838465def4SGreg Kroah-Hartman 
1848465def4SGreg Kroah-Hartman /* Locking: Caller holds the interface mutex. */
1858465def4SGreg Kroah-Hartman static int gb_interface_legacy_mode_switch(struct gb_interface *intf)
1868465def4SGreg Kroah-Hartman {
1878465def4SGreg Kroah-Hartman 	int ret;
1888465def4SGreg Kroah-Hartman 
1898465def4SGreg Kroah-Hartman 	dev_info(&intf->dev, "legacy mode switch detected\n");
1908465def4SGreg Kroah-Hartman 
1918465def4SGreg Kroah-Hartman 	/* Mark as disconnected to prevent I/O during disable. */
1928465def4SGreg Kroah-Hartman 	intf->disconnected = true;
1938465def4SGreg Kroah-Hartman 	gb_interface_disable(intf);
1948465def4SGreg Kroah-Hartman 	intf->disconnected = false;
1958465def4SGreg Kroah-Hartman 
1968465def4SGreg Kroah-Hartman 	ret = gb_interface_enable(intf);
1978465def4SGreg Kroah-Hartman 	if (ret) {
1988465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to re-enable interface: %d\n", ret);
1998465def4SGreg Kroah-Hartman 		gb_interface_deactivate(intf);
2008465def4SGreg Kroah-Hartman 	}
2018465def4SGreg Kroah-Hartman 
2028465def4SGreg Kroah-Hartman 	return ret;
2038465def4SGreg Kroah-Hartman }
2048465def4SGreg Kroah-Hartman 
2058465def4SGreg Kroah-Hartman void gb_interface_mailbox_event(struct gb_interface *intf, u16 result,
2068465def4SGreg Kroah-Hartman 				u32 mailbox)
2078465def4SGreg Kroah-Hartman {
2088465def4SGreg Kroah-Hartman 	mutex_lock(&intf->mutex);
2098465def4SGreg Kroah-Hartman 
2108465def4SGreg Kroah-Hartman 	if (result) {
2118465def4SGreg Kroah-Hartman 		dev_warn(&intf->dev,
2128465def4SGreg Kroah-Hartman 			 "mailbox event with UniPro error: 0x%04x\n",
2138465def4SGreg Kroah-Hartman 			 result);
2148465def4SGreg Kroah-Hartman 		goto err_disable;
2158465def4SGreg Kroah-Hartman 	}
2168465def4SGreg Kroah-Hartman 
2178465def4SGreg Kroah-Hartman 	if (mailbox != GB_SVC_INTF_MAILBOX_GREYBUS) {
2188465def4SGreg Kroah-Hartman 		dev_warn(&intf->dev,
2198465def4SGreg Kroah-Hartman 			 "mailbox event with unexpected value: 0x%08x\n",
2208465def4SGreg Kroah-Hartman 			 mailbox);
2218465def4SGreg Kroah-Hartman 		goto err_disable;
2228465def4SGreg Kroah-Hartman 	}
2238465def4SGreg Kroah-Hartman 
2248465def4SGreg Kroah-Hartman 	if (intf->quirks & GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH) {
2258465def4SGreg Kroah-Hartman 		gb_interface_legacy_mode_switch(intf);
2268465def4SGreg Kroah-Hartman 		goto out_unlock;
2278465def4SGreg Kroah-Hartman 	}
2288465def4SGreg Kroah-Hartman 
2298465def4SGreg Kroah-Hartman 	if (!intf->mode_switch) {
2308465def4SGreg Kroah-Hartman 		dev_warn(&intf->dev, "unexpected mailbox event: 0x%08x\n",
2318465def4SGreg Kroah-Hartman 			 mailbox);
2328465def4SGreg Kroah-Hartman 		goto err_disable;
2338465def4SGreg Kroah-Hartman 	}
2348465def4SGreg Kroah-Hartman 
2358465def4SGreg Kroah-Hartman 	dev_info(&intf->dev, "mode switch detected\n");
2368465def4SGreg Kroah-Hartman 
2378465def4SGreg Kroah-Hartman 	complete(&intf->mode_switch_completion);
2388465def4SGreg Kroah-Hartman 
2398465def4SGreg Kroah-Hartman out_unlock:
2408465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
2418465def4SGreg Kroah-Hartman 
2428465def4SGreg Kroah-Hartman 	return;
2438465def4SGreg Kroah-Hartman 
2448465def4SGreg Kroah-Hartman err_disable:
2458465def4SGreg Kroah-Hartman 	gb_interface_disable(intf);
2468465def4SGreg Kroah-Hartman 	gb_interface_deactivate(intf);
2478465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
2488465def4SGreg Kroah-Hartman }
2498465def4SGreg Kroah-Hartman 
2508465def4SGreg Kroah-Hartman static void gb_interface_mode_switch_work(struct work_struct *work)
2518465def4SGreg Kroah-Hartman {
2528465def4SGreg Kroah-Hartman 	struct gb_interface *intf;
2538465def4SGreg Kroah-Hartman 	struct gb_control *control;
2548465def4SGreg Kroah-Hartman 	unsigned long timeout;
2558465def4SGreg Kroah-Hartman 	int ret;
2568465def4SGreg Kroah-Hartman 
2578465def4SGreg Kroah-Hartman 	intf = container_of(work, struct gb_interface, mode_switch_work);
2588465def4SGreg Kroah-Hartman 
2598465def4SGreg Kroah-Hartman 	mutex_lock(&intf->mutex);
2608465def4SGreg Kroah-Hartman 	/* Make sure interface is still enabled. */
2618465def4SGreg Kroah-Hartman 	if (!intf->enabled) {
2628465def4SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "mode switch aborted\n");
2638465def4SGreg Kroah-Hartman 		intf->mode_switch = false;
2648465def4SGreg Kroah-Hartman 		mutex_unlock(&intf->mutex);
2658465def4SGreg Kroah-Hartman 		goto out_interface_put;
2668465def4SGreg Kroah-Hartman 	}
2678465def4SGreg Kroah-Hartman 
2688465def4SGreg Kroah-Hartman 	/*
2698465def4SGreg Kroah-Hartman 	 * Prepare the control device for mode switch and make sure to get an
2708465def4SGreg Kroah-Hartman 	 * extra reference before it goes away during interface disable.
2718465def4SGreg Kroah-Hartman 	 */
2728465def4SGreg Kroah-Hartman 	control = gb_control_get(intf->control);
2738465def4SGreg Kroah-Hartman 	gb_control_mode_switch_prepare(control);
2748465def4SGreg Kroah-Hartman 	gb_interface_disable(intf);
2758465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
2768465def4SGreg Kroah-Hartman 
2778465def4SGreg Kroah-Hartman 	timeout = msecs_to_jiffies(GB_INTERFACE_MODE_SWITCH_TIMEOUT);
2788465def4SGreg Kroah-Hartman 	ret = wait_for_completion_interruptible_timeout(
2798465def4SGreg Kroah-Hartman 			&intf->mode_switch_completion, timeout);
2808465def4SGreg Kroah-Hartman 
2818465def4SGreg Kroah-Hartman 	/* Finalise control-connection mode switch. */
2828465def4SGreg Kroah-Hartman 	gb_control_mode_switch_complete(control);
2838465def4SGreg Kroah-Hartman 	gb_control_put(control);
2848465def4SGreg Kroah-Hartman 
2858465def4SGreg Kroah-Hartman 	if (ret < 0) {
2868465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "mode switch interrupted\n");
2878465def4SGreg Kroah-Hartman 		goto err_deactivate;
2888465def4SGreg Kroah-Hartman 	} else if (ret == 0) {
2898465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "mode switch timed out\n");
2908465def4SGreg Kroah-Hartman 		goto err_deactivate;
2918465def4SGreg Kroah-Hartman 	}
2928465def4SGreg Kroah-Hartman 
2938465def4SGreg Kroah-Hartman 	/* Re-enable (re-enumerate) interface if still active. */
2948465def4SGreg Kroah-Hartman 	mutex_lock(&intf->mutex);
2958465def4SGreg Kroah-Hartman 	intf->mode_switch = false;
2968465def4SGreg Kroah-Hartman 	if (intf->active) {
2978465def4SGreg Kroah-Hartman 		ret = gb_interface_enable(intf);
2988465def4SGreg Kroah-Hartman 		if (ret) {
2998465def4SGreg Kroah-Hartman 			dev_err(&intf->dev, "failed to re-enable interface: %d\n",
3008465def4SGreg Kroah-Hartman 				ret);
3018465def4SGreg Kroah-Hartman 			gb_interface_deactivate(intf);
3028465def4SGreg Kroah-Hartman 		}
3038465def4SGreg Kroah-Hartman 	}
3048465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
3058465def4SGreg Kroah-Hartman 
3068465def4SGreg Kroah-Hartman out_interface_put:
3078465def4SGreg Kroah-Hartman 	gb_interface_put(intf);
3088465def4SGreg Kroah-Hartman 
3098465def4SGreg Kroah-Hartman 	return;
3108465def4SGreg Kroah-Hartman 
3118465def4SGreg Kroah-Hartman err_deactivate:
3128465def4SGreg Kroah-Hartman 	mutex_lock(&intf->mutex);
3138465def4SGreg Kroah-Hartman 	intf->mode_switch = false;
3148465def4SGreg Kroah-Hartman 	gb_interface_deactivate(intf);
3158465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
3168465def4SGreg Kroah-Hartman 
3178465def4SGreg Kroah-Hartman 	gb_interface_put(intf);
3188465def4SGreg Kroah-Hartman }
3198465def4SGreg Kroah-Hartman 
3208465def4SGreg Kroah-Hartman int gb_interface_request_mode_switch(struct gb_interface *intf)
3218465def4SGreg Kroah-Hartman {
3228465def4SGreg Kroah-Hartman 	int ret = 0;
3238465def4SGreg Kroah-Hartman 
3248465def4SGreg Kroah-Hartman 	mutex_lock(&intf->mutex);
3258465def4SGreg Kroah-Hartman 	if (intf->mode_switch) {
3268465def4SGreg Kroah-Hartman 		ret = -EBUSY;
3278465def4SGreg Kroah-Hartman 		goto out_unlock;
3288465def4SGreg Kroah-Hartman 	}
3298465def4SGreg Kroah-Hartman 
3308465def4SGreg Kroah-Hartman 	intf->mode_switch = true;
3318465def4SGreg Kroah-Hartman 	reinit_completion(&intf->mode_switch_completion);
3328465def4SGreg Kroah-Hartman 
3338465def4SGreg Kroah-Hartman 	/*
3348465def4SGreg Kroah-Hartman 	 * Get a reference to the interface device, which will be put once the
3358465def4SGreg Kroah-Hartman 	 * mode switch is complete.
3368465def4SGreg Kroah-Hartman 	 */
3378465def4SGreg Kroah-Hartman 	get_device(&intf->dev);
3388465def4SGreg Kroah-Hartman 
3398465def4SGreg Kroah-Hartman 	if (!queue_work(system_long_wq, &intf->mode_switch_work)) {
3408465def4SGreg Kroah-Hartman 		put_device(&intf->dev);
3418465def4SGreg Kroah-Hartman 		ret = -EBUSY;
3428465def4SGreg Kroah-Hartman 		goto out_unlock;
3438465def4SGreg Kroah-Hartman 	}
3448465def4SGreg Kroah-Hartman 
3458465def4SGreg Kroah-Hartman out_unlock:
3468465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
3478465def4SGreg Kroah-Hartman 
3488465def4SGreg Kroah-Hartman 	return ret;
3498465def4SGreg Kroah-Hartman }
3508465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_interface_request_mode_switch);
3518465def4SGreg Kroah-Hartman 
3528465def4SGreg Kroah-Hartman /*
3538465def4SGreg Kroah-Hartman  * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the
3548465def4SGreg Kroah-Hartman  * init-status attribute DME_TOSHIBA_INIT_STATUS. The AP needs to read and
3558465def4SGreg Kroah-Hartman  * clear it after reading a non-zero value from it.
3568465def4SGreg Kroah-Hartman  *
3578465def4SGreg Kroah-Hartman  * FIXME: This is module-hardware dependent and needs to be extended for every
3588465def4SGreg Kroah-Hartman  * type of module we want to support.
3598465def4SGreg Kroah-Hartman  */
3608465def4SGreg Kroah-Hartman static int gb_interface_read_and_clear_init_status(struct gb_interface *intf)
3618465def4SGreg Kroah-Hartman {
3628465def4SGreg Kroah-Hartman 	struct gb_host_device *hd = intf->hd;
3638465def4SGreg Kroah-Hartman 	unsigned long bootrom_quirks;
3648465def4SGreg Kroah-Hartman 	unsigned long s2l_quirks;
3658465def4SGreg Kroah-Hartman 	int ret;
3668465def4SGreg Kroah-Hartman 	u32 value;
3678465def4SGreg Kroah-Hartman 	u16 attr;
3688465def4SGreg Kroah-Hartman 	u8 init_status;
3698465def4SGreg Kroah-Hartman 
3708465def4SGreg Kroah-Hartman 	/*
3718465def4SGreg Kroah-Hartman 	 * ES2 bridges use T_TstSrcIncrement for the init status.
3728465def4SGreg Kroah-Hartman 	 *
3738465def4SGreg Kroah-Hartman 	 * FIXME: Remove ES2 support
3748465def4SGreg Kroah-Hartman 	 */
3758465def4SGreg Kroah-Hartman 	if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS)
3768465def4SGreg Kroah-Hartman 		attr = DME_T_TST_SRC_INCREMENT;
3778465def4SGreg Kroah-Hartman 	else
3788465def4SGreg Kroah-Hartman 		attr = DME_TOSHIBA_GMP_INIT_STATUS;
3798465def4SGreg Kroah-Hartman 
3808465def4SGreg Kroah-Hartman 	ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr,
3818465def4SGreg Kroah-Hartman 				  DME_SELECTOR_INDEX_NULL, &value);
3828465def4SGreg Kroah-Hartman 	if (ret)
3838465def4SGreg Kroah-Hartman 		return ret;
3848465def4SGreg Kroah-Hartman 
3858465def4SGreg Kroah-Hartman 	/*
3868465def4SGreg Kroah-Hartman 	 * A nonzero init status indicates the module has finished
3878465def4SGreg Kroah-Hartman 	 * initializing.
3888465def4SGreg Kroah-Hartman 	 */
3898465def4SGreg Kroah-Hartman 	if (!value) {
3908465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "invalid init status\n");
3918465def4SGreg Kroah-Hartman 		return -ENODEV;
3928465def4SGreg Kroah-Hartman 	}
3938465def4SGreg Kroah-Hartman 
3948465def4SGreg Kroah-Hartman 	/*
3958465def4SGreg Kroah-Hartman 	 * Extract the init status.
3968465def4SGreg Kroah-Hartman 	 *
3978465def4SGreg Kroah-Hartman 	 * For ES2: We need to check lowest 8 bits of 'value'.
3988465def4SGreg Kroah-Hartman 	 * For ES3: We need to check highest 8 bits out of 32 of 'value'.
3998465def4SGreg Kroah-Hartman 	 *
4008465def4SGreg Kroah-Hartman 	 * FIXME: Remove ES2 support
4018465def4SGreg Kroah-Hartman 	 */
4028465def4SGreg Kroah-Hartman 	if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS)
4038465def4SGreg Kroah-Hartman 		init_status = value & 0xff;
4048465def4SGreg Kroah-Hartman 	else
4058465def4SGreg Kroah-Hartman 		init_status = value >> 24;
4068465def4SGreg Kroah-Hartman 
4078465def4SGreg Kroah-Hartman 	/*
4088465def4SGreg Kroah-Hartman 	 * Check if the interface is executing the quirky ES3 bootrom that,
4098465def4SGreg Kroah-Hartman 	 * for example, requires E2EFC, CSD and CSV to be disabled.
4108465def4SGreg Kroah-Hartman 	 */
4118465def4SGreg Kroah-Hartman 	bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES |
4128465def4SGreg Kroah-Hartman 				GB_INTERFACE_QUIRK_FORCED_DISABLE |
4138465def4SGreg Kroah-Hartman 				GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH |
4148465def4SGreg Kroah-Hartman 				GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE;
4158465def4SGreg Kroah-Hartman 
4168465def4SGreg Kroah-Hartman 	s2l_quirks = GB_INTERFACE_QUIRK_NO_PM;
4178465def4SGreg Kroah-Hartman 
4188465def4SGreg Kroah-Hartman 	switch (init_status) {
4198465def4SGreg Kroah-Hartman 	case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED:
4208465def4SGreg Kroah-Hartman 	case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED:
4218465def4SGreg Kroah-Hartman 		intf->quirks |= bootrom_quirks;
4228465def4SGreg Kroah-Hartman 		break;
4238465def4SGreg Kroah-Hartman 	case GB_INIT_S2_LOADER_BOOT_STARTED:
4248465def4SGreg Kroah-Hartman 		/* S2 Loader doesn't support runtime PM */
4258465def4SGreg Kroah-Hartman 		intf->quirks &= ~bootrom_quirks;
4268465def4SGreg Kroah-Hartman 		intf->quirks |= s2l_quirks;
4278465def4SGreg Kroah-Hartman 		break;
4288465def4SGreg Kroah-Hartman 	default:
4298465def4SGreg Kroah-Hartman 		intf->quirks &= ~bootrom_quirks;
4308465def4SGreg Kroah-Hartman 		intf->quirks &= ~s2l_quirks;
4318465def4SGreg Kroah-Hartman 	}
4328465def4SGreg Kroah-Hartman 
4338465def4SGreg Kroah-Hartman 	/* Clear the init status. */
4348465def4SGreg Kroah-Hartman 	return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr,
4358465def4SGreg Kroah-Hartman 				   DME_SELECTOR_INDEX_NULL, 0);
4368465def4SGreg Kroah-Hartman }
4378465def4SGreg Kroah-Hartman 
4388465def4SGreg Kroah-Hartman /* interface sysfs attributes */
4398465def4SGreg Kroah-Hartman #define gb_interface_attr(field, type)					\
4408465def4SGreg Kroah-Hartman static ssize_t field##_show(struct device *dev,				\
4418465def4SGreg Kroah-Hartman 			    struct device_attribute *attr,		\
4428465def4SGreg Kroah-Hartman 			    char *buf)					\
4438465def4SGreg Kroah-Hartman {									\
4448465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);		\
4458465def4SGreg Kroah-Hartman 	return scnprintf(buf, PAGE_SIZE, type"\n", intf->field);	\
4468465def4SGreg Kroah-Hartman }									\
4478465def4SGreg Kroah-Hartman static DEVICE_ATTR_RO(field)
4488465def4SGreg Kroah-Hartman 
4498465def4SGreg Kroah-Hartman gb_interface_attr(ddbl1_manufacturer_id, "0x%08x");
4508465def4SGreg Kroah-Hartman gb_interface_attr(ddbl1_product_id, "0x%08x");
4518465def4SGreg Kroah-Hartman gb_interface_attr(interface_id, "%u");
4528465def4SGreg Kroah-Hartman gb_interface_attr(vendor_id, "0x%08x");
4538465def4SGreg Kroah-Hartman gb_interface_attr(product_id, "0x%08x");
4548465def4SGreg Kroah-Hartman gb_interface_attr(serial_number, "0x%016llx");
4558465def4SGreg Kroah-Hartman 
4568465def4SGreg Kroah-Hartman static ssize_t voltage_now_show(struct device *dev,
4578465def4SGreg Kroah-Hartman 				struct device_attribute *attr, char *buf)
4588465def4SGreg Kroah-Hartman {
4598465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
4608465def4SGreg Kroah-Hartman 	int ret;
4618465def4SGreg Kroah-Hartman 	u32 measurement;
4628465def4SGreg Kroah-Hartman 
4638465def4SGreg Kroah-Hartman 	ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id,
4648465def4SGreg Kroah-Hartman 					    GB_SVC_PWRMON_TYPE_VOL,
4658465def4SGreg Kroah-Hartman 					    &measurement);
4668465def4SGreg Kroah-Hartman 	if (ret) {
4678465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to get voltage sample (%d)\n", ret);
4688465def4SGreg Kroah-Hartman 		return ret;
4698465def4SGreg Kroah-Hartman 	}
4708465def4SGreg Kroah-Hartman 
4718465def4SGreg Kroah-Hartman 	return sprintf(buf, "%u\n", measurement);
4728465def4SGreg Kroah-Hartman }
4738465def4SGreg Kroah-Hartman static DEVICE_ATTR_RO(voltage_now);
4748465def4SGreg Kroah-Hartman 
4758465def4SGreg Kroah-Hartman static ssize_t current_now_show(struct device *dev,
4768465def4SGreg Kroah-Hartman 				struct device_attribute *attr, char *buf)
4778465def4SGreg Kroah-Hartman {
4788465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
4798465def4SGreg Kroah-Hartman 	int ret;
4808465def4SGreg Kroah-Hartman 	u32 measurement;
4818465def4SGreg Kroah-Hartman 
4828465def4SGreg Kroah-Hartman 	ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id,
4838465def4SGreg Kroah-Hartman 					    GB_SVC_PWRMON_TYPE_CURR,
4848465def4SGreg Kroah-Hartman 					    &measurement);
4858465def4SGreg Kroah-Hartman 	if (ret) {
4868465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to get current sample (%d)\n", ret);
4878465def4SGreg Kroah-Hartman 		return ret;
4888465def4SGreg Kroah-Hartman 	}
4898465def4SGreg Kroah-Hartman 
4908465def4SGreg Kroah-Hartman 	return sprintf(buf, "%u\n", measurement);
4918465def4SGreg Kroah-Hartman }
4928465def4SGreg Kroah-Hartman static DEVICE_ATTR_RO(current_now);
4938465def4SGreg Kroah-Hartman 
4948465def4SGreg Kroah-Hartman static ssize_t power_now_show(struct device *dev,
4958465def4SGreg Kroah-Hartman 			      struct device_attribute *attr, char *buf)
4968465def4SGreg Kroah-Hartman {
4978465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
4988465def4SGreg Kroah-Hartman 	int ret;
4998465def4SGreg Kroah-Hartman 	u32 measurement;
5008465def4SGreg Kroah-Hartman 
5018465def4SGreg Kroah-Hartman 	ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id,
5028465def4SGreg Kroah-Hartman 					    GB_SVC_PWRMON_TYPE_PWR,
5038465def4SGreg Kroah-Hartman 					    &measurement);
5048465def4SGreg Kroah-Hartman 	if (ret) {
5058465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to get power sample (%d)\n", ret);
5068465def4SGreg Kroah-Hartman 		return ret;
5078465def4SGreg Kroah-Hartman 	}
5088465def4SGreg Kroah-Hartman 
5098465def4SGreg Kroah-Hartman 	return sprintf(buf, "%u\n", measurement);
5108465def4SGreg Kroah-Hartman }
5118465def4SGreg Kroah-Hartman static DEVICE_ATTR_RO(power_now);
5128465def4SGreg Kroah-Hartman 
5138465def4SGreg Kroah-Hartman static ssize_t power_state_show(struct device *dev,
5148465def4SGreg Kroah-Hartman 				struct device_attribute *attr, char *buf)
5158465def4SGreg Kroah-Hartman {
5168465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
5178465def4SGreg Kroah-Hartman 
5188465def4SGreg Kroah-Hartman 	if (intf->active)
5198465def4SGreg Kroah-Hartman 		return scnprintf(buf, PAGE_SIZE, "on\n");
5208465def4SGreg Kroah-Hartman 	else
5218465def4SGreg Kroah-Hartman 		return scnprintf(buf, PAGE_SIZE, "off\n");
5228465def4SGreg Kroah-Hartman }
5238465def4SGreg Kroah-Hartman 
5248465def4SGreg Kroah-Hartman static ssize_t power_state_store(struct device *dev,
5258465def4SGreg Kroah-Hartman 				 struct device_attribute *attr, const char *buf,
5268465def4SGreg Kroah-Hartman 				 size_t len)
5278465def4SGreg Kroah-Hartman {
5288465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
5298465def4SGreg Kroah-Hartman 	bool activate;
5308465def4SGreg Kroah-Hartman 	int ret = 0;
5318465def4SGreg Kroah-Hartman 
5328465def4SGreg Kroah-Hartman 	if (kstrtobool(buf, &activate))
5338465def4SGreg Kroah-Hartman 		return -EINVAL;
5348465def4SGreg Kroah-Hartman 
5358465def4SGreg Kroah-Hartman 	mutex_lock(&intf->mutex);
5368465def4SGreg Kroah-Hartman 
5378465def4SGreg Kroah-Hartman 	if (activate == intf->active)
5388465def4SGreg Kroah-Hartman 		goto unlock;
5398465def4SGreg Kroah-Hartman 
5408465def4SGreg Kroah-Hartman 	if (activate) {
5418465def4SGreg Kroah-Hartman 		ret = gb_interface_activate(intf);
5428465def4SGreg Kroah-Hartman 		if (ret) {
5438465def4SGreg Kroah-Hartman 			dev_err(&intf->dev,
5448465def4SGreg Kroah-Hartman 				"failed to activate interface: %d\n", ret);
5458465def4SGreg Kroah-Hartman 			goto unlock;
5468465def4SGreg Kroah-Hartman 		}
5478465def4SGreg Kroah-Hartman 
5488465def4SGreg Kroah-Hartman 		ret = gb_interface_enable(intf);
5498465def4SGreg Kroah-Hartman 		if (ret) {
5508465def4SGreg Kroah-Hartman 			dev_err(&intf->dev,
5518465def4SGreg Kroah-Hartman 				"failed to enable interface: %d\n", ret);
5528465def4SGreg Kroah-Hartman 			gb_interface_deactivate(intf);
5538465def4SGreg Kroah-Hartman 			goto unlock;
5548465def4SGreg Kroah-Hartman 		}
5558465def4SGreg Kroah-Hartman 	} else {
5568465def4SGreg Kroah-Hartman 		gb_interface_disable(intf);
5578465def4SGreg Kroah-Hartman 		gb_interface_deactivate(intf);
5588465def4SGreg Kroah-Hartman 	}
5598465def4SGreg Kroah-Hartman 
5608465def4SGreg Kroah-Hartman unlock:
5618465def4SGreg Kroah-Hartman 	mutex_unlock(&intf->mutex);
5628465def4SGreg Kroah-Hartman 
5638465def4SGreg Kroah-Hartman 	if (ret)
5648465def4SGreg Kroah-Hartman 		return ret;
5658465def4SGreg Kroah-Hartman 
5668465def4SGreg Kroah-Hartman 	return len;
5678465def4SGreg Kroah-Hartman }
5688465def4SGreg Kroah-Hartman static DEVICE_ATTR_RW(power_state);
5698465def4SGreg Kroah-Hartman 
5708465def4SGreg Kroah-Hartman static const char *gb_interface_type_string(struct gb_interface *intf)
5718465def4SGreg Kroah-Hartman {
5728465def4SGreg Kroah-Hartman 	static const char * const types[] = {
5738465def4SGreg Kroah-Hartman 		[GB_INTERFACE_TYPE_INVALID] = "invalid",
5748465def4SGreg Kroah-Hartman 		[GB_INTERFACE_TYPE_UNKNOWN] = "unknown",
5758465def4SGreg Kroah-Hartman 		[GB_INTERFACE_TYPE_DUMMY] = "dummy",
5768465def4SGreg Kroah-Hartman 		[GB_INTERFACE_TYPE_UNIPRO] = "unipro",
5778465def4SGreg Kroah-Hartman 		[GB_INTERFACE_TYPE_GREYBUS] = "greybus",
5788465def4SGreg Kroah-Hartman 	};
5798465def4SGreg Kroah-Hartman 
5808465def4SGreg Kroah-Hartman 	return types[intf->type];
5818465def4SGreg Kroah-Hartman }
5828465def4SGreg Kroah-Hartman 
5838465def4SGreg Kroah-Hartman static ssize_t interface_type_show(struct device *dev,
5848465def4SGreg Kroah-Hartman 				   struct device_attribute *attr, char *buf)
5858465def4SGreg Kroah-Hartman {
5868465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
5878465def4SGreg Kroah-Hartman 
5888465def4SGreg Kroah-Hartman 	return sprintf(buf, "%s\n", gb_interface_type_string(intf));
5898465def4SGreg Kroah-Hartman }
5908465def4SGreg Kroah-Hartman static DEVICE_ATTR_RO(interface_type);
5918465def4SGreg Kroah-Hartman 
5928465def4SGreg Kroah-Hartman static struct attribute *interface_unipro_attrs[] = {
5938465def4SGreg Kroah-Hartman 	&dev_attr_ddbl1_manufacturer_id.attr,
5948465def4SGreg Kroah-Hartman 	&dev_attr_ddbl1_product_id.attr,
5958465def4SGreg Kroah-Hartman 	NULL
5968465def4SGreg Kroah-Hartman };
5978465def4SGreg Kroah-Hartman 
5988465def4SGreg Kroah-Hartman static struct attribute *interface_greybus_attrs[] = {
5998465def4SGreg Kroah-Hartman 	&dev_attr_vendor_id.attr,
6008465def4SGreg Kroah-Hartman 	&dev_attr_product_id.attr,
6018465def4SGreg Kroah-Hartman 	&dev_attr_serial_number.attr,
6028465def4SGreg Kroah-Hartman 	NULL
6038465def4SGreg Kroah-Hartman };
6048465def4SGreg Kroah-Hartman 
6058465def4SGreg Kroah-Hartman static struct attribute *interface_power_attrs[] = {
6068465def4SGreg Kroah-Hartman 	&dev_attr_voltage_now.attr,
6078465def4SGreg Kroah-Hartman 	&dev_attr_current_now.attr,
6088465def4SGreg Kroah-Hartman 	&dev_attr_power_now.attr,
6098465def4SGreg Kroah-Hartman 	&dev_attr_power_state.attr,
6108465def4SGreg Kroah-Hartman 	NULL
6118465def4SGreg Kroah-Hartman };
6128465def4SGreg Kroah-Hartman 
6138465def4SGreg Kroah-Hartman static struct attribute *interface_common_attrs[] = {
6148465def4SGreg Kroah-Hartman 	&dev_attr_interface_id.attr,
6158465def4SGreg Kroah-Hartman 	&dev_attr_interface_type.attr,
6168465def4SGreg Kroah-Hartman 	NULL
6178465def4SGreg Kroah-Hartman };
6188465def4SGreg Kroah-Hartman 
6198465def4SGreg Kroah-Hartman static umode_t interface_unipro_is_visible(struct kobject *kobj,
6208465def4SGreg Kroah-Hartman 					   struct attribute *attr, int n)
6218465def4SGreg Kroah-Hartman {
622947bece1SWang Qing 	struct device *dev = kobj_to_dev(kobj);
6238465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
6248465def4SGreg Kroah-Hartman 
6258465def4SGreg Kroah-Hartman 	switch (intf->type) {
6268465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_UNIPRO:
6278465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_GREYBUS:
6288465def4SGreg Kroah-Hartman 		return attr->mode;
6298465def4SGreg Kroah-Hartman 	default:
6308465def4SGreg Kroah-Hartman 		return 0;
6318465def4SGreg Kroah-Hartman 	}
6328465def4SGreg Kroah-Hartman }
6338465def4SGreg Kroah-Hartman 
6348465def4SGreg Kroah-Hartman static umode_t interface_greybus_is_visible(struct kobject *kobj,
6358465def4SGreg Kroah-Hartman 					    struct attribute *attr, int n)
6368465def4SGreg Kroah-Hartman {
637947bece1SWang Qing 	struct device *dev = kobj_to_dev(kobj);
6388465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
6398465def4SGreg Kroah-Hartman 
6408465def4SGreg Kroah-Hartman 	switch (intf->type) {
6418465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_GREYBUS:
6428465def4SGreg Kroah-Hartman 		return attr->mode;
6438465def4SGreg Kroah-Hartman 	default:
6448465def4SGreg Kroah-Hartman 		return 0;
6458465def4SGreg Kroah-Hartman 	}
6468465def4SGreg Kroah-Hartman }
6478465def4SGreg Kroah-Hartman 
6488465def4SGreg Kroah-Hartman static umode_t interface_power_is_visible(struct kobject *kobj,
6498465def4SGreg Kroah-Hartman 					  struct attribute *attr, int n)
6508465def4SGreg Kroah-Hartman {
651947bece1SWang Qing 	struct device *dev = kobj_to_dev(kobj);
6528465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
6538465def4SGreg Kroah-Hartman 
6548465def4SGreg Kroah-Hartman 	switch (intf->type) {
6558465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_UNIPRO:
6568465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_GREYBUS:
6578465def4SGreg Kroah-Hartman 		return attr->mode;
6588465def4SGreg Kroah-Hartman 	default:
6598465def4SGreg Kroah-Hartman 		return 0;
6608465def4SGreg Kroah-Hartman 	}
6618465def4SGreg Kroah-Hartman }
6628465def4SGreg Kroah-Hartman 
6638465def4SGreg Kroah-Hartman static const struct attribute_group interface_unipro_group = {
6648465def4SGreg Kroah-Hartman 	.is_visible	= interface_unipro_is_visible,
6658465def4SGreg Kroah-Hartman 	.attrs		= interface_unipro_attrs,
6668465def4SGreg Kroah-Hartman };
6678465def4SGreg Kroah-Hartman 
6688465def4SGreg Kroah-Hartman static const struct attribute_group interface_greybus_group = {
6698465def4SGreg Kroah-Hartman 	.is_visible	= interface_greybus_is_visible,
6708465def4SGreg Kroah-Hartman 	.attrs		= interface_greybus_attrs,
6718465def4SGreg Kroah-Hartman };
6728465def4SGreg Kroah-Hartman 
6738465def4SGreg Kroah-Hartman static const struct attribute_group interface_power_group = {
6748465def4SGreg Kroah-Hartman 	.is_visible	= interface_power_is_visible,
6758465def4SGreg Kroah-Hartman 	.attrs		= interface_power_attrs,
6768465def4SGreg Kroah-Hartman };
6778465def4SGreg Kroah-Hartman 
6788465def4SGreg Kroah-Hartman static const struct attribute_group interface_common_group = {
6798465def4SGreg Kroah-Hartman 	.attrs		= interface_common_attrs,
6808465def4SGreg Kroah-Hartman };
6818465def4SGreg Kroah-Hartman 
6828465def4SGreg Kroah-Hartman static const struct attribute_group *interface_groups[] = {
6838465def4SGreg Kroah-Hartman 	&interface_unipro_group,
6848465def4SGreg Kroah-Hartman 	&interface_greybus_group,
6858465def4SGreg Kroah-Hartman 	&interface_power_group,
6868465def4SGreg Kroah-Hartman 	&interface_common_group,
6878465def4SGreg Kroah-Hartman 	NULL
6888465def4SGreg Kroah-Hartman };
6898465def4SGreg Kroah-Hartman 
6908465def4SGreg Kroah-Hartman static void gb_interface_release(struct device *dev)
6918465def4SGreg Kroah-Hartman {
6928465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
6938465def4SGreg Kroah-Hartman 
6948465def4SGreg Kroah-Hartman 	trace_gb_interface_release(intf);
6958465def4SGreg Kroah-Hartman 
6965c9c5d7fSSicong Huang 	cancel_work_sync(&intf->mode_switch_work);
6978465def4SGreg Kroah-Hartman 	kfree(intf);
6988465def4SGreg Kroah-Hartman }
6998465def4SGreg Kroah-Hartman 
7008465def4SGreg Kroah-Hartman #ifdef CONFIG_PM
7018465def4SGreg Kroah-Hartman static int gb_interface_suspend(struct device *dev)
7028465def4SGreg Kroah-Hartman {
7038465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
7048465def4SGreg Kroah-Hartman 	int ret;
7058465def4SGreg Kroah-Hartman 
7068465def4SGreg Kroah-Hartman 	ret = gb_control_interface_suspend_prepare(intf->control);
7078465def4SGreg Kroah-Hartman 	if (ret)
7088465def4SGreg Kroah-Hartman 		return ret;
7098465def4SGreg Kroah-Hartman 
7108465def4SGreg Kroah-Hartman 	ret = gb_control_suspend(intf->control);
7118465def4SGreg Kroah-Hartman 	if (ret)
7128465def4SGreg Kroah-Hartman 		goto err_hibernate_abort;
7138465def4SGreg Kroah-Hartman 
7148465def4SGreg Kroah-Hartman 	ret = gb_interface_hibernate_link(intf);
7158465def4SGreg Kroah-Hartman 	if (ret)
7168465def4SGreg Kroah-Hartman 		return ret;
7178465def4SGreg Kroah-Hartman 
7188465def4SGreg Kroah-Hartman 	/* Delay to allow interface to enter standby before disabling refclk */
7198465def4SGreg Kroah-Hartman 	msleep(GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS);
7208465def4SGreg Kroah-Hartman 
7218465def4SGreg Kroah-Hartman 	ret = gb_interface_refclk_set(intf, false);
7228465def4SGreg Kroah-Hartman 	if (ret)
7238465def4SGreg Kroah-Hartman 		return ret;
7248465def4SGreg Kroah-Hartman 
7258465def4SGreg Kroah-Hartman 	return 0;
7268465def4SGreg Kroah-Hartman 
7278465def4SGreg Kroah-Hartman err_hibernate_abort:
7288465def4SGreg Kroah-Hartman 	gb_control_interface_hibernate_abort(intf->control);
7298465def4SGreg Kroah-Hartman 
7308465def4SGreg Kroah-Hartman 	return ret;
7318465def4SGreg Kroah-Hartman }
7328465def4SGreg Kroah-Hartman 
7338465def4SGreg Kroah-Hartman static int gb_interface_resume(struct device *dev)
7348465def4SGreg Kroah-Hartman {
7358465def4SGreg Kroah-Hartman 	struct gb_interface *intf = to_gb_interface(dev);
7368465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
7378465def4SGreg Kroah-Hartman 	int ret;
7388465def4SGreg Kroah-Hartman 
7398465def4SGreg Kroah-Hartman 	ret = gb_interface_refclk_set(intf, true);
7408465def4SGreg Kroah-Hartman 	if (ret)
7418465def4SGreg Kroah-Hartman 		return ret;
7428465def4SGreg Kroah-Hartman 
7438465def4SGreg Kroah-Hartman 	ret = gb_svc_intf_resume(svc, intf->interface_id);
7448465def4SGreg Kroah-Hartman 	if (ret)
7458465def4SGreg Kroah-Hartman 		return ret;
7468465def4SGreg Kroah-Hartman 
7478465def4SGreg Kroah-Hartman 	ret = gb_control_resume(intf->control);
7488465def4SGreg Kroah-Hartman 	if (ret)
7498465def4SGreg Kroah-Hartman 		return ret;
7508465def4SGreg Kroah-Hartman 
7518465def4SGreg Kroah-Hartman 	return 0;
7528465def4SGreg Kroah-Hartman }
7538465def4SGreg Kroah-Hartman 
7548465def4SGreg Kroah-Hartman static int gb_interface_runtime_idle(struct device *dev)
7558465def4SGreg Kroah-Hartman {
7568465def4SGreg Kroah-Hartman 	pm_runtime_mark_last_busy(dev);
7578465def4SGreg Kroah-Hartman 	pm_request_autosuspend(dev);
7588465def4SGreg Kroah-Hartman 
7598465def4SGreg Kroah-Hartman 	return 0;
7608465def4SGreg Kroah-Hartman }
7618465def4SGreg Kroah-Hartman #endif
7628465def4SGreg Kroah-Hartman 
7638465def4SGreg Kroah-Hartman static const struct dev_pm_ops gb_interface_pm_ops = {
7648465def4SGreg Kroah-Hartman 	SET_RUNTIME_PM_OPS(gb_interface_suspend, gb_interface_resume,
7658465def4SGreg Kroah-Hartman 			   gb_interface_runtime_idle)
7668465def4SGreg Kroah-Hartman };
7678465def4SGreg Kroah-Hartman 
768e869b72bSRicardo B. Marliere const struct device_type greybus_interface_type = {
7698465def4SGreg Kroah-Hartman 	.name =		"greybus_interface",
7708465def4SGreg Kroah-Hartman 	.release =	gb_interface_release,
7718465def4SGreg Kroah-Hartman 	.pm =		&gb_interface_pm_ops,
7728465def4SGreg Kroah-Hartman };
7738465def4SGreg Kroah-Hartman 
7748465def4SGreg Kroah-Hartman /*
7758465def4SGreg Kroah-Hartman  * A Greybus module represents a user-replaceable component on a GMP
7768465def4SGreg Kroah-Hartman  * phone.  An interface is the physical connection on that module.  A
7778465def4SGreg Kroah-Hartman  * module may have more than one interface.
7788465def4SGreg Kroah-Hartman  *
7798465def4SGreg Kroah-Hartman  * Create a gb_interface structure to represent a discovered interface.
7808465def4SGreg Kroah-Hartman  * The position of interface within the Endo is encoded in "interface_id"
7818465def4SGreg Kroah-Hartman  * argument.
7828465def4SGreg Kroah-Hartman  *
783*feb776a6SChristophe JAILLET  * Returns a pointer to the new interface or a null pointer if a
7848465def4SGreg Kroah-Hartman  * failure occurs due to memory exhaustion.
7858465def4SGreg Kroah-Hartman  */
7868465def4SGreg Kroah-Hartman struct gb_interface *gb_interface_create(struct gb_module *module,
7878465def4SGreg Kroah-Hartman 					 u8 interface_id)
7888465def4SGreg Kroah-Hartman {
7898465def4SGreg Kroah-Hartman 	struct gb_host_device *hd = module->hd;
7908465def4SGreg Kroah-Hartman 	struct gb_interface *intf;
7918465def4SGreg Kroah-Hartman 
7928465def4SGreg Kroah-Hartman 	intf = kzalloc(sizeof(*intf), GFP_KERNEL);
7938465def4SGreg Kroah-Hartman 	if (!intf)
7948465def4SGreg Kroah-Hartman 		return NULL;
7958465def4SGreg Kroah-Hartman 
7968465def4SGreg Kroah-Hartman 	intf->hd = hd;		/* XXX refcount? */
7978465def4SGreg Kroah-Hartman 	intf->module = module;
7988465def4SGreg Kroah-Hartman 	intf->interface_id = interface_id;
7998465def4SGreg Kroah-Hartman 	INIT_LIST_HEAD(&intf->bundles);
8008465def4SGreg Kroah-Hartman 	INIT_LIST_HEAD(&intf->manifest_descs);
8018465def4SGreg Kroah-Hartman 	mutex_init(&intf->mutex);
8028465def4SGreg Kroah-Hartman 	INIT_WORK(&intf->mode_switch_work, gb_interface_mode_switch_work);
8038465def4SGreg Kroah-Hartman 	init_completion(&intf->mode_switch_completion);
8048465def4SGreg Kroah-Hartman 
8058465def4SGreg Kroah-Hartman 	/* Invalid device id to start with */
8068465def4SGreg Kroah-Hartman 	intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
8078465def4SGreg Kroah-Hartman 
8088465def4SGreg Kroah-Hartman 	intf->dev.parent = &module->dev;
8098465def4SGreg Kroah-Hartman 	intf->dev.bus = &greybus_bus_type;
8108465def4SGreg Kroah-Hartman 	intf->dev.type = &greybus_interface_type;
8118465def4SGreg Kroah-Hartman 	intf->dev.groups = interface_groups;
8128465def4SGreg Kroah-Hartman 	intf->dev.dma_mask = module->dev.dma_mask;
8138465def4SGreg Kroah-Hartman 	device_initialize(&intf->dev);
8148465def4SGreg Kroah-Hartman 	dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev),
8158465def4SGreg Kroah-Hartman 		     interface_id);
8168465def4SGreg Kroah-Hartman 
8178465def4SGreg Kroah-Hartman 	pm_runtime_set_autosuspend_delay(&intf->dev,
8188465def4SGreg Kroah-Hartman 					 GB_INTERFACE_AUTOSUSPEND_MS);
8198465def4SGreg Kroah-Hartman 
8208465def4SGreg Kroah-Hartman 	trace_gb_interface_create(intf);
8218465def4SGreg Kroah-Hartman 
8228465def4SGreg Kroah-Hartman 	return intf;
8238465def4SGreg Kroah-Hartman }
8248465def4SGreg Kroah-Hartman 
8258465def4SGreg Kroah-Hartman static int gb_interface_vsys_set(struct gb_interface *intf, bool enable)
8268465def4SGreg Kroah-Hartman {
8278465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
8288465def4SGreg Kroah-Hartman 	int ret;
8298465def4SGreg Kroah-Hartman 
8308465def4SGreg Kroah-Hartman 	dev_dbg(&intf->dev, "%s - %d\n", __func__, enable);
8318465def4SGreg Kroah-Hartman 
8328465def4SGreg Kroah-Hartman 	ret = gb_svc_intf_vsys_set(svc, intf->interface_id, enable);
8338465def4SGreg Kroah-Hartman 	if (ret) {
8348465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to set v_sys: %d\n", ret);
8358465def4SGreg Kroah-Hartman 		return ret;
8368465def4SGreg Kroah-Hartman 	}
8378465def4SGreg Kroah-Hartman 
8388465def4SGreg Kroah-Hartman 	return 0;
8398465def4SGreg Kroah-Hartman }
8408465def4SGreg Kroah-Hartman 
8418465def4SGreg Kroah-Hartman static int gb_interface_refclk_set(struct gb_interface *intf, bool enable)
8428465def4SGreg Kroah-Hartman {
8438465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
8448465def4SGreg Kroah-Hartman 	int ret;
8458465def4SGreg Kroah-Hartman 
8468465def4SGreg Kroah-Hartman 	dev_dbg(&intf->dev, "%s - %d\n", __func__, enable);
8478465def4SGreg Kroah-Hartman 
8488465def4SGreg Kroah-Hartman 	ret = gb_svc_intf_refclk_set(svc, intf->interface_id, enable);
8498465def4SGreg Kroah-Hartman 	if (ret) {
8508465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to set refclk: %d\n", ret);
8518465def4SGreg Kroah-Hartman 		return ret;
8528465def4SGreg Kroah-Hartman 	}
8538465def4SGreg Kroah-Hartman 
8548465def4SGreg Kroah-Hartman 	return 0;
8558465def4SGreg Kroah-Hartman }
8568465def4SGreg Kroah-Hartman 
8578465def4SGreg Kroah-Hartman static int gb_interface_unipro_set(struct gb_interface *intf, bool enable)
8588465def4SGreg Kroah-Hartman {
8598465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
8608465def4SGreg Kroah-Hartman 	int ret;
8618465def4SGreg Kroah-Hartman 
8628465def4SGreg Kroah-Hartman 	dev_dbg(&intf->dev, "%s - %d\n", __func__, enable);
8638465def4SGreg Kroah-Hartman 
8648465def4SGreg Kroah-Hartman 	ret = gb_svc_intf_unipro_set(svc, intf->interface_id, enable);
8658465def4SGreg Kroah-Hartman 	if (ret) {
8668465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to set UniPro: %d\n", ret);
8678465def4SGreg Kroah-Hartman 		return ret;
8688465def4SGreg Kroah-Hartman 	}
8698465def4SGreg Kroah-Hartman 
8708465def4SGreg Kroah-Hartman 	return 0;
8718465def4SGreg Kroah-Hartman }
8728465def4SGreg Kroah-Hartman 
8738465def4SGreg Kroah-Hartman static int gb_interface_activate_operation(struct gb_interface *intf,
8748465def4SGreg Kroah-Hartman 					   enum gb_interface_type *intf_type)
8758465def4SGreg Kroah-Hartman {
8768465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
8778465def4SGreg Kroah-Hartman 	u8 type;
8788465def4SGreg Kroah-Hartman 	int ret;
8798465def4SGreg Kroah-Hartman 
8808465def4SGreg Kroah-Hartman 	dev_dbg(&intf->dev, "%s\n", __func__);
8818465def4SGreg Kroah-Hartman 
8828465def4SGreg Kroah-Hartman 	ret = gb_svc_intf_activate(svc, intf->interface_id, &type);
8838465def4SGreg Kroah-Hartman 	if (ret) {
8848465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to activate: %d\n", ret);
8858465def4SGreg Kroah-Hartman 		return ret;
8868465def4SGreg Kroah-Hartman 	}
8878465def4SGreg Kroah-Hartman 
8888465def4SGreg Kroah-Hartman 	switch (type) {
8898465def4SGreg Kroah-Hartman 	case GB_SVC_INTF_TYPE_DUMMY:
8908465def4SGreg Kroah-Hartman 		*intf_type = GB_INTERFACE_TYPE_DUMMY;
8918465def4SGreg Kroah-Hartman 		/* FIXME: handle as an error for now */
8928465def4SGreg Kroah-Hartman 		return -ENODEV;
8938465def4SGreg Kroah-Hartman 	case GB_SVC_INTF_TYPE_UNIPRO:
8948465def4SGreg Kroah-Hartman 		*intf_type = GB_INTERFACE_TYPE_UNIPRO;
8958465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "interface type UniPro not supported\n");
8968465def4SGreg Kroah-Hartman 		/* FIXME: handle as an error for now */
8978465def4SGreg Kroah-Hartman 		return -ENODEV;
8988465def4SGreg Kroah-Hartman 	case GB_SVC_INTF_TYPE_GREYBUS:
8998465def4SGreg Kroah-Hartman 		*intf_type = GB_INTERFACE_TYPE_GREYBUS;
9008465def4SGreg Kroah-Hartman 		break;
9018465def4SGreg Kroah-Hartman 	default:
9028465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "unknown interface type: %u\n", type);
9038465def4SGreg Kroah-Hartman 		*intf_type = GB_INTERFACE_TYPE_UNKNOWN;
9048465def4SGreg Kroah-Hartman 		return -ENODEV;
9058465def4SGreg Kroah-Hartman 	}
9068465def4SGreg Kroah-Hartman 
9078465def4SGreg Kroah-Hartman 	return 0;
9088465def4SGreg Kroah-Hartman }
9098465def4SGreg Kroah-Hartman 
9108465def4SGreg Kroah-Hartman static int gb_interface_hibernate_link(struct gb_interface *intf)
9118465def4SGreg Kroah-Hartman {
9128465def4SGreg Kroah-Hartman 	struct gb_svc *svc = intf->hd->svc;
9138465def4SGreg Kroah-Hartman 
9148465def4SGreg Kroah-Hartman 	return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id);
9158465def4SGreg Kroah-Hartman }
9168465def4SGreg Kroah-Hartman 
9178465def4SGreg Kroah-Hartman static int _gb_interface_activate(struct gb_interface *intf,
9188465def4SGreg Kroah-Hartman 				  enum gb_interface_type *type)
9198465def4SGreg Kroah-Hartman {
9208465def4SGreg Kroah-Hartman 	int ret;
9218465def4SGreg Kroah-Hartman 
9228465def4SGreg Kroah-Hartman 	*type = GB_INTERFACE_TYPE_UNKNOWN;
9238465def4SGreg Kroah-Hartman 
9248465def4SGreg Kroah-Hartman 	if (intf->ejected || intf->removed)
9258465def4SGreg Kroah-Hartman 		return -ENODEV;
9268465def4SGreg Kroah-Hartman 
9278465def4SGreg Kroah-Hartman 	ret = gb_interface_vsys_set(intf, true);
9288465def4SGreg Kroah-Hartman 	if (ret)
9298465def4SGreg Kroah-Hartman 		return ret;
9308465def4SGreg Kroah-Hartman 
9318465def4SGreg Kroah-Hartman 	ret = gb_interface_refclk_set(intf, true);
9328465def4SGreg Kroah-Hartman 	if (ret)
9338465def4SGreg Kroah-Hartman 		goto err_vsys_disable;
9348465def4SGreg Kroah-Hartman 
9358465def4SGreg Kroah-Hartman 	ret = gb_interface_unipro_set(intf, true);
9368465def4SGreg Kroah-Hartman 	if (ret)
9378465def4SGreg Kroah-Hartman 		goto err_refclk_disable;
9388465def4SGreg Kroah-Hartman 
9398465def4SGreg Kroah-Hartman 	ret = gb_interface_activate_operation(intf, type);
9408465def4SGreg Kroah-Hartman 	if (ret) {
9418465def4SGreg Kroah-Hartman 		switch (*type) {
9428465def4SGreg Kroah-Hartman 		case GB_INTERFACE_TYPE_UNIPRO:
9438465def4SGreg Kroah-Hartman 		case GB_INTERFACE_TYPE_GREYBUS:
9448465def4SGreg Kroah-Hartman 			goto err_hibernate_link;
9458465def4SGreg Kroah-Hartman 		default:
9468465def4SGreg Kroah-Hartman 			goto err_unipro_disable;
9478465def4SGreg Kroah-Hartman 		}
9488465def4SGreg Kroah-Hartman 	}
9498465def4SGreg Kroah-Hartman 
9508465def4SGreg Kroah-Hartman 	ret = gb_interface_read_dme(intf);
9518465def4SGreg Kroah-Hartman 	if (ret)
9528465def4SGreg Kroah-Hartman 		goto err_hibernate_link;
9538465def4SGreg Kroah-Hartman 
9548465def4SGreg Kroah-Hartman 	ret = gb_interface_route_create(intf);
9558465def4SGreg Kroah-Hartman 	if (ret)
9568465def4SGreg Kroah-Hartman 		goto err_hibernate_link;
9578465def4SGreg Kroah-Hartman 
9588465def4SGreg Kroah-Hartman 	intf->active = true;
9598465def4SGreg Kroah-Hartman 
9608465def4SGreg Kroah-Hartman 	trace_gb_interface_activate(intf);
9618465def4SGreg Kroah-Hartman 
9628465def4SGreg Kroah-Hartman 	return 0;
9638465def4SGreg Kroah-Hartman 
9648465def4SGreg Kroah-Hartman err_hibernate_link:
9658465def4SGreg Kroah-Hartman 	gb_interface_hibernate_link(intf);
9668465def4SGreg Kroah-Hartman err_unipro_disable:
9678465def4SGreg Kroah-Hartman 	gb_interface_unipro_set(intf, false);
9688465def4SGreg Kroah-Hartman err_refclk_disable:
9698465def4SGreg Kroah-Hartman 	gb_interface_refclk_set(intf, false);
9708465def4SGreg Kroah-Hartman err_vsys_disable:
9718465def4SGreg Kroah-Hartman 	gb_interface_vsys_set(intf, false);
9728465def4SGreg Kroah-Hartman 
9738465def4SGreg Kroah-Hartman 	return ret;
9748465def4SGreg Kroah-Hartman }
9758465def4SGreg Kroah-Hartman 
9768465def4SGreg Kroah-Hartman /*
9778465def4SGreg Kroah-Hartman  * At present, we assume a UniPro-only module to be a Greybus module that
9788465def4SGreg Kroah-Hartman  * failed to send its mailbox poke. There is some reason to believe that this
9798465def4SGreg Kroah-Hartman  * is because of a bug in the ES3 bootrom.
9808465def4SGreg Kroah-Hartman  *
9818465def4SGreg Kroah-Hartman  * FIXME: Check if this is a Toshiba bridge before retrying?
9828465def4SGreg Kroah-Hartman  */
9838465def4SGreg Kroah-Hartman static int _gb_interface_activate_es3_hack(struct gb_interface *intf,
9848465def4SGreg Kroah-Hartman 					   enum gb_interface_type *type)
9858465def4SGreg Kroah-Hartman {
9868465def4SGreg Kroah-Hartman 	int retries = 3;
9878465def4SGreg Kroah-Hartman 	int ret;
9888465def4SGreg Kroah-Hartman 
9898465def4SGreg Kroah-Hartman 	while (retries--) {
9908465def4SGreg Kroah-Hartman 		ret = _gb_interface_activate(intf, type);
9918465def4SGreg Kroah-Hartman 		if (ret == -ENODEV && *type == GB_INTERFACE_TYPE_UNIPRO)
9928465def4SGreg Kroah-Hartman 			continue;
9938465def4SGreg Kroah-Hartman 
9948465def4SGreg Kroah-Hartman 		break;
9958465def4SGreg Kroah-Hartman 	}
9968465def4SGreg Kroah-Hartman 
9978465def4SGreg Kroah-Hartman 	return ret;
9988465def4SGreg Kroah-Hartman }
9998465def4SGreg Kroah-Hartman 
10008465def4SGreg Kroah-Hartman /*
10018465def4SGreg Kroah-Hartman  * Activate an interface.
10028465def4SGreg Kroah-Hartman  *
10038465def4SGreg Kroah-Hartman  * Locking: Caller holds the interface mutex.
10048465def4SGreg Kroah-Hartman  */
10058465def4SGreg Kroah-Hartman int gb_interface_activate(struct gb_interface *intf)
10068465def4SGreg Kroah-Hartman {
10078465def4SGreg Kroah-Hartman 	enum gb_interface_type type;
10088465def4SGreg Kroah-Hartman 	int ret;
10098465def4SGreg Kroah-Hartman 
10108465def4SGreg Kroah-Hartman 	switch (intf->type) {
10118465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_INVALID:
10128465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_GREYBUS:
10138465def4SGreg Kroah-Hartman 		ret = _gb_interface_activate_es3_hack(intf, &type);
10148465def4SGreg Kroah-Hartman 		break;
10158465def4SGreg Kroah-Hartman 	default:
10168465def4SGreg Kroah-Hartman 		ret = _gb_interface_activate(intf, &type);
10178465def4SGreg Kroah-Hartman 	}
10188465def4SGreg Kroah-Hartman 
10198465def4SGreg Kroah-Hartman 	/* Make sure type is detected correctly during reactivation. */
10208465def4SGreg Kroah-Hartman 	if (intf->type != GB_INTERFACE_TYPE_INVALID) {
10218465def4SGreg Kroah-Hartman 		if (type != intf->type) {
10228465def4SGreg Kroah-Hartman 			dev_err(&intf->dev, "failed to detect interface type\n");
10238465def4SGreg Kroah-Hartman 
10248465def4SGreg Kroah-Hartman 			if (!ret)
10258465def4SGreg Kroah-Hartman 				gb_interface_deactivate(intf);
10268465def4SGreg Kroah-Hartman 
10278465def4SGreg Kroah-Hartman 			return -EIO;
10288465def4SGreg Kroah-Hartman 		}
10298465def4SGreg Kroah-Hartman 	} else {
10308465def4SGreg Kroah-Hartman 		intf->type = type;
10318465def4SGreg Kroah-Hartman 	}
10328465def4SGreg Kroah-Hartman 
10338465def4SGreg Kroah-Hartman 	return ret;
10348465def4SGreg Kroah-Hartman }
10358465def4SGreg Kroah-Hartman 
10368465def4SGreg Kroah-Hartman /*
10378465def4SGreg Kroah-Hartman  * Deactivate an interface.
10388465def4SGreg Kroah-Hartman  *
10398465def4SGreg Kroah-Hartman  * Locking: Caller holds the interface mutex.
10408465def4SGreg Kroah-Hartman  */
10418465def4SGreg Kroah-Hartman void gb_interface_deactivate(struct gb_interface *intf)
10428465def4SGreg Kroah-Hartman {
10438465def4SGreg Kroah-Hartman 	if (!intf->active)
10448465def4SGreg Kroah-Hartman 		return;
10458465def4SGreg Kroah-Hartman 
10468465def4SGreg Kroah-Hartman 	trace_gb_interface_deactivate(intf);
10478465def4SGreg Kroah-Hartman 
10488465def4SGreg Kroah-Hartman 	/* Abort any ongoing mode switch. */
10498465def4SGreg Kroah-Hartman 	if (intf->mode_switch)
10508465def4SGreg Kroah-Hartman 		complete(&intf->mode_switch_completion);
10518465def4SGreg Kroah-Hartman 
10528465def4SGreg Kroah-Hartman 	gb_interface_route_destroy(intf);
10538465def4SGreg Kroah-Hartman 	gb_interface_hibernate_link(intf);
10548465def4SGreg Kroah-Hartman 	gb_interface_unipro_set(intf, false);
10558465def4SGreg Kroah-Hartman 	gb_interface_refclk_set(intf, false);
10568465def4SGreg Kroah-Hartman 	gb_interface_vsys_set(intf, false);
10578465def4SGreg Kroah-Hartman 
10588465def4SGreg Kroah-Hartman 	intf->active = false;
10598465def4SGreg Kroah-Hartman }
10608465def4SGreg Kroah-Hartman 
10618465def4SGreg Kroah-Hartman /*
10628465def4SGreg Kroah-Hartman  * Enable an interface by enabling its control connection, fetching the
10638465def4SGreg Kroah-Hartman  * manifest and other information over it, and finally registering its child
10648465def4SGreg Kroah-Hartman  * devices.
10658465def4SGreg Kroah-Hartman  *
10668465def4SGreg Kroah-Hartman  * Locking: Caller holds the interface mutex.
10678465def4SGreg Kroah-Hartman  */
10688465def4SGreg Kroah-Hartman int gb_interface_enable(struct gb_interface *intf)
10698465def4SGreg Kroah-Hartman {
10708465def4SGreg Kroah-Hartman 	struct gb_control *control;
10718465def4SGreg Kroah-Hartman 	struct gb_bundle *bundle, *tmp;
10728465def4SGreg Kroah-Hartman 	int ret, size;
10738465def4SGreg Kroah-Hartman 	void *manifest;
10748465def4SGreg Kroah-Hartman 
10758465def4SGreg Kroah-Hartman 	ret = gb_interface_read_and_clear_init_status(intf);
10768465def4SGreg Kroah-Hartman 	if (ret) {
10778465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to clear init status: %d\n", ret);
10788465def4SGreg Kroah-Hartman 		return ret;
10798465def4SGreg Kroah-Hartman 	}
10808465def4SGreg Kroah-Hartman 
10818465def4SGreg Kroah-Hartman 	/* Establish control connection */
10828465def4SGreg Kroah-Hartman 	control = gb_control_create(intf);
10838465def4SGreg Kroah-Hartman 	if (IS_ERR(control)) {
10848465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to create control device: %ld\n",
10858465def4SGreg Kroah-Hartman 			PTR_ERR(control));
10868465def4SGreg Kroah-Hartman 		return PTR_ERR(control);
10878465def4SGreg Kroah-Hartman 	}
10888465def4SGreg Kroah-Hartman 	intf->control = control;
10898465def4SGreg Kroah-Hartman 
10908465def4SGreg Kroah-Hartman 	ret = gb_control_enable(intf->control);
10918465def4SGreg Kroah-Hartman 	if (ret)
10928465def4SGreg Kroah-Hartman 		goto err_put_control;
10938465def4SGreg Kroah-Hartman 
10948465def4SGreg Kroah-Hartman 	/* Get manifest size using control protocol on CPort */
10958465def4SGreg Kroah-Hartman 	size = gb_control_get_manifest_size_operation(intf);
10968465def4SGreg Kroah-Hartman 	if (size <= 0) {
10978465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to get manifest size: %d\n", size);
10988465def4SGreg Kroah-Hartman 
10998465def4SGreg Kroah-Hartman 		if (size)
11008465def4SGreg Kroah-Hartman 			ret = size;
11018465def4SGreg Kroah-Hartman 		else
11028465def4SGreg Kroah-Hartman 			ret =  -EINVAL;
11038465def4SGreg Kroah-Hartman 
11048465def4SGreg Kroah-Hartman 		goto err_disable_control;
11058465def4SGreg Kroah-Hartman 	}
11068465def4SGreg Kroah-Hartman 
11078465def4SGreg Kroah-Hartman 	manifest = kmalloc(size, GFP_KERNEL);
11088465def4SGreg Kroah-Hartman 	if (!manifest) {
11098465def4SGreg Kroah-Hartman 		ret = -ENOMEM;
11108465def4SGreg Kroah-Hartman 		goto err_disable_control;
11118465def4SGreg Kroah-Hartman 	}
11128465def4SGreg Kroah-Hartman 
11138465def4SGreg Kroah-Hartman 	/* Get manifest using control protocol on CPort */
11148465def4SGreg Kroah-Hartman 	ret = gb_control_get_manifest_operation(intf, manifest, size);
11158465def4SGreg Kroah-Hartman 	if (ret) {
11168465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to get manifest: %d\n", ret);
11178465def4SGreg Kroah-Hartman 		goto err_free_manifest;
11188465def4SGreg Kroah-Hartman 	}
11198465def4SGreg Kroah-Hartman 
11208465def4SGreg Kroah-Hartman 	/*
11218465def4SGreg Kroah-Hartman 	 * Parse the manifest and build up our data structures representing
11228465def4SGreg Kroah-Hartman 	 * what's in it.
11238465def4SGreg Kroah-Hartman 	 */
11248465def4SGreg Kroah-Hartman 	if (!gb_manifest_parse(intf, manifest, size)) {
11258465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to parse manifest\n");
11268465def4SGreg Kroah-Hartman 		ret = -EINVAL;
11278465def4SGreg Kroah-Hartman 		goto err_destroy_bundles;
11288465def4SGreg Kroah-Hartman 	}
11298465def4SGreg Kroah-Hartman 
11308465def4SGreg Kroah-Hartman 	ret = gb_control_get_bundle_versions(intf->control);
11318465def4SGreg Kroah-Hartman 	if (ret)
11328465def4SGreg Kroah-Hartman 		goto err_destroy_bundles;
11338465def4SGreg Kroah-Hartman 
11348465def4SGreg Kroah-Hartman 	/* Register the control device and any bundles */
11358465def4SGreg Kroah-Hartman 	ret = gb_control_add(intf->control);
11368465def4SGreg Kroah-Hartman 	if (ret)
11378465def4SGreg Kroah-Hartman 		goto err_destroy_bundles;
11388465def4SGreg Kroah-Hartman 
11398465def4SGreg Kroah-Hartman 	pm_runtime_use_autosuspend(&intf->dev);
11408465def4SGreg Kroah-Hartman 	pm_runtime_get_noresume(&intf->dev);
11418465def4SGreg Kroah-Hartman 	pm_runtime_set_active(&intf->dev);
11428465def4SGreg Kroah-Hartman 	pm_runtime_enable(&intf->dev);
11438465def4SGreg Kroah-Hartman 
11448465def4SGreg Kroah-Hartman 	list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) {
11458465def4SGreg Kroah-Hartman 		ret = gb_bundle_add(bundle);
11468465def4SGreg Kroah-Hartman 		if (ret) {
11478465def4SGreg Kroah-Hartman 			gb_bundle_destroy(bundle);
11488465def4SGreg Kroah-Hartman 			continue;
11498465def4SGreg Kroah-Hartman 		}
11508465def4SGreg Kroah-Hartman 	}
11518465def4SGreg Kroah-Hartman 
11528465def4SGreg Kroah-Hartman 	kfree(manifest);
11538465def4SGreg Kroah-Hartman 
11548465def4SGreg Kroah-Hartman 	intf->enabled = true;
11558465def4SGreg Kroah-Hartman 
11568465def4SGreg Kroah-Hartman 	pm_runtime_put(&intf->dev);
11578465def4SGreg Kroah-Hartman 
11588465def4SGreg Kroah-Hartman 	trace_gb_interface_enable(intf);
11598465def4SGreg Kroah-Hartman 
11608465def4SGreg Kroah-Hartman 	return 0;
11618465def4SGreg Kroah-Hartman 
11628465def4SGreg Kroah-Hartman err_destroy_bundles:
11638465def4SGreg Kroah-Hartman 	list_for_each_entry_safe(bundle, tmp, &intf->bundles, links)
11648465def4SGreg Kroah-Hartman 		gb_bundle_destroy(bundle);
11658465def4SGreg Kroah-Hartman err_free_manifest:
11668465def4SGreg Kroah-Hartman 	kfree(manifest);
11678465def4SGreg Kroah-Hartman err_disable_control:
11688465def4SGreg Kroah-Hartman 	gb_control_disable(intf->control);
11698465def4SGreg Kroah-Hartman err_put_control:
11708465def4SGreg Kroah-Hartman 	gb_control_put(intf->control);
11718465def4SGreg Kroah-Hartman 	intf->control = NULL;
11728465def4SGreg Kroah-Hartman 
11738465def4SGreg Kroah-Hartman 	return ret;
11748465def4SGreg Kroah-Hartman }
11758465def4SGreg Kroah-Hartman 
11768465def4SGreg Kroah-Hartman /*
11778465def4SGreg Kroah-Hartman  * Disable an interface and destroy its bundles.
11788465def4SGreg Kroah-Hartman  *
11798465def4SGreg Kroah-Hartman  * Locking: Caller holds the interface mutex.
11808465def4SGreg Kroah-Hartman  */
11818465def4SGreg Kroah-Hartman void gb_interface_disable(struct gb_interface *intf)
11828465def4SGreg Kroah-Hartman {
11838465def4SGreg Kroah-Hartman 	struct gb_bundle *bundle;
11848465def4SGreg Kroah-Hartman 	struct gb_bundle *next;
11858465def4SGreg Kroah-Hartman 
11868465def4SGreg Kroah-Hartman 	if (!intf->enabled)
11878465def4SGreg Kroah-Hartman 		return;
11888465def4SGreg Kroah-Hartman 
11898465def4SGreg Kroah-Hartman 	trace_gb_interface_disable(intf);
11908465def4SGreg Kroah-Hartman 
11918465def4SGreg Kroah-Hartman 	pm_runtime_get_sync(&intf->dev);
11928465def4SGreg Kroah-Hartman 
11938465def4SGreg Kroah-Hartman 	/* Set disconnected flag to avoid I/O during connection tear down. */
11948465def4SGreg Kroah-Hartman 	if (intf->quirks & GB_INTERFACE_QUIRK_FORCED_DISABLE)
11958465def4SGreg Kroah-Hartman 		intf->disconnected = true;
11968465def4SGreg Kroah-Hartman 
11978465def4SGreg Kroah-Hartman 	list_for_each_entry_safe(bundle, next, &intf->bundles, links)
11988465def4SGreg Kroah-Hartman 		gb_bundle_destroy(bundle);
11998465def4SGreg Kroah-Hartman 
12008465def4SGreg Kroah-Hartman 	if (!intf->mode_switch && !intf->disconnected)
12018465def4SGreg Kroah-Hartman 		gb_control_interface_deactivate_prepare(intf->control);
12028465def4SGreg Kroah-Hartman 
12038465def4SGreg Kroah-Hartman 	gb_control_del(intf->control);
12048465def4SGreg Kroah-Hartman 	gb_control_disable(intf->control);
12058465def4SGreg Kroah-Hartman 	gb_control_put(intf->control);
12068465def4SGreg Kroah-Hartman 	intf->control = NULL;
12078465def4SGreg Kroah-Hartman 
12088465def4SGreg Kroah-Hartman 	intf->enabled = false;
12098465def4SGreg Kroah-Hartman 
12108465def4SGreg Kroah-Hartman 	pm_runtime_disable(&intf->dev);
12118465def4SGreg Kroah-Hartman 	pm_runtime_set_suspended(&intf->dev);
12128465def4SGreg Kroah-Hartman 	pm_runtime_dont_use_autosuspend(&intf->dev);
12138465def4SGreg Kroah-Hartman 	pm_runtime_put_noidle(&intf->dev);
12148465def4SGreg Kroah-Hartman }
12158465def4SGreg Kroah-Hartman 
12168465def4SGreg Kroah-Hartman /* Register an interface. */
12178465def4SGreg Kroah-Hartman int gb_interface_add(struct gb_interface *intf)
12188465def4SGreg Kroah-Hartman {
12198465def4SGreg Kroah-Hartman 	int ret;
12208465def4SGreg Kroah-Hartman 
12218465def4SGreg Kroah-Hartman 	ret = device_add(&intf->dev);
12228465def4SGreg Kroah-Hartman 	if (ret) {
12238465def4SGreg Kroah-Hartman 		dev_err(&intf->dev, "failed to register interface: %d\n", ret);
12248465def4SGreg Kroah-Hartman 		return ret;
12258465def4SGreg Kroah-Hartman 	}
12268465def4SGreg Kroah-Hartman 
12278465def4SGreg Kroah-Hartman 	trace_gb_interface_add(intf);
12288465def4SGreg Kroah-Hartman 
12298465def4SGreg Kroah-Hartman 	dev_info(&intf->dev, "Interface added (%s)\n",
12308465def4SGreg Kroah-Hartman 		 gb_interface_type_string(intf));
12318465def4SGreg Kroah-Hartman 
12328465def4SGreg Kroah-Hartman 	switch (intf->type) {
12338465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_GREYBUS:
12348465def4SGreg Kroah-Hartman 		dev_info(&intf->dev, "GMP VID=0x%08x, PID=0x%08x\n",
12358465def4SGreg Kroah-Hartman 			 intf->vendor_id, intf->product_id);
123637b8b73fSGustavo A. R. Silva 		fallthrough;
12378465def4SGreg Kroah-Hartman 	case GB_INTERFACE_TYPE_UNIPRO:
12388465def4SGreg Kroah-Hartman 		dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n",
12398465def4SGreg Kroah-Hartman 			 intf->ddbl1_manufacturer_id,
12408465def4SGreg Kroah-Hartman 			 intf->ddbl1_product_id);
12418465def4SGreg Kroah-Hartman 		break;
12428465def4SGreg Kroah-Hartman 	default:
12438465def4SGreg Kroah-Hartman 		break;
12448465def4SGreg Kroah-Hartman 	}
12458465def4SGreg Kroah-Hartman 
12468465def4SGreg Kroah-Hartman 	return 0;
12478465def4SGreg Kroah-Hartman }
12488465def4SGreg Kroah-Hartman 
12498465def4SGreg Kroah-Hartman /* Deregister an interface. */
12508465def4SGreg Kroah-Hartman void gb_interface_del(struct gb_interface *intf)
12518465def4SGreg Kroah-Hartman {
12528465def4SGreg Kroah-Hartman 	if (device_is_registered(&intf->dev)) {
12538465def4SGreg Kroah-Hartman 		trace_gb_interface_del(intf);
12548465def4SGreg Kroah-Hartman 
12558465def4SGreg Kroah-Hartman 		device_del(&intf->dev);
12568465def4SGreg Kroah-Hartman 		dev_info(&intf->dev, "Interface removed\n");
12578465def4SGreg Kroah-Hartman 	}
12588465def4SGreg Kroah-Hartman }
12598465def4SGreg Kroah-Hartman 
12608465def4SGreg Kroah-Hartman void gb_interface_put(struct gb_interface *intf)
12618465def4SGreg Kroah-Hartman {
12628465def4SGreg Kroah-Hartman 	put_device(&intf->dev);
12638465def4SGreg Kroah-Hartman }
1264