From f70575805708cabdedea7498aaa3f710fde4d920 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 31 Jan 2024 03:29:01 +0000 Subject: [PATCH] add lvds1024*800 --- kernel/drivers/thunderbolt/eeprom.c | 180 ++++++++++++++++++++++++++++++++++++----------------------- 1 files changed, 110 insertions(+), 70 deletions(-) diff --git a/kernel/drivers/thunderbolt/eeprom.c b/kernel/drivers/thunderbolt/eeprom.c index 3e8caf2..0c8471b 100644 --- a/kernel/drivers/thunderbolt/eeprom.c +++ b/kernel/drivers/thunderbolt/eeprom.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Thunderbolt Cactus Ridge driver - eeprom access + * Thunderbolt driver - eeprom access * * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> + * Copyright (C) 2018, Intel Corporation */ #include <linux/crc32.h> +#include <linux/delay.h> #include <linux/property.h> #include <linux/slab.h> #include "tb.h" @@ -130,12 +132,51 @@ } /** + * 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 -ENODEV; + } + 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 -ENODEV; + } + + 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; +} + +/** * tb_eeprom_read_n - read count bytes from offset into val */ static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val, size_t count) { + u16 drom_offset; int i, res; + + res = tb_eeprom_get_drom_offset(sw, &drom_offset); + if (res) + return res; + + offset += drom_offset; + res = tb_eeprom_active(sw, true); if (res) return res; @@ -207,7 +248,7 @@ struct tb_drom_entry_generic { struct tb_drom_entry_header header; - u8 data[0]; + u8 data[]; } __packed; struct tb_drom_entry_port { @@ -238,36 +279,6 @@ /** - * 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; -} - -/** * 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 @@ -276,17 +287,11 @@ 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; - - if (drom_offset == 0) - return -ENODEV; + int res; /* read uid */ - res = tb_eeprom_read_n(sw, drom_offset, data, 9); + res = tb_eeprom_read_n(sw, 0, data, 9); if (res) return res; @@ -385,8 +390,8 @@ 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_sw_warn(sw, "DROM buffer overrun\n"); + return -EILSEQ; } switch (entry->type) { @@ -413,7 +418,7 @@ struct device *dev = &sw->tb->nhi->pdev->dev; int len, res; - len = device_property_read_u8_array(dev, "ThunderboltDROM", NULL, 0); + len = device_property_count_u8(dev, "ThunderboltDROM"); if (len < 0 || len < sizeof(struct tb_drom_header)) return -EINVAL; @@ -483,16 +488,47 @@ return ret; } +static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size) +{ + int ret; + + ret = usb4_switch_drom_read(sw, 14, size, sizeof(*size)); + if (ret) + return ret; + + /* Size includes CRC8 + UID + CRC32 */ + *size += 1 + 8 + 4; + sw->drom = kzalloc(*size, GFP_KERNEL); + if (!sw->drom) + return -ENOMEM; + + ret = usb4_switch_drom_read(sw, 0, sw->drom, *size); + if (ret) { + kfree(sw->drom); + sw->drom = NULL; + } + + return ret; +} + +static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, + size_t count) +{ + if (tb_switch_is_usb4(sw)) + return usb4_switch_drom_read(sw, offset, val, count); + return tb_eeprom_read_n(sw, offset, val, count); +} + /** * 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; + int res, retries = 1; + if (sw->drom) return 0; @@ -509,38 +545,31 @@ goto parse; /* - * The root switch contains only a dummy drom (header only, - * no entries). Hardcode the configuration here. + * USB4 hosts may support reading DROM through router + * operations. */ - 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]; - - /* Port 5 is inaccessible on this gen 1 controller */ - if (sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE) - sw->ports[5].disabled = true; + if (tb_switch_is_usb4(sw)) { + usb4_switch_read_uid(sw, &sw->uid); + if (!usb4_copy_host_drom(sw, &size)) + goto parse; + } else { + /* + * The root switch contains only a dummy drom + * (header only, no entries). Hardcode the + * configuration here. + */ + tb_drom_read_uid_only(sw, &sw->uid); + } 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); + res = tb_drom_read_n(sw, 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); + tb_sw_dbg(sw, "reading drom (length: %#x)\n", size); if (size < sizeof(*header)) { tb_sw_warn(sw, "drom too small, aborting\n"); return -EIO; @@ -549,7 +578,7 @@ sw->drom = kzalloc(size, GFP_KERNEL); if (!sw->drom) return -ENOMEM; - res = tb_eeprom_read_n(sw, drom_offset, sw->drom, size); + res = tb_drom_read_n(sw, 0, sw->drom, size); if (res) goto err; @@ -572,6 +601,7 @@ sw->uid = header->uid; sw->vendor = header->vendor_id; sw->device = header->model_id; + tb_check_quirks(sw); crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); if (crc != header->data_crc32) { @@ -584,7 +614,17 @@ tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n", header->device_rom_revision); - return tb_drom_parse_entries(sw); + res = tb_drom_parse_entries(sw); + /* If the DROM parsing fails, wait a moment and retry once */ + if (res == -EILSEQ && retries--) { + tb_sw_warn(sw, "parsing DROM failed, retrying\n"); + msleep(100); + res = tb_drom_read_n(sw, 0, sw->drom, size); + if (!res) + goto parse; + } + + return res; err: kfree(sw->drom); sw->drom = NULL; -- Gitblit v1.6.2