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