18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Incremental bus scan, based on bus topology 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2006 Kristian Hoegsberg <krh@bitplanet.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bug.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/firewire.h> 118c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h> 128c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/list.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/atomic.h> 208c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "core.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define SELF_ID_PHY_ID(q) (((q) >> 24) & 0x3f) 258c2ecf20Sopenharmony_ci#define SELF_ID_EXTENDED(q) (((q) >> 23) & 0x01) 268c2ecf20Sopenharmony_ci#define SELF_ID_LINK_ON(q) (((q) >> 22) & 0x01) 278c2ecf20Sopenharmony_ci#define SELF_ID_GAP_COUNT(q) (((q) >> 16) & 0x3f) 288c2ecf20Sopenharmony_ci#define SELF_ID_PHY_SPEED(q) (((q) >> 14) & 0x03) 298c2ecf20Sopenharmony_ci#define SELF_ID_CONTENDER(q) (((q) >> 11) & 0x01) 308c2ecf20Sopenharmony_ci#define SELF_ID_PHY_INITIATOR(q) (((q) >> 1) & 0x01) 318c2ecf20Sopenharmony_ci#define SELF_ID_MORE_PACKETS(q) (((q) >> 0) & 0x01) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define SELF_ID_EXT_SEQUENCE(q) (((q) >> 20) & 0x07) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define SELFID_PORT_CHILD 0x3 368c2ecf20Sopenharmony_ci#define SELFID_PORT_PARENT 0x2 378c2ecf20Sopenharmony_ci#define SELFID_PORT_NCONN 0x1 388c2ecf20Sopenharmony_ci#define SELFID_PORT_NONE 0x0 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic u32 *count_ports(u32 *sid, int *total_port_count, int *child_port_count) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci u32 q; 438c2ecf20Sopenharmony_ci int port_type, shift, seq; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci *total_port_count = 0; 468c2ecf20Sopenharmony_ci *child_port_count = 0; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci shift = 6; 498c2ecf20Sopenharmony_ci q = *sid; 508c2ecf20Sopenharmony_ci seq = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci while (1) { 538c2ecf20Sopenharmony_ci port_type = (q >> shift) & 0x03; 548c2ecf20Sopenharmony_ci switch (port_type) { 558c2ecf20Sopenharmony_ci case SELFID_PORT_CHILD: 568c2ecf20Sopenharmony_ci (*child_port_count)++; 578c2ecf20Sopenharmony_ci fallthrough; 588c2ecf20Sopenharmony_ci case SELFID_PORT_PARENT: 598c2ecf20Sopenharmony_ci case SELFID_PORT_NCONN: 608c2ecf20Sopenharmony_ci (*total_port_count)++; 618c2ecf20Sopenharmony_ci case SELFID_PORT_NONE: 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci shift -= 2; 668c2ecf20Sopenharmony_ci if (shift == 0) { 678c2ecf20Sopenharmony_ci if (!SELF_ID_MORE_PACKETS(q)) 688c2ecf20Sopenharmony_ci return sid + 1; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci shift = 16; 718c2ecf20Sopenharmony_ci sid++; 728c2ecf20Sopenharmony_ci q = *sid; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Check that the extra packets actually are 768c2ecf20Sopenharmony_ci * extended self ID packets and that the 778c2ecf20Sopenharmony_ci * sequence numbers in the extended self ID 788c2ecf20Sopenharmony_ci * packets increase as expected. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (!SELF_ID_EXTENDED(q) || 828c2ecf20Sopenharmony_ci seq != SELF_ID_EXT_SEQUENCE(q)) 838c2ecf20Sopenharmony_ci return NULL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci seq++; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int get_port_type(u32 *sid, int port_index) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int index, shift; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci index = (port_index + 5) / 8; 958c2ecf20Sopenharmony_ci shift = 16 - ((port_index + 5) & 7) * 2; 968c2ecf20Sopenharmony_ci return (sid[index] >> shift) & 0x03; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct fw_node *fw_node_create(u32 sid, int port_count, int color) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct fw_node *node; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci node = kzalloc(struct_size(node, ports, port_count), GFP_ATOMIC); 1048c2ecf20Sopenharmony_ci if (node == NULL) 1058c2ecf20Sopenharmony_ci return NULL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci node->color = color; 1088c2ecf20Sopenharmony_ci node->node_id = LOCAL_BUS | SELF_ID_PHY_ID(sid); 1098c2ecf20Sopenharmony_ci node->link_on = SELF_ID_LINK_ON(sid); 1108c2ecf20Sopenharmony_ci node->phy_speed = SELF_ID_PHY_SPEED(sid); 1118c2ecf20Sopenharmony_ci node->initiated_reset = SELF_ID_PHY_INITIATOR(sid); 1128c2ecf20Sopenharmony_ci node->port_count = port_count; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci refcount_set(&node->ref_count, 1); 1158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&node->link); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return node; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Compute the maximum hop count for this node and it's children. The 1228c2ecf20Sopenharmony_ci * maximum hop count is the maximum number of connections between any 1238c2ecf20Sopenharmony_ci * two nodes in the subtree rooted at this node. We need this for 1248c2ecf20Sopenharmony_ci * setting the gap count. As we build the tree bottom up in 1258c2ecf20Sopenharmony_ci * build_tree() below, this is fairly easy to do: for each node we 1268c2ecf20Sopenharmony_ci * maintain the max hop count and the max depth, ie the number of hops 1278c2ecf20Sopenharmony_ci * to the furthest leaf. Computing the max hop count breaks down into 1288c2ecf20Sopenharmony_ci * two cases: either the path goes through this node, in which case 1298c2ecf20Sopenharmony_ci * the hop count is the sum of the two biggest child depths plus 2. 1308c2ecf20Sopenharmony_ci * Or it could be the case that the max hop path is entirely 1318c2ecf20Sopenharmony_ci * containted in a child tree, in which case the max hop count is just 1328c2ecf20Sopenharmony_ci * the max hop count of this child. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistatic void update_hop_count(struct fw_node *node) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int depths[2] = { -1, -1 }; 1378c2ecf20Sopenharmony_ci int max_child_hops = 0; 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (i = 0; i < node->port_count; i++) { 1418c2ecf20Sopenharmony_ci if (node->ports[i] == NULL) 1428c2ecf20Sopenharmony_ci continue; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (node->ports[i]->max_hops > max_child_hops) 1458c2ecf20Sopenharmony_ci max_child_hops = node->ports[i]->max_hops; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (node->ports[i]->max_depth > depths[0]) { 1488c2ecf20Sopenharmony_ci depths[1] = depths[0]; 1498c2ecf20Sopenharmony_ci depths[0] = node->ports[i]->max_depth; 1508c2ecf20Sopenharmony_ci } else if (node->ports[i]->max_depth > depths[1]) 1518c2ecf20Sopenharmony_ci depths[1] = node->ports[i]->max_depth; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci node->max_depth = depths[0] + 1; 1558c2ecf20Sopenharmony_ci node->max_hops = max(max_child_hops, depths[0] + depths[1] + 2); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic inline struct fw_node *fw_node(struct list_head *l) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci return list_entry(l, struct fw_node, link); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * This function builds the tree representation of the topology given 1658c2ecf20Sopenharmony_ci * by the self IDs from the latest bus reset. During the construction 1668c2ecf20Sopenharmony_ci * of the tree, the function checks that the self IDs are valid and 1678c2ecf20Sopenharmony_ci * internally consistent. On success this function returns the 1688c2ecf20Sopenharmony_ci * fw_node corresponding to the local card otherwise NULL. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic struct fw_node *build_tree(struct fw_card *card, 1718c2ecf20Sopenharmony_ci u32 *sid, int self_id_count) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct fw_node *node, *child, *local_node, *irm_node; 1748c2ecf20Sopenharmony_ci struct list_head stack, *h; 1758c2ecf20Sopenharmony_ci u32 *next_sid, *end, q; 1768c2ecf20Sopenharmony_ci int i, port_count, child_port_count, phy_id, parent_count, stack_depth; 1778c2ecf20Sopenharmony_ci int gap_count; 1788c2ecf20Sopenharmony_ci bool beta_repeaters_present; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci local_node = NULL; 1818c2ecf20Sopenharmony_ci node = NULL; 1828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&stack); 1838c2ecf20Sopenharmony_ci stack_depth = 0; 1848c2ecf20Sopenharmony_ci end = sid + self_id_count; 1858c2ecf20Sopenharmony_ci phy_id = 0; 1868c2ecf20Sopenharmony_ci irm_node = NULL; 1878c2ecf20Sopenharmony_ci gap_count = SELF_ID_GAP_COUNT(*sid); 1888c2ecf20Sopenharmony_ci beta_repeaters_present = false; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci while (sid < end) { 1918c2ecf20Sopenharmony_ci next_sid = count_ports(sid, &port_count, &child_port_count); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (next_sid == NULL) { 1948c2ecf20Sopenharmony_ci fw_err(card, "inconsistent extended self IDs\n"); 1958c2ecf20Sopenharmony_ci return NULL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci q = *sid; 1998c2ecf20Sopenharmony_ci if (phy_id != SELF_ID_PHY_ID(q)) { 2008c2ecf20Sopenharmony_ci fw_err(card, "PHY ID mismatch in self ID: %d != %d\n", 2018c2ecf20Sopenharmony_ci phy_id, SELF_ID_PHY_ID(q)); 2028c2ecf20Sopenharmony_ci return NULL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (child_port_count > stack_depth) { 2068c2ecf20Sopenharmony_ci fw_err(card, "topology stack underflow\n"); 2078c2ecf20Sopenharmony_ci return NULL; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Seek back from the top of our stack to find the 2128c2ecf20Sopenharmony_ci * start of the child nodes for this node. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci for (i = 0, h = &stack; i < child_port_count; i++) 2158c2ecf20Sopenharmony_ci h = h->prev; 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * When the stack is empty, this yields an invalid value, 2188c2ecf20Sopenharmony_ci * but that pointer will never be dereferenced. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci child = fw_node(h); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci node = fw_node_create(q, port_count, card->color); 2238c2ecf20Sopenharmony_ci if (node == NULL) { 2248c2ecf20Sopenharmony_ci fw_err(card, "out of memory while building topology\n"); 2258c2ecf20Sopenharmony_ci return NULL; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (phy_id == (card->node_id & 0x3f)) 2298c2ecf20Sopenharmony_ci local_node = node; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (SELF_ID_CONTENDER(q)) 2328c2ecf20Sopenharmony_ci irm_node = node; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci parent_count = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci for (i = 0; i < port_count; i++) { 2378c2ecf20Sopenharmony_ci switch (get_port_type(sid, i)) { 2388c2ecf20Sopenharmony_ci case SELFID_PORT_PARENT: 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * Who's your daddy? We dont know the 2418c2ecf20Sopenharmony_ci * parent node at this time, so we 2428c2ecf20Sopenharmony_ci * temporarily abuse node->color for 2438c2ecf20Sopenharmony_ci * remembering the entry in the 2448c2ecf20Sopenharmony_ci * node->ports array where the parent 2458c2ecf20Sopenharmony_ci * node should be. Later, when we 2468c2ecf20Sopenharmony_ci * handle the parent node, we fix up 2478c2ecf20Sopenharmony_ci * the reference. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci parent_count++; 2508c2ecf20Sopenharmony_ci node->color = i; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci case SELFID_PORT_CHILD: 2548c2ecf20Sopenharmony_ci node->ports[i] = child; 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * Fix up parent reference for this 2578c2ecf20Sopenharmony_ci * child node. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci child->ports[child->color] = node; 2608c2ecf20Sopenharmony_ci child->color = card->color; 2618c2ecf20Sopenharmony_ci child = fw_node(child->link.next); 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * Check that the node reports exactly one parent 2688c2ecf20Sopenharmony_ci * port, except for the root, which of course should 2698c2ecf20Sopenharmony_ci * have no parents. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci if ((next_sid == end && parent_count != 0) || 2728c2ecf20Sopenharmony_ci (next_sid < end && parent_count != 1)) { 2738c2ecf20Sopenharmony_ci fw_err(card, "parent port inconsistency for node %d: " 2748c2ecf20Sopenharmony_ci "parent_count=%d\n", phy_id, parent_count); 2758c2ecf20Sopenharmony_ci return NULL; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Pop the child nodes off the stack and push the new node. */ 2798c2ecf20Sopenharmony_ci __list_del(h->prev, &stack); 2808c2ecf20Sopenharmony_ci list_add_tail(&node->link, &stack); 2818c2ecf20Sopenharmony_ci stack_depth += 1 - child_port_count; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (node->phy_speed == SCODE_BETA && 2848c2ecf20Sopenharmony_ci parent_count + child_port_count > 1) 2858c2ecf20Sopenharmony_ci beta_repeaters_present = true; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * If PHYs report different gap counts, set an invalid count 2898c2ecf20Sopenharmony_ci * which will force a gap count reconfiguration and a reset. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if (SELF_ID_GAP_COUNT(q) != gap_count) 2928c2ecf20Sopenharmony_ci gap_count = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci update_hop_count(node); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci sid = next_sid; 2978c2ecf20Sopenharmony_ci phy_id++; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci card->root_node = node; 3018c2ecf20Sopenharmony_ci card->irm_node = irm_node; 3028c2ecf20Sopenharmony_ci card->gap_count = gap_count; 3038c2ecf20Sopenharmony_ci card->beta_repeaters_present = beta_repeaters_present; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return local_node; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_citypedef void (*fw_node_callback_t)(struct fw_card * card, 3098c2ecf20Sopenharmony_ci struct fw_node * node, 3108c2ecf20Sopenharmony_ci struct fw_node * parent); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void for_each_fw_node(struct fw_card *card, struct fw_node *root, 3138c2ecf20Sopenharmony_ci fw_node_callback_t callback) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct list_head list; 3168c2ecf20Sopenharmony_ci struct fw_node *node, *next, *child, *parent; 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci fw_node_get(root); 3228c2ecf20Sopenharmony_ci list_add_tail(&root->link, &list); 3238c2ecf20Sopenharmony_ci parent = NULL; 3248c2ecf20Sopenharmony_ci list_for_each_entry(node, &list, link) { 3258c2ecf20Sopenharmony_ci node->color = card->color; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < node->port_count; i++) { 3288c2ecf20Sopenharmony_ci child = node->ports[i]; 3298c2ecf20Sopenharmony_ci if (!child) 3308c2ecf20Sopenharmony_ci continue; 3318c2ecf20Sopenharmony_ci if (child->color == card->color) 3328c2ecf20Sopenharmony_ci parent = child; 3338c2ecf20Sopenharmony_ci else { 3348c2ecf20Sopenharmony_ci fw_node_get(child); 3358c2ecf20Sopenharmony_ci list_add_tail(&child->link, &list); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci callback(card, node, parent); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci list_for_each_entry_safe(node, next, &list, link) 3438c2ecf20Sopenharmony_ci fw_node_put(node); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void report_lost_node(struct fw_card *card, 3478c2ecf20Sopenharmony_ci struct fw_node *node, struct fw_node *parent) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci fw_node_event(card, node, FW_NODE_DESTROYED); 3508c2ecf20Sopenharmony_ci fw_node_put(node); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Topology has changed - reset bus manager retry counter */ 3538c2ecf20Sopenharmony_ci card->bm_retries = 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void report_found_node(struct fw_card *card, 3578c2ecf20Sopenharmony_ci struct fw_node *node, struct fw_node *parent) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci int b_path = (node->phy_speed == SCODE_BETA); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (parent != NULL) { 3628c2ecf20Sopenharmony_ci /* min() macro doesn't work here with gcc 3.4 */ 3638c2ecf20Sopenharmony_ci node->max_speed = parent->max_speed < node->phy_speed ? 3648c2ecf20Sopenharmony_ci parent->max_speed : node->phy_speed; 3658c2ecf20Sopenharmony_ci node->b_path = parent->b_path && b_path; 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci node->max_speed = node->phy_speed; 3688c2ecf20Sopenharmony_ci node->b_path = b_path; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci fw_node_event(card, node, FW_NODE_CREATED); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Topology has changed - reset bus manager retry counter */ 3748c2ecf20Sopenharmony_ci card->bm_retries = 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* Must be called with card->lock held */ 3788c2ecf20Sopenharmony_civoid fw_destroy_nodes(struct fw_card *card) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci card->color++; 3818c2ecf20Sopenharmony_ci if (card->local_node != NULL) 3828c2ecf20Sopenharmony_ci for_each_fw_node(card, card->local_node, report_lost_node); 3838c2ecf20Sopenharmony_ci card->local_node = NULL; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void move_tree(struct fw_node *node0, struct fw_node *node1, int port) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct fw_node *tree; 3898c2ecf20Sopenharmony_ci int i; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci tree = node1->ports[port]; 3928c2ecf20Sopenharmony_ci node0->ports[port] = tree; 3938c2ecf20Sopenharmony_ci for (i = 0; i < tree->port_count; i++) { 3948c2ecf20Sopenharmony_ci if (tree->ports[i] == node1) { 3958c2ecf20Sopenharmony_ci tree->ports[i] = node0; 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* 4028c2ecf20Sopenharmony_ci * Compare the old topology tree for card with the new one specified by root. 4038c2ecf20Sopenharmony_ci * Queue the nodes and mark them as either found, lost or updated. 4048c2ecf20Sopenharmony_ci * Update the nodes in the card topology tree as we go. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_cistatic void update_tree(struct fw_card *card, struct fw_node *root) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct list_head list0, list1; 4098c2ecf20Sopenharmony_ci struct fw_node *node0, *node1, *next1; 4108c2ecf20Sopenharmony_ci int i, event; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list0); 4138c2ecf20Sopenharmony_ci list_add_tail(&card->local_node->link, &list0); 4148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list1); 4158c2ecf20Sopenharmony_ci list_add_tail(&root->link, &list1); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci node0 = fw_node(list0.next); 4188c2ecf20Sopenharmony_ci node1 = fw_node(list1.next); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci while (&node0->link != &list0) { 4218c2ecf20Sopenharmony_ci WARN_ON(node0->port_count != node1->port_count); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (node0->link_on && !node1->link_on) 4248c2ecf20Sopenharmony_ci event = FW_NODE_LINK_OFF; 4258c2ecf20Sopenharmony_ci else if (!node0->link_on && node1->link_on) 4268c2ecf20Sopenharmony_ci event = FW_NODE_LINK_ON; 4278c2ecf20Sopenharmony_ci else if (node1->initiated_reset && node1->link_on) 4288c2ecf20Sopenharmony_ci event = FW_NODE_INITIATED_RESET; 4298c2ecf20Sopenharmony_ci else 4308c2ecf20Sopenharmony_ci event = FW_NODE_UPDATED; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci node0->node_id = node1->node_id; 4338c2ecf20Sopenharmony_ci node0->color = card->color; 4348c2ecf20Sopenharmony_ci node0->link_on = node1->link_on; 4358c2ecf20Sopenharmony_ci node0->initiated_reset = node1->initiated_reset; 4368c2ecf20Sopenharmony_ci node0->max_hops = node1->max_hops; 4378c2ecf20Sopenharmony_ci node1->color = card->color; 4388c2ecf20Sopenharmony_ci fw_node_event(card, node0, event); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (card->root_node == node1) 4418c2ecf20Sopenharmony_ci card->root_node = node0; 4428c2ecf20Sopenharmony_ci if (card->irm_node == node1) 4438c2ecf20Sopenharmony_ci card->irm_node = node0; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci for (i = 0; i < node0->port_count; i++) { 4468c2ecf20Sopenharmony_ci if (node0->ports[i] && node1->ports[i]) { 4478c2ecf20Sopenharmony_ci /* 4488c2ecf20Sopenharmony_ci * This port didn't change, queue the 4498c2ecf20Sopenharmony_ci * connected node for further 4508c2ecf20Sopenharmony_ci * investigation. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci if (node0->ports[i]->color == card->color) 4538c2ecf20Sopenharmony_ci continue; 4548c2ecf20Sopenharmony_ci list_add_tail(&node0->ports[i]->link, &list0); 4558c2ecf20Sopenharmony_ci list_add_tail(&node1->ports[i]->link, &list1); 4568c2ecf20Sopenharmony_ci } else if (node0->ports[i]) { 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * The nodes connected here were 4598c2ecf20Sopenharmony_ci * unplugged; unref the lost nodes and 4608c2ecf20Sopenharmony_ci * queue FW_NODE_LOST callbacks for 4618c2ecf20Sopenharmony_ci * them. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for_each_fw_node(card, node0->ports[i], 4658c2ecf20Sopenharmony_ci report_lost_node); 4668c2ecf20Sopenharmony_ci node0->ports[i] = NULL; 4678c2ecf20Sopenharmony_ci } else if (node1->ports[i]) { 4688c2ecf20Sopenharmony_ci /* 4698c2ecf20Sopenharmony_ci * One or more node were connected to 4708c2ecf20Sopenharmony_ci * this port. Move the new nodes into 4718c2ecf20Sopenharmony_ci * the tree and queue FW_NODE_CREATED 4728c2ecf20Sopenharmony_ci * callbacks for them. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci move_tree(node0, node1, i); 4758c2ecf20Sopenharmony_ci for_each_fw_node(card, node0->ports[i], 4768c2ecf20Sopenharmony_ci report_found_node); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci node0 = fw_node(node0->link.next); 4818c2ecf20Sopenharmony_ci next1 = fw_node(node1->link.next); 4828c2ecf20Sopenharmony_ci fw_node_put(node1); 4838c2ecf20Sopenharmony_ci node1 = next1; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void update_topology_map(struct fw_card *card, 4888c2ecf20Sopenharmony_ci u32 *self_ids, int self_id_count) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci int node_count = (card->root_node->node_id & 0x3f) + 1; 4918c2ecf20Sopenharmony_ci __be32 *map = card->topology_map; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci *map++ = cpu_to_be32((self_id_count + 2) << 16); 4948c2ecf20Sopenharmony_ci *map++ = cpu_to_be32(be32_to_cpu(card->topology_map[1]) + 1); 4958c2ecf20Sopenharmony_ci *map++ = cpu_to_be32((node_count << 16) | self_id_count); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci while (self_id_count--) 4988c2ecf20Sopenharmony_ci *map++ = cpu_to_be32p(self_ids++); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci fw_compute_block_crc(card->topology_map); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_civoid fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, 5048c2ecf20Sopenharmony_ci int self_id_count, u32 *self_ids, bool bm_abdicate) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct fw_node *local_node; 5078c2ecf20Sopenharmony_ci unsigned long flags; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * If the selfID buffer is not the immediate successor of the 5138c2ecf20Sopenharmony_ci * previously processed one, we cannot reliably compare the 5148c2ecf20Sopenharmony_ci * old and new topologies. 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci if (!is_next_generation(generation, card->generation) && 5178c2ecf20Sopenharmony_ci card->local_node != NULL) { 5188c2ecf20Sopenharmony_ci fw_destroy_nodes(card); 5198c2ecf20Sopenharmony_ci card->bm_retries = 0; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci card->broadcast_channel_allocated = card->broadcast_channel_auto_allocated; 5238c2ecf20Sopenharmony_ci card->node_id = node_id; 5248c2ecf20Sopenharmony_ci /* 5258c2ecf20Sopenharmony_ci * Update node_id before generation to prevent anybody from using 5268c2ecf20Sopenharmony_ci * a stale node_id together with a current generation. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci smp_wmb(); 5298c2ecf20Sopenharmony_ci card->generation = generation; 5308c2ecf20Sopenharmony_ci card->reset_jiffies = get_jiffies_64(); 5318c2ecf20Sopenharmony_ci card->bm_node_id = 0xffff; 5328c2ecf20Sopenharmony_ci card->bm_abdicate = bm_abdicate; 5338c2ecf20Sopenharmony_ci fw_schedule_bm_work(card, 0); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci local_node = build_tree(card, self_ids, self_id_count); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci update_topology_map(card, self_ids, self_id_count); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci card->color++; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (local_node == NULL) { 5428c2ecf20Sopenharmony_ci fw_err(card, "topology build failed\n"); 5438c2ecf20Sopenharmony_ci /* FIXME: We need to issue a bus reset in this case. */ 5448c2ecf20Sopenharmony_ci } else if (card->local_node == NULL) { 5458c2ecf20Sopenharmony_ci card->local_node = local_node; 5468c2ecf20Sopenharmony_ci for_each_fw_node(card, local_node, report_found_node); 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci update_tree(card, local_node); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_core_handle_bus_reset); 554