162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device probing and sysfs code. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005-2006 Kristian Hoegsberg <krh@bitplanet.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bug.h> 962306a36Sopenharmony_ci#include <linux/ctype.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/firewire.h> 1462306a36Sopenharmony_ci#include <linux/firewire-constants.h> 1562306a36Sopenharmony_ci#include <linux/idr.h> 1662306a36Sopenharmony_ci#include <linux/jiffies.h> 1762306a36Sopenharmony_ci#include <linux/kobject.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/mutex.h> 2262306a36Sopenharmony_ci#include <linux/random.h> 2362306a36Sopenharmony_ci#include <linux/rwsem.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/spinlock.h> 2662306a36Sopenharmony_ci#include <linux/string.h> 2762306a36Sopenharmony_ci#include <linux/workqueue.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/atomic.h> 3062306a36Sopenharmony_ci#include <asm/byteorder.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "core.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_civoid fw_csr_iterator_init(struct fw_csr_iterator *ci, const u32 *p) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci ci->p = p + 1; 3762306a36Sopenharmony_ci ci->end = ci->p + (p[0] >> 16); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ciEXPORT_SYMBOL(fw_csr_iterator_init); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci *key = *ci->p >> 24; 4462306a36Sopenharmony_ci *value = *ci->p & 0xffffff; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return ci->p++ < ci->end; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ciEXPORT_SYMBOL(fw_csr_iterator_next); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const u32 *search_leaf(const u32 *directory, int search_key) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct fw_csr_iterator ci; 5362306a36Sopenharmony_ci int last_key = 0, key, value; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci fw_csr_iterator_init(&ci, directory); 5662306a36Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 5762306a36Sopenharmony_ci if (last_key == search_key && 5862306a36Sopenharmony_ci key == (CSR_DESCRIPTOR | CSR_LEAF)) 5962306a36Sopenharmony_ci return ci.p - 1 + value; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci last_key = key; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return NULL; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int textual_leaf_to_string(const u32 *block, char *buf, size_t size) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci unsigned int quadlets, i; 7062306a36Sopenharmony_ci char c; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!size || !buf) 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci quadlets = min(block[0] >> 16, 256U); 7662306a36Sopenharmony_ci if (quadlets < 2) 7762306a36Sopenharmony_ci return -ENODATA; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (block[1] != 0 || block[2] != 0) 8062306a36Sopenharmony_ci /* unknown language/character set */ 8162306a36Sopenharmony_ci return -ENODATA; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci block += 3; 8462306a36Sopenharmony_ci quadlets -= 2; 8562306a36Sopenharmony_ci for (i = 0; i < quadlets * 4 && i < size - 1; i++) { 8662306a36Sopenharmony_ci c = block[i / 4] >> (24 - 8 * (i % 4)); 8762306a36Sopenharmony_ci if (c == '\0') 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci buf[i] = c; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci buf[i] = '\0'; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return i; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * fw_csr_string() - reads a string from the configuration ROM 9862306a36Sopenharmony_ci * @directory: e.g. root directory or unit directory 9962306a36Sopenharmony_ci * @key: the key of the preceding directory entry 10062306a36Sopenharmony_ci * @buf: where to put the string 10162306a36Sopenharmony_ci * @size: size of @buf, in bytes 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * The string is taken from a minimal ASCII text descriptor leaf just after the entry with the 10462306a36Sopenharmony_ci * @key. The string is zero-terminated. An overlong string is silently truncated such that it 10562306a36Sopenharmony_ci * and the zero byte fit into @size. 10662306a36Sopenharmony_ci * 10762306a36Sopenharmony_ci * Returns strlen(buf) or a negative error code. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ciint fw_csr_string(const u32 *directory, int key, char *buf, size_t size) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci const u32 *leaf = search_leaf(directory, key); 11262306a36Sopenharmony_ci if (!leaf) 11362306a36Sopenharmony_ci return -ENOENT; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return textual_leaf_to_string(leaf, buf, size); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL(fw_csr_string); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void get_ids(const u32 *directory, int *id) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct fw_csr_iterator ci; 12262306a36Sopenharmony_ci int key, value; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci fw_csr_iterator_init(&ci, directory); 12562306a36Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 12662306a36Sopenharmony_ci switch (key) { 12762306a36Sopenharmony_ci case CSR_VENDOR: id[0] = value; break; 12862306a36Sopenharmony_ci case CSR_MODEL: id[1] = value; break; 12962306a36Sopenharmony_ci case CSR_SPECIFIER_ID: id[2] = value; break; 13062306a36Sopenharmony_ci case CSR_VERSION: id[3] = value; break; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void get_modalias_ids(const struct fw_unit *unit, int *id) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci get_ids(&fw_parent_device(unit)->config_rom[5], id); 13862306a36Sopenharmony_ci get_ids(unit->directory, id); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic bool match_ids(const struct ieee1394_device_id *id_table, int *id) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int match = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (id[0] == id_table->vendor_id) 14662306a36Sopenharmony_ci match |= IEEE1394_MATCH_VENDOR_ID; 14762306a36Sopenharmony_ci if (id[1] == id_table->model_id) 14862306a36Sopenharmony_ci match |= IEEE1394_MATCH_MODEL_ID; 14962306a36Sopenharmony_ci if (id[2] == id_table->specifier_id) 15062306a36Sopenharmony_ci match |= IEEE1394_MATCH_SPECIFIER_ID; 15162306a36Sopenharmony_ci if (id[3] == id_table->version) 15262306a36Sopenharmony_ci match |= IEEE1394_MATCH_VERSION; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return (match & id_table->match_flags) == id_table->match_flags; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct ieee1394_device_id *unit_match(struct device *dev, 15862306a36Sopenharmony_ci struct device_driver *drv) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci const struct ieee1394_device_id *id_table = 16162306a36Sopenharmony_ci container_of(drv, struct fw_driver, driver)->id_table; 16262306a36Sopenharmony_ci int id[] = {0, 0, 0, 0}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci get_modalias_ids(fw_unit(dev), id); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (; id_table->match_flags != 0; id_table++) 16762306a36Sopenharmony_ci if (match_ids(id_table, id)) 16862306a36Sopenharmony_ci return id_table; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return NULL; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic bool is_fw_unit(struct device *dev); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int fw_unit_match(struct device *dev, struct device_driver *drv) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci /* We only allow binding to fw_units. */ 17862306a36Sopenharmony_ci return is_fw_unit(dev) && unit_match(dev, drv) != NULL; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int fw_unit_probe(struct device *dev) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct fw_driver *driver = 18462306a36Sopenharmony_ci container_of(dev->driver, struct fw_driver, driver); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return driver->probe(fw_unit(dev), unit_match(dev, dev->driver)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void fw_unit_remove(struct device *dev) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct fw_driver *driver = 19262306a36Sopenharmony_ci container_of(dev->driver, struct fw_driver, driver); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci driver->remove(fw_unit(dev)); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int get_modalias(const struct fw_unit *unit, char *buffer, size_t buffer_size) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int id[] = {0, 0, 0, 0}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci get_modalias_ids(unit, id); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return snprintf(buffer, buffer_size, 20462306a36Sopenharmony_ci "ieee1394:ven%08Xmo%08Xsp%08Xver%08X", 20562306a36Sopenharmony_ci id[0], id[1], id[2], id[3]); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int fw_unit_uevent(const struct device *dev, struct kobj_uevent_env *env) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci const struct fw_unit *unit = fw_unit(dev); 21162306a36Sopenharmony_ci char modalias[64]; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci get_modalias(unit, modalias, sizeof(modalias)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=%s", modalias)) 21662306a36Sopenharmony_ci return -ENOMEM; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistruct bus_type fw_bus_type = { 22262306a36Sopenharmony_ci .name = "firewire", 22362306a36Sopenharmony_ci .match = fw_unit_match, 22462306a36Sopenharmony_ci .probe = fw_unit_probe, 22562306a36Sopenharmony_ci .remove = fw_unit_remove, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ciEXPORT_SYMBOL(fw_bus_type); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciint fw_device_enable_phys_dma(struct fw_device *device) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int generation = device->generation; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* device->node_id, accessed below, must not be older than generation */ 23462306a36Sopenharmony_ci smp_rmb(); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return device->card->driver->enable_phys_dma(device->card, 23762306a36Sopenharmony_ci device->node_id, 23862306a36Sopenharmony_ci generation); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ciEXPORT_SYMBOL(fw_device_enable_phys_dma); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistruct config_rom_attribute { 24362306a36Sopenharmony_ci struct device_attribute attr; 24462306a36Sopenharmony_ci u32 key; 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic ssize_t show_immediate(struct device *dev, 24862306a36Sopenharmony_ci struct device_attribute *dattr, char *buf) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct config_rom_attribute *attr = 25162306a36Sopenharmony_ci container_of(dattr, struct config_rom_attribute, attr); 25262306a36Sopenharmony_ci struct fw_csr_iterator ci; 25362306a36Sopenharmony_ci const u32 *dir; 25462306a36Sopenharmony_ci int key, value, ret = -ENOENT; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci down_read(&fw_device_rwsem); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (is_fw_unit(dev)) 25962306a36Sopenharmony_ci dir = fw_unit(dev)->directory; 26062306a36Sopenharmony_ci else 26162306a36Sopenharmony_ci dir = fw_device(dev)->config_rom + 5; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci fw_csr_iterator_init(&ci, dir); 26462306a36Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) 26562306a36Sopenharmony_ci if (attr->key == key) { 26662306a36Sopenharmony_ci ret = snprintf(buf, buf ? PAGE_SIZE : 0, 26762306a36Sopenharmony_ci "0x%06x\n", value); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci up_read(&fw_device_rwsem); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return ret; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci#define IMMEDIATE_ATTR(name, key) \ 27762306a36Sopenharmony_ci { __ATTR(name, S_IRUGO, show_immediate, NULL), key } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t show_text_leaf(struct device *dev, 28062306a36Sopenharmony_ci struct device_attribute *dattr, char *buf) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct config_rom_attribute *attr = 28362306a36Sopenharmony_ci container_of(dattr, struct config_rom_attribute, attr); 28462306a36Sopenharmony_ci const u32 *dir; 28562306a36Sopenharmony_ci size_t bufsize; 28662306a36Sopenharmony_ci char dummy_buf[2]; 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci down_read(&fw_device_rwsem); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (is_fw_unit(dev)) 29262306a36Sopenharmony_ci dir = fw_unit(dev)->directory; 29362306a36Sopenharmony_ci else 29462306a36Sopenharmony_ci dir = fw_device(dev)->config_rom + 5; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (buf) { 29762306a36Sopenharmony_ci bufsize = PAGE_SIZE - 1; 29862306a36Sopenharmony_ci } else { 29962306a36Sopenharmony_ci buf = dummy_buf; 30062306a36Sopenharmony_ci bufsize = 1; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = fw_csr_string(dir, attr->key, buf, bufsize); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (ret >= 0) { 30662306a36Sopenharmony_ci /* Strip trailing whitespace and add newline. */ 30762306a36Sopenharmony_ci while (ret > 0 && isspace(buf[ret - 1])) 30862306a36Sopenharmony_ci ret--; 30962306a36Sopenharmony_ci strcpy(buf + ret, "\n"); 31062306a36Sopenharmony_ci ret++; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci up_read(&fw_device_rwsem); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci#define TEXT_LEAF_ATTR(name, key) \ 31962306a36Sopenharmony_ci { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct config_rom_attribute config_rom_attributes[] = { 32262306a36Sopenharmony_ci IMMEDIATE_ATTR(vendor, CSR_VENDOR), 32362306a36Sopenharmony_ci IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION), 32462306a36Sopenharmony_ci IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID), 32562306a36Sopenharmony_ci IMMEDIATE_ATTR(version, CSR_VERSION), 32662306a36Sopenharmony_ci IMMEDIATE_ATTR(model, CSR_MODEL), 32762306a36Sopenharmony_ci TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR), 32862306a36Sopenharmony_ci TEXT_LEAF_ATTR(model_name, CSR_MODEL), 32962306a36Sopenharmony_ci TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION), 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void init_fw_attribute_group(struct device *dev, 33362306a36Sopenharmony_ci struct device_attribute *attrs, 33462306a36Sopenharmony_ci struct fw_attribute_group *group) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct device_attribute *attr; 33762306a36Sopenharmony_ci int i, j; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (j = 0; attrs[j].attr.name != NULL; j++) 34062306a36Sopenharmony_ci group->attrs[j] = &attrs[j].attr; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) { 34362306a36Sopenharmony_ci attr = &config_rom_attributes[i].attr; 34462306a36Sopenharmony_ci if (attr->show(dev, attr, NULL) < 0) 34562306a36Sopenharmony_ci continue; 34662306a36Sopenharmony_ci group->attrs[j++] = &attr->attr; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci group->attrs[j] = NULL; 35062306a36Sopenharmony_ci group->groups[0] = &group->group; 35162306a36Sopenharmony_ci group->groups[1] = NULL; 35262306a36Sopenharmony_ci group->group.attrs = group->attrs; 35362306a36Sopenharmony_ci dev->groups = (const struct attribute_group **) group->groups; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 35762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 36062306a36Sopenharmony_ci int length; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci length = get_modalias(unit, buf, PAGE_SIZE); 36362306a36Sopenharmony_ci strcpy(buf + length, "\n"); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return length + 1; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic ssize_t rom_index_show(struct device *dev, 36962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct fw_device *device = fw_device(dev->parent); 37262306a36Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return sysfs_emit(buf, "%td\n", unit->directory - device->config_rom); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic struct device_attribute fw_unit_attributes[] = { 37862306a36Sopenharmony_ci __ATTR_RO(modalias), 37962306a36Sopenharmony_ci __ATTR_RO(rom_index), 38062306a36Sopenharmony_ci __ATTR_NULL, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic ssize_t config_rom_show(struct device *dev, 38462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct fw_device *device = fw_device(dev); 38762306a36Sopenharmony_ci size_t length; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci down_read(&fw_device_rwsem); 39062306a36Sopenharmony_ci length = device->config_rom_length * 4; 39162306a36Sopenharmony_ci memcpy(buf, device->config_rom, length); 39262306a36Sopenharmony_ci up_read(&fw_device_rwsem); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return length; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic ssize_t guid_show(struct device *dev, 39862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct fw_device *device = fw_device(dev); 40162306a36Sopenharmony_ci int ret; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci down_read(&fw_device_rwsem); 40462306a36Sopenharmony_ci ret = sysfs_emit(buf, "0x%08x%08x\n", device->config_rom[3], device->config_rom[4]); 40562306a36Sopenharmony_ci up_read(&fw_device_rwsem); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic ssize_t is_local_show(struct device *dev, 41162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct fw_device *device = fw_device(dev); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return sprintf(buf, "%u\n", device->is_local); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int units_sprintf(char *buf, const u32 *directory) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct fw_csr_iterator ci; 42162306a36Sopenharmony_ci int key, value; 42262306a36Sopenharmony_ci int specifier_id = 0; 42362306a36Sopenharmony_ci int version = 0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci fw_csr_iterator_init(&ci, directory); 42662306a36Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 42762306a36Sopenharmony_ci switch (key) { 42862306a36Sopenharmony_ci case CSR_SPECIFIER_ID: 42962306a36Sopenharmony_ci specifier_id = value; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci case CSR_VERSION: 43262306a36Sopenharmony_ci version = value; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return sprintf(buf, "0x%06x:0x%06x ", specifier_id, version); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic ssize_t units_show(struct device *dev, 44162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct fw_device *device = fw_device(dev); 44462306a36Sopenharmony_ci struct fw_csr_iterator ci; 44562306a36Sopenharmony_ci int key, value, i = 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci down_read(&fw_device_rwsem); 44862306a36Sopenharmony_ci fw_csr_iterator_init(&ci, &device->config_rom[5]); 44962306a36Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 45062306a36Sopenharmony_ci if (key != (CSR_UNIT | CSR_DIRECTORY)) 45162306a36Sopenharmony_ci continue; 45262306a36Sopenharmony_ci i += units_sprintf(&buf[i], ci.p + value - 1); 45362306a36Sopenharmony_ci if (i >= PAGE_SIZE - (8 + 1 + 8 + 1)) 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci up_read(&fw_device_rwsem); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (i) 45962306a36Sopenharmony_ci buf[i - 1] = '\n'; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return i; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic struct device_attribute fw_device_attributes[] = { 46562306a36Sopenharmony_ci __ATTR_RO(config_rom), 46662306a36Sopenharmony_ci __ATTR_RO(guid), 46762306a36Sopenharmony_ci __ATTR_RO(is_local), 46862306a36Sopenharmony_ci __ATTR_RO(units), 46962306a36Sopenharmony_ci __ATTR_NULL, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int read_rom(struct fw_device *device, 47362306a36Sopenharmony_ci int generation, int index, u32 *data) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci u64 offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4; 47662306a36Sopenharmony_ci int i, rcode; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* device->node_id, accessed below, must not be older than generation */ 47962306a36Sopenharmony_ci smp_rmb(); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (i = 10; i < 100; i += 10) { 48262306a36Sopenharmony_ci rcode = fw_run_transaction(device->card, 48362306a36Sopenharmony_ci TCODE_READ_QUADLET_REQUEST, device->node_id, 48462306a36Sopenharmony_ci generation, device->max_speed, offset, data, 4); 48562306a36Sopenharmony_ci if (rcode != RCODE_BUSY) 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci msleep(i); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci be32_to_cpus(data); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return rcode; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci#define MAX_CONFIG_ROM_SIZE 256 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/* 49762306a36Sopenharmony_ci * Read the bus info block, perform a speed probe, and read all of the rest of 49862306a36Sopenharmony_ci * the config ROM. We do all this with a cached bus generation. If the bus 49962306a36Sopenharmony_ci * generation changes under us, read_config_rom will fail and get retried. 50062306a36Sopenharmony_ci * It's better to start all over in this case because the node from which we 50162306a36Sopenharmony_ci * are reading the ROM may have changed the ROM during the reset. 50262306a36Sopenharmony_ci * Returns either a result code or a negative error code. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_cistatic int read_config_rom(struct fw_device *device, int generation) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct fw_card *card = device->card; 50762306a36Sopenharmony_ci const u32 *old_rom, *new_rom; 50862306a36Sopenharmony_ci u32 *rom, *stack; 50962306a36Sopenharmony_ci u32 sp, key; 51062306a36Sopenharmony_ci int i, end, length, ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE + 51362306a36Sopenharmony_ci sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL); 51462306a36Sopenharmony_ci if (rom == NULL) 51562306a36Sopenharmony_ci return -ENOMEM; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci stack = &rom[MAX_CONFIG_ROM_SIZE]; 51862306a36Sopenharmony_ci memset(rom, 0, sizeof(*rom) * MAX_CONFIG_ROM_SIZE); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci device->max_speed = SCODE_100; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* First read the bus info block. */ 52362306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 52462306a36Sopenharmony_ci ret = read_rom(device, generation, i, &rom[i]); 52562306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 52662306a36Sopenharmony_ci goto out; 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * As per IEEE1212 7.2, during initialization, devices can 52962306a36Sopenharmony_ci * reply with a 0 for the first quadlet of the config 53062306a36Sopenharmony_ci * rom to indicate that they are booting (for example, 53162306a36Sopenharmony_ci * if the firmware is on the disk of a external 53262306a36Sopenharmony_ci * harddisk). In that case we just fail, and the 53362306a36Sopenharmony_ci * retry mechanism will try again later. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci if (i == 0 && rom[i] == 0) { 53662306a36Sopenharmony_ci ret = RCODE_BUSY; 53762306a36Sopenharmony_ci goto out; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci device->max_speed = device->node->max_speed; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * Determine the speed of 54562306a36Sopenharmony_ci * - devices with link speed less than PHY speed, 54662306a36Sopenharmony_ci * - devices with 1394b PHY (unless only connected to 1394a PHYs), 54762306a36Sopenharmony_ci * - all devices if there are 1394b repeaters. 54862306a36Sopenharmony_ci * Note, we cannot use the bus info block's link_spd as starting point 54962306a36Sopenharmony_ci * because some buggy firmwares set it lower than necessary and because 55062306a36Sopenharmony_ci * 1394-1995 nodes do not have the field. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci if ((rom[2] & 0x7) < device->max_speed || 55362306a36Sopenharmony_ci device->max_speed == SCODE_BETA || 55462306a36Sopenharmony_ci card->beta_repeaters_present) { 55562306a36Sopenharmony_ci u32 dummy; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* for S1600 and S3200 */ 55862306a36Sopenharmony_ci if (device->max_speed == SCODE_BETA) 55962306a36Sopenharmony_ci device->max_speed = card->link_speed; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci while (device->max_speed > SCODE_100) { 56262306a36Sopenharmony_ci if (read_rom(device, generation, 0, &dummy) == 56362306a36Sopenharmony_ci RCODE_COMPLETE) 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci device->max_speed--; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * Now parse the config rom. The config rom is a recursive 57162306a36Sopenharmony_ci * directory structure so we parse it using a stack of 57262306a36Sopenharmony_ci * references to the blocks that make up the structure. We 57362306a36Sopenharmony_ci * push a reference to the root directory on the stack to 57462306a36Sopenharmony_ci * start things off. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci length = i; 57762306a36Sopenharmony_ci sp = 0; 57862306a36Sopenharmony_ci stack[sp++] = 0xc0000005; 57962306a36Sopenharmony_ci while (sp > 0) { 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * Pop the next block reference of the stack. The 58262306a36Sopenharmony_ci * lower 24 bits is the offset into the config rom, 58362306a36Sopenharmony_ci * the upper 8 bits are the type of the reference the 58462306a36Sopenharmony_ci * block. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci key = stack[--sp]; 58762306a36Sopenharmony_ci i = key & 0xffffff; 58862306a36Sopenharmony_ci if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE)) { 58962306a36Sopenharmony_ci ret = -ENXIO; 59062306a36Sopenharmony_ci goto out; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Read header quadlet for the block to get the length. */ 59462306a36Sopenharmony_ci ret = read_rom(device, generation, i, &rom[i]); 59562306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 59662306a36Sopenharmony_ci goto out; 59762306a36Sopenharmony_ci end = i + (rom[i] >> 16) + 1; 59862306a36Sopenharmony_ci if (end > MAX_CONFIG_ROM_SIZE) { 59962306a36Sopenharmony_ci /* 60062306a36Sopenharmony_ci * This block extends outside the config ROM which is 60162306a36Sopenharmony_ci * a firmware bug. Ignore this whole block, i.e. 60262306a36Sopenharmony_ci * simply set a fake block length of 0. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci fw_err(card, "skipped invalid ROM block %x at %llx\n", 60562306a36Sopenharmony_ci rom[i], 60662306a36Sopenharmony_ci i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); 60762306a36Sopenharmony_ci rom[i] = 0; 60862306a36Sopenharmony_ci end = i; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci i++; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Now read in the block. If this is a directory 61462306a36Sopenharmony_ci * block, check the entries as we read them to see if 61562306a36Sopenharmony_ci * it references another block, and push it in that case. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci for (; i < end; i++) { 61862306a36Sopenharmony_ci ret = read_rom(device, generation, i, &rom[i]); 61962306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 62062306a36Sopenharmony_ci goto out; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if ((key >> 30) != 3 || (rom[i] >> 30) < 2) 62362306a36Sopenharmony_ci continue; 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * Offset points outside the ROM. May be a firmware 62662306a36Sopenharmony_ci * bug or an Extended ROM entry (IEEE 1212-2001 clause 62762306a36Sopenharmony_ci * 7.7.18). Simply overwrite this pointer here by a 62862306a36Sopenharmony_ci * fake immediate entry so that later iterators over 62962306a36Sopenharmony_ci * the ROM don't have to check offsets all the time. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) { 63262306a36Sopenharmony_ci fw_err(card, 63362306a36Sopenharmony_ci "skipped unsupported ROM entry %x at %llx\n", 63462306a36Sopenharmony_ci rom[i], 63562306a36Sopenharmony_ci i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); 63662306a36Sopenharmony_ci rom[i] = 0; 63762306a36Sopenharmony_ci continue; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci stack[sp++] = i + rom[i]; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci if (length < i) 64262306a36Sopenharmony_ci length = i; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci old_rom = device->config_rom; 64662306a36Sopenharmony_ci new_rom = kmemdup(rom, length * 4, GFP_KERNEL); 64762306a36Sopenharmony_ci if (new_rom == NULL) { 64862306a36Sopenharmony_ci ret = -ENOMEM; 64962306a36Sopenharmony_ci goto out; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci down_write(&fw_device_rwsem); 65362306a36Sopenharmony_ci device->config_rom = new_rom; 65462306a36Sopenharmony_ci device->config_rom_length = length; 65562306a36Sopenharmony_ci up_write(&fw_device_rwsem); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci kfree(old_rom); 65862306a36Sopenharmony_ci ret = RCODE_COMPLETE; 65962306a36Sopenharmony_ci device->max_rec = rom[2] >> 12 & 0xf; 66062306a36Sopenharmony_ci device->cmc = rom[2] >> 30 & 1; 66162306a36Sopenharmony_ci device->irmc = rom[2] >> 31 & 1; 66262306a36Sopenharmony_ci out: 66362306a36Sopenharmony_ci kfree(rom); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return ret; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic void fw_unit_release(struct device *dev) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci fw_device_put(fw_parent_device(unit)); 67362306a36Sopenharmony_ci kfree(unit); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic struct device_type fw_unit_type = { 67762306a36Sopenharmony_ci .uevent = fw_unit_uevent, 67862306a36Sopenharmony_ci .release = fw_unit_release, 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic bool is_fw_unit(struct device *dev) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci return dev->type == &fw_unit_type; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic void create_units(struct fw_device *device) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci struct fw_csr_iterator ci; 68962306a36Sopenharmony_ci struct fw_unit *unit; 69062306a36Sopenharmony_ci int key, value, i; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci i = 0; 69362306a36Sopenharmony_ci fw_csr_iterator_init(&ci, &device->config_rom[5]); 69462306a36Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 69562306a36Sopenharmony_ci if (key != (CSR_UNIT | CSR_DIRECTORY)) 69662306a36Sopenharmony_ci continue; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * Get the address of the unit directory and try to 70062306a36Sopenharmony_ci * match the drivers id_tables against it. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci unit = kzalloc(sizeof(*unit), GFP_KERNEL); 70362306a36Sopenharmony_ci if (unit == NULL) 70462306a36Sopenharmony_ci continue; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci unit->directory = ci.p + value - 1; 70762306a36Sopenharmony_ci unit->device.bus = &fw_bus_type; 70862306a36Sopenharmony_ci unit->device.type = &fw_unit_type; 70962306a36Sopenharmony_ci unit->device.parent = &device->device; 71062306a36Sopenharmony_ci dev_set_name(&unit->device, "%s.%d", dev_name(&device->device), i++); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(unit->attribute_group.attrs) < 71362306a36Sopenharmony_ci ARRAY_SIZE(fw_unit_attributes) + 71462306a36Sopenharmony_ci ARRAY_SIZE(config_rom_attributes)); 71562306a36Sopenharmony_ci init_fw_attribute_group(&unit->device, 71662306a36Sopenharmony_ci fw_unit_attributes, 71762306a36Sopenharmony_ci &unit->attribute_group); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci fw_device_get(device); 72062306a36Sopenharmony_ci if (device_register(&unit->device) < 0) { 72162306a36Sopenharmony_ci put_device(&unit->device); 72262306a36Sopenharmony_ci continue; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int shutdown_unit(struct device *device, void *data) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci device_unregister(device); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* 73562306a36Sopenharmony_ci * fw_device_rwsem acts as dual purpose mutex: 73662306a36Sopenharmony_ci * - serializes accesses to fw_device_idr, 73762306a36Sopenharmony_ci * - serializes accesses to fw_device.config_rom/.config_rom_length and 73862306a36Sopenharmony_ci * fw_unit.directory, unless those accesses happen at safe occasions 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ciDECLARE_RWSEM(fw_device_rwsem); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ciDEFINE_IDR(fw_device_idr); 74362306a36Sopenharmony_ciint fw_cdev_major; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistruct fw_device *fw_device_get_by_devt(dev_t devt) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct fw_device *device; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci down_read(&fw_device_rwsem); 75062306a36Sopenharmony_ci device = idr_find(&fw_device_idr, MINOR(devt)); 75162306a36Sopenharmony_ci if (device) 75262306a36Sopenharmony_ci fw_device_get(device); 75362306a36Sopenharmony_ci up_read(&fw_device_rwsem); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return device; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistruct workqueue_struct *fw_workqueue; 75962306a36Sopenharmony_ciEXPORT_SYMBOL(fw_workqueue); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic void fw_schedule_device_work(struct fw_device *device, 76262306a36Sopenharmony_ci unsigned long delay) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci queue_delayed_work(fw_workqueue, &device->work, delay); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * These defines control the retry behavior for reading the config 76962306a36Sopenharmony_ci * rom. It shouldn't be necessary to tweak these; if the device 77062306a36Sopenharmony_ci * doesn't respond to a config rom read within 10 seconds, it's not 77162306a36Sopenharmony_ci * going to respond at all. As for the initial delay, a lot of 77262306a36Sopenharmony_ci * devices will be able to respond within half a second after bus 77362306a36Sopenharmony_ci * reset. On the other hand, it's not really worth being more 77462306a36Sopenharmony_ci * aggressive than that, since it scales pretty well; if 10 devices 77562306a36Sopenharmony_ci * are plugged in, they're all getting read within one second. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci#define MAX_RETRIES 10 77962306a36Sopenharmony_ci#define RETRY_DELAY (3 * HZ) 78062306a36Sopenharmony_ci#define INITIAL_DELAY (HZ / 2) 78162306a36Sopenharmony_ci#define SHUTDOWN_DELAY (2 * HZ) 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic void fw_device_shutdown(struct work_struct *work) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct fw_device *device = 78662306a36Sopenharmony_ci container_of(work, struct fw_device, work.work); 78762306a36Sopenharmony_ci int minor = MINOR(device->device.devt); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (time_before64(get_jiffies_64(), 79062306a36Sopenharmony_ci device->card->reset_jiffies + SHUTDOWN_DELAY) 79162306a36Sopenharmony_ci && !list_empty(&device->card->link)) { 79262306a36Sopenharmony_ci fw_schedule_device_work(device, SHUTDOWN_DELAY); 79362306a36Sopenharmony_ci return; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (atomic_cmpxchg(&device->state, 79762306a36Sopenharmony_ci FW_DEVICE_GONE, 79862306a36Sopenharmony_ci FW_DEVICE_SHUTDOWN) != FW_DEVICE_GONE) 79962306a36Sopenharmony_ci return; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci fw_device_cdev_remove(device); 80262306a36Sopenharmony_ci device_for_each_child(&device->device, NULL, shutdown_unit); 80362306a36Sopenharmony_ci device_unregister(&device->device); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci down_write(&fw_device_rwsem); 80662306a36Sopenharmony_ci idr_remove(&fw_device_idr, minor); 80762306a36Sopenharmony_ci up_write(&fw_device_rwsem); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci fw_device_put(device); 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void fw_device_release(struct device *dev) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct fw_device *device = fw_device(dev); 81562306a36Sopenharmony_ci struct fw_card *card = device->card; 81662306a36Sopenharmony_ci unsigned long flags; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* 81962306a36Sopenharmony_ci * Take the card lock so we don't set this to NULL while a 82062306a36Sopenharmony_ci * FW_NODE_UPDATED callback is being handled or while the 82162306a36Sopenharmony_ci * bus manager work looks at this node. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 82462306a36Sopenharmony_ci device->node->data = NULL; 82562306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci fw_node_put(device->node); 82862306a36Sopenharmony_ci kfree(device->config_rom); 82962306a36Sopenharmony_ci kfree(device); 83062306a36Sopenharmony_ci fw_card_put(card); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic struct device_type fw_device_type = { 83462306a36Sopenharmony_ci .release = fw_device_release, 83562306a36Sopenharmony_ci}; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic bool is_fw_device(struct device *dev) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci return dev->type == &fw_device_type; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int update_unit(struct device *dev, void *data) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 84562306a36Sopenharmony_ci struct fw_driver *driver = (struct fw_driver *)dev->driver; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) { 84862306a36Sopenharmony_ci device_lock(dev); 84962306a36Sopenharmony_ci driver->update(unit); 85062306a36Sopenharmony_ci device_unlock(dev); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return 0; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic void fw_device_update(struct work_struct *work) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct fw_device *device = 85962306a36Sopenharmony_ci container_of(work, struct fw_device, work.work); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci fw_device_cdev_update(device); 86262306a36Sopenharmony_ci device_for_each_child(&device->device, NULL, update_unit); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci/* 86662306a36Sopenharmony_ci * If a device was pending for deletion because its node went away but its 86762306a36Sopenharmony_ci * bus info block and root directory header matches that of a newly discovered 86862306a36Sopenharmony_ci * device, revive the existing fw_device. 86962306a36Sopenharmony_ci * The newly allocated fw_device becomes obsolete instead. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_cistatic int lookup_existing_device(struct device *dev, void *data) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct fw_device *old = fw_device(dev); 87462306a36Sopenharmony_ci struct fw_device *new = data; 87562306a36Sopenharmony_ci struct fw_card *card = new->card; 87662306a36Sopenharmony_ci int match = 0; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (!is_fw_device(dev)) 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci down_read(&fw_device_rwsem); /* serialize config_rom access */ 88262306a36Sopenharmony_ci spin_lock_irq(&card->lock); /* serialize node access */ 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && 88562306a36Sopenharmony_ci atomic_cmpxchg(&old->state, 88662306a36Sopenharmony_ci FW_DEVICE_GONE, 88762306a36Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { 88862306a36Sopenharmony_ci struct fw_node *current_node = new->node; 88962306a36Sopenharmony_ci struct fw_node *obsolete_node = old->node; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci new->node = obsolete_node; 89262306a36Sopenharmony_ci new->node->data = new; 89362306a36Sopenharmony_ci old->node = current_node; 89462306a36Sopenharmony_ci old->node->data = old; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci old->max_speed = new->max_speed; 89762306a36Sopenharmony_ci old->node_id = current_node->node_id; 89862306a36Sopenharmony_ci smp_wmb(); /* update node_id before generation */ 89962306a36Sopenharmony_ci old->generation = card->generation; 90062306a36Sopenharmony_ci old->config_rom_retries = 0; 90162306a36Sopenharmony_ci fw_notice(card, "rediscovered device %s\n", dev_name(dev)); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci old->workfn = fw_device_update; 90462306a36Sopenharmony_ci fw_schedule_device_work(old, 0); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (current_node == card->root_node) 90762306a36Sopenharmony_ci fw_schedule_bm_work(card, 0); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci match = 1; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci spin_unlock_irq(&card->lock); 91362306a36Sopenharmony_ci up_read(&fw_device_rwsem); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return match; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cienum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, }; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic void set_broadcast_channel(struct fw_device *device, int generation) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct fw_card *card = device->card; 92362306a36Sopenharmony_ci __be32 data; 92462306a36Sopenharmony_ci int rcode; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (!card->broadcast_channel_allocated) 92762306a36Sopenharmony_ci return; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* 93062306a36Sopenharmony_ci * The Broadcast_Channel Valid bit is required by nodes which want to 93162306a36Sopenharmony_ci * transmit on this channel. Such transmissions are practically 93262306a36Sopenharmony_ci * exclusive to IP over 1394 (RFC 2734). IP capable nodes are required 93362306a36Sopenharmony_ci * to be IRM capable and have a max_rec of 8 or more. We use this fact 93462306a36Sopenharmony_ci * to narrow down to which nodes we send Broadcast_Channel updates. 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci if (!device->irmc || device->max_rec < 8) 93762306a36Sopenharmony_ci return; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* 94062306a36Sopenharmony_ci * Some 1394-1995 nodes crash if this 1394a-2000 register is written. 94162306a36Sopenharmony_ci * Perform a read test first. 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_ci if (device->bc_implemented == BC_UNKNOWN) { 94462306a36Sopenharmony_ci rcode = fw_run_transaction(card, TCODE_READ_QUADLET_REQUEST, 94562306a36Sopenharmony_ci device->node_id, generation, device->max_speed, 94662306a36Sopenharmony_ci CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL, 94762306a36Sopenharmony_ci &data, 4); 94862306a36Sopenharmony_ci switch (rcode) { 94962306a36Sopenharmony_ci case RCODE_COMPLETE: 95062306a36Sopenharmony_ci if (data & cpu_to_be32(1 << 31)) { 95162306a36Sopenharmony_ci device->bc_implemented = BC_IMPLEMENTED; 95262306a36Sopenharmony_ci break; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci fallthrough; /* to case address error */ 95562306a36Sopenharmony_ci case RCODE_ADDRESS_ERROR: 95662306a36Sopenharmony_ci device->bc_implemented = BC_UNIMPLEMENTED; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (device->bc_implemented == BC_IMPLEMENTED) { 96162306a36Sopenharmony_ci data = cpu_to_be32(BROADCAST_CHANNEL_INITIAL | 96262306a36Sopenharmony_ci BROADCAST_CHANNEL_VALID); 96362306a36Sopenharmony_ci fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST, 96462306a36Sopenharmony_ci device->node_id, generation, device->max_speed, 96562306a36Sopenharmony_ci CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL, 96662306a36Sopenharmony_ci &data, 4); 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ciint fw_device_set_broadcast_channel(struct device *dev, void *gen) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci if (is_fw_device(dev)) 97362306a36Sopenharmony_ci set_broadcast_channel(fw_device(dev), (long)gen); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return 0; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void fw_device_init(struct work_struct *work) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct fw_device *device = 98162306a36Sopenharmony_ci container_of(work, struct fw_device, work.work); 98262306a36Sopenharmony_ci struct fw_card *card = device->card; 98362306a36Sopenharmony_ci struct device *revived_dev; 98462306a36Sopenharmony_ci int minor, ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* 98762306a36Sopenharmony_ci * All failure paths here set node->data to NULL, so that we 98862306a36Sopenharmony_ci * don't try to do device_for_each_child() on a kfree()'d 98962306a36Sopenharmony_ci * device. 99062306a36Sopenharmony_ci */ 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci ret = read_config_rom(device, device->generation); 99362306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 99462306a36Sopenharmony_ci if (device->config_rom_retries < MAX_RETRIES && 99562306a36Sopenharmony_ci atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { 99662306a36Sopenharmony_ci device->config_rom_retries++; 99762306a36Sopenharmony_ci fw_schedule_device_work(device, RETRY_DELAY); 99862306a36Sopenharmony_ci } else { 99962306a36Sopenharmony_ci if (device->node->link_on) 100062306a36Sopenharmony_ci fw_notice(card, "giving up on node %x: reading config rom failed: %s\n", 100162306a36Sopenharmony_ci device->node_id, 100262306a36Sopenharmony_ci fw_rcode_string(ret)); 100362306a36Sopenharmony_ci if (device->node == card->root_node) 100462306a36Sopenharmony_ci fw_schedule_bm_work(card, 0); 100562306a36Sopenharmony_ci fw_device_release(&device->device); 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci return; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci revived_dev = device_find_child(card->device, 101162306a36Sopenharmony_ci device, lookup_existing_device); 101262306a36Sopenharmony_ci if (revived_dev) { 101362306a36Sopenharmony_ci put_device(revived_dev); 101462306a36Sopenharmony_ci fw_device_release(&device->device); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci device_initialize(&device->device); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci fw_device_get(device); 102262306a36Sopenharmony_ci down_write(&fw_device_rwsem); 102362306a36Sopenharmony_ci minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, 102462306a36Sopenharmony_ci GFP_KERNEL); 102562306a36Sopenharmony_ci up_write(&fw_device_rwsem); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (minor < 0) 102862306a36Sopenharmony_ci goto error; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci device->device.bus = &fw_bus_type; 103162306a36Sopenharmony_ci device->device.type = &fw_device_type; 103262306a36Sopenharmony_ci device->device.parent = card->device; 103362306a36Sopenharmony_ci device->device.devt = MKDEV(fw_cdev_major, minor); 103462306a36Sopenharmony_ci dev_set_name(&device->device, "fw%d", minor); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(device->attribute_group.attrs) < 103762306a36Sopenharmony_ci ARRAY_SIZE(fw_device_attributes) + 103862306a36Sopenharmony_ci ARRAY_SIZE(config_rom_attributes)); 103962306a36Sopenharmony_ci init_fw_attribute_group(&device->device, 104062306a36Sopenharmony_ci fw_device_attributes, 104162306a36Sopenharmony_ci &device->attribute_group); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (device_add(&device->device)) { 104462306a36Sopenharmony_ci fw_err(card, "failed to add device\n"); 104562306a36Sopenharmony_ci goto error_with_cdev; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci create_units(device); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* 105162306a36Sopenharmony_ci * Transition the device to running state. If it got pulled 105262306a36Sopenharmony_ci * out from under us while we did the initialization work, we 105362306a36Sopenharmony_ci * have to shut down the device again here. Normally, though, 105462306a36Sopenharmony_ci * fw_node_event will be responsible for shutting it down when 105562306a36Sopenharmony_ci * necessary. We have to use the atomic cmpxchg here to avoid 105662306a36Sopenharmony_ci * racing with the FW_NODE_DESTROYED case in 105762306a36Sopenharmony_ci * fw_node_event(). 105862306a36Sopenharmony_ci */ 105962306a36Sopenharmony_ci if (atomic_cmpxchg(&device->state, 106062306a36Sopenharmony_ci FW_DEVICE_INITIALIZING, 106162306a36Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { 106262306a36Sopenharmony_ci device->workfn = fw_device_shutdown; 106362306a36Sopenharmony_ci fw_schedule_device_work(device, SHUTDOWN_DELAY); 106462306a36Sopenharmony_ci } else { 106562306a36Sopenharmony_ci fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n", 106662306a36Sopenharmony_ci dev_name(&device->device), 106762306a36Sopenharmony_ci device->config_rom[3], device->config_rom[4], 106862306a36Sopenharmony_ci 1 << device->max_speed); 106962306a36Sopenharmony_ci device->config_rom_retries = 0; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci set_broadcast_channel(device, device->generation); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci add_device_randomness(&device->config_rom[3], 8); 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * Reschedule the IRM work if we just finished reading the 107862306a36Sopenharmony_ci * root node config rom. If this races with a bus reset we 107962306a36Sopenharmony_ci * just end up running the IRM work a couple of extra times - 108062306a36Sopenharmony_ci * pretty harmless. 108162306a36Sopenharmony_ci */ 108262306a36Sopenharmony_ci if (device->node == card->root_node) 108362306a36Sopenharmony_ci fw_schedule_bm_work(card, 0); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci error_with_cdev: 108862306a36Sopenharmony_ci down_write(&fw_device_rwsem); 108962306a36Sopenharmony_ci idr_remove(&fw_device_idr, minor); 109062306a36Sopenharmony_ci up_write(&fw_device_rwsem); 109162306a36Sopenharmony_ci error: 109262306a36Sopenharmony_ci fw_device_put(device); /* fw_device_idr's reference */ 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci put_device(&device->device); /* our reference */ 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci/* Reread and compare bus info block and header of root directory */ 109862306a36Sopenharmony_cistatic int reread_config_rom(struct fw_device *device, int generation, 109962306a36Sopenharmony_ci bool *changed) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci u32 q; 110262306a36Sopenharmony_ci int i, rcode; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 110562306a36Sopenharmony_ci rcode = read_rom(device, generation, i, &q); 110662306a36Sopenharmony_ci if (rcode != RCODE_COMPLETE) 110762306a36Sopenharmony_ci return rcode; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (i == 0 && q == 0) 111062306a36Sopenharmony_ci /* inaccessible (see read_config_rom); retry later */ 111162306a36Sopenharmony_ci return RCODE_BUSY; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (q != device->config_rom[i]) { 111462306a36Sopenharmony_ci *changed = true; 111562306a36Sopenharmony_ci return RCODE_COMPLETE; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci *changed = false; 112062306a36Sopenharmony_ci return RCODE_COMPLETE; 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic void fw_device_refresh(struct work_struct *work) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct fw_device *device = 112662306a36Sopenharmony_ci container_of(work, struct fw_device, work.work); 112762306a36Sopenharmony_ci struct fw_card *card = device->card; 112862306a36Sopenharmony_ci int ret, node_id = device->node_id; 112962306a36Sopenharmony_ci bool changed; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci ret = reread_config_rom(device, device->generation, &changed); 113262306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 113362306a36Sopenharmony_ci goto failed_config_rom; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (!changed) { 113662306a36Sopenharmony_ci if (atomic_cmpxchg(&device->state, 113762306a36Sopenharmony_ci FW_DEVICE_INITIALIZING, 113862306a36Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) 113962306a36Sopenharmony_ci goto gone; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci fw_device_update(work); 114262306a36Sopenharmony_ci device->config_rom_retries = 0; 114362306a36Sopenharmony_ci goto out; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* 114762306a36Sopenharmony_ci * Something changed. We keep things simple and don't investigate 114862306a36Sopenharmony_ci * further. We just destroy all previous units and create new ones. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci device_for_each_child(&device->device, NULL, shutdown_unit); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci ret = read_config_rom(device, device->generation); 115362306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 115462306a36Sopenharmony_ci goto failed_config_rom; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci fw_device_cdev_update(device); 115762306a36Sopenharmony_ci create_units(device); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* Userspace may want to re-read attributes. */ 116062306a36Sopenharmony_ci kobject_uevent(&device->device.kobj, KOBJ_CHANGE); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (atomic_cmpxchg(&device->state, 116362306a36Sopenharmony_ci FW_DEVICE_INITIALIZING, 116462306a36Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) 116562306a36Sopenharmony_ci goto gone; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci fw_notice(card, "refreshed device %s\n", dev_name(&device->device)); 116862306a36Sopenharmony_ci device->config_rom_retries = 0; 116962306a36Sopenharmony_ci goto out; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci failed_config_rom: 117262306a36Sopenharmony_ci if (device->config_rom_retries < MAX_RETRIES && 117362306a36Sopenharmony_ci atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { 117462306a36Sopenharmony_ci device->config_rom_retries++; 117562306a36Sopenharmony_ci fw_schedule_device_work(device, RETRY_DELAY); 117662306a36Sopenharmony_ci return; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci fw_notice(card, "giving up on refresh of device %s: %s\n", 118062306a36Sopenharmony_ci dev_name(&device->device), fw_rcode_string(ret)); 118162306a36Sopenharmony_ci gone: 118262306a36Sopenharmony_ci atomic_set(&device->state, FW_DEVICE_GONE); 118362306a36Sopenharmony_ci device->workfn = fw_device_shutdown; 118462306a36Sopenharmony_ci fw_schedule_device_work(device, SHUTDOWN_DELAY); 118562306a36Sopenharmony_ci out: 118662306a36Sopenharmony_ci if (node_id == card->root_node->node_id) 118762306a36Sopenharmony_ci fw_schedule_bm_work(card, 0); 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_cistatic void fw_device_workfn(struct work_struct *work) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci struct fw_device *device = container_of(to_delayed_work(work), 119362306a36Sopenharmony_ci struct fw_device, work); 119462306a36Sopenharmony_ci device->workfn(work); 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_civoid fw_node_event(struct fw_card *card, struct fw_node *node, int event) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci struct fw_device *device; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci switch (event) { 120262306a36Sopenharmony_ci case FW_NODE_CREATED: 120362306a36Sopenharmony_ci /* 120462306a36Sopenharmony_ci * Attempt to scan the node, regardless whether its self ID has 120562306a36Sopenharmony_ci * the L (link active) flag set or not. Some broken devices 120662306a36Sopenharmony_ci * send L=0 but have an up-and-running link; others send L=1 120762306a36Sopenharmony_ci * without actually having a link. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci create: 121062306a36Sopenharmony_ci device = kzalloc(sizeof(*device), GFP_ATOMIC); 121162306a36Sopenharmony_ci if (device == NULL) 121262306a36Sopenharmony_ci break; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* 121562306a36Sopenharmony_ci * Do minimal initialization of the device here, the 121662306a36Sopenharmony_ci * rest will happen in fw_device_init(). 121762306a36Sopenharmony_ci * 121862306a36Sopenharmony_ci * Attention: A lot of things, even fw_device_get(), 121962306a36Sopenharmony_ci * cannot be done before fw_device_init() finished! 122062306a36Sopenharmony_ci * You can basically just check device->state and 122162306a36Sopenharmony_ci * schedule work until then, but only while holding 122262306a36Sopenharmony_ci * card->lock. 122362306a36Sopenharmony_ci */ 122462306a36Sopenharmony_ci atomic_set(&device->state, FW_DEVICE_INITIALIZING); 122562306a36Sopenharmony_ci device->card = fw_card_get(card); 122662306a36Sopenharmony_ci device->node = fw_node_get(node); 122762306a36Sopenharmony_ci device->node_id = node->node_id; 122862306a36Sopenharmony_ci device->generation = card->generation; 122962306a36Sopenharmony_ci device->is_local = node == card->local_node; 123062306a36Sopenharmony_ci mutex_init(&device->client_list_mutex); 123162306a36Sopenharmony_ci INIT_LIST_HEAD(&device->client_list); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* 123462306a36Sopenharmony_ci * Set the node data to point back to this device so 123562306a36Sopenharmony_ci * FW_NODE_UPDATED callbacks can update the node_id 123662306a36Sopenharmony_ci * and generation for the device. 123762306a36Sopenharmony_ci */ 123862306a36Sopenharmony_ci node->data = device; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* 124162306a36Sopenharmony_ci * Many devices are slow to respond after bus resets, 124262306a36Sopenharmony_ci * especially if they are bus powered and go through 124362306a36Sopenharmony_ci * power-up after getting plugged in. We schedule the 124462306a36Sopenharmony_ci * first config rom scan half a second after bus reset. 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci device->workfn = fw_device_init; 124762306a36Sopenharmony_ci INIT_DELAYED_WORK(&device->work, fw_device_workfn); 124862306a36Sopenharmony_ci fw_schedule_device_work(device, INITIAL_DELAY); 124962306a36Sopenharmony_ci break; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci case FW_NODE_INITIATED_RESET: 125262306a36Sopenharmony_ci case FW_NODE_LINK_ON: 125362306a36Sopenharmony_ci device = node->data; 125462306a36Sopenharmony_ci if (device == NULL) 125562306a36Sopenharmony_ci goto create; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci device->node_id = node->node_id; 125862306a36Sopenharmony_ci smp_wmb(); /* update node_id before generation */ 125962306a36Sopenharmony_ci device->generation = card->generation; 126062306a36Sopenharmony_ci if (atomic_cmpxchg(&device->state, 126162306a36Sopenharmony_ci FW_DEVICE_RUNNING, 126262306a36Sopenharmony_ci FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { 126362306a36Sopenharmony_ci device->workfn = fw_device_refresh; 126462306a36Sopenharmony_ci fw_schedule_device_work(device, 126562306a36Sopenharmony_ci device->is_local ? 0 : INITIAL_DELAY); 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci break; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci case FW_NODE_UPDATED: 127062306a36Sopenharmony_ci device = node->data; 127162306a36Sopenharmony_ci if (device == NULL) 127262306a36Sopenharmony_ci break; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci device->node_id = node->node_id; 127562306a36Sopenharmony_ci smp_wmb(); /* update node_id before generation */ 127662306a36Sopenharmony_ci device->generation = card->generation; 127762306a36Sopenharmony_ci if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { 127862306a36Sopenharmony_ci device->workfn = fw_device_update; 127962306a36Sopenharmony_ci fw_schedule_device_work(device, 0); 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci break; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci case FW_NODE_DESTROYED: 128462306a36Sopenharmony_ci case FW_NODE_LINK_OFF: 128562306a36Sopenharmony_ci if (!node->data) 128662306a36Sopenharmony_ci break; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* 128962306a36Sopenharmony_ci * Destroy the device associated with the node. There 129062306a36Sopenharmony_ci * are two cases here: either the device is fully 129162306a36Sopenharmony_ci * initialized (FW_DEVICE_RUNNING) or we're in the 129262306a36Sopenharmony_ci * process of reading its config rom 129362306a36Sopenharmony_ci * (FW_DEVICE_INITIALIZING). If it is fully 129462306a36Sopenharmony_ci * initialized we can reuse device->work to schedule a 129562306a36Sopenharmony_ci * full fw_device_shutdown(). If not, there's work 129662306a36Sopenharmony_ci * scheduled to read it's config rom, and we just put 129762306a36Sopenharmony_ci * the device in shutdown state to have that code fail 129862306a36Sopenharmony_ci * to create the device. 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci device = node->data; 130162306a36Sopenharmony_ci if (atomic_xchg(&device->state, 130262306a36Sopenharmony_ci FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { 130362306a36Sopenharmony_ci device->workfn = fw_device_shutdown; 130462306a36Sopenharmony_ci fw_schedule_device_work(device, 130562306a36Sopenharmony_ci list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci break; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci} 1310