コード例 #1
0
ファイル: eeprom.c プロジェクト: 020gzh/linux
/**
 * tb_eeprom_get_drom_offset - get drom offset within eeprom
 */
static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset)
{
	struct tb_cap_plug_events cap;
	int res;
	if (!sw->cap_plug_events) {
		tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n");
		return -ENOSYS;
	}
	res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events,
			     sizeof(cap) / 4);
	if (res)
		return res;

	if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) {
		tb_sw_warn(sw, "no NVM\n");
		return -ENOSYS;
	}

	if (cap.drom_offset > 0xffff) {
		tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n",
				cap.drom_offset);
		return -ENXIO;
	}
	*offset = cap.drom_offset;
	return 0;
}
コード例 #2
0
ファイル: eeprom.c プロジェクト: 020gzh/linux
static int tb_drom_parse_entry(struct tb_switch *sw,
		struct tb_drom_entry_header *header)
{
	struct tb_port *port;
	int res;
	enum tb_port_type type;

	if (header->type != TB_DROM_ENTRY_PORT)
		return 0;

	port = &sw->ports[header->index];
	port->disabled = header->port_disabled;
	if (port->disabled)
		return 0;

	res = tb_port_read(port, &type, TB_CFG_PORT, 2, 1);
	if (res)
		return res;
	type &= 0xffffff;

	if (type == TB_TYPE_PORT) {
		struct tb_drom_entry_port *entry = (void *) header;
		if (header->len != sizeof(*entry)) {
			tb_sw_warn(sw,
				"port entry has size %#x (expected %#zx)\n",
				header->len, sizeof(struct tb_drom_entry_port));
			return -EIO;
		}
		tb_drom_parse_port_entry(port, entry);
	}
	return 0;
}
コード例 #3
0
ファイル: switch.c プロジェクト: Announcement/linux
int tb_switch_resume(struct tb_switch *sw)
{
	int i, err;
	u64 uid;
	tb_sw_info(sw, "resuming switch\n");

	err = tb_drom_read_uid_only(sw, &uid);
	if (err) {
		tb_sw_warn(sw, "uid read failed\n");
		return err;
	}
	if (sw != sw->tb->root_switch && sw->uid != uid) {
		tb_sw_info(sw,
			"changed while suspended (uid %#llx -> %#llx)\n",
			sw->uid, uid);
		return -ENODEV;
	}

	/* upload configuration */
	err = tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3);
	if (err)
		return err;

	err = tb_plug_events_active(sw, true);
	if (err)
		return err;

	/* check for surviving downstream switches */
	for (i = 1; i <= sw->config.max_port_number; i++) {
		struct tb_port *port = &sw->ports[i];
		if (tb_is_upstream_port(port))
			continue;
		if (!port->remote)
			continue;
		if (tb_wait_for_port(port, true) <= 0
			|| tb_switch_resume(port->remote->sw)) {
			tb_port_warn(port,
				     "lost during suspend, disconnecting\n");
			tb_sw_set_unplugged(port->remote->sw);
		}
	}
	return 0;
}
コード例 #4
0
ファイル: eeprom.c プロジェクト: 020gzh/linux
/**
 * tb_drom_parse_entries - parse the linked list of drom entries
 *
 * Drom must have been copied to sw->drom.
 */
static int tb_drom_parse_entries(struct tb_switch *sw)
{
	struct tb_drom_header *header = (void *) sw->drom;
	u16 pos = sizeof(*header);
	u16 drom_size = header->data_len + TB_DROM_DATA_START;

	while (pos < drom_size) {
		struct tb_drom_entry_header *entry = (void *) (sw->drom + pos);
		if (pos + 1 == drom_size || pos + entry->len > drom_size
				|| !entry->len) {
			tb_sw_warn(sw, "drom buffer overrun, aborting\n");
			return -EIO;
		}

		tb_drom_parse_entry(sw, entry);

		pos += entry->len;
	}
	return 0;
}
コード例 #5
0
ファイル: eeprom.c プロジェクト: 020gzh/linux
/**
 * tb_drom_read_uid_only - read uid directly from drom
 *
 * Does not use the cached copy in sw->drom. Used during resume to check switch
 * identity.
 */
int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid)
{
	u8 data[9];
	u16 drom_offset;
	u8 crc;
	int res = tb_eeprom_get_drom_offset(sw, &drom_offset);
	if (res)
		return res;

	/* read uid */
	res = tb_eeprom_read_n(sw, drom_offset, data, 9);
	if (res)
		return res;

	crc = tb_crc8(data + 1, 8);
	if (crc != data[0]) {
		tb_sw_warn(sw, "uid crc8 missmatch (expected: %#x, got: %#x)\n",
				data[0], crc);
		return -EIO;
	}

	*uid = *(u64 *)(data+1);
	return 0;
}
コード例 #6
0
ファイル: switch.c プロジェクト: Announcement/linux
/**
 * tb_switch_alloc() - allocate and initialize a switch
 *
 * Return: Returns a NULL on failure.
 */
struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
{
	int i;
	int cap;
	struct tb_switch *sw;
	int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route);
	if (upstream_port < 0)
		return NULL;

	sw = kzalloc(sizeof(*sw), GFP_KERNEL);
	if (!sw)
		return NULL;

	sw->tb = tb;
	if (tb_cfg_read(tb->ctl, &sw->config, route, 0, TB_CFG_SWITCH, 0, 5))
		goto err;
	tb_info(tb,
		"initializing Switch at %#llx (depth: %d, up port: %d)\n",
		route, tb_route_length(route), upstream_port);
	tb_info(tb, "old switch config:\n");
	tb_dump_switch(tb, &sw->config);

	/* configure switch */
	sw->config.upstream_port_number = upstream_port;
	sw->config.depth = tb_route_length(route);
	sw->config.route_lo = route;
	sw->config.route_hi = route >> 32;
	sw->config.enabled = 1;
	/* from here on we may use the tb_sw_* functions & macros */

	if (sw->config.vendor_id != 0x8086)
		tb_sw_warn(sw, "unknown switch vendor id %#x\n",
			   sw->config.vendor_id);

	if (sw->config.device_id != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
	    sw->config.device_id != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
	    sw->config.device_id != PCI_DEVICE_ID_INTEL_PORT_RIDGE &&
	    sw->config.device_id != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE &&
	    sw->config.device_id != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE)
		tb_sw_warn(sw, "unsupported switch device id %#x\n",
			   sw->config.device_id);

	/* upload configuration */
	if (tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3))
		goto err;

	/* initialize ports */
	sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports),
				GFP_KERNEL);
	if (!sw->ports)
		goto err;

	for (i = 0; i <= sw->config.max_port_number; i++) {
		/* minimum setup for tb_find_cap and tb_drom_read to work */
		sw->ports[i].sw = sw;
		sw->ports[i].port = i;
	}

	cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS);
	if (cap < 0) {
		tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n");
		goto err;
	}
	sw->cap_plug_events = cap;

	/* read drom */
	if (tb_drom_read(sw))
		tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n");
	tb_sw_info(sw, "uid: %#llx\n", sw->uid);

	for (i = 0; i <= sw->config.max_port_number; i++) {
		if (sw->ports[i].disabled) {
			tb_port_info(&sw->ports[i], "disabled by eeprom\n");
			continue;
		}
		if (tb_init_port(&sw->ports[i]))
			goto err;
	}

	/* TODO: I2C, IECS, link controller */

	if (tb_plug_events_active(sw, true))
		goto err;

	return sw;
err:
	kfree(sw->ports);
	kfree(sw->drom);
	kfree(sw);
	return NULL;
}
コード例 #7
0
ファイル: eeprom.c プロジェクト: 020gzh/linux
/**
 * tb_drom_read - copy drom to sw->drom and parse it
 */
int tb_drom_read(struct tb_switch *sw)
{
	u16 drom_offset;
	u16 size;
	u32 crc;
	struct tb_drom_header *header;
	int res;
	if (sw->drom)
		return 0;

	if (tb_route(sw) == 0) {
		/*
		 * The root switch contains only a dummy drom (header only,
		 * no entries). Hardcode the configuration here.
		 */
		tb_drom_read_uid_only(sw, &sw->uid);

		sw->ports[1].link_nr = 0;
		sw->ports[2].link_nr = 1;
		sw->ports[1].dual_link_port = &sw->ports[2];
		sw->ports[2].dual_link_port = &sw->ports[1];

		sw->ports[3].link_nr = 0;
		sw->ports[4].link_nr = 1;
		sw->ports[3].dual_link_port = &sw->ports[4];
		sw->ports[4].dual_link_port = &sw->ports[3];
		return 0;
	}

	res = tb_eeprom_get_drom_offset(sw, &drom_offset);
	if (res)
		return res;

	res = tb_eeprom_read_n(sw, drom_offset + 14, (u8 *) &size, 2);
	if (res)
		return res;
	size &= 0x3ff;
	size += TB_DROM_DATA_START;
	tb_sw_info(sw, "reading drom (length: %#x)\n", size);
	if (size < sizeof(*header)) {
		tb_sw_warn(sw, "drom too small, aborting\n");
		return -EIO;
	}

	sw->drom = kzalloc(size, GFP_KERNEL);
	if (!sw->drom)
		return -ENOMEM;
	res = tb_eeprom_read_n(sw, drom_offset, sw->drom, size);
	if (res)
		goto err;

	header = (void *) sw->drom;

	if (header->data_len + TB_DROM_DATA_START != size) {
		tb_sw_warn(sw, "drom size mismatch, aborting\n");
		goto err;
	}

	crc = tb_crc8((u8 *) &header->uid, 8);
	if (crc != header->uid_crc8) {
		tb_sw_warn(sw,
			"drom uid crc8 mismatch (expected: %#x, got: %#x), aborting\n",
			header->uid_crc8, crc);
		goto err;
	}
	sw->uid = header->uid;

	crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len);
	if (crc != header->data_crc32) {
		tb_sw_warn(sw,
			"drom data crc32 mismatch (expected: %#x, got: %#x), aborting\n",
			header->data_crc32, crc);
		goto err;
	}

	if (header->device_rom_revision > 1)
		tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n",
			header->device_rom_revision);

	return tb_drom_parse_entries(sw);
err:
	kfree(sw->drom);
	return -EIO;

}