From 0ed56252c9567351344cb7b5cff6140e1bcec943 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 May 2019 11:27:17 +0900 Subject: spi: Fix Raspberry Pi breakage This reverts commit c9ba7a16d0f1 (Release spi_res after finalizing message) which causes races during cleanup. Reported-by: Nicolas Saenz Julienne Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5e75944ad5d1..8eb7460dd744 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1181,10 +1181,10 @@ out: if (msg->status && ctlr->handle_err) ctlr->handle_err(ctlr, msg); - spi_finalize_current_message(ctlr); - spi_res_release(ctlr, msg); + spi_finalize_current_message(ctlr); + return ret; } -- cgit v1.2.3 From eb6964fa6509b4f1152313f1e0bb67f0c54a6046 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 22 Apr 2019 14:57:04 +0800 Subject: HID: i2c-hid: add iBall Aer3 to descriptor override This device uses the SIPODEV SP1064 touchpad, which does not supply descriptors, so it has to be added to the override list. BugLink: https://bugs.launchpad.net/bugs/1825718 Signed-off-by: Kai-Heng Feng Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c index fd1b6eea6d2f..75078c83be1a 100644 --- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c +++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c @@ -354,6 +354,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = { }, .driver_data = (void *)&sipodev_desc }, + { + .ident = "iBall Aer3", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "iBall"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Aer3"), + }, + .driver_data = (void *)&sipodev_desc + }, { } /* Terminate list */ }; -- cgit v1.2.3 From b9a94fbe0ed0c37c0eb8b32c418506563259dc57 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 Apr 2019 21:25:51 +0200 Subject: HID: logitech-dj: add support for the Logitech MX5500's Bluetooth Mini-Receiver Add support for the HID proxy mode of the Logitech Bluetooth Mini-Receiver which comes with the Logitech MX5500 keyboard. This receiver works the same as the Bluetooth Mini-Receiver coming with the MX5000 keyboard and also presents itself as an USB-hub with 2 separate USB devices for the keyboard (boot sub-class) interface and for the (boot sub-class) mouse interface. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index b1e894618eed..dfc34fe85319 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1851,6 +1851,14 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xc70a), .driver_data = recvr_type_bluetooth}, + { /* Logitech MX5500 HID++ / bluetooth receiver keyboard intf. */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + 0xc71b), + .driver_data = recvr_type_bluetooth}, + { /* Logitech MX5500 HID++ / bluetooth receiver mouse intf. */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + 0xc71c), + .driver_data = recvr_type_bluetooth}, {} }; -- cgit v1.2.3 From ced2c0c53d08e44b1443d66620c1a883db53c72f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 28 Apr 2019 21:25:52 +0200 Subject: HID: logitech-hidpp: add support for the MX5500 keyboard The MX5500 keyboard needs the HIDPP_CONSUMER_VENDOR_KEYS quirk to report events for the 'A' - 'D' Smart Keys and for the "Photo Gallery" and "Gadgets" keys. In addition to this adding the Bluetooth match, so that hid-logitech-hidpp is used instead of the generic HID driver, also adds battery monitoring support when the keyboard is connected over Bluetooth. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 72fc9c0566db..b99619d64080 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -3732,6 +3732,9 @@ static const struct hid_device_id hidpp_devices[] = { { /* Keyboard MX5000 (Bluetooth-receiver in HID proxy mode) */ LDJ_DEVICE(0xb305), .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS }, + { /* Keyboard MX5500 (Bluetooth-receiver in HID proxy mode) */ + LDJ_DEVICE(0xb30b), + .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS }, { LDJ_DEVICE(HID_ANY_ID) }, @@ -3760,6 +3763,9 @@ static const struct hid_device_id hidpp_devices[] = { { /* MX5000 keyboard over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305), .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS }, + { /* MX5500 keyboard over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b), + .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS }, {} }; -- cgit v1.2.3 From 10235380d18e8f666446970078d1ee69b33ab643 Mon Sep 17 00:00:00 2001 From: Tobias Auerochs Date: Sat, 20 Apr 2019 21:19:51 +0200 Subject: HID: rmi: Use SET_REPORT request on control endpoint for Acer Switch 3 and 5 The touchpad on the cover keyboard for the Acer Switch 3 and 5 does not work as-is under Linux. Both devices have the same usb id for the cover keyboard. The kernel correctly assigns the hid-rmi driver to the device using usbhid for transport. Any attempts of hid-rmi to talk to the device using hid_hw_output_report fail however as usbhid does not have a working urbout due to the lack of any out endpoints. Looking through Wireshark usbmon recordings from the Windows Synaptics driver for this computer running inside of QEMU shows that it should be using SET_REPORT requests instead. This replaces the hid_hw_output_report in hid-rmi with a hid_hw_raw_request for this device, which is at least enough to enable the kernel to get working multi-touch input. Signed-off-by: Tobias Auerochs Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-rmi.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 1c8c72093b5a..22456586034a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1089,6 +1089,7 @@ #define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 #define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 +#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7 #define USB_VENDOR_ID_TEXAS_INSTRUMENTS 0x2047 #define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA 0x0855 diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 9e33165250a3..34465a7c3713 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -39,6 +39,7 @@ /* device flags */ #define RMI_DEVICE BIT(0) #define RMI_DEVICE_HAS_PHYS_BUTTONS BIT(1) +#define RMI_DEVICE_OUTPUT_SET_REPORT BIT(2) /* * retrieve the ctrl registers @@ -167,9 +168,19 @@ static int rmi_set_mode(struct hid_device *hdev, u8 mode) static int rmi_write_report(struct hid_device *hdev, u8 *report, int len) { + struct rmi_data *data = hid_get_drvdata(hdev); int ret; - ret = hid_hw_output_report(hdev, (void *)report, len); + if (data->device_flags & RMI_DEVICE_OUTPUT_SET_REPORT) { + /* + * Talk to device by using SET_REPORT requests instead. + */ + ret = hid_hw_raw_request(hdev, report[0], report, + len, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + } else { + ret = hid_hw_output_report(hdev, (void *)report, len); + } + if (ret < 0) { dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret); return ret; @@ -751,6 +762,8 @@ static const struct hid_device_id rmi_id[] = { .driver_data = RMI_DEVICE_HAS_PHYS_BUTTONS }, { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_REZEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5), + .driver_data = RMI_DEVICE_OUTPUT_SET_REPORT }, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) }, { } }; -- cgit v1.2.3 From 5caaf29af5ca82d5da8bc1d0ad07d9e664ccf1d8 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 16 May 2019 15:56:56 +0800 Subject: spi: bitbang: Fix NULL pointer dereference in spi_unregister_master If spi_register_master fails in spi_bitbang_start because device_add failure, We should return the error code other than 0, otherwise calling spi_bitbang_stop may trigger NULL pointer dereference like this: BUG: KASAN: null-ptr-deref in __list_del_entry_valid+0x45/0xd0 Read of size 8 at addr 0000000000000000 by task syz-executor.0/3661 CPU: 0 PID: 3661 Comm: syz-executor.0 Not tainted 5.1.0+ #28 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 Call Trace: dump_stack+0xa9/0x10e ? __list_del_entry_valid+0x45/0xd0 ? __list_del_entry_valid+0x45/0xd0 __kasan_report+0x171/0x18d ? __list_del_entry_valid+0x45/0xd0 kasan_report+0xe/0x20 __list_del_entry_valid+0x45/0xd0 spi_unregister_controller+0x99/0x1b0 spi_lm70llp_attach+0x3ae/0x4b0 [spi_lm70llp] ? 0xffffffffc1128000 ? klist_next+0x131/0x1e0 ? driver_detach+0x40/0x40 [parport] port_check+0x3b/0x50 [parport] bus_for_each_dev+0x115/0x180 ? subsys_dev_iter_exit+0x20/0x20 __parport_register_driver+0x1f0/0x210 [parport] ? 0xffffffffc1150000 do_one_initcall+0xb9/0x3b5 ? perf_trace_initcall_level+0x270/0x270 ? kasan_unpoison_shadow+0x30/0x40 ? kasan_unpoison_shadow+0x30/0x40 do_init_module+0xe0/0x330 load_module+0x38eb/0x4270 ? module_frob_arch_sections+0x20/0x20 ? kernel_read_file+0x188/0x3f0 ? find_held_lock+0x6d/0xd0 ? fput_many+0x1a/0xe0 ? __do_sys_finit_module+0x162/0x190 __do_sys_finit_module+0x162/0x190 ? __ia32_sys_init_module+0x40/0x40 ? __mutex_unlock_slowpath+0xb4/0x3f0 ? wait_for_completion+0x240/0x240 ? vfs_write+0x160/0x2a0 ? lockdep_hardirqs_off+0xb5/0x100 ? mark_held_locks+0x1a/0x90 ? do_syscall_64+0x14/0x2a0 do_syscall_64+0x72/0x2a0 entry_SYSCALL_64_after_hwframe+0x49/0xbe Reported-by: Hulk Robot Fixes: 702a4879ec33 ("spi: bitbang: Let spi_bitbang_start() take a reference to master") Signed-off-by: YueHaibing Reviewed-by: Geert Uytterhoeven Reviewed-by: Axel Lin Reviewed-by: Mukesh Ojha Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 4243e53f9f7b..e8fd95276315 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -415,7 +415,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) if (ret) spi_master_put(master); - return 0; + return ret; } EXPORT_SYMBOL_GPL(spi_bitbang_start); -- cgit v1.2.3 From 7d293f56456120efa97c4e18250d86d2a05ad0bd Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 16 May 2019 20:48:08 +0800 Subject: regulator: tps6507x: Fix boot regression due to testing wrong init_data pointer A NULL init_data once incremented will lead to oops, fix it. Fixes: f979c08f7624 ("regulator: tps6507x: Convert to regulator core's simplified DT parsing code") Reported-by: Sekhar Nori Signed-off-by: Axel Lin Tested-by: Sekhar Nori Signed-off-by: Mark Brown --- drivers/regulator/tps6507x-regulator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index a1b7fab91dd4..d2a8f69b2665 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -403,12 +403,12 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) /* common for all regulators */ tps->mfd = tps6507x_dev; - for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) { + for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++) { /* Register the regulators */ tps->info[i] = info; - if (init_data && init_data->driver_data) { + if (init_data && init_data[i].driver_data) { struct tps6507x_reg_platform_data *data = - init_data->driver_data; + init_data[i].driver_data; info->defdcdc_default = data->defdcdc_default; } -- cgit v1.2.3 From 2cc08800a6b9fcda7c7afbcf2da1a6e8808da725 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 24 Apr 2019 15:12:57 -0700 Subject: HID: wacom: Don't set tool type until we're in range The serial number and tool type information that is reported by the tablet while a pen is merely "in prox" instead of fully "in range" can be stale and cause us to report incorrect tool information. Serial number, tool type, and other information is only valid once the pen comes fully in range so we should be careful to not use this information until that point. In particular, this issue may cause the driver to incorectly report BTN_TOOL_RUBBER after switching from the eraser tool back to the pen. Fixes: a48324de6d4d ("HID: wacom: Bluetooth IRQ for Intuos Pro should handle prox/range") Cc: # 4.11+ Signed-off-by: Jason Gerecke Reviewed-by: Aaron Armstrong Skomra Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_wac.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 747730d32ab6..4c1bc239207e 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1236,13 +1236,13 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) /* Add back in missing bits of ID for non-USI pens */ wacom->id[0] |= (wacom->serial[0] >> 32) & 0xFFFFF; } - wacom->tool[0] = wacom_intuos_get_tool_type(wacom_intuos_id_mangle(wacom->id[0])); for (i = 0; i < pen_frames; i++) { unsigned char *frame = &data[i*pen_frame_len + 1]; bool valid = frame[0] & 0x80; bool prox = frame[0] & 0x40; bool range = frame[0] & 0x20; + bool invert = frame[0] & 0x10; if (!valid) continue; @@ -1251,9 +1251,24 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) wacom->shared->stylus_in_proximity = false; wacom_exit_report(wacom); input_sync(pen_input); + + wacom->tool[0] = 0; + wacom->id[0] = 0; + wacom->serial[0] = 0; return; } + if (range) { + if (!wacom->tool[0]) { /* first in range */ + /* Going into range select tool */ + if (invert) + wacom->tool[0] = BTN_TOOL_RUBBER; + else if (wacom->id[0]) + wacom->tool[0] = wacom_intuos_get_tool_type(wacom->id[0]); + else + wacom->tool[0] = BTN_TOOL_PEN; + } + input_report_abs(pen_input, ABS_X, get_unaligned_le16(&frame[1])); input_report_abs(pen_input, ABS_Y, get_unaligned_le16(&frame[3])); -- cgit v1.2.3 From e92a7be7fe5b2510fa60965eaf25f9e3dc08b8cc Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 24 Apr 2019 15:12:58 -0700 Subject: HID: wacom: Don't report anything prior to the tool entering range If the tool spends some time in prox before entering range, a series of events (e.g. ABS_DISTANCE, MSC_SERIAL) can be sent before we or userspace have any clue about the pen whose data is being reported. We need to hold off on reporting anything until the pen has entered range. Since we still want to report events that occur "in prox" after the pen has *left* range we use 'wacom-tool[0]' as the indicator that the pen did at one point enter range and provide us/userspace with tool type and serial number information. Fixes: a48324de6d4d ("HID: wacom: Bluetooth IRQ for Intuos Pro should handle prox/range") Cc: # 4.11+ Signed-off-by: Jason Gerecke Reviewed-by: Aaron Armstrong Skomra Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_wac.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 4c1bc239207e..613342bb9d6b 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1290,23 +1290,26 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) get_unaligned_le16(&frame[11])); } } - input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5])); - if (wacom->features.type == INTUOSP2_BT) { - input_report_abs(pen_input, ABS_DISTANCE, - range ? frame[13] : wacom->features.distance_max); - } else { - input_report_abs(pen_input, ABS_DISTANCE, - range ? frame[7] : wacom->features.distance_max); - } - input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01); - input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02); - input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04); + if (wacom->tool[0]) { + input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5])); + if (wacom->features.type == INTUOSP2_BT) { + input_report_abs(pen_input, ABS_DISTANCE, + range ? frame[13] : wacom->features.distance_max); + } else { + input_report_abs(pen_input, ABS_DISTANCE, + range ? frame[7] : wacom->features.distance_max); + } + + input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01); + input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02); + input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04); - input_report_key(pen_input, wacom->tool[0], prox); - input_event(pen_input, EV_MSC, MSC_SERIAL, wacom->serial[0]); - input_report_abs(pen_input, ABS_MISC, - wacom_intuos_id_mangle(wacom->id[0])); /* report tool id */ + input_report_key(pen_input, wacom->tool[0], prox); + input_event(pen_input, EV_MSC, MSC_SERIAL, wacom->serial[0]); + input_report_abs(pen_input, ABS_MISC, + wacom_intuos_id_mangle(wacom->id[0])); /* report tool id */ + } wacom->shared->stylus_in_proximity = prox; -- cgit v1.2.3 From fe7f8d73d1af19b678171170e4e5384deb57833d Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 7 May 2019 11:53:20 -0700 Subject: HID: wacom: Send BTN_TOUCH in response to INTUOSP2_BT eraser contact The Bluetooth reports from the 2nd-gen Intuos Pro have separate bits for indicating if the tip or eraser is in contact with the tablet. At the moment, only the tip contact bit controls the state of the BTN_TOUCH event. This prevents the eraser from working as expected. This commit changes the driver to send BTN_TOUCH whenever either the tip or eraser contact bit is set. Fixes: 4922cd26f03c ("HID: wacom: Support 2nd-gen Intuos Pro's Bluetooth classic interface") Cc: # 4.11+ Signed-off-by: Jason Gerecke Reviewed-by: Aaron Skomra Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_wac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 613342bb9d6b..af62a630fee9 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1301,7 +1301,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) range ? frame[7] : wacom->features.distance_max); } - input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01); + input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x09); input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02); input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04); -- cgit v1.2.3 From 6441fc781c344df61402be1fde582c4491fa35fa Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 7 May 2019 11:53:21 -0700 Subject: HID: wacom: Correct button numbering 2nd-gen Intuos Pro over Bluetooth The button numbering of the 2nd-gen Intuos Pro is not consistent between the USB and Bluetooth interfaces. Over USB, the HID_GENERIC codepath enumerates the eight ExpressKeys first (BTN_0 - BTN_7) followed by the center modeswitch button (BTN_8). The Bluetooth codepath, however, has the center modeswitch button as BTN_0 and the the eight ExpressKeys as BTN_1 - BTN_8. To ensure userspace button mappings do not change depending on how the tablet is connected, modify the Bluetooth codepath to report buttons in the same order as USB. To ensure the mode switch LED continues to toggle in response to the mode switch button, the `wacom_is_led_toggled` function also requires a small update. Link: https://github.com/linuxwacom/input-wacom/pull/79 Fixes: 4922cd26f03c ("HID: wacom: Support 2nd-gen Intuos Pro's Bluetooth classic interface") Cc: # 4.11+ Signed-off-by: Jason Gerecke Reviewed-by: Aaron Skomra Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_wac.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index af62a630fee9..e848445236d8 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1383,7 +1383,7 @@ static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom) struct input_dev *pad_input = wacom->pad_input; unsigned char *data = wacom->data; - int buttons = (data[282] << 1) | ((data[281] >> 6) & 0x01); + int buttons = data[282] | ((data[281] & 0x40) << 2); int ring = data[285] & 0x7F; bool ringstatus = data[285] & 0x80; bool prox = buttons || ringstatus; @@ -3832,7 +3832,7 @@ static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group) static bool wacom_is_led_toggled(struct wacom *wacom, int button_count, int mask, int group) { - int button_per_group; + int group_button; /* * 21UX2 has LED group 1 to the left and LED group 0 @@ -3842,9 +3842,12 @@ static bool wacom_is_led_toggled(struct wacom *wacom, int button_count, if (wacom->wacom_wac.features.type == WACOM_21UX2) group = 1 - group; - button_per_group = button_count/wacom->led.count; + group_button = group * (button_count/wacom->led.count); - return mask & (1 << (group * button_per_group)); + if (wacom->wacom_wac.features.type == INTUOSP2_BT) + group_button = 8; + + return mask & (1 << group_button); } static void wacom_update_led(struct wacom *wacom, int button_count, int mask, -- cgit v1.2.3 From 69dbdfffef20c715df9f381b2cee4e9e0a4efd93 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 7 May 2019 11:53:22 -0700 Subject: HID: wacom: Sync INTUOSP2_BT touch state after each frame if necessary The Bluetooth interface of the 2nd-gen Intuos Pro batches together four independent "frames" of finger data into a single report. Each frame is essentially equivalent to a single USB report, with the up-to-10 fingers worth of information being spread across two frames. At the moment the driver only calls `input_sync` after processing all four frames have been processed, which can result in the driver sending multiple updates for a single slot within the same SYN_REPORT. This can confuse userspace, so modify the driver to sync more often if necessary (i.e., after reporting the state of all fingers). Fixes: 4922cd26f03c ("HID: wacom: Support 2nd-gen Intuos Pro's Bluetooth classic interface") Cc: # 4.11+ Signed-off-by: Jason Gerecke Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_wac.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index e848445236d8..09b8e4aac82f 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1371,11 +1371,17 @@ static void wacom_intuos_pro2_bt_touch(struct wacom_wac *wacom) if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); + input_sync(touch_input); } } - input_report_switch(touch_input, SW_MUTE_DEVICE, !(data[281] >> 7)); - input_sync(touch_input); + if (wacom->num_contacts_left == 0) { + // Be careful that we don't accidentally call input_sync with + // only a partial set of fingers of processed + input_report_switch(touch_input, SW_MUTE_DEVICE, !(data[281] >> 7)); + input_sync(touch_input); + } + } static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom) -- cgit v1.2.3 From 5417a7e482962952e622eabd60cd3600dd65dedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rgen=20Storvist?= Date: Mon, 13 May 2019 18:37:52 +0200 Subject: USB: serial: option: add support for Simcom SIM7500/SIM7600 RNDIS mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added IDs for Simcom SIM7500/SIM7600 series cellular module in RNDIS mode. Reserved the interface for ADB. T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 7 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1e0e ProdID=9011 Rev=03.18 S: Manufacturer=SimTech, Incorporated S: Product=SimTech, Incorporated S: SerialNumber=0123456789ABCDEF C: #Ifs= 8 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=02 Prot=ff Driver=rndis_host I: If#=0x1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host I: If#=0x2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x6 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x7 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) Signed-off-by: Jörgen Storvist Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 83869065b802..100a5c0ec3e7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1772,6 +1772,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E), .driver_info = RSVD(5) | RSVD(6) }, { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ + .driver_info = RSVD(7) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200), .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D), -- cgit v1.2.3 From c5f81656a18b271976a86724dadd8344e54de74e Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 14 May 2019 17:35:42 +1200 Subject: USB: serial: pl2303: add Allied Telesis VT-Kit3 This is adds the vendor and device id for the AT-VT-Kit3 which is a pl2303-based device. Signed-off-by: Chris Packham Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 55122ac84518..d7abde14b3cf 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -106,6 +106,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, + { USB_DEVICE(AT_VENDOR_ID, AT_VTKIT3_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 559941ca884d..b0175f17d1a2 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -155,3 +155,6 @@ #define SMART_VENDOR_ID 0x0b8c #define SMART_PRODUCT_ID 0x2303 +/* Allied Telesis VT-Kit3 */ +#define AT_VENDOR_ID 0x0caa +#define AT_VTKIT3_PRODUCT_ID 0x3001 -- cgit v1.2.3 From f3dfd4072c3ee6e287f501a18b5718b185d6a940 Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Wed, 15 May 2019 17:27:49 +0200 Subject: USB: serial: option: add Telit 0x1260 and 0x1261 compositions Added support for Telit LE910Cx 0x1260 and 0x1261 compositions. Signed-off-by: Daniele Palmas Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 100a5c0ec3e7..a0aaf0635359 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1171,6 +1171,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214), .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, + { USB_DEVICE(TELIT_VENDOR_ID, 0x1260), + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, + { USB_DEVICE(TELIT_VENDOR_ID, 0x1261), + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, 0x1900), /* Telit LN940 (QMI) */ .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff), /* Telit LN940 (MBIM) */ -- cgit v1.2.3 From 81bcbad53bab4bf9f200eda303d7a05cdb9bd73b Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 21 May 2019 15:38:31 +0200 Subject: HID: multitouch: handle faulty Elo touch device Since kernel v5.0, one single win8 touchscreen device failed. And it turns out this is because it reports 2 InRange usage per touch. It's a first, and I *really* wonder how this was allowed by Microsoft in the first place. But IIRC, Breno told me this happened *after* a firmware upgrade... Anyway, better be safe for those crappy devices, and make sure we have a full slot before jumping to the next. This won't prevent all crappy devices to fail here, but at least we will have a safeguard as long as the contact ID and the X and Y coordinates are placed in the report after the grabage. Fixes: 01eaac7e5713 ("HID: multitouch: remove one copy of values") CC: stable@vger.kernel.org # v5.0+ Reported-and-tested-by: Breno Leitao Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index c02d4cad1893..1565a307170a 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -641,6 +641,13 @@ static void mt_store_field(struct hid_device *hdev, if (*target != DEFAULT_TRUE && *target != DEFAULT_FALSE && *target != DEFAULT_ZERO) { + if (usage->contactid == DEFAULT_ZERO || + usage->x == DEFAULT_ZERO || + usage->y == DEFAULT_ZERO) { + hid_dbg(hdev, + "ignoring duplicate usage on incomplete"); + return; + } usage = mt_allocate_usage(hdev, application); if (!usage) return; -- cgit v1.2.3 From 2409877d386de709606ca7eecf7c10d2838525a1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 12 May 2019 12:36:02 +0200 Subject: HID: logitech-hidpp: Add support for the S510 remote control The S510 remote-control shows up as a second keyboard (keypad) on the receiver. It has a scroll-wheel, which normally sends wheel event originating from the mouse's evdev node. Add a HIDPP_QUIRK_KBD_SCROLL_WHEEL quirk for it, so that the wheel events properly originate from the evdev node of the remote control itself. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index b99619d64080..14c84e1189a4 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -3747,6 +3747,9 @@ static const struct hid_device_id hidpp_devices[] = { { /* Keyboard MX3200 (Y-RAV80) */ L27MHZ_DEVICE(0x005c), .driver_data = HIDPP_QUIRK_KBD_ZOOM_WHEEL }, + { /* S510 Media Remote */ + L27MHZ_DEVICE(0x00fe), + .driver_data = HIDPP_QUIRK_KBD_SCROLL_WHEEL }, { L27MHZ_DEVICE(HID_ANY_ID) }, -- cgit v1.2.3 From 44a042182cb1e9f7916e015c836967bf638b33c4 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 22 May 2019 11:00:36 +0000 Subject: spi: spi-fsl-spi: call spi_finalize_current_message() at the end spi_finalize_current_message() shall be called once all actions are finished, otherwise the last actions might step over a newly started transfer. Fixes: c592becbe704 ("spi: fsl-(e)spi: migrate to generic master queueing") Signed-off-by: Christophe Leroy Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index b36ac6aa3b1f..7fbdaf066719 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -432,7 +432,6 @@ static int fsl_spi_do_one_msg(struct spi_master *master, } m->status = status; - spi_finalize_current_message(master); if (status || !cs_change) { ndelay(nsecs); @@ -440,6 +439,7 @@ static int fsl_spi_do_one_msg(struct spi_master *master, } fsl_spi_setup_transfer(spi, NULL); + spi_finalize_current_message(master); return 0; } -- cgit v1.2.3 From eb96e57b913ff668b8b804178cdc509f9b3d4472 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 20 May 2019 15:43:49 -0400 Subject: media: dvb: warning about dvb frequency limits produces too much noise This can be a debug message. Favour dev_dbg() over dprintk() as this is already used much more than dprintk(). dvb_frontend: dvb_frontend_get_frequency_limits: frequency interval: tuner: 45000000...860000000, frontend: 44250000...867250000 Fixes: 00ecd6bc7128 ("media: dvb_frontend: add debug message for frequency intervals") Cc: # 5.0 Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index fbdb4ecc7c50..7402c9834189 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -917,7 +917,7 @@ static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", fe->dvb->num, fe->id); - dprintk("frequency interval: tuner: %u...%u, frontend: %u...%u", + dev_dbg(fe->dvb->device, "frequency interval: tuner: %u...%u, frontend: %u...%u", tuner_min, tuner_max, frontend_min, frontend_max); /* If the standard is for satellite, convert frequencies to kHz */ -- cgit v1.2.3 From f3440d9a0da292dc4998d9393162fcfb996fd671 Mon Sep 17 00:00:00 2001 From: Super Liu Date: Wed, 22 May 2019 14:30:14 +0800 Subject: spi: abort spi_sync if failed to prepare_transfer_hardware There is no chance to wait spi message complete if failed to prepare_transfer_hardware(). Therefore, finalize this message and abort transfer with corresponding return status to release this block case. Logs: [17400.283005] c7 3267 PM: PM: suspend entry 2019-05-04 03:01:14.403097147 UTC [17400.283013] c7 3267 PM: suspend entry (deep) [17400.283016] c6 3267 PM: Syncing filesystems ... done. [17400.584395] c1 753 spi_geni 890000.spi: spi_geni_prepare_transfer_hardware:Error enabling SE resources -13 [17400.584404] c1 753 spi_master spi1: failed to prepare transfer hardware [17400.664611] c4 3267 PM: PM: suspend exit 2019-05-04 03:01:15.235273018 UTC Flow: __spi_sync@spi.c | if (status == 0) { | /* Push out the messages in the calling context if we | * can. | */ | if (ctlr->transfer == spi_queued_transfer) { | SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, | spi_sync_immediate); | SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, | spi_sync_immediate); | __spi_pump_messages(ctlr, false); | } | | wait_for_completion(&done); <== stuck here!!! | status = message->status; | } | message->context = NULL; | return status; | --> __spi_pump_messages@spi.c | if (!was_busy && ctlr->prepare_transfer_hardware) { | ret = ctlr->prepare_transfer_hardware(ctlr); | if (ret) { | dev_err(&ctlr->dev, | "failed to prepare transfer hardware\n"); | | if (ctlr->auto_runtime_pm) | pm_runtime_put(ctlr->dev.parent); | mutex_unlock(&ctlr->io_mutex); | return; | } | } | --> spi_geni_prepare_transfer_hardware@spi-geni-qcom.c | ret = pm_runtime_get_sync(mas->dev); | if (ret < 0) { | dev_err(mas->dev, | "%s:Error enabling SE resources %d\n", | __func__, ret); | pm_runtime_put_noidle(mas->dev); | goto exit_prepare_transfer_hardware; Signed-off-by: Super Liu Signed-off-by: Mark Brown --- drivers/spi/spi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 8eb7460dd744..5e4654032bfa 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1307,10 +1307,15 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread) ret = ctlr->prepare_transfer_hardware(ctlr); if (ret) { dev_err(&ctlr->dev, - "failed to prepare transfer hardware\n"); + "failed to prepare transfer hardware: %d\n", + ret); if (ctlr->auto_runtime_pm) pm_runtime_put(ctlr->dev.parent); + + ctlr->cur_msg->status = ret; + spi_finalize_current_message(ctlr); + mutex_unlock(&ctlr->io_mutex); return; } -- cgit v1.2.3 From cda03a9a7be6b94c637cffae5be1d269c538661b Mon Sep 17 00:00:00 2001 From: Philippe Mazenauer Date: Tue, 21 May 2019 12:26:04 +0000 Subject: clocksource/drivers/timer-ti-dm: Change to new style declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable 'dmtimer_ops' was declared const static instead of static const. ../drivers/clocksource/timer-ti-dm.c:899:1: warning: ‘static’ is not at beginning of declaration [-Wold-style-declaration] const static struct omap_dm_timer_ops dmtimer_ops = { ^~~~~ Signed-off-by: Philippe Mazenauer Signed-off-by: Daniel Lezcano --- drivers/clocksource/timer-ti-dm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index e40b55a7086f..5394d9dbdfbc 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -896,7 +896,7 @@ static int omap_dm_timer_remove(struct platform_device *pdev) return ret; } -const static struct omap_dm_timer_ops dmtimer_ops = { +static const struct omap_dm_timer_ops dmtimer_ops = { .request_by_node = omap_dm_timer_request_by_node, .request_specific = omap_dm_timer_request_specific, .request = omap_dm_timer_request, -- cgit v1.2.3 From 24f307d8abf79486dd3c1b645037df7d91602aaa Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Fri, 24 May 2019 14:30:56 +0800 Subject: iommu: Add missing new line for dma type So that all types are printed in the same format. Fixes: c52c72d3dee81 ("iommu: Add sysfs attribyte for domain type") Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 67ee6623f9b2..c9cfd08673e1 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -341,7 +341,7 @@ static ssize_t iommu_group_show_type(struct iommu_group *group, type = "unmanaged\n"; break; case IOMMU_DOMAIN_DMA: - type = "DMA"; + type = "DMA\n"; break; } } -- cgit v1.2.3 From 7560cc3ca7d9d11555f80c830544e463fcdb28b8 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 21 May 2019 15:30:15 +0800 Subject: iommu/vt-d: Fix lock inversion between iommu->lock and device_domain_lock Lockdep debug reported lock inversion related with the iommu code caused by dmar_insert_one_dev_info() grabbing the iommu->lock and the device_domain_lock out of order versus the code path in iommu_flush_dev_iotlb(). Expanding the scope of the iommu->lock and reversing the order of lock acquisition fixes the issue. [ 76.238180] dsa_bus wq0.0: dsa wq wq0.0 disabled [ 76.248706] [ 76.250486] ======================================================== [ 76.257113] WARNING: possible irq lock inversion dependency detected [ 76.263736] 5.1.0-rc5+ #162 Not tainted [ 76.267854] -------------------------------------------------------- [ 76.274485] systemd-journal/521 just changed the state of lock: [ 76.280685] 0000000055b330f5 (device_domain_lock){..-.}, at: iommu_flush_dev_iotlb.part.63+0x29/0x90 [ 76.290099] but this lock took another, SOFTIRQ-unsafe lock in the past: [ 76.297093] (&(&iommu->lock)->rlock){+.+.} [ 76.297094] [ 76.297094] [ 76.297094] and interrupts could create inverse lock ordering between them. [ 76.297094] [ 76.314257] [ 76.314257] other info that might help us debug this: [ 76.321448] Possible interrupt unsafe locking scenario: [ 76.321448] [ 76.328907] CPU0 CPU1 [ 76.333777] ---- ---- [ 76.338642] lock(&(&iommu->lock)->rlock); [ 76.343165] local_irq_disable(); [ 76.349422] lock(device_domain_lock); [ 76.356116] lock(&(&iommu->lock)->rlock); [ 76.363154] [ 76.366134] lock(device_domain_lock); [ 76.370548] [ 76.370548] *** DEADLOCK *** Fixes: 745f2586e78e ("iommu/vt-d: Simplify function get_domain_for_dev()") Signed-off-by: Dave Jiang Reviewed-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index a209199f3af6..91f4912c09c6 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2512,6 +2512,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, } } + spin_lock(&iommu->lock); spin_lock_irqsave(&device_domain_lock, flags); if (dev) found = find_domain(dev); @@ -2527,17 +2528,16 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, if (found) { spin_unlock_irqrestore(&device_domain_lock, flags); + spin_unlock(&iommu->lock); free_devinfo_mem(info); /* Caller must free the original domain */ return found; } - spin_lock(&iommu->lock); ret = domain_attach_iommu(domain, iommu); - spin_unlock(&iommu->lock); - if (ret) { spin_unlock_irqrestore(&device_domain_lock, flags); + spin_unlock(&iommu->lock); free_devinfo_mem(info); return NULL; } @@ -2547,6 +2547,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, if (dev) dev->archdata.iommu = info; spin_unlock_irqrestore(&device_domain_lock, flags); + spin_unlock(&iommu->lock); /* PASID table is mandatory for a PCI device in scalable mode. */ if (dev && dev_is_pci(dev) && sm_supported(iommu)) { -- cgit v1.2.3 From 66d78ad316b0e1ca5ae19663468554e2c0e31c26 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Tue, 21 May 2019 15:30:16 +0800 Subject: iommu/vt-d: Set the right field for Page Walk Snoop Set the page walk snoop to the right bit, otherwise the domain id field will be overlapped. Reported-by: Dave Jiang Fixes: 6f7db75e1c469 ("iommu/vt-d: Add second level page table interface") Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-pasid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/intel-pasid.c b/drivers/iommu/intel-pasid.c index 2fefeafda437..fe51d8af457f 100644 --- a/drivers/iommu/intel-pasid.c +++ b/drivers/iommu/intel-pasid.c @@ -389,7 +389,7 @@ static inline void pasid_set_present(struct pasid_entry *pe) */ static inline void pasid_set_page_snoop(struct pasid_entry *pe, bool value) { - pasid_set_bits(&pe->val[1], 1 << 23, value); + pasid_set_bits(&pe->val[1], 1 << 23, value << 23); } /* -- cgit v1.2.3 From 937f861def1a1d49abb92e041efaa5c259281fbf Mon Sep 17 00:00:00 2001 From: Yongqiang Niu Date: Wed, 27 Mar 2019 14:19:18 +0800 Subject: drm/mediatek: adjust ddp clock control flow display hardware clock will not unprepare when crtc is disable, until crtc is destroyed. with this patch, hard clock will disable and unprepare at the same time. Signed-off-by: Yongqiang Niu Signed-off-by: Hsin-Yi Wang Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index acad088173da..529b8a4af715 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -98,10 +98,6 @@ static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) static void mtk_drm_crtc_destroy(struct drm_crtc *crtc) { struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); - int i; - - for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) - clk_unprepare(mtk_crtc->ddp_comp[i]->clk); mtk_disp_mutex_put(mtk_crtc->mutex); @@ -194,7 +190,7 @@ static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc) DRM_DEBUG_DRIVER("%s\n", __func__); for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) { - ret = clk_enable(mtk_crtc->ddp_comp[i]->clk); + ret = clk_prepare_enable(mtk_crtc->ddp_comp[i]->clk); if (ret) { DRM_ERROR("Failed to enable clock %d: %d\n", i, ret); goto err; @@ -204,7 +200,7 @@ static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc) return 0; err: while (--i >= 0) - clk_disable(mtk_crtc->ddp_comp[i]->clk); + clk_disable_unprepare(mtk_crtc->ddp_comp[i]->clk); return ret; } @@ -214,7 +210,7 @@ static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc) DRM_DEBUG_DRIVER("%s\n", __func__); for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) - clk_disable(mtk_crtc->ddp_comp[i]->clk); + clk_disable_unprepare(mtk_crtc->ddp_comp[i]->clk); } static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) @@ -585,15 +581,7 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev, if (!comp) { dev_err(dev, "Component %pOF not initialized\n", node); ret = -ENODEV; - goto unprepare; - } - - ret = clk_prepare(comp->clk); - if (ret) { - dev_err(dev, - "Failed to prepare clock for component %pOF: %d\n", - node, ret); - goto unprepare; + return ret; } mtk_crtc->ddp_comp[i] = comp; @@ -611,23 +599,17 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev, ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos], BIT(pipe), type); if (ret) - goto unprepare; + return ret; } ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0], mtk_crtc->layer_nr > 1 ? &mtk_crtc->planes[1] : NULL, pipe); if (ret < 0) - goto unprepare; + return ret; drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE); drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE); priv->num_pipes++; return 0; - -unprepare: - while (--i >= 0) - clk_unprepare(mtk_crtc->ddp_comp[i]->clk); - - return ret; } -- cgit v1.2.3 From 46a3b9fce29386704a56fbfb22e0dcdbd813b4b5 Mon Sep 17 00:00:00 2001 From: Yongqiang Niu Date: Wed, 27 Mar 2019 14:19:20 +0800 Subject: drm/mediatek: respect page offset for PRIME mmap calls Respect page offset for PRIME mmap calls Signed-off-by: Yongqiang Niu Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_drm_gem.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c index 38483e9ee071..7eabdf335a79 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_gem.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c @@ -144,7 +144,6 @@ static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj, * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). */ vma->vm_flags &= ~VM_PFNMAP; - vma->vm_pgoff = 0; ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie, mtk_gem->dma_addr, obj->size, mtk_gem->dma_attrs); @@ -176,6 +175,12 @@ int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) obj = vma->vm_private_data; + /* + * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the + * whole buffer from the start. + */ + vma->vm_pgoff = 0; + return mtk_drm_gem_object_mmap(obj, vma); } -- cgit v1.2.3 From a200c721956ca026f44416acccc8efcca41109c5 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 27 May 2019 11:34:17 -0400 Subject: media: venus: hfi_parser: fix a regression in parser This fixes the following data abort: Unable to handle kernel paging request at virtual address 0000078000000040 Mem abort info: ESR = 0x96000004 Exception class = DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000004 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp = (____ptrval____) [0000078000000040] pgd=0000000000000000 Internal error: Oops: 96000004 [#1] PREEMPT SMP Process irq/28-venus (pid: 292, stack limit = 0x(____ptrval____)) CPU: 0 PID: 292 Comm: irq/28-venus Not tainted 5.2.0-rc1+ #60 Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT) pstate: 60000005 (nZCv daif -PAN -UAO) pc : __memcpy+0x100/0x180 lr : parse_caps+0x94/0xc0 [venus_core] sp : ffff0000114e3990 x29: ffff0000114e3990 x28: ffff80003a7d0148 x27: 00000000000df018 x26: ffff000008bd4618 x25: 0000000000020003 x24: 0000078000000040 x23: 0000000000000002 x22: 0000000000000002 x21: ffff80003b9b8080 x20: 0000000000000008 x19: ffff000010f59000 x18: 0000000000000000 x17: 0000000000000000 x16: ffff80003c0f9b80 x15: 0000000000000000 x14: 0100000100000002 x13: 000000010020100b x12: 000000010000100a x11: 0000100000000040 x10: 0000100000000004 x9 : 0000000000000000 x8 : ffff0000114e3bd8 x7 : 0000000000000000 x6 : ffff0000114e39d8 x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000010 x2 : 0000000000000000 x1 : 0000078000000040 x0 : ffff0000114e39d8 Call trace: __memcpy+0x100/0x180 hfi_parser+0x23c/0x3b8 [venus_core] hfi_session_init_done+0x40/0x60 [venus_core] hfi_process_msg_packet+0xd4/0x1d8 [venus_core] venus_isr_thread+0x1e0/0x230 [venus_core] hfi_isr_thread+0x18/0x20 [venus_core] irq_thread_fn+0x28/0x78 irq_thread+0x124/0x1c0 kthread+0x124/0x128 ret_from_fork+0x10/0x18 Code: d503201f d503201f d503201f d503201f (a8c12027) ---[ end trace fd253ebaeea05ffc ]--- genirq: exiting task "irq/28-venus" (292) is an active IRQ thread (irq 28) by modifying structure members to flexible array members. Fixes: ded716267196862809e5926072adc962a611a1e3 media: hfi_parser: don't trick gcc with a wrong expected size Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_helper.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 34ea503a9842..a2b95ff79c4a 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -569,7 +569,7 @@ struct hfi_capability { struct hfi_capabilities { u32 num_capabilities; - struct hfi_capability *data; + struct hfi_capability data[]; }; #define HFI_DEBUG_MSG_LOW 0x01 @@ -726,7 +726,7 @@ struct hfi_profile_level { struct hfi_profile_level_supported { u32 profile_count; - struct hfi_profile_level *profile_level; + struct hfi_profile_level profile_level[]; }; struct hfi_quality_vs_speed { -- cgit v1.2.3 From 64caea53e07f0a3bc4af74f4123b258c1ceb4d67 Mon Sep 17 00:00:00 2001 From: Joseph Salisbury Date: Thu, 30 May 2019 13:37:20 -0400 Subject: HID: hyperv: Add a module description line This patch only adds a MODULE_DESCRIPTION statement to the driver. This change is only cosmetic, so there should be no runtime impact. Signed-off-by: Joseph Salisbury Reviewed-by: Michael Kelley Signed-off-by: Sasha Levin Signed-off-by: Jiri Kosina --- drivers/hid/hid-hyperv.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 704049e62d58..d3311d714d35 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -614,5 +614,7 @@ static void __exit mousevsc_exit(void) } MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic HID Driver"); + module_init(mousevsc_init); module_exit(mousevsc_exit); -- cgit v1.2.3 From abf82e8f7e9af40a49e3d905187c662a43c96c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= Date: Sun, 12 May 2019 22:33:13 +0200 Subject: HID: a4tech: fix horizontal scrolling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since recent high resolution scrolling changes the A4Tech driver must check for the "REL_WHEEL_HI_RES" usage code. Link: https://bugzilla.kernel.org/show_bug.cgi?id=203369 Fixes: 2dc702c991e3774af9d7ce410eef410ca9e2357e ("HID: input: use the Resolution Multiplier for high-resolution scrolling") Signed-off-by: Błażej Szczygieł Signed-off-by: Jiri Kosina --- drivers/hid/hid-a4tech.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 9428ea7cdf8a..c3a6ce3613fe 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -38,8 +38,10 @@ static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi, { struct a4tech_sc *a4 = hid_get_drvdata(hdev); - if (usage->type == EV_REL && usage->code == REL_WHEEL) + if (usage->type == EV_REL && usage->code == REL_WHEEL_HI_RES) { set_bit(REL_HWHEEL, *bit); + set_bit(REL_HWHEEL_HI_RES, *bit); + } if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) return -1; @@ -60,7 +62,7 @@ static int a4_event(struct hid_device *hdev, struct hid_field *field, input = field->hidinput->input; if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) { - if (usage->type == EV_REL && usage->code == REL_WHEEL) { + if (usage->type == EV_REL && usage->code == REL_WHEEL_HI_RES) { a4->delayed_value = value; return 1; } @@ -68,6 +70,8 @@ static int a4_event(struct hid_device *hdev, struct hid_field *field, if (usage->hid == 0x000100b8) { input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, a4->delayed_value); + input_event(input, EV_REL, value ? REL_HWHEEL_HI_RES : + REL_WHEEL_HI_RES, a4->delayed_value * 120); return 1; } } @@ -77,8 +81,9 @@ static int a4_event(struct hid_device *hdev, struct hid_field *field, return 1; } - if (usage->code == REL_WHEEL && a4->hw_wheel) { + if (usage->code == REL_WHEEL_HI_RES && a4->hw_wheel) { input_event(input, usage->type, REL_HWHEEL, value); + input_event(input, usage->type, REL_HWHEEL_HI_RES, value * 120); return 1; } -- cgit v1.2.3 From 2649939ad750db48f94d3cdab26e3a0115672e6b Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 27 May 2019 13:10:50 -0500 Subject: usb: typec: ucsi: ccg: fix memory leak in do_flash In case memory resources for *fw* were successfully allocated, release them before return. Addresses-Coverity-ID: 1445499 ("Resource leak") Fixes: 5c9ae5a87573 ("usb: typec: ucsi: ccg: add firmware flashing support") Signed-off-by: Gustavo A. R. Silva Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d46aa9e4e35..bf63074675fc 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -862,8 +862,10 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) not_signed_fw: wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL); - if (!wr_buf) - return -ENOMEM; + if (!wr_buf) { + err = -ENOMEM; + goto release_fw; + } err = ccg_cmd_enter_flashing(uc); if (err) -- cgit v1.2.3 From 8fd7a37b191f93737f6280a9b5de65f98acc12c9 Mon Sep 17 00:00:00 2001 From: Hsin-Yi Wang Date: Wed, 29 May 2019 18:25:52 +0800 Subject: drm/mediatek: fix unbind functions detatch panel in mtk_dsi_destroy_conn_enc(), since .bind will try to attach it again. Fixes: 2e54c14e310f ("drm/mediatek: Add DSI sub driver") Signed-off-by: Hsin-Yi Wang Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_dsi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index b00eb2d2e086..1ae3be99e0ff 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -844,6 +844,8 @@ static void mtk_dsi_destroy_conn_enc(struct mtk_dsi *dsi) /* Skip connector cleanup if creation was delegated to the bridge */ if (dsi->conn.dev) drm_connector_cleanup(&dsi->conn); + if (dsi->panel) + drm_panel_detach(dsi->panel); } static void mtk_dsi_ddp_start(struct mtk_ddp_comp *comp) -- cgit v1.2.3 From f0fd848342802bc0f74620d387eead53e8905804 Mon Sep 17 00:00:00 2001 From: Hsin-Yi Wang Date: Wed, 29 May 2019 18:25:53 +0800 Subject: drm/mediatek: unbind components in mtk_drm_unbind() Unbinding components (i.e. mtk_dsi and mtk_disp_ovl/rdma/color) will trigger master(mtk_drm)'s .unbind(), and currently mtk_drm's unbind won't actually unbind components. During the next bind, mtk_drm_kms_init() is called, and the components are added back. .unbind() should call mtk_drm_kms_deinit() to unbind components. And since component_master_del() in .remove() will trigger .unbind(), which will also unregister device, it's fine to remove original functions called here. Fixes: 119f5173628a ("drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.") Signed-off-by: Hsin-Yi Wang Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 57ce4708ef1b..e7362bdafa82 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -397,6 +397,7 @@ static void mtk_drm_unbind(struct device *dev) struct mtk_drm_private *private = dev_get_drvdata(dev); drm_dev_unregister(private->drm); + mtk_drm_kms_deinit(private->drm); drm_dev_put(private->drm); private->drm = NULL; } @@ -568,13 +569,8 @@ err_node: static int mtk_drm_remove(struct platform_device *pdev) { struct mtk_drm_private *private = platform_get_drvdata(pdev); - struct drm_device *drm = private->drm; int i; - drm_dev_unregister(drm); - mtk_drm_kms_deinit(drm); - drm_dev_put(drm); - component_master_del(&pdev->dev, &mtk_drm_ops); pm_runtime_disable(&pdev->dev); of_node_put(private->mutex_node); -- cgit v1.2.3 From cf49b24ffa62766f8f04cd1c4cf17b75d29b240a Mon Sep 17 00:00:00 2001 From: Hsin-Yi Wang Date: Wed, 29 May 2019 18:25:54 +0800 Subject: drm/mediatek: call drm_atomic_helper_shutdown() when unbinding driver shutdown all CRTC when unbinding drm driver. Fixes: 119f5173628a ("drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.") Signed-off-by: Hsin-Yi Wang Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index e7362bdafa82..8718d123ccaa 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -311,6 +311,7 @@ err_config_cleanup: static void mtk_drm_kms_deinit(struct drm_device *drm) { drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); component_unbind_all(drm->dev, drm); drm_mode_config_cleanup(drm); -- cgit v1.2.3 From a4cd1d2b016d5d043ab2c4b9c4ec50a5805f5396 Mon Sep 17 00:00:00 2001 From: Hsin-Yi Wang Date: Wed, 29 May 2019 18:25:55 +0800 Subject: drm/mediatek: clear num_pipes when unbind driver num_pipes is used for mutex created in mtk_drm_crtc_create(). If we don't clear num_pipes count, when rebinding driver, the count will be accumulated. From mtk_disp_mutex_get(), there can only be at most 10 mutex id. Clear this number so it starts from 0 in every rebind. Fixes: 119f5173628a ("drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.") Signed-off-by: Hsin-Yi Wang Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 8718d123ccaa..bbfe3a464aea 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -400,6 +400,7 @@ static void mtk_drm_unbind(struct device *dev) drm_dev_unregister(private->drm); mtk_drm_kms_deinit(private->drm); drm_dev_put(private->drm); + private->num_pipes = 0; private->drm = NULL; } -- cgit v1.2.3 From 2458d9d6d94be982b917e93c61a89b4426f32e31 Mon Sep 17 00:00:00 2001 From: Hsin-Yi Wang Date: Thu, 30 May 2019 17:18:47 +0800 Subject: drm/mediatek: call mtk_dsi_stop() after mtk_drm_crtc_atomic_disable() mtk_dsi_stop() should be called after mtk_drm_crtc_atomic_disable(), which needs ovl irq for drm_crtc_wait_one_vblank(), since after mtk_dsi_stop() is called, ovl irq will be disabled. If drm_crtc_wait_one_vblank() is called after last irq, it will timeout with this message: "vblank wait timed out on crtc 0". This happens sometimes when turning off the screen. In drm_atomic_helper.c#disable_outputs(), the calling sequence when turning off the screen is: 1. mtk_dsi_encoder_disable() --> mtk_output_dsi_disable() --> mtk_dsi_stop(); /* sometimes make vblank timeout in atomic_disable */ --> mtk_dsi_poweroff(); 2. mtk_drm_crtc_atomic_disable() --> drm_crtc_wait_one_vblank(); ... --> mtk_dsi_ddp_stop() --> mtk_dsi_poweroff(); mtk_dsi_poweroff() has reference count design, change to make mtk_dsi_stop() called in mtk_dsi_poweroff() when refcount is 0. Fixes: 0707632b5bac ("drm/mediatek: update DSI sub driver flow for sending commands to panel") Signed-off-by: Hsin-Yi Wang Signed-off-by: CK Hu --- drivers/gpu/drm/mediatek/mtk_dsi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 1ae3be99e0ff..179f2b080342 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -630,6 +630,15 @@ static void mtk_dsi_poweroff(struct mtk_dsi *dsi) if (--dsi->refcount != 0) return; + /* + * mtk_dsi_stop() and mtk_dsi_start() is asymmetric, since + * mtk_dsi_stop() should be called after mtk_drm_crtc_atomic_disable(), + * which needs irq for vblank, and mtk_dsi_stop() will disable irq. + * mtk_dsi_start() needs to be called in mtk_output_dsi_enable(), + * after dsi is fully set. + */ + mtk_dsi_stop(dsi); + if (!mtk_dsi_switch_to_cmd_mode(dsi, VM_DONE_INT_FLAG, 500)) { if (dsi->panel) { if (drm_panel_unprepare(dsi->panel)) { @@ -696,7 +705,6 @@ static void mtk_output_dsi_disable(struct mtk_dsi *dsi) } } - mtk_dsi_stop(dsi); mtk_dsi_poweroff(dsi); dsi->enabled = false; -- cgit v1.2.3 From 1a6dd3fea131276a4fc44ae77b0f471b0b473577 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 4 Jun 2019 00:20:49 +0800 Subject: USB: usb-storage: Add new ID to ums-realtek There is one more Realtek card reader requires ums-realtek to work correctly. Add the device ID to support it. Signed-off-by: Kai-Heng Feng Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_realtek.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h index 6b2140f966ef..7e14c2d7cf73 100644 --- a/drivers/usb/storage/unusual_realtek.h +++ b/drivers/usb/storage/unusual_realtek.h @@ -17,6 +17,11 @@ UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, "USB Card Reader", USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), +UNUSUAL_DEV(0x0bda, 0x0153, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999, "Realtek", "USB Card Reader", -- cgit v1.2.3 From bd21f0222adab64974b7d1b4b8c7ce6b23e9ea4d Mon Sep 17 00:00:00 2001 From: Marco Zatta Date: Sat, 1 Jun 2019 09:52:57 +0200 Subject: USB: Fix chipmunk-like voice when using Logitech C270 for recording audio. This patch fixes the chipmunk-like voice that manifets randomly when using the integrated mic of the Logitech Webcam HD C270. The issue was solved initially for this device by commit 2394d67e446b ("USB: add RESET_RESUME for webcams shown to be quirky") but it was then reintroduced by e387ef5c47dd ("usb: Add USB_QUIRK_RESET_RESUME for all Logitech UVC webcams"). This patch is to have the fix back. Signed-off-by: Marco Zatta Signed-off-by: Greg Kroah-Hartman Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 6082b008969b..6b6413073584 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -215,6 +215,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */ { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech HD Webcam C270 */ + { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */ { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, -- cgit v1.2.3 From 15fc1b5c86128f91c8c6699c3b0d9615740b13f1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Jun 2019 14:44:05 +0200 Subject: Revert "HID: Increase maximum report size allowed by hid_field_extract()" This reverts commit 94a9992f7dbdfb28976b565af220e0c4a117144a. The commit allows for more than 32 bits in hid_field_extract(), but the return value is a 32 bits int. So basically what this commit is doing is just silencing those legitimate errors. Revert to a previous situation in the hope that a proper fix will be impletemented. Fixes: 94a9992f7dbd ("HID: Increase maximum report size allowed by hid_field_extract()") Cc: stable@vger.kernel.org # v5.1 Acked-by: Jiri Kosina Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 92387fc0bf18..4407cf549908 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1314,10 +1314,10 @@ static u32 __extract(u8 *report, unsigned offset, int n) u32 hid_field_extract(const struct hid_device *hid, u8 *report, unsigned offset, unsigned n) { - if (n > 256) { - hid_warn(hid, "hid_field_extract() called with n (%d) > 256! (%s)\n", + if (n > 32) { + hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n", n, current->comm); - n = 256; + n = 32; } return __extract(report, offset, n); -- cgit v1.2.3 From e0b7f9bc0246bc642d1de2ff3ff133730584c956 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Jun 2019 14:44:06 +0200 Subject: Revert "HID: core: Do not call request_module() in async context" This reverts commit 4ceabaf7909d109db6afbffc4ed182741fc32043. This patch and a025a18fe are giving extended timeouts on boot for at least Ubuntu and openSUSE. Revert them until we get a better fix. Acked-by: Jiri Kosina Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 4407cf549908..aeae545859d4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -2370,9 +2369,8 @@ int hid_add_device(struct hid_device *hdev) * not first have hid-generic binding only to have it replaced * immediately afterwards with a specialized driver. */ - if (!current_is_async()) - request_module("hid:b%04Xg%04Xv%08Xp%08X", hdev->bus, - hdev->group, hdev->vendor, hdev->product); + request_module("hid:b%04Xg%04Xv%08Xp%08X", + hdev->bus, hdev->group, hdev->vendor, hdev->product); hid_debug_register(hdev, dev_name(&hdev->dev)); ret = device_add(&hdev->dev); -- cgit v1.2.3 From f9482dabfd1686987cc6044e06ae0e4c05915518 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Jun 2019 14:44:07 +0200 Subject: Revert "HID: core: Call request_module before doing device_add" This reverts commit a025a18fecd4429f4ca66b1746001263c052ecbb. This patch and 4ceabaf79 are giving extended timeouts on boot for at least Ubuntu and openSUSE. Revert them until we get a better fix. Link: https://bugzilla.kernel.org/show_bug.cgi?id=203741 Acked-by: Jiri Kosina Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index aeae545859d4..39eba8106d40 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2364,14 +2364,6 @@ int hid_add_device(struct hid_device *hdev) dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, hdev->vendor, hdev->product, atomic_inc_return(&id)); - /* - * Try loading the module for the device before the add, so that we do - * not first have hid-generic binding only to have it replaced - * immediately afterwards with a specialized driver. - */ - request_module("hid:b%04Xg%04Xv%08Xp%08X", - hdev->bus, hdev->group, hdev->vendor, hdev->product); - hid_debug_register(hdev, dev_name(&hdev->dev)); ret = device_add(&hdev->dev); if (!ret) -- cgit v1.2.3 From 3ed224e273ac5880eeab4c3043a6b06b0478dd56 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 5 Jun 2019 14:44:08 +0200 Subject: HID: logitech-dj: Fix 064d:c52f receiver support The c52f nano receiver is a mouse only receiver. This means that it needs some special handling compared to the c534 nano receiver: 1) It sends unnumbered mouse reports with a size of 8 bytes, so we need to extend the unnumbered mouse report handling to support reports upto 8 bytes large 2) It mouse reports have the same high-resolution format as those from the gaming mouse receivers 3) It can report consumer/multimedia buttons on its second interface, since this is a mouse-only receiver these must be forwarded to the mouse child device and not to the keyboard child-device (which will not exist) Link: https://bugzilla.kernel.org/show_bug.cgi?id=203619 Signed-off-by: Hans de Goede Acked-by: Jiri Kosina Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-logitech-dj.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index dfc34fe85319..ce00dc299404 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -128,6 +128,7 @@ enum recvr_type { recvr_type_dj, recvr_type_hidpp, recvr_type_gaming_hidpp, + recvr_type_mouse_only, recvr_type_27mhz, recvr_type_bluetooth, }; @@ -879,9 +880,12 @@ static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev, schedule_work(&djrcv_dev->work); } -static void logi_hidpp_dev_conn_notif_equad(struct hidpp_event *hidpp_report, +static void logi_hidpp_dev_conn_notif_equad(struct hid_device *hdev, + struct hidpp_event *hidpp_report, struct dj_workitem *workitem) { + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); + workitem->type = WORKITEM_TYPE_PAIRED; workitem->device_type = hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] & HIDPP_DEVICE_TYPE_MASK; @@ -895,6 +899,8 @@ static void logi_hidpp_dev_conn_notif_equad(struct hidpp_event *hidpp_report, break; case REPORT_TYPE_MOUSE: workitem->reports_supported |= STD_MOUSE | HIDPP; + if (djrcv_dev->type == recvr_type_mouse_only) + workitem->reports_supported |= MULTIMEDIA; break; } } @@ -938,7 +944,7 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, case 0x01: device_type = "Bluetooth"; /* Bluetooth connect packet contents is the same as (e)QUAD */ - logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem); if (!(hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] & HIDPP_MANUFACTURER_MASK)) { hid_info(hdev, "Non Logitech device connected on slot %d\n", @@ -952,18 +958,18 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, break; case 0x03: device_type = "QUAD or eQUAD"; - logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem); break; case 0x04: device_type = "eQUAD step 4 DJ"; - logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem); break; case 0x05: device_type = "DFU Lite"; break; case 0x06: device_type = "eQUAD step 4 Lite"; - logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem); break; case 0x07: device_type = "eQUAD step 4 Gaming"; @@ -973,11 +979,11 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, break; case 0x0a: device_type = "eQUAD nano Lite"; - logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem); break; case 0x0c: device_type = "eQUAD Lightspeed"; - logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem); + logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem); workitem.reports_supported |= STD_KEYBOARD; break; } @@ -1328,7 +1334,8 @@ static int logi_dj_ll_parse(struct hid_device *hid) if (djdev->reports_supported & STD_MOUSE) { dbg_hid("%s: sending a mouse descriptor, reports_supported: %llx\n", __func__, djdev->reports_supported); - if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp) + if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp || + djdev->dj_receiver_dev->type == recvr_type_mouse_only) rdcat(rdesc, &rsize, mse_high_res_descriptor, sizeof(mse_high_res_descriptor)); else if (djdev->dj_receiver_dev->type == recvr_type_27mhz) @@ -1571,15 +1578,19 @@ static int logi_dj_raw_event(struct hid_device *hdev, data[0] = data[1]; data[1] = 0; } - /* The 27 MHz mouse-only receiver sends unnumbered mouse data */ + /* + * Mouse-only receivers send unnumbered mouse data. The 27 MHz + * receiver uses 6 byte packets, the nano receiver 8 bytes. + */ if (djrcv_dev->unnumbered_application == HID_GD_MOUSE && - size == 6) { - u8 mouse_report[7]; + size <= 8) { + u8 mouse_report[9]; /* Prepend report id */ mouse_report[0] = REPORT_TYPE_MOUSE; - memcpy(mouse_report + 1, data, 6); - logi_dj_recv_forward_input_report(hdev, mouse_report, 7); + memcpy(mouse_report + 1, data, size); + logi_dj_recv_forward_input_report(hdev, mouse_report, + size + 1); } return false; @@ -1650,6 +1661,7 @@ static int logi_dj_probe(struct hid_device *hdev, case recvr_type_dj: no_dj_interfaces = 3; break; case recvr_type_hidpp: no_dj_interfaces = 2; break; case recvr_type_gaming_hidpp: no_dj_interfaces = 3; break; + case recvr_type_mouse_only: no_dj_interfaces = 2; break; case recvr_type_27mhz: no_dj_interfaces = 2; break; case recvr_type_bluetooth: no_dj_interfaces = 2; break; } @@ -1823,10 +1835,10 @@ static const struct hid_device_id logi_dj_receivers[] = { {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2), .driver_data = recvr_type_dj}, - { /* Logitech Nano (non DJ) receiver */ + { /* Logitech Nano mouse only receiver */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER), - .driver_data = recvr_type_hidpp}, + .driver_data = recvr_type_mouse_only}, { /* Logitech Nano (non DJ) receiver */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2), -- cgit v1.2.3 From 4e7372e0dc5d7d2078fbdb13505635cd5b11f93d Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 28 May 2019 15:48:22 -0700 Subject: xen/swiotlb: don't initialize swiotlb twice on arm64 On arm64 swiotlb is often (not always) already initialized by mem_init. We don't want to initialize it twice, which would trigger a second memory allocation. Moreover, the second memory pool is typically made of high pages and ends up replacing the original memory pool of low pages. As a side effect of this change, it is possible to have low pages in swiotlb-xen on arm64. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/swiotlb-xen.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 64cb94dfedd4..342762b1aefb 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -211,6 +211,15 @@ int __ref xen_swiotlb_init(int verbose, bool early) retry: bytes = xen_set_nslabs(xen_io_tlb_nslabs); order = get_order(xen_io_tlb_nslabs << IO_TLB_SHIFT); + + /* + * IO TLB memory already allocated. Just use it. + */ + if (io_tlb_start != 0) { + xen_io_tlb_start = phys_to_virt(io_tlb_start); + goto end; + } + /* * Get IO TLB memory from any location. */ @@ -237,7 +246,6 @@ retry: m_ret = XEN_SWIOTLB_ENOMEM; goto error; } - xen_io_tlb_end = xen_io_tlb_start + bytes; /* * And replace that memory with pages under 4GB. */ @@ -264,6 +272,8 @@ retry: } else rc = swiotlb_late_init_with_tbl(xen_io_tlb_start, xen_io_tlb_nslabs); +end: + xen_io_tlb_end = xen_io_tlb_start + bytes; if (!rc) swiotlb_set_max_segment(PAGE_SIZE); -- cgit v1.2.3 From babd183915e91a64e976b9e8ab682bb56624df76 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 31 May 2019 13:04:12 -0700 Subject: usb: dwc2: host: Fix wMaxPacketSize handling (fix webcam regression) In commit abb621844f6a ("usb: ch9: make usb_endpoint_maxp() return only packet size") the API to usb_endpoint_maxp() changed. It used to just return wMaxPacketSize but after that commit it returned wMaxPacketSize with the high bits (the multiplier) masked off. If you wanted to get the multiplier it was now up to your code to call the new usb_endpoint_maxp_mult() which was introduced in commit 541b6fe63023 ("usb: add helper to extract bits 12:11 of wMaxPacketSize"). Prior to the API change most host drivers were updated, but no update was made to dwc2. Presumably it was assumed that dwc2 was too simplistic to use the multiplier and thus just didn't support a certain class of USB devices. However, it turns out that dwc2 did use the multiplier and many devices using it were working quite nicely. That means that many USB devices have been broken since the API change. One such device is a Logitech HD Pro Webcam C920. Specifically, though dwc2 didn't directly call usb_endpoint_maxp(), it did call usb_maxpacket() which in turn called usb_endpoint_maxp(). Let's update dwc2 to work properly with the new API. Fixes: abb621844f6a ("usb: ch9: make usb_endpoint_maxp() return only packet size") Cc: stable@vger.kernel.org Acked-by: Minas Harutyunyan Reviewed-by: Matthias Kaehlcke Signed-off-by: Douglas Anderson Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 29 +++++++++++++++++------------ drivers/usb/dwc2/hcd.h | 20 +++++++++++--------- drivers/usb/dwc2/hcd_intr.c | 5 +++-- drivers/usb/dwc2/hcd_queue.c | 10 ++++++---- 4 files changed, 37 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index b50ec3714fd8..5c51bf5506d1 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2608,7 +2608,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) chan->dev_addr = dwc2_hcd_get_dev_addr(&urb->pipe_info); chan->ep_num = dwc2_hcd_get_ep_num(&urb->pipe_info); chan->speed = qh->dev_speed; - chan->max_packet = dwc2_max_packet(qh->maxp); + chan->max_packet = qh->maxp; chan->xfer_started = 0; chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS; @@ -2686,7 +2686,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) * This value may be modified when the transfer is started * to reflect the actual transfer length */ - chan->multi_count = dwc2_hb_mult(qh->maxp); + chan->multi_count = qh->maxp_mult; if (hsotg->params.dma_desc_enable) { chan->desc_list_addr = qh->desc_list_dma; @@ -3806,19 +3806,21 @@ static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg, static void dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb, u8 dev_addr, - u8 ep_num, u8 ep_type, u8 ep_dir, u16 mps) + u8 ep_num, u8 ep_type, u8 ep_dir, + u16 maxp, u16 maxp_mult) { if (dbg_perio() || ep_type == USB_ENDPOINT_XFER_BULK || ep_type == USB_ENDPOINT_XFER_CONTROL) dev_vdbg(hsotg->dev, - "addr=%d, ep_num=%d, ep_dir=%1x, ep_type=%1x, mps=%d\n", - dev_addr, ep_num, ep_dir, ep_type, mps); + "addr=%d, ep_num=%d, ep_dir=%1x, ep_type=%1x, maxp=%d (%d mult)\n", + dev_addr, ep_num, ep_dir, ep_type, maxp, maxp_mult); urb->pipe_info.dev_addr = dev_addr; urb->pipe_info.ep_num = ep_num; urb->pipe_info.pipe_type = ep_type; urb->pipe_info.pipe_dir = ep_dir; - urb->pipe_info.mps = mps; + urb->pipe_info.maxp = maxp; + urb->pipe_info.maxp_mult = maxp_mult; } /* @@ -3909,8 +3911,9 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); dev_dbg(hsotg->dev, - " Max packet size: %d\n", - dwc2_hcd_get_mps(&urb->pipe_info)); + " Max packet size: %d (%d mult)\n", + dwc2_hcd_get_maxp(&urb->pipe_info), + dwc2_hcd_get_maxp_mult(&urb->pipe_info)); dev_dbg(hsotg->dev, " transfer_buffer: %p\n", urb->buf); @@ -4510,8 +4513,10 @@ static void dwc2_dump_urb_info(struct usb_hcd *hcd, struct urb *urb, } dev_vdbg(hsotg->dev, " Speed: %s\n", speed); - dev_vdbg(hsotg->dev, " Max packet size: %d\n", - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); + dev_vdbg(hsotg->dev, " Max packet size: %d (%d mult)\n", + usb_endpoint_maxp(&urb->ep->desc), + usb_endpoint_maxp_mult(&urb->ep->desc)); + dev_vdbg(hsotg->dev, " Data buffer length: %d\n", urb->transfer_buffer_length); dev_vdbg(hsotg->dev, " Transfer buffer: %p, Transfer DMA: %08lx\n", @@ -4594,8 +4599,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), ep_type, usb_pipein(urb->pipe), - usb_maxpacket(urb->dev, urb->pipe, - !(usb_pipein(urb->pipe)))); + usb_endpoint_maxp(&ep->desc), + usb_endpoint_maxp_mult(&ep->desc)); buf = urb->transfer_buffer; diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index c089ffa1f0a8..ce6445a06588 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -171,7 +171,8 @@ struct dwc2_hcd_pipe_info { u8 ep_num; u8 pipe_type; u8 pipe_dir; - u16 mps; + u16 maxp; + u16 maxp_mult; }; struct dwc2_hcd_iso_packet_desc { @@ -264,6 +265,7 @@ struct dwc2_hs_transfer_time { * - USB_ENDPOINT_XFER_ISOC * @ep_is_in: Endpoint direction * @maxp: Value from wMaxPacketSize field of Endpoint Descriptor + * @maxp_mult: Multiplier for maxp * @dev_speed: Device speed. One of the following values: * - USB_SPEED_LOW * - USB_SPEED_FULL @@ -340,6 +342,7 @@ struct dwc2_qh { u8 ep_type; u8 ep_is_in; u16 maxp; + u16 maxp_mult; u8 dev_speed; u8 data_toggle; u8 ping_state; @@ -503,9 +506,14 @@ static inline u8 dwc2_hcd_get_pipe_type(struct dwc2_hcd_pipe_info *pipe) return pipe->pipe_type; } -static inline u16 dwc2_hcd_get_mps(struct dwc2_hcd_pipe_info *pipe) +static inline u16 dwc2_hcd_get_maxp(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->maxp; +} + +static inline u16 dwc2_hcd_get_maxp_mult(struct dwc2_hcd_pipe_info *pipe) { - return pipe->mps; + return pipe->maxp_mult; } static inline u8 dwc2_hcd_get_dev_addr(struct dwc2_hcd_pipe_info *pipe) @@ -620,12 +628,6 @@ static inline bool dbg_urb(struct urb *urb) static inline bool dbg_perio(void) { return false; } #endif -/* High bandwidth multiplier as encoded in highspeed endpoint descriptors */ -#define dwc2_hb_mult(wmaxpacketsize) (1 + (((wmaxpacketsize) >> 11) & 0x03)) - -/* Packet size for any kind of endpoint descriptor */ -#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff) - /* * Returns true if frame1 index is greater than frame2 index. The comparison * is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 88b5dcf3aefc..a052d39b4375 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -1617,8 +1617,9 @@ static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg, dev_err(hsotg->dev, " Speed: %s\n", speed); - dev_err(hsotg->dev, " Max packet size: %d\n", - dwc2_hcd_get_mps(&urb->pipe_info)); + dev_err(hsotg->dev, " Max packet size: %d (mult %d)\n", + dwc2_hcd_get_maxp(&urb->pipe_info), + dwc2_hcd_get_maxp_mult(&urb->pipe_info)); dev_err(hsotg->dev, " Data buffer length: %d\n", urb->length); dev_err(hsotg->dev, " Transfer buffer: %p, Transfer DMA: %08lx\n", urb->buf, (unsigned long)urb->dma); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index ea3aa640c15c..68bbac64b753 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -708,7 +708,7 @@ static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg, static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - int bytecount = dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp); + int bytecount = qh->maxp_mult * qh->maxp; int ls_search_slice; int err = 0; int host_interval_in_sched; @@ -1332,7 +1332,7 @@ static int dwc2_check_max_xfer_size(struct dwc2_hsotg *hsotg, u32 max_channel_xfer_size; int status = 0; - max_xfer_size = dwc2_max_packet(qh->maxp) * dwc2_hb_mult(qh->maxp); + max_xfer_size = qh->maxp * qh->maxp_mult; max_channel_xfer_size = hsotg->params.max_transfer_size; if (max_xfer_size > max_channel_xfer_size) { @@ -1517,8 +1517,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED && dev_speed != USB_SPEED_HIGH); - int maxp = dwc2_hcd_get_mps(&urb->pipe_info); - int bytecount = dwc2_hb_mult(maxp) * dwc2_max_packet(maxp); + int maxp = dwc2_hcd_get_maxp(&urb->pipe_info); + int maxp_mult = dwc2_hcd_get_maxp_mult(&urb->pipe_info); + int bytecount = maxp_mult * maxp; char *speed, *type; /* Initialize QH */ @@ -1531,6 +1532,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, qh->data_toggle = DWC2_HC_PID_DATA0; qh->maxp = maxp; + qh->maxp_mult = maxp_mult; INIT_LIST_HEAD(&qh->qtd_list); INIT_LIST_HEAD(&qh->qh_list_entry); -- cgit v1.2.3 From 4a4863bf2e7932e584a3a462d3c6daf891142ddc Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Mon, 18 Feb 2019 07:37:30 +0100 Subject: usb: dwc2: Fix DMA cache alignment issues Insert a padding between data and the stored_xfer_buffer pointer to ensure they are not on the same cache line. Otherwise, the stored_xfer_buffer gets corrupted for IN URBs on non-cache-coherent systems. (In my case: Lantiq xRX200 MIPS) Fixes: 3bc04e28a030 ("usb: dwc2: host: Get aligned DMA in a more supported way") Fixes: 56406e017a88 ("usb: dwc2: Fix DMA alignment to start at allocated boundary") Cc: Tested-by: Douglas Anderson Reviewed-by: Douglas Anderson Acked-by: Minas Harutyunyan Signed-off-by: Martin Schiller Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 5c51bf5506d1..2192a2873c7c 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2480,8 +2480,10 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb) return; /* Restore urb->transfer_buffer from the end of the allocated area */ - memcpy(&stored_xfer_buffer, urb->transfer_buffer + - urb->transfer_buffer_length, sizeof(urb->transfer_buffer)); + memcpy(&stored_xfer_buffer, + PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length, + dma_get_cache_alignment()), + sizeof(urb->transfer_buffer)); if (usb_urb_dir_in(urb)) { if (usb_pipeisoc(urb->pipe)) @@ -2513,6 +2515,7 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags) * DMA */ kmalloc_size = urb->transfer_buffer_length + + (dma_get_cache_alignment() - 1) + sizeof(urb->transfer_buffer); kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); @@ -2523,7 +2526,8 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags) * Position value of original urb->transfer_buffer pointer to the end * of allocation for later referencing */ - memcpy(kmalloc_ptr + urb->transfer_buffer_length, + memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length, + dma_get_cache_alignment()), &urb->transfer_buffer, sizeof(urb->transfer_buffer)); if (usb_urb_dir_out(urb)) -- cgit v1.2.3 From b5bbe22353614d6a4d7a4b33f326e3009ec38052 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 28 May 2019 23:59:48 -0700 Subject: usb: phy: mxs: Disable external charger detect in mxs_phy_hw_init() Since this driver already handles changer detction state, copy the workaround code currently residing in arch/arm/mach-imx/anatop.c into this drier to consolidate the places modifying it. Signed-off-by: Andrey Smirnov Cc: Chris Healy Cc: Felipe Balbi Cc: Shawn Guo Cc: Fabio Estevam Cc: NXP Linux Team Cc: linux-usb@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 1b1bb0ad40c3..6fa16ab31e2e 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -63,6 +63,7 @@ #define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 #define ANADIG_USB1_CHRG_DETECT_CLR 0x1b8 +#define ANADIG_USB2_CHRG_DETECT_SET 0x214 #define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) #define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) #define ANADIG_USB1_CHRG_DETECT_CHK_CONTACT BIT(18) @@ -250,6 +251,19 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); + if (mxs_phy->regmap_anatop) { + unsigned int reg = mxs_phy->port_id ? + ANADIG_USB1_CHRG_DETECT_SET : + ANADIG_USB2_CHRG_DETECT_SET; + /* + * The external charger detector needs to be disabled, + * or the signal at DP will be poor + */ + regmap_write(mxs_phy->regmap_anatop, reg, + ANADIG_USB1_CHRG_DETECT_EN_B | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + } + mxs_phy_tx_init(mxs_phy); return 0; -- cgit v1.2.3 From 62fd0e0a24abeebe2c19fce49dd5716d9b62042d Mon Sep 17 00:00:00 2001 From: Young Xiao <92siuyang@gmail.com> Date: Tue, 28 May 2019 20:17:54 +0800 Subject: usb: gadget: fusb300_udc: Fix memory leak of fusb300->ep[i] There is no deallocation of fusb300->ep[i] elements, allocated at fusb300_probe. The patch adds deallocation of fusb300->ep array elements. Signed-off-by: Young Xiao <92siuyang@gmail.com> Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fusb300_udc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 263804d154a7..00e3f66836a9 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1342,12 +1342,15 @@ static const struct usb_gadget_ops fusb300_gadget_ops = { static int fusb300_remove(struct platform_device *pdev) { struct fusb300 *fusb300 = platform_get_drvdata(pdev); + int i; usb_del_gadget_udc(&fusb300->gadget); iounmap(fusb300->reg); free_irq(platform_get_irq(pdev, 0), fusb300); fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); + for (i = 0; i < FUSB300_MAX_NUM_EP; i++) + kfree(fusb300->ep[i]); kfree(fusb300); return 0; @@ -1491,6 +1494,8 @@ clean_up: if (fusb300->ep0_req) fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); + for (i = 0; i < FUSB300_MAX_NUM_EP; i++) + kfree(fusb300->ep[i]); kfree(fusb300); } if (reg) -- cgit v1.2.3 From fbc318afadd6e7ae2252d6158cf7d0c5a2132f7d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 22 May 2019 14:07:36 +0200 Subject: usb: gadget: udc: lpc32xx: allocate descriptor with GFP_ATOMIC Gadget drivers may queue request in interrupt context. This would lead to a descriptor allocation in that context. In that case we would hit BUG_ON(in_interrupt()) in __get_vm_area_node. Also remove the unnecessary cast. Acked-by: Sylvain Lemieux Tested-by: James Grant Signed-off-by: Alexandre Belloni Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index d8f1c60793ed..2719194ebf42 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -937,8 +937,7 @@ static struct lpc32xx_usbd_dd_gad *udc_dd_alloc(struct lpc32xx_udc *udc) dma_addr_t dma; struct lpc32xx_usbd_dd_gad *dd; - dd = (struct lpc32xx_usbd_dd_gad *) dma_pool_alloc( - udc->dd_cache, (GFP_KERNEL | GFP_DMA), &dma); + dd = dma_pool_alloc(udc->dd_cache, GFP_ATOMIC | GFP_DMA, &dma); if (dd) dd->this_dma = dma; -- cgit v1.2.3 From 4faf3b36553a0a5304f58ee3a620e659739a2e20 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Mon, 29 Apr 2019 15:23:43 +0400 Subject: usb: dwc2: Set actual frame number for completed ISOC transfer for none DDMA On ISOC OUT transfer completion, in none DDMA mode, set actual frame number returning to function driver in usb_request. Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 16ffd9fd9361..1d4aef8d4137 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2417,6 +2417,10 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) dwc2_gadget_incr_frame_num(hs_ep); } + /* Set actual frame number for completed transfers */ + if (!using_desc_dma(hsotg) && hs_ep->isochronous) + req->frame_number = hsotg->frame_number; + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result); } -- cgit v1.2.3 From 066cfd0770aba8a9ac79b59d99530653885d919d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 1 Apr 2019 12:50:45 +0200 Subject: usb: gadget: dwc2: fix zlp handling The patch 10209abe87f5ebfd482a00323f5236d6094d0865 usb: dwc2: gadget: Add scatter-gather mode avoided a NULL pointer dereference (hs_ep->req == NULL) by calling dwc2_gadget_fill_nonisoc_xfer_dma_one() directly instead of through the dwc2_gadget_config_nonisoc_xfer_ddma() wrapper, which unconditionally dereferenced the said pointer. However, this was based on an incorrect assumption that in the context of dwc2_hsotg_program_zlp() the pointer is always NULL, which is not the case. The result were SB CV MSC tests failing starting from Test Case 6. Instead, this patch reverts to calling the wrapper and adds a check for the pointer being NULL inside the wrapper. Fixes: 10209abe87f5 (usb: dwc2: gadget: Add scatter-gather mode) Acked-by: Minas Harutyunyan Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 1d4aef8d4137..bff48a8a1984 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -835,19 +835,22 @@ static void dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep *hs_ep, * with corresponding information based on transfer data. */ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, - struct usb_request *ureq, - unsigned int offset, + dma_addr_t dma_buff, unsigned int len) { + struct usb_request *ureq = NULL; struct dwc2_dma_desc *desc = hs_ep->desc_list; struct scatterlist *sg; int i; u8 desc_count = 0; + if (hs_ep->req) + ureq = &hs_ep->req->req; + /* non-DMA sg buffer */ - if (!ureq->num_sgs) { + if (!ureq || !ureq->num_sgs) { dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc, - ureq->dma + offset, len, true); + dma_buff, len, true); return; } @@ -1135,7 +1138,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, offset = ureq->actual; /* Fill DDMA chain entries */ - dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq, offset, + dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset, length); /* write descriptor chain address to control register */ @@ -2037,12 +2040,13 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n", index); if (using_desc_dma(hsotg)) { + /* Not specific buffer needed for ep0 ZLP */ + dma_addr_t dma = hs_ep->desc_list_dma; + if (!index) dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep); - /* Not specific buffer needed for ep0 ZLP */ - dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &hs_ep->desc_list, - hs_ep->desc_list_dma, 0, true); + dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0); } else { dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(0), -- cgit v1.2.3 From 42cc68868ce9d5f5277f561bb17b4746a332bb28 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 4 May 2019 07:04:07 +0000 Subject: usb: gadget: udc: lpc32xx: fix return value check in lpc32xx_udc_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). This issue was detected by using the Coccinelle software. Fixes: 408b56ca5c8e ("usb: gadget: udc: lpc32xx: simplify probe") Acked-by: Sylvain Lemieux Acked-by: Vladimir Zapolskiy Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 2719194ebf42..5f1b14f3e5a0 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3069,9 +3069,9 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) } udc->udp_baseaddr = devm_ioremap_resource(dev, res); - if (!udc->udp_baseaddr) { + if (IS_ERR(udc->udp_baseaddr)) { dev_err(udc->dev, "IO map failure\n"); - return -ENOMEM; + return PTR_ERR(udc->udp_baseaddr); } /* Get USB device clock */ -- cgit v1.2.3 From 522ecce08ab20b57342d65b05601818e0f95fb2c Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Thu, 6 Jun 2019 10:52:32 -0600 Subject: vfio/mdev: Improve the create/remove sequence This patch addresses below two issues and prepares the code to address 3rd issue listed below. 1. mdev device is placed on the mdev bus before it is created in the vendor driver. Once a device is placed on the mdev bus without creating its supporting underlying vendor device, mdev driver's probe() gets triggered. However there isn't a stable mdev available to work on. create_store() mdev_create_device() device_register() ... vfio_mdev_probe() [...] parent->ops->create() vfio_ap_mdev_create() mdev_set_drvdata(mdev, matrix_mdev); /* Valid pointer set above */ Due to this way of initialization, mdev driver who wants to use the mdev, doesn't have a valid mdev to work on. 2. Current creation sequence is, parent->ops_create() groups_register() Remove sequence is, parent->ops->remove() groups_unregister() However, remove sequence should be exact mirror of creation sequence. Once this is achieved, all users of the mdev will be terminated first before removing underlying vendor device. (Follow standard linux driver model). At that point vendor's remove() ops shouldn't fail because taking the device off the bus should terminate any usage. 3. When remove operation fails, mdev sysfs removal attempts to add the file back on already removed device. Following call trace [1] is observed. [1] call trace: kernel: WARNING: CPU: 2 PID: 9348 at fs/sysfs/file.c:327 sysfs_create_file_ns+0x7f/0x90 kernel: CPU: 2 PID: 9348 Comm: bash Kdump: loaded Not tainted 5.1.0-rc6-vdevbus+ #6 kernel: Hardware name: Supermicro SYS-6028U-TR4+/X10DRU-i+, BIOS 2.0b 08/09/2016 kernel: RIP: 0010:sysfs_create_file_ns+0x7f/0x90 kernel: Call Trace: kernel: remove_store+0xdc/0x100 [mdev] kernel: kernfs_fop_write+0x113/0x1a0 kernel: vfs_write+0xad/0x1b0 kernel: ksys_write+0x5a/0xe0 kernel: do_syscall_64+0x5a/0x210 kernel: entry_SYSCALL_64_after_hwframe+0x49/0xbe Therefore, mdev core is improved in following ways. 1. Split the device registration/deregistration sequence so that some things can be done between initialization of the device and hooking it up to the bus respectively after deregistering it from the bus but before giving up our final reference. In particular, this means invoking the ->create() and ->remove() callbacks in those new windows. This gives the vendor driver an initialized mdev device to work with during creation. At the same time, a bus driver who wish to bind to mdev driver also gets initialized mdev device. This follows standard Linux kernel bus and device model. 2. During remove flow, first remove the device from the bus. This ensures that any bus specific devices are removed. Once device is taken off the mdev bus, invoke remove() of mdev from the vendor driver. 3. The driver core device model provides way to register and auto unregister the device sysfs attribute groups at dev->groups. Make use of dev->groups to let core create the groups and eliminate code to avoid explicit groups creation and removal. To ensure, that new sequence is solid, a below stack dump of a process is taken who attempts to remove the device while device is in use by vfio driver and user application. This stack dump validates that vfio driver guards against such device removal when device is in use. cat /proc/21962/stack [<0>] vfio_del_group_dev+0x216/0x3c0 [vfio] [<0>] mdev_remove+0x21/0x40 [mdev] [<0>] device_release_driver_internal+0xe8/0x1b0 [<0>] bus_remove_device+0xf9/0x170 [<0>] device_del+0x168/0x350 [<0>] mdev_device_remove_common+0x1d/0x50 [mdev] [<0>] mdev_device_remove+0x8c/0xd0 [mdev] [<0>] remove_store+0x71/0x90 [mdev] [<0>] kernfs_fop_write+0x113/0x1a0 [<0>] vfs_write+0xad/0x1b0 [<0>] ksys_write+0x5a/0xe0 [<0>] do_syscall_64+0x5a/0x210 [<0>] entry_SYSCALL_64_after_hwframe+0x49/0xbe [<0>] 0xffffffffffffffff This prepares the code to eliminate calling device_create_file() in subsequent patch. Reviewed-by: Cornelia Huck Signed-off-by: Parav Pandit Signed-off-by: Alex Williamson --- drivers/vfio/mdev/mdev_core.c | 94 +++++++++++----------------------------- drivers/vfio/mdev/mdev_private.h | 2 +- drivers/vfio/mdev/mdev_sysfs.c | 2 +- 3 files changed, 27 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index 3cc1a05fde1c..0bef0cae1d4b 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -102,55 +102,10 @@ static void mdev_put_parent(struct mdev_parent *parent) kref_put(&parent->ref, mdev_release_parent); } -static int mdev_device_create_ops(struct kobject *kobj, - struct mdev_device *mdev) -{ - struct mdev_parent *parent = mdev->parent; - int ret; - - ret = parent->ops->create(kobj, mdev); - if (ret) - return ret; - - ret = sysfs_create_groups(&mdev->dev.kobj, - parent->ops->mdev_attr_groups); - if (ret) - parent->ops->remove(mdev); - - return ret; -} - -/* - * mdev_device_remove_ops gets called from sysfs's 'remove' and when parent - * device is being unregistered from mdev device framework. - * - 'force_remove' is set to 'false' when called from sysfs's 'remove' which - * indicates that if the mdev device is active, used by VMM or userspace - * application, vendor driver could return error then don't remove the device. - * - 'force_remove' is set to 'true' when called from mdev_unregister_device() - * which indicate that parent device is being removed from mdev device - * framework so remove mdev device forcefully. - */ -static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) -{ - struct mdev_parent *parent = mdev->parent; - int ret; - - /* - * Vendor driver can return error if VMM or userspace application is - * using this mdev device. - */ - ret = parent->ops->remove(mdev); - if (ret && !force_remove) - return ret; - - sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups); - return 0; -} - static int mdev_device_remove_cb(struct device *dev, void *data) { if (dev_is_mdev(dev)) - mdev_device_remove(dev, true); + mdev_device_remove(dev); return 0; } @@ -310,41 +265,43 @@ int mdev_device_create(struct kobject *kobj, mdev->parent = parent; + device_initialize(&mdev->dev); mdev->dev.parent = dev; mdev->dev.bus = &mdev_bus_type; mdev->dev.release = mdev_device_release; dev_set_name(&mdev->dev, "%pUl", uuid); + mdev->dev.groups = parent->ops->mdev_attr_groups; + mdev->type_kobj = kobj; - ret = device_register(&mdev->dev); - if (ret) { - put_device(&mdev->dev); - goto mdev_fail; - } + ret = parent->ops->create(kobj, mdev); + if (ret) + goto ops_create_fail; - ret = mdev_device_create_ops(kobj, mdev); + ret = device_add(&mdev->dev); if (ret) - goto create_fail; + goto add_fail; ret = mdev_create_sysfs_files(&mdev->dev, type); - if (ret) { - mdev_device_remove_ops(mdev, true); - goto create_fail; - } + if (ret) + goto sysfs_fail; - mdev->type_kobj = kobj; mdev->active = true; dev_dbg(&mdev->dev, "MDEV: created\n"); return 0; -create_fail: - device_unregister(&mdev->dev); +sysfs_fail: + device_del(&mdev->dev); +add_fail: + parent->ops->remove(mdev); +ops_create_fail: + put_device(&mdev->dev); mdev_fail: mdev_put_parent(parent); return ret; } -int mdev_device_remove(struct device *dev, bool force_remove) +int mdev_device_remove(struct device *dev) { struct mdev_device *mdev, *tmp; struct mdev_parent *parent; @@ -373,16 +330,15 @@ int mdev_device_remove(struct device *dev, bool force_remove) mutex_unlock(&mdev_list_lock); type = to_mdev_type(mdev->type_kobj); + mdev_remove_sysfs_files(dev, type); + device_del(&mdev->dev); parent = mdev->parent; + ret = parent->ops->remove(mdev); + if (ret) + dev_err(&mdev->dev, "Remove failed: err=%d\n", ret); - ret = mdev_device_remove_ops(mdev, force_remove); - if (ret) { - mdev->active = true; - return ret; - } - - mdev_remove_sysfs_files(dev, type); - device_unregister(dev); + /* Balances with device_initialize() */ + put_device(&mdev->dev); mdev_put_parent(parent); return 0; diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h index 36cbbdb754de..924ed2274941 100644 --- a/drivers/vfio/mdev/mdev_private.h +++ b/drivers/vfio/mdev/mdev_private.h @@ -60,6 +60,6 @@ void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type); int mdev_device_create(struct kobject *kobj, struct device *dev, const guid_t *uuid); -int mdev_device_remove(struct device *dev, bool force_remove); +int mdev_device_remove(struct device *dev); #endif /* MDEV_PRIVATE_H */ diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c index cbf94b8165ea..9f774b91d275 100644 --- a/drivers/vfio/mdev/mdev_sysfs.c +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -236,7 +236,7 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr, if (val && device_remove_file_self(dev, attr)) { int ret; - ret = mdev_device_remove(dev, false); + ret = mdev_device_remove(dev); if (ret) { device_create_file(dev, attr); return ret; -- cgit v1.2.3 From 26c9e3988eec6b858c08b0fc352d8eb13832d828 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Thu, 6 Jun 2019 10:52:33 -0600 Subject: vfio/mdev: Avoid creating sysfs remove file on stale device removal If device is removal is initiated by two threads as below, mdev core attempts to create a syfs remove file on stale device. During this flow, below [1] call trace is observed. cpu-0 cpu-1 ----- ----- mdev_unregister_device() device_for_each_child mdev_device_remove_cb mdev_device_remove user_syscall remove_store() mdev_device_remove() [..] unregister device(); /* not found in list or * active=false. */ sysfs_create_file() ..Call trace Now that mdev core follows correct device removal sequence of the linux bus model, remove shouldn't fail in normal cases. If it fails, there is no point of creating a stale file or checking for specific error status. kernel: WARNING: CPU: 2 PID: 9348 at fs/sysfs/file.c:327 sysfs_create_file_ns+0x7f/0x90 kernel: CPU: 2 PID: 9348 Comm: bash Kdump: loaded Not tainted 5.1.0-rc6-vdevbus+ #6 kernel: Hardware name: Supermicro SYS-6028U-TR4+/X10DRU-i+, BIOS 2.0b 08/09/2016 kernel: RIP: 0010:sysfs_create_file_ns+0x7f/0x90 kernel: Call Trace: kernel: remove_store+0xdc/0x100 [mdev] kernel: kernfs_fop_write+0x113/0x1a0 kernel: vfs_write+0xad/0x1b0 kernel: ksys_write+0x5a/0xe0 kernel: do_syscall_64+0x5a/0x210 kernel: entry_SYSCALL_64_after_hwframe+0x49/0xbe Reviewed-by: Cornelia Huck Signed-off-by: Parav Pandit Signed-off-by: Alex Williamson --- drivers/vfio/mdev/mdev_sysfs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c index 9f774b91d275..ffa3dcebf201 100644 --- a/drivers/vfio/mdev/mdev_sysfs.c +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -237,10 +237,8 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr, int ret; ret = mdev_device_remove(dev); - if (ret) { - device_create_file(dev, attr); + if (ret) return ret; - } } return count; -- cgit v1.2.3 From 5715c4dd66a315515eedef3fc4cbe1bf4620f009 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Thu, 6 Jun 2019 10:52:33 -0600 Subject: vfio/mdev: Synchronize device create/remove with parent removal In following sequences, child devices created while removing mdev parent device can be left out, or it may lead to race of removing half initialized child mdev devices. issue-1: -------- cpu-0 cpu-1 ----- ----- mdev_unregister_device() device_for_each_child() mdev_device_remove_cb() mdev_device_remove() create_store() mdev_device_create() [...] device_add() parent_remove_sysfs_files() /* BUG: device added by cpu-0 * whose parent is getting removed * and it won't process this mdev. */ issue-2: -------- Below crash is observed when user initiated remove is in progress and mdev_unregister_driver() completes parent unregistration. cpu-0 cpu-1 ----- ----- remove_store() mdev_device_remove() active = false; mdev_unregister_device() parent device removed. [...] parents->ops->remove() /* * BUG: Accessing invalid parent. */ This is similar race like create() racing with mdev_unregister_device(). BUG: unable to handle kernel paging request at ffffffffc0585668 PGD e8f618067 P4D e8f618067 PUD e8f61a067 PMD 85adca067 PTE 0 Oops: 0000 [#1] SMP PTI CPU: 41 PID: 37403 Comm: bash Kdump: loaded Not tainted 5.1.0-rc6-vdevbus+ #6 Hardware name: Supermicro SYS-6028U-TR4+/X10DRU-i+, BIOS 2.0b 08/09/2016 RIP: 0010:mdev_device_remove+0xfa/0x140 [mdev] Call Trace: remove_store+0x71/0x90 [mdev] kernfs_fop_write+0x113/0x1a0 vfs_write+0xad/0x1b0 ksys_write+0x5a/0xe0 do_syscall_64+0x5a/0x210 entry_SYSCALL_64_after_hwframe+0x49/0xbe Therefore, mdev core is improved as below to overcome above issues. Wait for any ongoing mdev create() and remove() to finish before unregistering parent device. This continues to allow multiple create and remove to progress in parallel for different mdev devices as most common case. At the same time guard parent removal while parent is being accessed by create() and remove() callbacks. create()/remove() and unregister_device() are synchronized by the rwsem. Refactor device removal code to mdev_device_remove_common() to avoid acquiring unreg_sem of the parent. Fixes: 7b96953bc640 ("vfio: Mediated device Core driver") Signed-off-by: Parav Pandit Reviewed-by: Cornelia Huck Signed-off-by: Alex Williamson --- drivers/vfio/mdev/mdev_core.c | 72 ++++++++++++++++++++++++++++++---------- drivers/vfio/mdev/mdev_private.h | 2 ++ 2 files changed, 56 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index 0bef0cae1d4b..ae23151442cb 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -102,11 +102,35 @@ static void mdev_put_parent(struct mdev_parent *parent) kref_put(&parent->ref, mdev_release_parent); } +/* Caller must hold parent unreg_sem read or write lock */ +static void mdev_device_remove_common(struct mdev_device *mdev) +{ + struct mdev_parent *parent; + struct mdev_type *type; + int ret; + + type = to_mdev_type(mdev->type_kobj); + mdev_remove_sysfs_files(&mdev->dev, type); + device_del(&mdev->dev); + parent = mdev->parent; + lockdep_assert_held(&parent->unreg_sem); + ret = parent->ops->remove(mdev); + if (ret) + dev_err(&mdev->dev, "Remove failed: err=%d\n", ret); + + /* Balances with device_initialize() */ + put_device(&mdev->dev); + mdev_put_parent(parent); +} + static int mdev_device_remove_cb(struct device *dev, void *data) { - if (dev_is_mdev(dev)) - mdev_device_remove(dev); + if (dev_is_mdev(dev)) { + struct mdev_device *mdev; + mdev = to_mdev_device(dev); + mdev_device_remove_common(mdev); + } return 0; } @@ -148,6 +172,7 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops) } kref_init(&parent->ref); + init_rwsem(&parent->unreg_sem); parent->dev = dev; parent->ops = ops; @@ -206,21 +231,23 @@ void mdev_unregister_device(struct device *dev) dev_info(dev, "MDEV: Unregistering\n"); list_del(&parent->next); + mutex_unlock(&parent_list_lock); + + down_write(&parent->unreg_sem); + class_compat_remove_link(mdev_bus_compat_class, dev, NULL); device_for_each_child(dev, NULL, mdev_device_remove_cb); parent_remove_sysfs_files(parent); + up_write(&parent->unreg_sem); - mutex_unlock(&parent_list_lock); mdev_put_parent(parent); } EXPORT_SYMBOL(mdev_unregister_device); -static void mdev_device_release(struct device *dev) +static void mdev_device_free(struct mdev_device *mdev) { - struct mdev_device *mdev = to_mdev_device(dev); - mutex_lock(&mdev_list_lock); list_del(&mdev->next); mutex_unlock(&mdev_list_lock); @@ -229,6 +256,13 @@ static void mdev_device_release(struct device *dev) kfree(mdev); } +static void mdev_device_release(struct device *dev) +{ + struct mdev_device *mdev = to_mdev_device(dev); + + mdev_device_free(mdev); +} + int mdev_device_create(struct kobject *kobj, struct device *dev, const guid_t *uuid) { @@ -265,6 +299,13 @@ int mdev_device_create(struct kobject *kobj, mdev->parent = parent; + /* Check if parent unregistration has started */ + if (!down_read_trylock(&parent->unreg_sem)) { + mdev_device_free(mdev); + ret = -ENODEV; + goto mdev_fail; + } + device_initialize(&mdev->dev); mdev->dev.parent = dev; mdev->dev.bus = &mdev_bus_type; @@ -287,6 +328,7 @@ int mdev_device_create(struct kobject *kobj, mdev->active = true; dev_dbg(&mdev->dev, "MDEV: created\n"); + up_read(&parent->unreg_sem); return 0; @@ -295,6 +337,7 @@ sysfs_fail: add_fail: parent->ops->remove(mdev); ops_create_fail: + up_read(&parent->unreg_sem); put_device(&mdev->dev); mdev_fail: mdev_put_parent(parent); @@ -305,8 +348,6 @@ int mdev_device_remove(struct device *dev) { struct mdev_device *mdev, *tmp; struct mdev_parent *parent; - struct mdev_type *type; - int ret; mdev = to_mdev_device(dev); @@ -329,18 +370,13 @@ int mdev_device_remove(struct device *dev) mdev->active = false; mutex_unlock(&mdev_list_lock); - type = to_mdev_type(mdev->type_kobj); - mdev_remove_sysfs_files(dev, type); - device_del(&mdev->dev); parent = mdev->parent; - ret = parent->ops->remove(mdev); - if (ret) - dev_err(&mdev->dev, "Remove failed: err=%d\n", ret); - - /* Balances with device_initialize() */ - put_device(&mdev->dev); - mdev_put_parent(parent); + /* Check if parent unregistration has started */ + if (!down_read_trylock(&parent->unreg_sem)) + return -ENODEV; + mdev_device_remove_common(mdev); + up_read(&parent->unreg_sem); return 0; } diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h index 924ed2274941..398767526276 100644 --- a/drivers/vfio/mdev/mdev_private.h +++ b/drivers/vfio/mdev/mdev_private.h @@ -23,6 +23,8 @@ struct mdev_parent { struct list_head next; struct kset *mdev_types_kset; struct list_head type_list; + /* Synchronize device creation/removal with parent unregistration */ + struct rw_semaphore unreg_sem; }; struct mdev_device { -- cgit v1.2.3 From e98c7952db1a832c671ef70d00bfe0365069e5ff Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 5 Jun 2019 14:53:20 +0200 Subject: drm/meson: fix G12A HDMI PLL settings for 4K60 1000/1001 variations The Amlogic G12A HDMI PLL needs some specific settings to lock with different fractional values for the 5,4GHz mode. Handle the 1000/1001 variation fractional case here to avoid having the PLL in an non lockable state. Fixes: 202b9808f8ed ("drm/meson: Add G12A Video Clock setup") Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Link: https://patchwork.freedesktop.org/patch/msgid/20190605125320.8708-1-narmstrong@baylibre.com --- drivers/gpu/drm/meson/meson_vclk.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c index 58b4af5fbb6d..26732f038d19 100644 --- a/drivers/gpu/drm/meson/meson_vclk.c +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -503,8 +503,17 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m, /* G12A HDMI PLL Needs specific parameters for 5.4GHz */ if (m >= 0xf7) { - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0xea68dc00); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x65771290); + if (frac < 0x10000) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, + 0x6a685c00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, + 0x11551293); + } else { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, + 0xea68dc00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, + 0x65771290); + } regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39272000); regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x55540000); } else { -- cgit v1.2.3 From c03ea50e27ec44c2ed6adda48f2a495ce424ce46 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 5 Jun 2019 16:12:52 +0200 Subject: drm/meson: fix primary plane disabling The primary plane disable logic is flawed, when the primary plane is disabled, it is re-enabled in the vsync irq when another plane is updated. Handle the plane disabling correctly by handling the primary plane enable flag in the primary plane update & disable callbacks. Fixes: 490f50c109d1 ("drm/meson: Add G12A support for OSD1 Plane") Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Link: https://patchwork.freedesktop.org/patch/msgid/20190605141253.24165-2-narmstrong@baylibre.com --- drivers/gpu/drm/meson/meson_crtc.c | 4 ---- drivers/gpu/drm/meson/meson_plane.c | 4 +++- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index 685715144156..50a9a96720b9 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -107,8 +107,6 @@ static void meson_g12a_crtc_atomic_enable(struct drm_crtc *crtc, priv->io_base + _REG(VPP_OUT_H_V_SIZE)); drm_crtc_vblank_on(crtc); - - priv->viu.osd1_enabled = true; } static void meson_crtc_atomic_enable(struct drm_crtc *crtc, @@ -137,8 +135,6 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc, priv->io_base + _REG(VPP_MISC)); drm_crtc_vblank_on(crtc); - - priv->viu.osd1_enabled = true; } static void meson_g12a_crtc_atomic_disable(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 22490047932e..b788280895c6 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -305,6 +305,8 @@ static void meson_plane_atomic_update(struct drm_plane *plane, meson_plane->enabled = true; } + priv->viu.osd1_enabled = true; + spin_unlock_irqrestore(&priv->drm->event_lock, flags); } @@ -323,7 +325,7 @@ static void meson_plane_atomic_disable(struct drm_plane *plane, priv->io_base + _REG(VPP_MISC)); meson_plane->enabled = false; - + priv->viu.osd1_enabled = false; } static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { -- cgit v1.2.3 From 0b84933db7f2d1349d77d6db2d6ba17f06da1208 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 5 Jun 2019 16:12:53 +0200 Subject: drm/meson: fix G12A primary plane disabling The G12A Primary plane was disabled by writing in the OSD1 configuration registers, but this caused the plane blender to stall instead of continuing to blend only the overlay plane. Fix this by disabling the OSD1 plane in the blender registers, and also enabling it back using the same register. Fixes: 490f50c109d1 ("drm/meson: Add G12A support for OSD1 Plane") Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman [narmstrong: fixed nit in commit log] Link: https://patchwork.freedesktop.org/patch/msgid/20190605141253.24165-3-narmstrong@baylibre.com --- drivers/gpu/drm/meson/meson_crtc.c | 2 ++ drivers/gpu/drm/meson/meson_plane.c | 4 ++-- drivers/gpu/drm/meson/meson_viu.c | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index 50a9a96720b9..aa8ea107524e 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -252,6 +252,8 @@ static void meson_g12a_crtc_enable_osd1(struct meson_drm *priv) writel_relaxed(priv->viu.osb_blend1_size, priv->io_base + _REG(VIU_OSD_BLEND_BLEND1_SIZE)); + writel_bits_relaxed(3 << 8, 3 << 8, + priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); } static void meson_crtc_enable_vd1(struct meson_drm *priv) diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index b788280895c6..d90427b93a51 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -318,8 +318,8 @@ static void meson_plane_atomic_disable(struct drm_plane *plane, /* Disable OSD1 */ if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) - writel_bits_relaxed(BIT(0) | BIT(21), 0, - priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_bits_relaxed(3 << 8, 0, + priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); else writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, priv->io_base + _REG(VPP_MISC)); diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c index 462c7cb3e1bd..4b2b3024d371 100644 --- a/drivers/gpu/drm/meson/meson_viu.c +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -405,8 +405,7 @@ void meson_viu_init(struct meson_drm *priv) 0 << 16 | 1, priv->io_base + _REG(VIU_OSD_BLEND_CTRL)); - writel_relaxed(3 << 8 | - 1 << 20, + writel_relaxed(1 << 20, priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); writel_relaxed(1 << 20, priv->io_base + _REG(OSD2_BLEND_SRC_CTRL)); -- cgit v1.2.3 From 625d7d3518875c4d303c652a198feaa13d9f52d9 Mon Sep 17 00:00:00 2001 From: Don Brace Date: Mon, 3 Jun 2019 16:43:29 -0500 Subject: scsi: hpsa: correct ioaccel2 chaining - set ioaccel2_sg_element member 'chain_indicator' to IOACCEL2_LAST_SG for the last s/g element. - set ioaccel2_sg_element member 'chain_indicator' to IOACCEL2_CHAIN when chaining. Reviewed-by: Bader Ali - Saleh Reviewed-by: Scott Teel Reviewed-by: Matt Perricone Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 7 ++++++- drivers/scsi/hpsa_cmd.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 1bef1da273c2..8068520cf89e 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4940,7 +4940,7 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, curr_sg->reserved[0] = 0; curr_sg->reserved[1] = 0; curr_sg->reserved[2] = 0; - curr_sg->chain_indicator = 0x80; + curr_sg->chain_indicator = IOACCEL2_CHAIN; curr_sg = h->ioaccel2_cmd_sg_list[c->cmdindex]; } @@ -4957,6 +4957,11 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, curr_sg++; } + /* + * Set the last s/g element bit + */ + (curr_sg - 1)->chain_indicator = IOACCEL2_LAST_SG; + switch (cmd->sc_data_direction) { case DMA_TO_DEVICE: cp->direction &= ~IOACCEL2_DIRECTION_MASK; diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index 21a726e2eec6..f6afca4b2319 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -517,6 +517,7 @@ struct ioaccel2_sg_element { u8 reserved[3]; u8 chain_indicator; #define IOACCEL2_CHAIN 0x80 +#define IOACCEL2_LAST_SG 0x40 }; /* -- cgit v1.2.3 From f3c74b38a55aefe1004200d15a83f109b510068c Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sat, 20 Apr 2019 13:27:51 +0200 Subject: RAS/CEC: Fix binary search function Switch to using Donald Knuth's binary search algorithm (The Art of Computer Programming, vol. 3, section 6.2.1). This should've been done from the very beginning but the author must've been smoking something very potent at the time. The problem with the current one was that it would return the wrong element index in certain situations: https://lkml.kernel.org/r/CAM_iQpVd02zkVJ846cj-Fg1yUNuz6tY5q1Vpj4LrXmE06dPYYg@mail.gmail.com and the noodling code after the loop was fishy at best. So switch to using Knuth's binary search. The final result is much cleaner and straightforward. Fixes: 011d82611172 ("RAS: Add a Corrected Errors Collector") Reported-by: Cong Wang Signed-off-by: Borislav Petkov Cc: Tony Luck Cc: linux-edac Cc: --- drivers/ras/cec.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/ras/cec.c b/drivers/ras/cec.c index 88e4f3ff0cb8..dbfe3e61d2c2 100644 --- a/drivers/ras/cec.c +++ b/drivers/ras/cec.c @@ -183,32 +183,38 @@ static void cec_timer_fn(struct timer_list *unused) */ static int __find_elem(struct ce_array *ca, u64 pfn, unsigned int *to) { + int min = 0, max = ca->n - 1; u64 this_pfn; - int min = 0, max = ca->n; - while (min < max) { - int tmp = (max + min) >> 1; + while (min <= max) { + int i = (min + max) >> 1; - this_pfn = PFN(ca->array[tmp]); + this_pfn = PFN(ca->array[i]); if (this_pfn < pfn) - min = tmp + 1; + min = i + 1; else if (this_pfn > pfn) - max = tmp; - else { - min = tmp; - break; + max = i - 1; + else if (this_pfn == pfn) { + if (to) + *to = i; + + return i; } } + /* + * When the loop terminates without finding @pfn, min has the index of + * the element slot where the new @pfn should be inserted. The loop + * terminates when min > max, which means the min index points to the + * bigger element while the max index to the smaller element, in-between + * which the new @pfn belongs to. + * + * For more details, see exercise 1, Section 6.2.1 in TAOCP, vol. 3. + */ if (to) *to = min; - this_pfn = PFN(ca->array[min]); - - if (this_pfn == pfn) - return min; - return -ENOKEY; } -- cgit v1.2.3 From 0ade0b6240c4853cf9725924c46c10f4251639d7 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 16 Apr 2019 14:33:51 -0700 Subject: RAS/CEC: Convert the timer callback to a workqueue cec_timer_fn() is a timer callback which reads ce_arr.array[] and updates its decay values. However, it runs in interrupt context and the mutex protection the CEC uses for that array, is inadequate. Convert the used timer to a workqueue to keep the tasks the CEC performs preemptible and thus low-prio. [ bp: Rewrite commit message. s/timer/decay/gi to make it agnostic as to what facility is used. ] Fixes: 011d82611172 ("RAS: Add a Corrected Errors Collector") Signed-off-by: Cong Wang Signed-off-by: Borislav Petkov Cc: Thomas Gleixner Cc: Tony Luck Cc: linux-edac Cc: Link: https://lkml.kernel.org/r/20190416213351.28999-2-xiyou.wangcong@gmail.com --- drivers/ras/cec.c | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/ras/cec.c b/drivers/ras/cec.c index dbfe3e61d2c2..673f8a128397 100644 --- a/drivers/ras/cec.c +++ b/drivers/ras/cec.c @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -123,16 +124,12 @@ static u64 dfs_pfn; /* Amount of errors after which we offline */ static unsigned int count_threshold = COUNT_MASK; -/* - * The timer "decays" element count each timer_interval which is 24hrs by - * default. - */ - -#define CEC_TIMER_DEFAULT_INTERVAL 24 * 60 * 60 /* 24 hrs */ -#define CEC_TIMER_MIN_INTERVAL 1 * 60 * 60 /* 1h */ -#define CEC_TIMER_MAX_INTERVAL 30 * 24 * 60 * 60 /* one month */ -static struct timer_list cec_timer; -static u64 timer_interval = CEC_TIMER_DEFAULT_INTERVAL; +/* Each element "decays" each decay_interval which is 24hrs by default. */ +#define CEC_DECAY_DEFAULT_INTERVAL 24 * 60 * 60 /* 24 hrs */ +#define CEC_DECAY_MIN_INTERVAL 1 * 60 * 60 /* 1h */ +#define CEC_DECAY_MAX_INTERVAL 30 * 24 * 60 * 60 /* one month */ +static struct delayed_work cec_work; +static u64 decay_interval = CEC_DECAY_DEFAULT_INTERVAL; /* * Decrement decay value. We're using DECAY_BITS bits to denote decay of an @@ -160,20 +157,21 @@ static void do_spring_cleaning(struct ce_array *ca) /* * @interval in seconds */ -static void cec_mod_timer(struct timer_list *t, unsigned long interval) +static void cec_mod_work(unsigned long interval) { unsigned long iv; - iv = interval * HZ + jiffies; - - mod_timer(t, round_jiffies(iv)); + iv = interval * HZ; + mod_delayed_work(system_wq, &cec_work, round_jiffies(iv)); } -static void cec_timer_fn(struct timer_list *unused) +static void cec_work_fn(struct work_struct *work) { + mutex_lock(&ce_mutex); do_spring_cleaning(&ce_arr); + mutex_unlock(&ce_mutex); - cec_mod_timer(&cec_timer, timer_interval); + cec_mod_work(decay_interval); } /* @@ -380,15 +378,15 @@ static int decay_interval_set(void *data, u64 val) { *(u64 *)data = val; - if (val < CEC_TIMER_MIN_INTERVAL) + if (val < CEC_DECAY_MIN_INTERVAL) return -EINVAL; - if (val > CEC_TIMER_MAX_INTERVAL) + if (val > CEC_DECAY_MAX_INTERVAL) return -EINVAL; - timer_interval = val; + decay_interval = val; - cec_mod_timer(&cec_timer, timer_interval); + cec_mod_work(decay_interval); return 0; } DEFINE_DEBUGFS_ATTRIBUTE(decay_interval_ops, u64_get, decay_interval_set, "%lld\n"); @@ -432,7 +430,7 @@ static int array_dump(struct seq_file *m, void *v) seq_printf(m, "Flags: 0x%x\n", ca->flags); - seq_printf(m, "Timer interval: %lld seconds\n", timer_interval); + seq_printf(m, "Decay interval: %lld seconds\n", decay_interval); seq_printf(m, "Decays: %lld\n", ca->decays_done); seq_printf(m, "Action threshold: %d\n", count_threshold); @@ -478,7 +476,7 @@ static int __init create_debugfs_nodes(void) } decay = debugfs_create_file("decay_interval", S_IRUSR | S_IWUSR, d, - &timer_interval, &decay_interval_ops); + &decay_interval, &decay_interval_ops); if (!decay) { pr_warn("Error creating decay_interval debugfs node!\n"); goto err; @@ -514,8 +512,8 @@ void __init cec_init(void) if (create_debugfs_nodes()) return; - timer_setup(&cec_timer, cec_timer_fn, 0); - cec_mod_timer(&cec_timer, CEC_TIMER_DEFAULT_INTERVAL); + INIT_DELAYED_WORK(&cec_work, cec_work_fn); + schedule_delayed_work(&cec_work, CEC_DECAY_DEFAULT_INTERVAL); pr_info("Correctable Errors collector initialized.\n"); } -- cgit v1.2.3 From 3b00691cc46a4089368a008b30655a8343411715 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Tue, 4 Jun 2019 14:35:58 +0200 Subject: gpio: pca953x: hack to fix 24 bit gpio expanders 24 bit expanders use REG_ADDR_AI in combination with register addressing. This conflicts with regmap which takes this bit as part of the register number, i.e. a second cache entry is defined for accessed with REG_ADDR_AI being set although on the chip it is the same register as with REG_ADDR_AI being cleared. The problem was introduced by commit b32cecb46bdc ("gpio: pca953x: Extract the register address mangling to single function") but only became visible by commit 8b9f9d4dc511 ("regmap: verify if register is writeable before writing operations") because before, the regmap size was effectively ignored and pca953x_writeable_register() did know to ignore REG_ADDR_AI. Still, there were two separate cache entries created. Since the use of REG_ADDR_AI seems to be static we can work around this issue by simply increasing the size of the regmap to cover the "virtual" registers with REG_ADDR_AI being set. This only means that half of the regmap buffer will be unused. Reported-by: H. Nikolaus Schaller Suggested-by: Linus Walleij Signed-off-by: H. Nikolaus Schaller Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pca953x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b7ef33f63392..2b27d18410aa 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -308,7 +308,8 @@ static const struct regmap_config pca953x_i2c_regmap = { .volatile_reg = pca953x_volatile_register, .cache_type = REGCACHE_RBTREE, - .max_register = 0x7f, + /* REVISIT: should be 0x7f but some 24 bit chips use REG_ADDR_AI */ + .max_register = 0xff, }; static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off, -- cgit v1.2.3 From 15abc7110a77555d3bf72aaef46d1557db0a4ac5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 May 2019 14:57:58 +0200 Subject: drm: panel-orientation-quirks: Add quirk for GPD pocket2 GPD has done it again, make a nice device (good), use way too generic DMI strings (bad) and use a portrait screen rotated 90 degrees (ugly). Because of the too generic DMI strings this entry is also doing bios-date matching, so the gpd_pocket2 data struct may very well need to be updated with some extra bios-dates in the future. Changes in v2: -Add one more known BIOS date to the list of BIOS dates Cc: Jurgen Kramer Reported-by: Jurgen Kramer Acked-by: Maxime Ripard Signed-off-by: Hans de Goede Link: https://patchwork.freedesktop.org/patch/msgid/20190524125759.14131-1-hdegoede@redhat.com (cherry picked from commit 6dab9102dd7b144e5723915438e0d6c473018cd0) --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 521aff99b08a..98679c831f66 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -50,6 +50,14 @@ static const struct drm_dmi_panel_orientation_data gpd_pocket = { .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +static const struct drm_dmi_panel_orientation_data gpd_pocket2 = { + .width = 1200, + .height = 1920, + .bios_dates = (const char * const []){ "06/28/2018", "08/28/2018", + "12/07/2018", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + static const struct drm_dmi_panel_orientation_data gpd_win = { .width = 720, .height = 1280, @@ -112,6 +120,14 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), }, .driver_data = (void *)&gpd_pocket, + }, { /* GPD Pocket 2 (generic strings, also match on bios date) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_pocket2, }, { /* GPD Win (same note on DMI match as GPD Pocket) */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), -- cgit v1.2.3 From 652b8b086538c8a10de5aa5cbdaef79333b46358 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 May 2019 14:57:59 +0200 Subject: drm: panel-orientation-quirks: Add quirk for GPD MicroPC GPD has done it again, make a nice device (good), use way too generic DMI strings (bad) and use a portrait screen rotated 90 degrees (ugly). Because of the too generic DMI strings this entry is also doing bios-date matching, so the gpd_micropc data struct may very well need to be updated with some extra bios-dates in the future. Acked-by: Maxime Ripard Signed-off-by: Hans de Goede Link: https://patchwork.freedesktop.org/patch/msgid/20190524125759.14131-2-hdegoede@redhat.com (cherry picked from commit f2f2bb60d998abde10de7e483ef9e17639892450) --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 98679c831f66..d8a0bcd02f34 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -42,6 +42,14 @@ static const struct drm_dmi_panel_orientation_data asus_t100ha = { .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, }; +static const struct drm_dmi_panel_orientation_data gpd_micropc = { + .width = 720, + .height = 1280, + .bios_dates = (const char * const []){ "04/26/2019", + NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + static const struct drm_dmi_panel_orientation_data gpd_pocket = { .width = 1200, .height = 1920, @@ -107,6 +115,14 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"), }, .driver_data = (void *)&asus_t100ha, + }, { /* GPD MicroPC (generic strings, also match on bios date) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_micropc, }, { /* * GPD Pocket, note that the the DMI data is less generic then * it seems, devices with a board-vendor of "AMI Corporation" -- cgit v1.2.3 From be7d9f05c53e6fc88525f8e55cf2dae937761799 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 29 May 2019 08:51:21 +0200 Subject: drm/gem_shmem: Use a writecombine mapping for ->vaddr Right now, the BO is mapped as a cached region when ->vmap() is called and the underlying object is not a dmabuf. Doing that makes cache management a bit more complicated (you'd need to call dma_map/unmap_sg() on the ->sgt field everytime the BO is about to be passed to the GPU/CPU), so let's map the BO with writecombine attributes instead (as done in most drivers). Fixes: 2194a63a818d ("drm: Add library for shmem backed GEM objects") Signed-off-by: Boris Brezillon Reviewed-by: Daniel Vetter Reviewed-by: Eric Anholt Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20190529065121.13485-1-boris.brezillon@collabora.com --- drivers/gpu/drm/drm_gem_shmem_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 1ee208c2c85e..472ea5d81f82 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -255,7 +255,8 @@ static void *drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem) if (obj->import_attach) shmem->vaddr = dma_buf_vmap(obj->import_attach->dmabuf); else - shmem->vaddr = vmap(shmem->pages, obj->size >> PAGE_SHIFT, VM_MAP, PAGE_KERNEL); + shmem->vaddr = vmap(shmem->pages, obj->size >> PAGE_SHIFT, + VM_MAP, pgprot_writecombine(PAGE_KERNEL)); if (!shmem->vaddr) { DRM_DEBUG_KMS("Failed to vmap pages\n"); -- cgit v1.2.3 From 1c3b526e559deada4506732acec4af1879fc1fa0 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 5 Jun 2019 17:02:33 +0200 Subject: drm/panfrost: make devfreq optional again Devfreq runtime usage was made mandatory, thus making panfrost fail to probe on Amlogic S912 SoCs missing the "operating-points-v2" property. Make it optional again, leaving PM_DEVFREQ selected by default. Fixes: f3617b449d0b ("drm/panfrost: Select devfreq") Signed-off-by: Neil Armstrong Reviewed-by: Ezequiel Garcia Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20190605150233.32722-1-narmstrong@baylibre.com --- drivers/gpu/drm/panfrost/panfrost_devfreq.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index 29fcffdf2d57..db798532b0b6 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -140,7 +140,9 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev) return 0; ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev); - if (ret) + if (ret == -ENODEV) /* Optional, continue without devfreq */ + return 0; + else if (ret) return ret; panfrost_devfreq_reset(pfdev); @@ -170,6 +172,9 @@ void panfrost_devfreq_resume(struct panfrost_device *pfdev) { int i; + if (!pfdev->devfreq.devfreq) + return; + panfrost_devfreq_reset(pfdev); for (i = 0; i < NUM_JOB_SLOTS; i++) pfdev->devfreq.slot[i].busy = false; @@ -179,6 +184,9 @@ void panfrost_devfreq_resume(struct panfrost_device *pfdev) void panfrost_devfreq_suspend(struct panfrost_device *pfdev) { + if (!pfdev->devfreq.devfreq) + return; + devfreq_suspend_device(pfdev->devfreq.devfreq); } @@ -188,6 +196,9 @@ static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, i ktime_t now; ktime_t last; + if (!pfdev->devfreq.devfreq) + return; + now = ktime_get(); last = pfdev->devfreq.slot[slot].time_last_update; -- cgit v1.2.3 From de060de333eff20c1c2dad0139983f19150f776b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 5 Jun 2019 15:48:59 -0300 Subject: drm/panfrost: Require the simple_ondemand governor Panfrost depends on the simple_ondemand governor, and therefore it's a required configuration. Select it. Fixes: f3617b449d0b ("drm/panfrost: Select devfreq") Signed-off-by: Ezequiel Garcia Reviewed-by: Tomeu Vizoso Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20190605184859.9432-1-ezequiel@collabora.com --- drivers/gpu/drm/panfrost/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/panfrost/Kconfig b/drivers/gpu/drm/panfrost/Kconfig index 81963e964b0f..86cdc0ce79e6 100644 --- a/drivers/gpu/drm/panfrost/Kconfig +++ b/drivers/gpu/drm/panfrost/Kconfig @@ -10,6 +10,7 @@ config DRM_PANFROST select IOMMU_IO_PGTABLE_LPAE select DRM_GEM_SHMEM_HELPER select PM_DEVFREQ + select DEVFREQ_GOV_SIMPLE_ONDEMAND help DRM driver for ARM Mali Midgard (T6xx, T7xx, T8xx) and Bifrost (G3x, G5x, G7x) GPUs. -- cgit v1.2.3 From 517b91f4cde3043d77b2178548473e8545ef07cb Mon Sep 17 00:00:00 2001 From: Shirish S Date: Tue, 4 Jun 2019 21:25:03 +0530 Subject: drm/amdgpu/{uvd,vcn}: fetch ring's read_ptr after alloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [What] readptr read always returns zero, since most likely these blocks are either power or clock gated. [How] fetch rptr after amdgpu_ring_alloc() which informs the power management code that the block is about to be used and hence the gating is turned off. Signed-off-by: Louis Li Signed-off-by: Shirish S Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c | 4 +++- drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c | 5 ++++- drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index ecf6f96df2ad..e6b07ece3910 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -594,7 +594,7 @@ error: int amdgpu_vcn_enc_ring_test_ring(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - uint32_t rptr = amdgpu_ring_get_rptr(ring); + uint32_t rptr; unsigned i; int r; @@ -602,6 +602,8 @@ int amdgpu_vcn_enc_ring_test_ring(struct amdgpu_ring *ring) if (r) return r; + rptr = amdgpu_ring_get_rptr(ring); + amdgpu_ring_write(ring, VCN_ENC_CMD_END); amdgpu_ring_commit(ring); diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index c9edddf9f88a..be70e6e5f9df 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -170,13 +170,16 @@ static void uvd_v6_0_enc_ring_set_wptr(struct amdgpu_ring *ring) static int uvd_v6_0_enc_ring_test_ring(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - uint32_t rptr = amdgpu_ring_get_rptr(ring); + uint32_t rptr; unsigned i; int r; r = amdgpu_ring_alloc(ring, 16); if (r) return r; + + rptr = amdgpu_ring_get_rptr(ring); + amdgpu_ring_write(ring, HEVC_ENC_CMD_END); amdgpu_ring_commit(ring); diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c index 2191d3d0a219..fc4f0bb9a2e7 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c @@ -175,7 +175,7 @@ static void uvd_v7_0_enc_ring_set_wptr(struct amdgpu_ring *ring) static int uvd_v7_0_enc_ring_test_ring(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - uint32_t rptr = amdgpu_ring_get_rptr(ring); + uint32_t rptr; unsigned i; int r; @@ -185,6 +185,9 @@ static int uvd_v7_0_enc_ring_test_ring(struct amdgpu_ring *ring) r = amdgpu_ring_alloc(ring, 16); if (r) return r; + + rptr = amdgpu_ring_get_rptr(ring); + amdgpu_ring_write(ring, HEVC_ENC_CMD_END); amdgpu_ring_commit(ring); -- cgit v1.2.3 From f9a99131ce18d9dddcaa14ec2c436e42f0bbee5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 May 2019 19:40:27 +0200 Subject: drm/i915/dsi: Use a fuzzy check for burst mode clock check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to this commit we fail to init the DSI panel on the GPD MicroPC: https://www.indiegogo.com/projects/gpd-micropc-6-inch-handheld-industry-laptop#/ The problem is intel_dsi_vbt_init() failing with the following error: *ERROR* Burst mode freq is less than computed The pclk in the VBT panel modeline is 70000, together with 24 bpp and 4 lines this results in a bitrate value of 70000 * 24 / 4 = 420000. But the target_burst_mode_freq in the VBT is 418000. This commit works around this problem by adding an intel_fuzzy_clock_check when target_burst_mode_freq < bitrate and setting target_burst_mode_freq to bitrate when that checks succeeds, fixing the panel not working. Cc: stable@vger.kernel.org Reviewed-by: Ville Syrjälä Signed-off-by: Hans de Goede Link: https://patchwork.freedesktop.org/patch/msgid/20190524174028.21659-2-hdegoede@redhat.com (cherry picked from commit 2c1c55252647abd989b94f725b190c700312d053) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_dsi_vbt.c | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5098228f1302..ceb78f44f087 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11942,7 +11942,7 @@ encoder_retry: return 0; } -static bool intel_fuzzy_clock_check(int clock1, int clock2) +bool intel_fuzzy_clock_check(int clock1, int clock2) { int diff; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a38b9cff5cd0..e85cd377a652 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1742,6 +1742,7 @@ int vlv_force_pll_on(struct drm_i915_private *dev_priv, enum pipe pipe, const struct dpll *dpll); void vlv_force_pll_off(struct drm_i915_private *dev_priv, enum pipe pipe); int lpt_get_iclkip(struct drm_i915_private *dev_priv); +bool intel_fuzzy_clock_check(int clock1, int clock2); /* modesetting asserts */ void assert_panel_unlocked(struct drm_i915_private *dev_priv, diff --git a/drivers/gpu/drm/i915/intel_dsi_vbt.c b/drivers/gpu/drm/i915/intel_dsi_vbt.c index 3074448446bc..4b8e48db1843 100644 --- a/drivers/gpu/drm/i915/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_vbt.c @@ -853,6 +853,17 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) if (mipi_config->target_burst_mode_freq) { u32 bitrate = intel_dsi_bitrate(intel_dsi); + /* + * Sometimes the VBT contains a slightly lower clock, + * then the bitrate we have calculated, in this case + * just replace it with the calculated bitrate. + */ + if (mipi_config->target_burst_mode_freq < bitrate && + intel_fuzzy_clock_check( + mipi_config->target_burst_mode_freq, + bitrate)) + mipi_config->target_burst_mode_freq = bitrate; + if (mipi_config->target_burst_mode_freq < bitrate) { DRM_ERROR("Burst mode freq is less than computed\n"); return false; -- cgit v1.2.3 From 326fb6dd1483c985a6ef47db3fa8788bb99e8b83 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Wed, 5 Jun 2019 16:55:35 -0700 Subject: drm/i915/dmc: protect against reading random memory While loading the DMC firmware we were double checking the headers made sense, but in no place we checked that we were actually reading memory we were supposed to. This could be wrong in case the firmware file is truncated or malformed. Before this patch: # ls -l /lib/firmware/i915/icl_dmc_ver1_07.bin -rw-r--r-- 1 root root 25716 Feb 1 12:26 icl_dmc_ver1_07.bin # truncate -s 25700 /lib/firmware/i915/icl_dmc_ver1_07.bin # modprobe i915 # dmesg| grep -i dmc [drm:intel_csr_ucode_init [i915]] Loading i915/icl_dmc_ver1_07.bin [drm] Finished loading DMC firmware i915/icl_dmc_ver1_07.bin (v1.7) i.e. it loads random data. Now it fails like below: [drm:intel_csr_ucode_init [i915]] Loading i915/icl_dmc_ver1_07.bin [drm:csr_load_work_fn [i915]] *ERROR* Truncated DMC firmware, rejecting. i915 0000:00:02.0: Failed to load DMC firmware i915/icl_dmc_ver1_07.bin. Disabling runtime power management. i915 0000:00:02.0: DMC firmware homepage: https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/i915 Before reading any part of the firmware file, validate the input first. Fixes: eb805623d8b1 ("drm/i915/skl: Add support to load SKL CSR firmware.") Cc: stable@vger.kernel.org Signed-off-by: Lucas De Marchi Reviewed-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20190605235535.17791-1-lucas.demarchi@intel.com (cherry picked from commit bc7b488b1d1c71dc4c5182206911127bc6c410d6) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_csr.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index f43c2a2563a5..96618af47088 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -303,10 +303,17 @@ static u32 *parse_csr_fw(struct drm_i915_private *dev_priv, u32 dmc_offset = CSR_DEFAULT_FW_OFFSET, readcount = 0, nbytes; u32 i; u32 *dmc_payload; + size_t fsize; if (!fw) return NULL; + fsize = sizeof(struct intel_css_header) + + sizeof(struct intel_package_header) + + sizeof(struct intel_dmc_header); + if (fsize > fw->size) + goto error_truncated; + /* Extract CSS Header information*/ css_header = (struct intel_css_header *)fw->data; if (sizeof(struct intel_css_header) != @@ -366,6 +373,9 @@ static u32 *parse_csr_fw(struct drm_i915_private *dev_priv, /* Convert dmc_offset into number of bytes. By default it is in dwords*/ dmc_offset *= 4; readcount += dmc_offset; + fsize += dmc_offset; + if (fsize > fw->size) + goto error_truncated; /* Extract dmc_header information. */ dmc_header = (struct intel_dmc_header *)&fw->data[readcount]; @@ -397,6 +407,10 @@ static u32 *parse_csr_fw(struct drm_i915_private *dev_priv, /* fw_size is in dwords, so multiplied by 4 to convert into bytes. */ nbytes = dmc_header->fw_size * 4; + fsize += nbytes; + if (fsize > fw->size) + goto error_truncated; + if (nbytes > csr->max_fw_size) { DRM_ERROR("DMC FW too big (%u bytes)\n", nbytes); return NULL; @@ -410,6 +424,10 @@ static u32 *parse_csr_fw(struct drm_i915_private *dev_priv, } return memcpy(dmc_payload, &fw->data[readcount], nbytes); + +error_truncated: + DRM_ERROR("Truncated DMC firmware, rejecting.\n"); + return NULL; } static void intel_csr_runtime_pm_get(struct drm_i915_private *dev_priv) -- cgit v1.2.3 From 77ce94dbe586c1a6a26cf021c08109c9ce71b3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 3 Jun 2019 17:25:00 +0300 Subject: drm/i915: Fix per-pixel alpha with CCS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We forgot to set .has_alpha=true for the A+CCS formats when the code started to consult .has_alpha. This manifests as A+CCS being treated as X+CCS which means no per-pixel alpha blending. Fix the format list appropriately. Cc: stable@vger.kernel.org Cc: Maarten Lankhorst Cc: Matt Roper Cc: Heinrich Fink Reported-by: Heinrich Fink Tested-by: Heinrich Fink Fixes: b20815255693 ("drm/i915: Add plane alpha blending support, v2.") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20190603142500.25680-1-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst (cherry picked from commit 38f300410f3e15b6fec76c8d8baed7111b5ea4e4) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_display.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ceb78f44f087..b69440cf41ea 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2432,10 +2432,14 @@ static unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier) * main surface. */ static const struct drm_format_info ccs_formats[] = { - { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, - { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, - { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, - { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, .has_alpha = true, }, + { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, + .cpp = { 4, 1, }, .hsub = 8, .vsub = 16, .has_alpha = true, }, }; static const struct drm_format_info * -- cgit v1.2.3 From d74408f528261f900dddb9778f61b5c5a7a6249c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 9 Apr 2019 17:40:49 +0300 Subject: drm/i915/sdvo: Implement proper HDMI audio support for SDVO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our SDVO audio support is pretty bogus. We can't push audio over the SDVO bus, so trying to enable audio in the SDVO control register doesn't do anything. In fact it looks like the SDVO encoder will always mix in the audio coming over HDA, and there's no (at least documented) way to disable that from our side. So HDMI audio does work currently on gen4 but only by luck really. On gen3 it got broken by the referenced commit. And what has always been missing on every platform is the ELD. To pass the ELD to the audio driver we need to write it to magic buffer in the SDVO encoder hardware which then gets pulled out via HDA in the other end. Ie. pretty much the same thing we had for native HDMI before we started to just pass the ELD between the drivers. This sort of explains why we even have that silly hardware buffer with native HDMI. $ cat /proc/asound/card0/eld#1.0 -monitor_present 0 -eld_valid 0 +monitor_present 1 +eld_valid 1 +monitor_name LG TV +connection_type HDMI +... This also fixes our state readout since we can now query the SDVO encoder about the state of the "ELD valid" and "presence detect" bits. As mentioned those don't actually control whether audio gets sent over the HDMI cable, but it's the best we can do. And with the state checker appeased we can re-enable HDMI audio for gen3. Cc: stable@vger.kernel.org Cc: Daniel Vetter Cc: zardam@gmail.com Tested-by: zardam@gmail.com Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=108976 Fixes: de44e256b92c ("drm/i915/sdvo: Shut up state checker with hdmi cards on gen3") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20190409144054.24561-3-ville.syrjala@linux.intel.com Reviewed-by: Imre Deak (cherry picked from commit dc49a56bd43bb04982e64b44436831da801d0237) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_sdvo.c | 58 +++++++++++++++++++++++++++------- drivers/gpu/drm/i915/intel_sdvo_regs.h | 3 ++ 2 files changed, 50 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 0e3d91d9ef13..9ecfba0a54a1 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -916,6 +916,13 @@ static bool intel_sdvo_set_colorimetry(struct intel_sdvo *intel_sdvo, return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_COLORIMETRY, &mode, 1); } +static bool intel_sdvo_set_audio_state(struct intel_sdvo *intel_sdvo, + u8 audio_state) +{ + return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_AUDIO_STAT, + &audio_state, 1); +} + #if 0 static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo) { @@ -1487,11 +1494,6 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder, else sdvox |= SDVO_PIPE_SEL(crtc->pipe); - if (crtc_state->has_audio) { - WARN_ON_ONCE(INTEL_GEN(dev_priv) < 4); - sdvox |= SDVO_AUDIO_ENABLE; - } - if (INTEL_GEN(dev_priv) >= 4) { /* done in crtc_mode_set as the dpll_md reg must be written early */ } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || @@ -1635,8 +1637,13 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, if (sdvox & HDMI_COLOR_RANGE_16_235) pipe_config->limited_color_range = true; - if (sdvox & SDVO_AUDIO_ENABLE) - pipe_config->has_audio = true; + if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_AUDIO_STAT, + &val, 1)) { + u8 mask = SDVO_AUDIO_ELD_VALID | SDVO_AUDIO_PRESENCE_DETECT; + + if ((val & mask) == mask) + pipe_config->has_audio = true; + } if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, &val, 1)) { @@ -1647,6 +1654,32 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, intel_sdvo_get_avi_infoframe(intel_sdvo, pipe_config); } +static void intel_sdvo_disable_audio(struct intel_sdvo *intel_sdvo) +{ + intel_sdvo_set_audio_state(intel_sdvo, 0); +} + +static void intel_sdvo_enable_audio(struct intel_sdvo *intel_sdvo, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + const struct drm_display_mode *adjusted_mode = + &crtc_state->base.adjusted_mode; + struct drm_connector *connector = conn_state->connector; + u8 *eld = connector->eld; + + eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2; + + intel_sdvo_set_audio_state(intel_sdvo, 0); + + intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_ELD, + SDVO_HBUF_TX_DISABLED, + eld, drm_eld_size(eld)); + + intel_sdvo_set_audio_state(intel_sdvo, SDVO_AUDIO_ELD_VALID | + SDVO_AUDIO_PRESENCE_DETECT); +} + static void intel_disable_sdvo(struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *conn_state) @@ -1656,6 +1689,9 @@ static void intel_disable_sdvo(struct intel_encoder *encoder, struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); u32 temp; + if (old_crtc_state->has_audio) + intel_sdvo_disable_audio(intel_sdvo); + intel_sdvo_set_active_outputs(intel_sdvo, 0); if (0) intel_sdvo_set_encoder_power_state(intel_sdvo, @@ -1741,6 +1777,9 @@ static void intel_enable_sdvo(struct intel_encoder *encoder, intel_sdvo_set_encoder_power_state(intel_sdvo, DRM_MODE_DPMS_ON); intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); + + if (pipe_config->has_audio) + intel_sdvo_enable_audio(intel_sdvo, pipe_config, conn_state); } static enum drm_mode_status @@ -2603,7 +2642,6 @@ static bool intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) { struct drm_encoder *encoder = &intel_sdvo->base.base; - struct drm_i915_private *dev_priv = to_i915(encoder->dev); struct drm_connector *connector; struct intel_encoder *intel_encoder = to_intel_encoder(encoder); struct intel_connector *intel_connector; @@ -2640,9 +2678,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) encoder->encoder_type = DRM_MODE_ENCODER_TMDS; connector->connector_type = DRM_MODE_CONNECTOR_DVID; - /* gen3 doesn't do the hdmi bits in the SDVO register */ - if (INTEL_GEN(dev_priv) >= 4 && - intel_sdvo_is_hdmi_connector(intel_sdvo, device)) { + if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) { connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; intel_sdvo_connector->is_hdmi = true; } diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h index db0ed499268a..e9ba3b047f93 100644 --- a/drivers/gpu/drm/i915/intel_sdvo_regs.h +++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h @@ -707,6 +707,9 @@ struct intel_sdvo_enhancements_arg { #define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90 #define SDVO_CMD_SET_AUDIO_STAT 0x91 #define SDVO_CMD_GET_AUDIO_STAT 0x92 + #define SDVO_AUDIO_ELD_VALID (1 << 0) + #define SDVO_AUDIO_PRESENCE_DETECT (1 << 1) + #define SDVO_AUDIO_CP_READY (1 << 2) #define SDVO_CMD_SET_HBUF_INDEX 0x93 #define SDVO_HBUF_INDEX_ELD 0 #define SDVO_HBUF_INDEX_AVI_IF 1 -- cgit v1.2.3 From c5cc0bf870cfb80b27899e916b4397ce11c61d96 Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Sun, 2 Jun 2019 01:58:45 +0300 Subject: drm/i915/perf: fix whitelist on Gen10+ Gen10 added an additional NOA_WRITE register (high bits) and we forgot to whitelist it for userspace. Fixes: 95690a02fb5d96 ("drm/i915/perf: enable perf support on CNL") Signed-off-by: Lionel Landwerlin Reviewed-by: Kenneth Graunke Link: https://patchwork.freedesktop.org/patch/msgid/20190601225845.12600-1-lionel.g.landwerlin@intel.com (cherry picked from commit bf210f6c9e6fd8dc0d154ad18f741f20e64a3fce) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_perf.c | 1 + drivers/gpu/drm/i915/i915_reg.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 39a4804091d7..dc4ce694c06a 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -3005,6 +3005,7 @@ static bool gen8_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) static bool gen10_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) { return gen8_is_valid_mux_addr(dev_priv, addr) || + addr == i915_mmio_reg_offset(GEN10_NOA_WRITE_HIGH) || (addr >= i915_mmio_reg_offset(OA_PERFCNT3_LO) && addr <= i915_mmio_reg_offset(OA_PERFCNT4_HI)); } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2aa69d347ec4..13d6bd4e17b2 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1062,6 +1062,7 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define NOA_DATA _MMIO(0x986C) #define NOA_WRITE _MMIO(0x9888) +#define GEN10_NOA_WRITE_HIGH _MMIO(0x9884) #define _GEN7_PIPEA_DE_LOAD_SL 0x70068 #define _GEN7_PIPEB_DE_LOAD_SL 0x71068 -- cgit v1.2.3 From 4e4abae311e4b44aaf61f18a826fd7136037f199 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 3 Jun 2019 14:15:37 +0200 Subject: iommu/arm-smmu: Avoid constant zero in TLBI writes Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU global register space are still, in fact, using a hypervisor to mediate it by trapping and emulating register accesses. Sadly, some deployed versions of said trapping code have bugs wherein they go horribly wrong for stores using r31 (i.e. XZR/WZR) as the source register. While this can be mitigated for GCC today by tweaking the constraints for the implementation of writel_relaxed(), to avoid any potential arms race with future compilers more aggressively optimising register allocation, the simple way is to just remove all the problematic constant zeros. For the write-only TLB operations, the actual value is irrelevant anyway and any old nearby variable will provide a suitable GPR to encode. The one point at which we really do need a zero to clear a context bank happens before any of the TLB maintenance where crashes have been reported, so is apparently not a problem... :/ Reported-by: AngeloGioacchino Del Regno Tested-by: Marc Gonzalez Signed-off-by: Robin Murphy Signed-off-by: Marc Gonzalez Acked-by: Will Deacon Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/arm-smmu.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 5e54cc0a28b3..4f900c663fb8 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -59,6 +59,15 @@ #include "arm-smmu-regs.h" +/* + * Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU + * global register space are still, in fact, using a hypervisor to mediate it + * by trapping and emulating register accesses. Sadly, some deployed versions + * of said trapping code have bugs wherein they go horribly wrong for stores + * using r31 (i.e. XZR/WZR) as the source register. + */ +#define QCOM_DUMMY_VAL -1 + #define ARM_MMU500_ACTLR_CPRE (1 << 1) #define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) @@ -423,7 +432,7 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, { unsigned int spin_cnt, delay; - writel_relaxed(0, sync); + writel_relaxed(QCOM_DUMMY_VAL, sync); for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) { for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) { if (!(readl_relaxed(status) & sTLBGSTATUS_GSACTIVE)) @@ -1763,8 +1772,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) } /* Invalidate the TLB, just in case */ - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH); - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); + writel_relaxed(QCOM_DUMMY_VAL, gr0_base + ARM_SMMU_GR0_TLBIALLH); + writel_relaxed(QCOM_DUMMY_VAL, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); -- cgit v1.2.3 From 401fee8195d401b2b94dee57383f627050724d5b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Jun 2019 09:02:02 +0200 Subject: platform/x86: asus-wmi: Only Tell EC the OS will handle display hotkeys from asus_nb_wmi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 78f3ac76d9e5 ("platform/x86: asus-wmi: Tell the EC the OS will handle the display off hotkey") causes the backlight to be permanently off on various EeePC laptop models using the eeepc-wmi driver (Asus EeePC 1015BX, Asus EeePC 1025C). The asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL) call added by that commit is made conditional in this commit and only enabled in the quirk_entry structs in the asus-nb-wmi driver fixing the broken display / backlight on various EeePC laptop models. Cc: João Paulo Rechi Vita Fixes: 78f3ac76d9e5 ("platform/x86: asus-wmi: Tell the EC the OS will handle the display off hotkey") Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/asus-nb-wmi.c | 8 ++++++++ drivers/platform/x86/asus-wmi.c | 2 +- drivers/platform/x86/asus-wmi.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 81642102bf65..8d9e30dbb5af 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -65,10 +65,12 @@ static bool asus_q500a_i8042_filter(unsigned char data, unsigned char str, static struct quirk_entry quirk_asus_unknown = { .wapf = 0, + .wmi_backlight_set_devstate = true, }; static struct quirk_entry quirk_asus_q500a = { .i8042_filter = asus_q500a_i8042_filter, + .wmi_backlight_set_devstate = true, }; /* @@ -79,26 +81,32 @@ static struct quirk_entry quirk_asus_q500a = { static struct quirk_entry quirk_asus_x55u = { .wapf = 4, .wmi_backlight_power = true, + .wmi_backlight_set_devstate = true, .no_display_toggle = true, }; static struct quirk_entry quirk_asus_wapf4 = { .wapf = 4, + .wmi_backlight_set_devstate = true, }; static struct quirk_entry quirk_asus_x200ca = { .wapf = 2, + .wmi_backlight_set_devstate = true, }; static struct quirk_entry quirk_asus_ux303ub = { .wmi_backlight_native = true, + .wmi_backlight_set_devstate = true, }; static struct quirk_entry quirk_asus_x550lb = { + .wmi_backlight_set_devstate = true, .xusb2pr = 0x01D9, }; static struct quirk_entry quirk_asus_forceals = { + .wmi_backlight_set_devstate = true, .wmi_force_als_set = true, }; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3e4336025e8f..9b18a184e0aa 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2146,7 +2146,7 @@ static int asus_wmi_add(struct platform_device *pdev) err = asus_wmi_backlight_init(asus); if (err && err != -ENODEV) goto fail_backlight; - } else + } else if (asus->driver->quirks->wmi_backlight_set_devstate) err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL); if (asus_wmi_has_fnlock_key(asus)) { diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 0930be770688..4f31b68642a0 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -31,6 +31,7 @@ struct quirk_entry { bool store_backlight_power; bool wmi_backlight_power; bool wmi_backlight_native; + bool wmi_backlight_set_devstate; bool wmi_force_als_set; int wapf; /* -- cgit v1.2.3 From cb1921b17adbe6509538098ac431033378cd7165 Mon Sep 17 00:00:00 2001 From: Mathew King Date: Mon, 20 May 2019 16:41:24 -0600 Subject: platform/x86: intel-vbtn: Report switch events when event wakes device When a switch event, such as tablet mode/laptop mode or docked/undocked, wakes a device make sure that the value of the swich is reported. Without when a device is put in tablet mode from laptop mode when it is suspended or vice versa the device will wake up but mode will be incorrect. Tested by suspending a device in laptop mode and putting it in tablet mode, the device resumes and is in tablet mode. When suspending the device in tablet mode and putting it in laptop mode the device resumes and is in laptop mode. Signed-off-by: Mathew King Reviewed-by: Jett Rink Reviewed-by: Mario Limonciello Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel-vbtn.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index 06cd7e818ed5..a0d0cecff55f 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -76,12 +76,24 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) struct platform_device *device = context; struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); unsigned int val = !(event & 1); /* Even=press, Odd=release */ - const struct key_entry *ke_rel; + const struct key_entry *ke, *ke_rel; bool autorelease; if (priv->wakeup_mode) { - if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) { + ke = sparse_keymap_entry_from_scancode(priv->input_dev, event); + if (ke) { pm_wakeup_hard_event(&device->dev); + + /* + * Switch events like tablet mode will wake the device + * and report the new switch position to the input + * subsystem. + */ + if (ke->type == KE_SW) + sparse_keymap_report_event(priv->input_dev, + event, + val, + 0); return; } goto out_unknown; -- cgit v1.2.3 From 160da20b254dd4bfc5828f12c208fa831ad4be6c Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 5 Jun 2019 07:51:03 +0000 Subject: platform/x86: mlx-platform: Fix parent device in i2c-mux-reg device registration Fix the issue found while running kernel with the option CONFIG_DEBUG_TEST_DRIVER_REMOVE. Driver 'mlx-platform' registers 'i2c_mlxcpld' device and then registers few underlying 'i2c-mux-reg' devices: priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr, NULL, 0); ... for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { priv->pdev_mux[i] = platform_device_register_resndata( &mlxplat_dev->dev, "i2c-mux-reg", i, NULL, 0, &mlxplat_mux_data[i], sizeof(mlxplat_mux_data[i])); But actual parent of "i2c-mux-reg" device is priv->pdev_i2c->dev and not mlxplat_dev->dev. Patch fixes parent device parameter in a call to platform_device_register_resndata() for "i2c-mux-reg". It solves the race during initialization flow while 'i2c_mlxcpld.1' is removing after probe, while 'i2c-mux-reg.0' is still in probing flow: 'i2c_mlxcpld.1' flow: probe -> remove -> probe. 'i2c-mux-reg.0' flow: probe -> ... [ 12:621096] Registering platform device 'i2c_mlxcpld.1'. Parent at platform [ 12:621117] device: 'i2c_mlxcpld.1': device_add [ 12:621155] bus: 'platform': add device i2c_mlxcpld.1 [ 12:621384] Registering platform device 'i2c-mux-reg.0'. Parent at mlxplat [ 12:621395] device: 'i2c-mux-reg.0': device_add [ 12:621425] bus: 'platform': add device i2c-mux-reg.0 [ 12:621806] Registering platform device 'i2c-mux-reg.1'. Parent at mlxplat [ 12:621828] device: 'i2c-mux-reg.1': device_add [ 12:621892] bus: 'platform': add device i2c-mux-reg.1 [ 12:621906] bus: 'platform': add driver i2c_mlxcpld [ 12:621996] bus: 'platform': driver_probe_device: matched device i2c_mlxcpld.1 with driver i2c_mlxcpld [ 12:622003] bus: 'platform': really_probe: probing driver i2c_mlxcpld with device i2c_mlxcpld.1 [ 12:622100] i2c_mlxcpld i2c_mlxcpld.1: no default pinctrl state [ 12:622293] device: 'i2c-1': device_add [ 12:627280] bus: 'i2c': add device i2c-1 [ 12:627692] device: 'i2c-1': device_add [ 12.629639] bus: 'platform': add driver i2c-mux-reg [ 12.629718] bus: 'platform': driver_probe_device: matched device i2c-mux-reg.0 with driver i2c-mux-reg [ 12.629723] bus: 'platform': really_probe: probing driver i2c-mux-reg with device i2c-mux-reg.0 [ 12.629818] i2c-mux-reg i2c-mux-reg.0: no default pinctrl state [ 12.629981] platform i2c-mux-reg.0: Driver i2c-mux-reg requests probe deferral [ 12.629986] platform i2c-mux-reg.0: Added to deferred list [ 12.629992] bus: 'platform': driver_probe_device: matched device i2c-mux-reg.1 with driver i2c-mux-reg [ 12.629997] bus: 'platform': really_probe: probing driver i2c-mux-reg with device i2c-mux-reg.1 [ 12.630091] i2c-mux-reg i2c-mux-reg.1: no default pinctrl state [ 12.630247] platform i2c-mux-reg.1: Driver i2c-mux-reg requests probe deferral [ 12.630252] platform i2c-mux-reg.1: Added to deferred list [ 12.640892] devices_kset: Moving i2c-mux-reg.0 to end of list [ 12.640900] platform i2c-mux-reg.0: Retrying from deferred list [ 12.640911] bus: 'platform': driver_probe_device: matched device i2c-mux-reg.0 with driver i2c-mux-reg [ 12.640919] bus: 'platform': really_probe: probing driver i2c-mux-reg with device i2c-mux-reg.0 [ 12.640999] i2c-mux-reg i2c-mux-reg.0: no default pinctrl state [ 12.641177] platform i2c-mux-reg.0: Driver i2c-mux-reg requests probe deferral [ 12.641187] platform i2c-mux-reg.0: Added to deferred list [ 12.641198] devices_kset: Moving i2c-mux-reg.1 to end of list [ 12.641219] platform i2c-mux-reg.1: Retrying from deferred list [ 12.641237] bus: 'platform': driver_probe_device: matched device i2c-mux-reg.1 with driver i2c-mux-reg [ 12.641247] bus: 'platform': really_probe: probing driver i2c-mux-reg with device i2c-mux-reg.1 [ 12.641331] i2c-mux-reg i2c-mux-reg.1: no default pinctrl state [ 12.641465] platform i2c-mux-reg.1: Driver i2c-mux-reg requests probe deferral [ 12.641469] platform i2c-mux-reg.1: Added to deferred list [ 12.646427] device: 'i2c-1': device_add [ 12.646647] bus: 'i2c': add device i2c-1 [ 12.647104] device: 'i2c-1': device_add [ 12.669231] devices_kset: Moving i2c-mux-reg.0 to end of list [ 12.669240] platform i2c-mux-reg.0: Retrying from deferred list [ 12.669258] bus: 'platform': driver_probe_device: matched device i2c-mux-reg.0 with driver i2c-mux-reg [ 12.669263] bus: 'platform': really_probe: probing driver i2c-mux-reg with device i2c-mux-reg.0 [ 12.669343] i2c-mux-reg i2c-mux-reg.0: no default pinctrl state [ 12.669585] device: 'i2c-2': device_add [ 12.669795] bus: 'i2c': add device i2c-2 [ 12.670201] device: 'i2c-2': device_add [ 12.671427] i2c i2c-1: Added multiplexed i2c bus 2 [ 12.671514] device: 'i2c-3': device_add [ 12.671724] bus: 'i2c': add device i2c-3 [ 12.672136] device: 'i2c-3': device_add [ 12.673378] i2c i2c-1: Added multiplexed i2c bus 3 [ 12.673472] device: 'i2c-4': device_add [ 12.673676] bus: 'i2c': add device i2c-4 [ 12.674060] device: 'i2c-4': device_add [ 12.675861] i2c i2c-1: Added multiplexed i2c bus 4 [ 12.675941] device: 'i2c-5': device_add [ 12.676150] bus: 'i2c': add device i2c-5 [ 12.676550] device: 'i2c-5': device_add [ 12.678103] i2c i2c-1: Added multiplexed i2c bus 5 [ 12.678193] device: 'i2c-6': device_add [ 12.678395] bus: 'i2c': add device i2c-6 [ 12.678774] device: 'i2c-6': device_add [ 12.679969] i2c i2c-1: Added multiplexed i2c bus 6 [ 12.680065] device: 'i2c-7': device_add [ 12.680275] bus: 'i2c': add device i2c-7 [ 12.680913] device: 'i2c-7': device_add [ 12.682506] i2c i2c-1: Added multiplexed i2c bus 7 [ 12.682600] device: 'i2c-8': device_add [ 12.682808] bus: 'i2c': add device i2c-8 [ 12.683189] device: 'i2c-8': device_add [ 12.683907] device: 'i2c-1': device_unregister [ 12.683945] device: 'i2c-1': device_unregister [ 12.684387] device: 'i2c-1': device_create_release [ 12.684536] bus: 'i2c': remove device i2c-1 [ 12.686019] i2c i2c-8: Failed to create compatibility class link [ 12.686086] ------------[ cut here ]------------ [ 12.686087] can't create symlink to mux device [ 12.686224] Workqueue: events deferred_probe_work_func [ 12.686135] WARNING: CPU: 7 PID: 436 at drivers/i2c/i2c-mux.c:416 i2c_mux_add_adapter+0x729/0x7d0 [i2c_mux] [ 12.686232] RIP: 0010:i2c_mux_add_adapter+0x729/0x7d0 [i2c_mux] [ 0x190/0x190 [i2c_mux] [ 12.686300] ? i2c_mux_alloc+0xac/0x110 [i2c_mux] [ 12.686306] ? i2c_mux_reg_set+0x200/0x200 [i2c_mux_reg] [ 12.686313] i2c_mux_reg_probe+0x22c/0x731 [i2c_mux_reg] [ 12.686322] ? i2c_mux_reg_deselect+0x60/0x60 [i2c_mux_reg] [ 12.686346] platform_drv_probe+0xa8/0x110 [ 12.686351] really_probe+0x185/0x720 [ 12.686358] driver_probe_device+0xdf/0x1f0 ... [ 12.686522] i2c i2c-1: Added multiplexed i2c bus 8 [ 12.686621] device: 'i2c-9': device_add [ 12.686626] kobject_add_internal failed for i2c-9 (error: -2 parent: i2c-1) [ 12.694729] i2c-core: adapter 'i2c-1-mux (chan_id 8)': can't register device (-2) [ 12.705726] i2c i2c-1: failed to add mux-adapter 8 as bus 9 (error=-2) [ 12.714494] device: 'i2c-8': device_unregister [ 12.714537] device: 'i2c-8': device_unregister Fixes: 6613d18e9038 ("platform/x86: mlx-platform: Move module from arch/x86") Signed-off-by: Vadim Pasternak Signed-off-by: Andy Shevchenko --- drivers/platform/x86/mlx-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index cee039f57499..983f02b5b106 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -2032,7 +2032,7 @@ static int __init mlxplat_init(void) for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { priv->pdev_mux[i] = platform_device_register_resndata( - &mlxplat_dev->dev, + &priv->pdev_i2c->dev, "i2c-mux-reg", i, NULL, 0, &mlxplat_mux_data[i], sizeof(mlxplat_mux_data[i])); -- cgit v1.2.3 From 8c2eb7b6468ad4aa5600aed01aa0715f921a3f8b Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Thu, 23 May 2019 16:41:52 +0000 Subject: platform/mellanox: mlxreg-hotplug: Add devm_free_irq call to remove flow Add devm_free_irq() call to mlxreg-hotplug remove() for clean release of devices irq resource. Fix debugobjects warning triggered by rmmod It prevents of use-after-free memory, related to mlxreg_hotplug_work_handler. Issue has been reported as debugobjects warning triggered by 'rmmod mlxtreg-hotplug' flow, while running kernel with CONFIG_DEBUG_OBJECTS* options. [ 2489.623551] ODEBUG: free active (active state 0) object type: work_struct hint: mlxreg_hotplug_work_handler+0x0/0x7f0 [mlxreg_hotplug] [ 2489.637097] WARNING: CPU: 5 PID: 3924 at lib/debugobjects.c:328 debug_print_object+0xfe/0x180 [ 2489.637165] RIP: 0010:debug_print_object+0xfe/0x180 ? [ 2489.637214] Call Trace: [ 2489.637225] __debug_check_no_obj_freed+0x25e/0x320 [ 2489.637231] kfree+0x82/0x110 [ 2489.637238] release_nodes+0x33c/0x4e0 [ 2489.637242] ? devres_remove_group+0x1b0/0x1b0 [ 2489.637247] device_release_driver_internal+0x146/0x270 [ 2489.637251] driver_detach+0x73/0xe0 [ 2489.637254] bus_remove_driver+0xa1/0x170 [ 2489.637261] __x64_sys_delete_module+0x29e/0x320 [ 2489.637265] ? __ia32_sys_delete_module+0x320/0x320 [ 2489.637268] ? blkcg_exit_queue+0x20/0x20 [ 2489.637273] ? task_work_run+0x7d/0x100 [ 2489.637278] ? exit_to_usermode_loop+0x5b/0xf0 [ 2489.637281] do_syscall_64+0x73/0x160 [ 2489.637287] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 2489.637290] RIP: 0033:0x7f95c3596fd7 The difference in release flow with and with no devm_free_irq is listed below: bus: 'platform': remove driver mlxreg-hotplug mlxreg_hotplug_remove(start) -> devm_free_irq (with new code) mlxreg_hotplug_remove (end) release_nodes (start) mlxreg-hotplug: DEVRES REL devm_hwmon_release (8 bytes) device: 'hwmon3': device_unregister PM: Removing info for No Bus:hwmon3 mlxreg-hotplug: DEVRES REL devm_kzalloc_release (88 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (6 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (5 bytes) mlxreg-hotplug: DEVRES REL devm_irq_release (16 bytes) (no new code) mlxreg-hotplug: DEVRES REL devm_kzalloc_release (1376 bytes) ------------[ cut here ]------------ (no new code): ODEBUG: free active (active state 0) object type: work_struct hint: mlxreg_hotplug_work_handler release_nodes(end) driver: 'mlxreg-hotplug': driver_release Fixes: 1f976f6978bf ("platform/x86: Move Mellanox platform hotplug driver to platform/mellanox") Signed-off-by: Vadim Pasternak Signed-off-by: Andy Shevchenko --- drivers/platform/mellanox/mlxreg-hotplug.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index 687ce6817d0d..f85a1b9d129b 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -694,6 +694,7 @@ static int mlxreg_hotplug_remove(struct platform_device *pdev) /* Clean interrupts setup. */ mlxreg_hotplug_unset_irq(priv); + devm_free_irq(&pdev->dev, priv->irq, priv); return 0; } -- cgit v1.2.3 From 56a2b7f2a39a8d4b16a628e113decde3d7400879 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 7 Jun 2019 14:05:12 +0300 Subject: drm/edid: abstract override/firmware EDID retrieval Abstract the debugfs override and the firmware EDID retrieval function. We'll be needing it in the follow-up. No functional changes. Cc: Daniel Vetter Cc: Harish Chegondi Reviewed-by: Daniel Vetter Tested-by: Tested-by: Paul Wise Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20190607110513.12072-1-jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 649cfd8b4200..c1952b6e5747 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1570,6 +1570,20 @@ static void connector_bad_edid(struct drm_connector *connector, } } +/* Get override or firmware EDID */ +static struct edid *drm_get_override_edid(struct drm_connector *connector) +{ + struct edid *override = NULL; + + if (connector->override_edid) + override = drm_edid_duplicate(connector->edid_blob_ptr->data); + + if (!override) + override = drm_load_edid_firmware(connector); + + return IS_ERR(override) ? NULL : override; +} + /** * drm_do_get_edid - get EDID data using a custom EDID block read function * @connector: connector we're probing @@ -1597,15 +1611,10 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, { int i, j = 0, valid_extensions = 0; u8 *edid, *new; - struct edid *override = NULL; - - if (connector->override_edid) - override = drm_edid_duplicate(connector->edid_blob_ptr->data); - - if (!override) - override = drm_load_edid_firmware(connector); + struct edid *override; - if (!IS_ERR_OR_NULL(override)) + override = drm_get_override_edid(connector); + if (override) return override; if ((edid = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) -- cgit v1.2.3 From ca21f851cc9643af049226d57fabc3c883ea648e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 11 Jun 2019 17:48:18 +0100 Subject: i2c: acorn: fix i2c warning The Acorn i2c driver (for RiscPC) triggers the "i2c adapter has no name" warning in the I2C core driver, resulting in the RTC being inaccessible. Fix this. Fixes: 2236baa75f70 ("i2c: Sanity checks on adapter registration") Signed-off-by: Russell King Signed-off-by: Wolfram Sang Cc: stable@kernel.org --- drivers/i2c/busses/i2c-acorn.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c index f4a5ae69bf6a..fa3763e4b3ee 100644 --- a/drivers/i2c/busses/i2c-acorn.c +++ b/drivers/i2c/busses/i2c-acorn.c @@ -81,6 +81,7 @@ static struct i2c_algo_bit_data ioc_data = { static struct i2c_adapter ioc_ops = { .nr = 0, + .name = "ioc", .algo_data = &ioc_data, }; -- cgit v1.2.3 From 48eaeb7664c76139438724d520a1ea4a84a3ed92 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 10 Jun 2019 12:30:54 +0300 Subject: drm: add fallback override/firmware EDID modes workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've moved the override and firmware EDID (simply "override EDID" from now on) handling to the low level drm_do_get_edid() function in order to transparently use the override throughout the stack. The idea is that you get the override EDID via the ->get_modes() hook. Unfortunately, there are scenarios where the DDC probe in drm_get_edid() called via ->get_modes() fails, although the preceding ->detect() succeeds. In the case reported by Paul Wise, the ->detect() hook, intel_crt_detect(), relies on hotplug detect, bypassing the DDC. In the case reported by Ilpo Järvinen, there is no ->detect() hook, which is interpreted as connected. The subsequent DDC probe reached via ->get_modes() fails, and we don't even look at the override EDID, resulting in no modes being added. Because drm_get_edid() is used via ->detect() all over the place, we can't trivially remove the DDC probe, as it leads to override EDID effectively meaning connector forcing. The goal is that connector forcing and override EDID remain orthogonal. Generally, the underlying problem here is the conflation of ->detect() and ->get_modes() via drm_get_edid(). The former should just detect, and the latter should just get the modes, typically via reading the EDID. As long as drm_get_edid() is used in ->detect(), it needs to retain the DDC probe. Or such users need to have a separate DDC probe step first. The EDID caching between ->detect() and ->get_modes() done by some drivers is a further complication that prevents us from making drm_do_get_edid() adapt to the two cases. Work around the regression by falling back to a separate attempt at getting the override EDID at drm_helper_probe_single_connector_modes() level. With a working DDC and override EDID, it'll never be called; the override EDID will come via ->get_modes(). There will still be a failing DDC probe attempt in the cases that require the fallback. v2: - Call drm_connector_update_edid_property (Paul) - Update commit message about EDID caching (Daniel) Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=107583 Reported-by: Paul Wise Cc: Paul Wise References: http://mid.mail-archive.com/alpine.DEB.2.20.1905262211270.24390@whs-18.cs.helsinki.fi Reported-by: Ilpo Järvinen Cc: Ilpo Järvinen Suggested-by: Daniel Vetter References: 15f080f08d48 ("drm/edid: respect connector force for drm_get_edid ddc probe") Fixes: 53fd40a90f3c ("drm: handle override and firmware EDID at drm_do_get_edid() level") Cc: # v4.15+ 56a2b7f2a39a drm/edid: abstract override/firmware EDID retrieval Cc: # v4.15+ Cc: Daniel Vetter Cc: Ville Syrjälä Cc: Harish Chegondi Tested-by: Paul Wise Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20190610093054.28445-1-jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_probe_helper.c | 7 +++++++ 2 files changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c1952b6e5747..e804ac5dec02 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1584,6 +1584,36 @@ static struct edid *drm_get_override_edid(struct drm_connector *connector) return IS_ERR(override) ? NULL : override; } +/** + * drm_add_override_edid_modes - add modes from override/firmware EDID + * @connector: connector we're probing + * + * Add modes from the override/firmware EDID, if available. Only to be used from + * drm_helper_probe_single_connector_modes() as a fallback for when DDC probe + * failed during drm_get_edid() and caused the override/firmware EDID to be + * skipped. + * + * Return: The number of modes added or 0 if we couldn't find any. + */ +int drm_add_override_edid_modes(struct drm_connector *connector) +{ + struct edid *override; + int num_modes = 0; + + override = drm_get_override_edid(connector); + if (override) { + drm_connector_update_edid_property(connector, override); + num_modes = drm_add_edid_modes(connector, override); + kfree(override); + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n", + connector->base.id, connector->name, num_modes); + } + + return num_modes; +} +EXPORT_SYMBOL(drm_add_override_edid_modes); + /** * drm_do_get_edid - get EDID data using a custom EDID block read function * @connector: connector we're probing diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6fd08e04b323..dd427c7ff967 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -479,6 +479,13 @@ retry: count = (*connector_funcs->get_modes)(connector); + /* + * Fallback for when DDC probe failed in drm_get_edid() and thus skipped + * override/firmware EDID. + */ + if (count == 0 && connector->status == connector_status_connected) + count = drm_add_override_edid_modes(connector); + if (count == 0 && connector->status == connector_status_connected) count = drm_add_modes_noedid(connector, 1024, 768); count += drm_helper_probe_add_cmdline_mode(connector); -- cgit v1.2.3 From a0cac264a86fbf4d6cb201fbbb73c1d335e3248a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 30 May 2019 22:24:24 +0200 Subject: i2c: pca-platform: Fix GPIO lookup code The devm_gpiod_request_gpiod() call will add "-gpios" to any passed connection ID before looking it up. I do not think the reset GPIO on this platform is named "reset-gpios-gpios" but rather "reset-gpios" in the device tree, so fix this up so that we get a proper reset GPIO handle. Also drop the inclusion of the legacy GPIO header. Fixes: 0e8ce93bdceb ("i2c: pca-platform: add devicetree awareness") Signed-off-by: Linus Walleij Reviewed-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-pca-platform.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c index de3fe6e828cb..f50afa8e3cba 100644 --- a/drivers/i2c/busses/i2c-pca-platform.c +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -173,7 +172,7 @@ static int i2c_pca_pf_probe(struct platform_device *pdev) i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.of_node = np; - i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW); + i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(i2c->gpio)) return PTR_ERR(i2c->gpio); -- cgit v1.2.3 From 5d6168fc61b7f13baf27ae5567be7ea1fccb463e Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Fri, 24 May 2019 10:10:25 +0100 Subject: clocksource/drivers/arm_arch_timer: Don't trace count reader functions With v5.2-rc1, The ftrace functions_graph tracer locks up whenever it is enabled on arm64. Since commit 0ea415390cd3 ("clocksource/arm_arch_timer: Use arch_timer_read_counter to access stable counters") a function pointer is consistently used to read the counter instead of potentially referencing an inlinable function. The graph tracers relies on accessing the timer counters to compute the time spent in functions which causes the lockup when attempting to trace these code paths. Annotate the arm arch timer counter accessors as notrace. Fixes: 0ea415390cd3 ("clocksource/arm_arch_timer: Use arch_timer_read_counter to access stable counters") Signed-off-by: Julien Thierry Cc: Marc Zyngier Cc: Mark Rutland Cc: Daniel Lezcano Cc: Thomas Gleixner Cc: Steven Rostedt Acked-by: Marc Zyngier Signed-off-by: Daniel Lezcano --- drivers/clocksource/arm_arch_timer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index b2a951a798e2..5c69c9a9a6a4 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -149,22 +149,22 @@ u32 arch_timer_reg_read(int access, enum arch_timer_reg reg, return val; } -static u64 arch_counter_get_cntpct_stable(void) +static notrace u64 arch_counter_get_cntpct_stable(void) { return __arch_counter_get_cntpct_stable(); } -static u64 arch_counter_get_cntpct(void) +static notrace u64 arch_counter_get_cntpct(void) { return __arch_counter_get_cntpct(); } -static u64 arch_counter_get_cntvct_stable(void) +static notrace u64 arch_counter_get_cntvct_stable(void) { return __arch_counter_get_cntvct_stable(); } -static u64 arch_counter_get_cntvct(void) +static notrace u64 arch_counter_get_cntvct(void) { return __arch_counter_get_cntvct(); } -- cgit v1.2.3 From 5f54a85db5df67df8161739a4b2f9c2b7ab219fe Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 12 Jun 2019 17:15:40 +0300 Subject: usb: typec: Make sure an alt mode exist before getting its partner Adding check to typec_altmode_get_partner() to prevent potential NULL pointer dereference. Reported-by: Vladimir Yerilov Fixes: ad74b8649bea ("usb: typec: ucsi: Preliminary support for alternate modes") Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 76299b6ff06d..74cb3c2ecb34 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -192,7 +192,7 @@ EXPORT_SYMBOL_GPL(typec_altmode_vdm); const struct typec_altmode * typec_altmode_get_partner(struct typec_altmode *adev) { - return &to_altmode(adev)->partner->adev; + return adev ? &to_altmode(adev)->partner->adev : NULL; } EXPORT_SYMBOL_GPL(typec_altmode_get_partner); -- cgit v1.2.3 From 99f304beb82a86713dc4bc1fd65b8dfda25b1f5e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 8 Jun 2019 12:23:57 +0300 Subject: drm/amdgpu: Fix bounds checking in amdgpu_ras_is_supported() The "block" variable can be set by the user through debugfs, so it can be quite large which leads to shift wrapping here. This means we report a "block" as supported when it's not, and that leads to array overflows later on. This bug is not really a security issue in real life, because debugfs is generally root only. Fixes: 36ea1bd2d084 ("drm/amdgpu: add debugfs ctrl node") Signed-off-by: Dan Carpenter Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h index eaef5edefc34..24c6e5fcda86 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h @@ -172,6 +172,8 @@ static inline int amdgpu_ras_is_supported(struct amdgpu_device *adev, { struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); + if (block >= AMDGPU_RAS_BLOCK_COUNT) + return 0; return ras && (ras->supported & (1 << block)); } -- cgit v1.2.3 From f3a5231c8f14acd42845e9e60f506b4e948f0e68 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 11 Jun 2019 09:45:51 -0500 Subject: drm/amdgpu: return 0 by default in amdgpu_pm_load_smu_firmware Fixes SI cards running on amdgpu. Fixes: 1929059893022 ("drm/amd/amdgpu: add RLC firmware to support raven1 refresh") Bug: https://bugs.freedesktop.org/show_bug.cgi?id=110883 Reviewed-by: Evan Quan Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c index 039cfa2ec89d..abeaab4bf1bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c @@ -2492,7 +2492,7 @@ void amdgpu_pm_print_power_states(struct amdgpu_device *adev) int amdgpu_pm_load_smu_firmware(struct amdgpu_device *adev, uint32_t *smu_version) { - int r = -EINVAL; + int r; if (adev->powerplay.pp_funcs && adev->powerplay.pp_funcs->load_firmware) { r = adev->powerplay.pp_funcs->load_firmware(adev->powerplay.pp_handle); @@ -2502,7 +2502,7 @@ int amdgpu_pm_load_smu_firmware(struct amdgpu_device *adev, uint32_t *smu_versio } *smu_version = adev->pm.fw_version; } - return r; + return 0; } int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) -- cgit v1.2.3 From 154085ffdcc6875851b80a5d2bd759bbf687b001 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Tue, 11 Jun 2019 15:10:17 -0700 Subject: null_blk: remove duplicate check for report zone This patch removes the check in the null_blk_zoned for report zone command, where it checks for the dev-,>zoned before executing the report zone. The null_zone_report() function is a block_device operation callback which is initialized in the null_blk_main.c and gets called as a part of blkdev for report zone IOCTL (BLKREPORTZONE). blkdev_ioctl() blkdev_report_zones_ioctl() blkdev_report_zones() blk_report_zones() disk->fops->report_zones() nullb_zone_report(); The null_zone_report() will never get executed on the non-zoned block device, in the non zoned block device blk_queue_is_zoned() will always be false which is first check the blkdev_report_zones_ioctl() before actual low level driver report zone callback is executed. Here is the detailed scenario:- 1. modprobe null_blk null_init null_alloc_dev dev->zoned = 0 null_add_dev dev->zoned == 0 so we don't set the q->limits.zoned = BLK_ZONED_HR 2. blkzone report /dev/nullb0 blkdev_ioctl() blkdev_report_zones_ioctl() blk_queue_is_zoned() blk_queue_is_zoned q->limits.zoned == 0 return false if (!blk_queue_is_zoned(q)) <--- true return -ENOTTY; Reviewed-by: Damien Le Moal Reviewed-by: Bob Liu Signed-off-by: Chaitanya Kulkarni Signed-off-by: Jens Axboe --- drivers/block/null_blk_zoned.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index 5d1c261a2cfd..fca0c97ff1aa 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -74,10 +74,6 @@ int null_zone_report(struct gendisk *disk, sector_t sector, struct nullb_device *dev = nullb->dev; unsigned int zno, nrz = 0; - if (!dev->zoned) - /* Not a zoned null device */ - return -EOPNOTSUPP; - zno = null_zone_no(dev, sector); if (zno < dev->nr_zones) { nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno); -- cgit v1.2.3 From 31b90956b124240aa8c63250243ae1a53585c5e2 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Mon, 10 Jun 2019 06:13:34 +0800 Subject: bcache: fix stack corruption by PRECEDING_KEY() Recently people report bcache code compiled with gcc9 is broken, one of the buggy behavior I observe is that two adjacent 4KB I/Os should merge into one but they don't. Finally it turns out to be a stack corruption caused by macro PRECEDING_KEY(). See how PRECEDING_KEY() is defined in bset.h, 437 #define PRECEDING_KEY(_k) \ 438 ({ \ 439 struct bkey *_ret = NULL; \ 440 \ 441 if (KEY_INODE(_k) || KEY_OFFSET(_k)) { \ 442 _ret = &KEY(KEY_INODE(_k), KEY_OFFSET(_k), 0); \ 443 \ 444 if (!_ret->low) \ 445 _ret->high--; \ 446 _ret->low--; \ 447 } \ 448 \ 449 _ret; \ 450 }) At line 442, _ret points to address of a on-stack variable combined by KEY(), the life range of this on-stack variable is in line 442-446, once _ret is returned to bch_btree_insert_key(), the returned address points to an invalid stack address and this address is overwritten in the following called bch_btree_iter_init(). Then argument 'search' of bch_btree_iter_init() points to some address inside stackframe of bch_btree_iter_init(), exact address depends on how the compiler allocates stack space. Now the stack is corrupted. Fixes: 0eacac22034c ("bcache: PRECEDING_KEY()") Signed-off-by: Coly Li Reviewed-by: Rolf Fokkens Reviewed-by: Pierre JUHEN Tested-by: Shenghui Wang Tested-by: Pierre JUHEN Cc: Kent Overstreet Cc: Nix Cc: stable@vger.kernel.org Signed-off-by: Jens Axboe --- drivers/md/bcache/bset.c | 16 +++++++++++++--- drivers/md/bcache/bset.h | 34 ++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c index 8f07fa6e1739..268f1b685084 100644 --- a/drivers/md/bcache/bset.c +++ b/drivers/md/bcache/bset.c @@ -887,12 +887,22 @@ unsigned int bch_btree_insert_key(struct btree_keys *b, struct bkey *k, struct bset *i = bset_tree_last(b)->data; struct bkey *m, *prev = NULL; struct btree_iter iter; + struct bkey preceding_key_on_stack = ZERO_KEY; + struct bkey *preceding_key_p = &preceding_key_on_stack; BUG_ON(b->ops->is_extents && !KEY_SIZE(k)); - m = bch_btree_iter_init(b, &iter, b->ops->is_extents - ? PRECEDING_KEY(&START_KEY(k)) - : PRECEDING_KEY(k)); + /* + * If k has preceding key, preceding_key_p will be set to address + * of k's preceding key; otherwise preceding_key_p will be set + * to NULL inside preceding_key(). + */ + if (b->ops->is_extents) + preceding_key(&START_KEY(k), &preceding_key_p); + else + preceding_key(k, &preceding_key_p); + + m = bch_btree_iter_init(b, &iter, preceding_key_p); if (b->ops->insert_fixup(b, k, &iter, replace_key)) return status; diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h index bac76aabca6d..c71365e7c1fa 100644 --- a/drivers/md/bcache/bset.h +++ b/drivers/md/bcache/bset.h @@ -434,20 +434,26 @@ static inline bool bch_cut_back(const struct bkey *where, struct bkey *k) return __bch_cut_back(where, k); } -#define PRECEDING_KEY(_k) \ -({ \ - struct bkey *_ret = NULL; \ - \ - if (KEY_INODE(_k) || KEY_OFFSET(_k)) { \ - _ret = &KEY(KEY_INODE(_k), KEY_OFFSET(_k), 0); \ - \ - if (!_ret->low) \ - _ret->high--; \ - _ret->low--; \ - } \ - \ - _ret; \ -}) +/* + * Pointer '*preceding_key_p' points to a memory object to store preceding + * key of k. If the preceding key does not exist, set '*preceding_key_p' to + * NULL. So the caller of preceding_key() needs to take care of memory + * which '*preceding_key_p' pointed to before calling preceding_key(). + * Currently the only caller of preceding_key() is bch_btree_insert_key(), + * and it points to an on-stack variable, so the memory release is handled + * by stackframe itself. + */ +static inline void preceding_key(struct bkey *k, struct bkey **preceding_key_p) +{ + if (KEY_INODE(k) || KEY_OFFSET(k)) { + (**preceding_key_p) = KEY(KEY_INODE(k), KEY_OFFSET(k), 0); + if (!(*preceding_key_p)->low) + (*preceding_key_p)->high--; + (*preceding_key_p)->low--; + } else { + (*preceding_key_p) = NULL; + } +} static inline bool bch_ptr_invalid(struct btree_keys *b, const struct bkey *k) { -- cgit v1.2.3 From 1f0ffa67349c56ea54c03ccfd1e073c990e7411e Mon Sep 17 00:00:00 2001 From: Coly Li Date: Mon, 10 Jun 2019 06:13:35 +0800 Subject: bcache: only set BCACHE_DEV_WB_RUNNING when cached device attached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When people set a writeback percent via sysfs file, /sys/block/bcache/bcache/writeback_percent current code directly sets BCACHE_DEV_WB_RUNNING to dc->disk.flags and schedules kworker dc->writeback_rate_update. If there is no cache set attached to, the writeback kernel thread is not running indeed, running dc->writeback_rate_update does not make sense and may cause NULL pointer deference when reference cache set pointer inside update_writeback_rate(). This patch checks whether the cache set point (dc->disk.c) is NULL in sysfs interface handler, and only set BCACHE_DEV_WB_RUNNING and schedule dc->writeback_rate_update when dc->disk.c is not NULL (it means the cache device is attached to a cache set). This problem might be introduced from initial bcache commit, but commit 3fd47bfe55b0 ("bcache: stop dc->writeback_rate_update properly") changes part of the original code piece, so I add 'Fixes: 3fd47bfe55b0' to indicate from which commit this patch can be applied. Fixes: 3fd47bfe55b0 ("bcache: stop dc->writeback_rate_update properly") Reported-by: Bjørn Forsman Signed-off-by: Coly Li Reviewed-by: Bjørn Forsman Cc: stable@vger.kernel.org Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 6cd44d3cf906..bfb437ffb13c 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -431,8 +431,13 @@ STORE(bch_cached_dev) bch_writeback_queue(dc); } + /* + * Only set BCACHE_DEV_WB_RUNNING when cached device attached to + * a cache set, otherwise it doesn't make sense. + */ if (attr == &sysfs_writeback_percent) - if (!test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags)) + if ((dc->disk.c != NULL) && + (!test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))) schedule_delayed_work(&dc->writeback_rate_update, dc->writeback_rate_update_seconds * HZ); -- cgit v1.2.3 From 31f6264e225fb92cf6f4b63031424f20797c297d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Jun 2019 16:32:59 +0200 Subject: libata: Extend quirks for the ST1000LM024 drives with NOLPM quirk We've received a bugreport that using LPM with ST1000LM024 drives leads to system lockups. So it seems that these models are buggy in more then 1 way. Add NOLPM quirk to the existing quirks entry for BROKEN_FPDMA_AA. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1571330 Cc: stable@vger.kernel.org Reviewed-by: Martin K. Petersen Signed-off-by: Hans de Goede Signed-off-by: Jens Axboe --- drivers/ata/libata-core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index aaa57e0c809d..4a2dff303865 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4460,9 +4460,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "ST3320[68]13AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, - /* drives which fail FPDMA_AA activation (some may freeze afterwards) */ - { "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA }, - { "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA }, + /* drives which fail FPDMA_AA activation (some may freeze afterwards) + the ST disks also have LPM issues */ + { "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA | + ATA_HORKAGE_NOLPM, }, + { "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA | + ATA_HORKAGE_NOLPM, }, { "VB0250EAVER", "HPG7", ATA_HORKAGE_BROKEN_FPDMA_AA }, /* Blacklist entries taken from Silicon Image 3124/3132 -- cgit v1.2.3 From 1d0c06513bd44e724f572ef9c932d0c889d183c6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 13 Jun 2019 09:30:06 +0200 Subject: block/ps3vram: Use %llu to format sector_t after LBDAF removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The removal of CONFIG_LBDAF changed the type of sector_t from "unsigned long" to "u64" aka "unsigned long long" on 64-bit platforms, leading to a compiler warning regression: drivers/block/ps3vram.c: In function ‘ps3vram_probe’: drivers/block/ps3vram.c:770:23: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 4 has type ‘sector_t {aka long long unsigned int}’ [-Wformat=] Fix this by using "%llu" instead. Fixes: 72deb455b5ec619f ("block: remove CONFIG_LBDAF") Signed-off-by: Geert Uytterhoeven Signed-off-by: Jens Axboe --- drivers/block/ps3vram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 4c7f51b1eda9..4628e1a27a2b 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -767,7 +767,7 @@ static int ps3vram_probe(struct ps3_system_bus_device *dev) strlcpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name)); set_capacity(gendisk, priv->size >> 9); - dev_info(&dev->core, "%s: Using %lu MiB of GPU memory\n", + dev_info(&dev->core, "%s: Using %llu MiB of GPU memory\n", gendisk->disk_name, get_capacity(gendisk) >> 11); device_add_disk(&dev->core, gendisk, NULL); -- cgit v1.2.3 From 2374b682255184d7ef75fcb507ce5af4995ead32 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 13 Jun 2019 15:56:18 -0700 Subject: drivers/base/devres: introduce devm_release_action() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "mm/devm_memremap_pages: Fix page release race", v2. Logan audited the devm_memremap_pages() shutdown path and noticed that it was possible to proceed to arch_remove_memory() before all potential page references have been reaped. Introduce a new ->cleanup() callback to do the work of waiting for any straggling page references and then perform the percpu_ref_exit() in devm_memremap_pages_release() context. For p2pdma this involves some deeper reworks to reference count resources on a per-instance basis rather than a per pci-device basis. A modified genalloc api is introduced to convey a driver-private pointer through gen_pool_{alloc,free}() interfaces. Also, a devm_memunmap_pages() api is introduced since p2pdma does not auto-release resources on a setup failure. The dax and pmem changes pass the nvdimm unit tests, and the p2pdma changes should now pass testing with the pci_p2pdma_release() fix. Jrme, how does this look for HMM? This patch (of 6): The devm_add_action() facility allows a resource allocation routine to add custom devm semantics. One such user is devm_memremap_pages(). There is now a need to manually trigger devm_memremap_pages_release(). Introduce devm_release_action() so the release action can be triggered via a new devm_memunmap_pages() api in a follow-on change. Link: http://lkml.kernel.org/r/155727336530.292046.2926860263201336366.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams Reviewed-by: Ira Weiny Reviewed-by: Logan Gunthorpe Cc: Bjorn Helgaas Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: "Jérôme Glisse" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/devres.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index e038e2b3b7ea..0bbb328bd17f 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -755,10 +755,32 @@ void devm_remove_action(struct device *dev, void (*action)(void *), void *data) WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match, &devres)); - } EXPORT_SYMBOL_GPL(devm_remove_action); +/** + * devm_release_action() - release previously added custom action + * @dev: Device that owns the action + * @action: Function implementing the action + * @data: Pointer to data passed to @action implementation + * + * Releases and removes instance of @action previously added by + * devm_add_action(). Both action and data should match one of the + * existing entries. + */ +void devm_release_action(struct device *dev, void (*action)(void *), void *data) +{ + struct action_devres devres = { + .data = data, + .action = action, + }; + + WARN_ON(devres_release(dev, devm_action_release, devm_action_match, + &devres)); + +} +EXPORT_SYMBOL_GPL(devm_release_action); + /* * Managed kmalloc/kfree */ -- cgit v1.2.3 From e615a191216e3fb4e9c0d239007f2b0cd48f28bf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 13 Jun 2019 15:56:24 -0700 Subject: PCI/P2PDMA: fix the gen_pool_add_virt() failure path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pci_p2pdma_add_resource() implementation immediately frees the pgmap if gen_pool_add_virt() fails. However, that means that when @dev triggers a devres release devm_memremap_pages_release() will crash trying to access the freed @pgmap. Use the new devm_memunmap_pages() to manually free the mapping in the error path. Link: http://lkml.kernel.org/r/155727337603.292046.13101332703665246702.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams Fixes: 52916982af48 ("PCI/P2PDMA: Support peer-to-peer memory") Reviewed-by: Ira Weiny Acked-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: "Jérôme Glisse" Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pci/p2pdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 742928d0053e..d5736b31ce66 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -208,13 +208,15 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, pci_bus_address(pdev, bar) + offset, resource_size(&pgmap->res), dev_to_node(&pdev->dev)); if (error) - goto pgmap_free; + goto pages_free; pci_info(pdev, "added peer-to-peer DMA memory %pR\n", &pgmap->res); return 0; +pages_free: + devm_memunmap_pages(&pdev->dev, pgmap); pgmap_free: devm_kfree(&pdev->dev, pgmap); return error; -- cgit v1.2.3 From 1570175abd164b32ea1cc677f9dfd2dc6bd093f5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 13 Jun 2019 15:56:30 -0700 Subject: PCI/P2PDMA: track pgmap references per resource, not globally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for fixing a race between devm_memremap_pages_release() and the final put of a page from the device-page-map, allocate a percpu-ref per p2pdma resource mapping. Link: http://lkml.kernel.org/r/155727338646.292046.9922678317501435597.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams Reviewed-by: Ira Weiny Reviewed-by: Logan Gunthorpe Cc: Bjorn Helgaas Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: "Jérôme Glisse" Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pci/p2pdma.c | 124 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index d5736b31ce66..eecba8fbe251 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -20,12 +20,16 @@ #include struct pci_p2pdma { - struct percpu_ref devmap_ref; - struct completion devmap_ref_done; struct gen_pool *pool; bool p2pmem_published; }; +struct p2pdma_pagemap { + struct dev_pagemap pgmap; + struct percpu_ref ref; + struct completion ref_done; +}; + static ssize_t size_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -74,41 +78,45 @@ static const struct attribute_group p2pmem_group = { .name = "p2pmem", }; +static struct p2pdma_pagemap *to_p2p_pgmap(struct percpu_ref *ref) +{ + return container_of(ref, struct p2pdma_pagemap, ref); +} + static void pci_p2pdma_percpu_release(struct percpu_ref *ref) { - struct pci_p2pdma *p2p = - container_of(ref, struct pci_p2pdma, devmap_ref); + struct p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(ref); - complete_all(&p2p->devmap_ref_done); + complete(&p2p_pgmap->ref_done); } static void pci_p2pdma_percpu_kill(struct percpu_ref *ref) { - /* - * pci_p2pdma_add_resource() may be called multiple times - * by a driver and may register the percpu_kill devm action multiple - * times. We only want the first action to actually kill the - * percpu_ref. - */ - if (percpu_ref_is_dying(ref)) - return; - percpu_ref_kill(ref); } +static void pci_p2pdma_percpu_cleanup(void *ref) +{ + struct p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(ref); + + wait_for_completion(&p2p_pgmap->ref_done); + percpu_ref_exit(&p2p_pgmap->ref); +} + static void pci_p2pdma_release(void *data) { struct pci_dev *pdev = data; + struct pci_p2pdma *p2pdma = pdev->p2pdma; - if (!pdev->p2pdma) + if (!p2pdma) return; - wait_for_completion(&pdev->p2pdma->devmap_ref_done); - percpu_ref_exit(&pdev->p2pdma->devmap_ref); + /* Flush and disable pci_alloc_p2p_mem() */ + pdev->p2pdma = NULL; + synchronize_rcu(); - gen_pool_destroy(pdev->p2pdma->pool); + gen_pool_destroy(p2pdma->pool); sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group); - pdev->p2pdma = NULL; } static int pci_p2pdma_setup(struct pci_dev *pdev) @@ -124,12 +132,6 @@ static int pci_p2pdma_setup(struct pci_dev *pdev) if (!p2p->pool) goto out; - init_completion(&p2p->devmap_ref_done); - error = percpu_ref_init(&p2p->devmap_ref, - pci_p2pdma_percpu_release, 0, GFP_KERNEL); - if (error) - goto out_pool_destroy; - error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev); if (error) goto out_pool_destroy; @@ -163,6 +165,7 @@ out: int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, u64 offset) { + struct p2pdma_pagemap *p2p_pgmap; struct dev_pagemap *pgmap; void *addr; int error; @@ -185,14 +188,32 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, return error; } - pgmap = devm_kzalloc(&pdev->dev, sizeof(*pgmap), GFP_KERNEL); - if (!pgmap) + p2p_pgmap = devm_kzalloc(&pdev->dev, sizeof(*p2p_pgmap), GFP_KERNEL); + if (!p2p_pgmap) return -ENOMEM; + init_completion(&p2p_pgmap->ref_done); + error = percpu_ref_init(&p2p_pgmap->ref, + pci_p2pdma_percpu_release, 0, GFP_KERNEL); + if (error) + goto pgmap_free; + + /* + * FIXME: the percpu_ref_exit needs to be coordinated internal + * to devm_memremap_pages_release(). Duplicate the same ordering + * as other devm_memremap_pages() users for now. + */ + error = devm_add_action(&pdev->dev, pci_p2pdma_percpu_cleanup, + &p2p_pgmap->ref); + if (error) + goto ref_cleanup; + + pgmap = &p2p_pgmap->pgmap; + pgmap->res.start = pci_resource_start(pdev, bar) + offset; pgmap->res.end = pgmap->res.start + size - 1; pgmap->res.flags = pci_resource_flags(pdev, bar); - pgmap->ref = &pdev->p2pdma->devmap_ref; + pgmap->ref = &p2p_pgmap->ref; pgmap->type = MEMORY_DEVICE_PCI_P2PDMA; pgmap->pci_p2pdma_bus_offset = pci_bus_address(pdev, bar) - pci_resource_start(pdev, bar); @@ -201,12 +222,13 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, addr = devm_memremap_pages(&pdev->dev, pgmap); if (IS_ERR(addr)) { error = PTR_ERR(addr); - goto pgmap_free; + goto ref_exit; } - error = gen_pool_add_virt(pdev->p2pdma->pool, (unsigned long)addr, + error = gen_pool_add_owner(pdev->p2pdma->pool, (unsigned long)addr, pci_bus_address(pdev, bar) + offset, - resource_size(&pgmap->res), dev_to_node(&pdev->dev)); + resource_size(&pgmap->res), dev_to_node(&pdev->dev), + &p2p_pgmap->ref); if (error) goto pages_free; @@ -217,8 +239,10 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, pages_free: devm_memunmap_pages(&pdev->dev, pgmap); +ref_cleanup: + percpu_ref_exit(&p2p_pgmap->ref); pgmap_free: - devm_kfree(&pdev->dev, pgmap); + devm_kfree(&pdev->dev, p2p_pgmap); return error; } EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource); @@ -587,19 +611,30 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_find_many); */ void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size) { - void *ret; + void *ret = NULL; + struct percpu_ref *ref; + /* + * Pairs with synchronize_rcu() in pci_p2pdma_release() to + * ensure pdev->p2pdma is non-NULL for the duration of the + * read-lock. + */ + rcu_read_lock(); if (unlikely(!pdev->p2pdma)) - return NULL; - - if (unlikely(!percpu_ref_tryget_live(&pdev->p2pdma->devmap_ref))) - return NULL; - - ret = (void *)gen_pool_alloc(pdev->p2pdma->pool, size); + goto out; - if (unlikely(!ret)) - percpu_ref_put(&pdev->p2pdma->devmap_ref); + ret = (void *)gen_pool_alloc_owner(pdev->p2pdma->pool, size, + (void **) &ref); + if (!ret) + goto out; + if (unlikely(!percpu_ref_tryget_live(ref))) { + gen_pool_free(pdev->p2pdma->pool, (unsigned long) ret, size); + ret = NULL; + goto out; + } +out: + rcu_read_unlock(); return ret; } EXPORT_SYMBOL_GPL(pci_alloc_p2pmem); @@ -612,8 +647,11 @@ EXPORT_SYMBOL_GPL(pci_alloc_p2pmem); */ void pci_free_p2pmem(struct pci_dev *pdev, void *addr, size_t size) { - gen_pool_free(pdev->p2pdma->pool, (uintptr_t)addr, size); - percpu_ref_put(&pdev->p2pdma->devmap_ref); + struct percpu_ref *ref; + + gen_pool_free_owner(pdev->p2pdma->pool, (uintptr_t)addr, size, + (void **) &ref); + percpu_ref_put(ref); } EXPORT_SYMBOL_GPL(pci_free_p2pmem); -- cgit v1.2.3 From 50f44ee7248ad2f7984ef081974a6ecd09724b3e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 13 Jun 2019 15:56:33 -0700 Subject: mm/devm_memremap_pages: fix final page put race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Logan noticed that devm_memremap_pages_release() kills the percpu_ref drops all the page references that were acquired at init and then immediately proceeds to unplug, arch_remove_memory(), the backing pages for the pagemap. If for some reason device shutdown actually collides with a busy / elevated-ref-count page then arch_remove_memory() should be deferred until after that reference is dropped. As it stands the "wait for last page ref drop" happens *after* devm_memremap_pages_release() returns, which is obviously too late and can lead to crashes. Fix this situation by assigning the responsibility to wait for the percpu_ref to go idle to devm_memremap_pages() with a new ->cleanup() callback. Implement the new cleanup callback for all devm_memremap_pages() users: pmem, devdax, hmm, and p2pdma. Link: http://lkml.kernel.org/r/155727339156.292046.5432007428235387859.stgit@dwillia2-desk3.amr.corp.intel.com Fixes: 41e94a851304 ("add devm_memremap_pages") Signed-off-by: Dan Williams Reported-by: Logan Gunthorpe Reviewed-by: Ira Weiny Reviewed-by: Logan Gunthorpe Cc: Bjorn Helgaas Cc: "Jérôme Glisse" Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/dax/device.c | 13 +++---------- drivers/nvdimm/pmem.c | 17 +++++++++++++---- drivers/pci/p2pdma.c | 17 +++-------------- 3 files changed, 19 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 996d68ff992a..8465d12fecba 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -27,9 +27,8 @@ static void dev_dax_percpu_release(struct percpu_ref *ref) complete(&dev_dax->cmp); } -static void dev_dax_percpu_exit(void *data) +static void dev_dax_percpu_exit(struct percpu_ref *ref) { - struct percpu_ref *ref = data; struct dev_dax *dev_dax = ref_to_dev_dax(ref); dev_dbg(&dev_dax->dev, "%s\n", __func__); @@ -466,18 +465,12 @@ int dev_dax_probe(struct device *dev) if (rc) return rc; - rc = devm_add_action_or_reset(dev, dev_dax_percpu_exit, &dev_dax->ref); - if (rc) - return rc; - dev_dax->pgmap.ref = &dev_dax->ref; dev_dax->pgmap.kill = dev_dax_percpu_kill; + dev_dax->pgmap.cleanup = dev_dax_percpu_exit; addr = devm_memremap_pages(dev, &dev_dax->pgmap); - if (IS_ERR(addr)) { - devm_remove_action(dev, dev_dax_percpu_exit, &dev_dax->ref); - percpu_ref_exit(&dev_dax->ref); + if (IS_ERR(addr)) return PTR_ERR(addr); - } inode = dax_inode(dax_dev); cdev = inode->i_cdev; diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 28cb44c61d4a..24d7fe7c74ed 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -303,11 +303,19 @@ static const struct attribute_group *pmem_attribute_groups[] = { NULL, }; -static void pmem_release_queue(void *q) +static void __pmem_release_queue(struct percpu_ref *ref) { + struct request_queue *q; + + q = container_of(ref, typeof(*q), q_usage_counter); blk_cleanup_queue(q); } +static void pmem_release_queue(void *ref) +{ + __pmem_release_queue(ref); +} + static void pmem_freeze_queue(struct percpu_ref *ref) { struct request_queue *q; @@ -399,12 +407,10 @@ static int pmem_attach_disk(struct device *dev, if (!q) return -ENOMEM; - if (devm_add_action_or_reset(dev, pmem_release_queue, q)) - return -ENOMEM; - pmem->pfn_flags = PFN_DEV; pmem->pgmap.ref = &q->q_usage_counter; pmem->pgmap.kill = pmem_freeze_queue; + pmem->pgmap.cleanup = __pmem_release_queue; if (is_nd_pfn(dev)) { if (setup_pagemap_fsdax(dev, &pmem->pgmap)) return -ENOMEM; @@ -425,6 +431,9 @@ static int pmem_attach_disk(struct device *dev, pmem->pfn_flags |= PFN_MAP; memcpy(&bb_res, &pmem->pgmap.res, sizeof(bb_res)); } else { + if (devm_add_action_or_reset(dev, pmem_release_queue, + &q->q_usage_counter)) + return -ENOMEM; addr = devm_memremap(dev, pmem->phys_addr, pmem->size, ARCH_MEMREMAP_PMEM); memcpy(&bb_res, &nsio->res, sizeof(bb_res)); diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index eecba8fbe251..a98126ad9c3a 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -95,7 +95,7 @@ static void pci_p2pdma_percpu_kill(struct percpu_ref *ref) percpu_ref_kill(ref); } -static void pci_p2pdma_percpu_cleanup(void *ref) +static void pci_p2pdma_percpu_cleanup(struct percpu_ref *ref) { struct p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(ref); @@ -198,16 +198,6 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, if (error) goto pgmap_free; - /* - * FIXME: the percpu_ref_exit needs to be coordinated internal - * to devm_memremap_pages_release(). Duplicate the same ordering - * as other devm_memremap_pages() users for now. - */ - error = devm_add_action(&pdev->dev, pci_p2pdma_percpu_cleanup, - &p2p_pgmap->ref); - if (error) - goto ref_cleanup; - pgmap = &p2p_pgmap->pgmap; pgmap->res.start = pci_resource_start(pdev, bar) + offset; @@ -218,11 +208,12 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, pgmap->pci_p2pdma_bus_offset = pci_bus_address(pdev, bar) - pci_resource_start(pdev, bar); pgmap->kill = pci_p2pdma_percpu_kill; + pgmap->cleanup = pci_p2pdma_percpu_cleanup; addr = devm_memremap_pages(&pdev->dev, pgmap); if (IS_ERR(addr)) { error = PTR_ERR(addr); - goto ref_exit; + goto pgmap_free; } error = gen_pool_add_owner(pdev->p2pdma->pool, (unsigned long)addr, @@ -239,8 +230,6 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, pages_free: devm_memunmap_pages(&pdev->dev, pgmap); -ref_cleanup: - percpu_ref_exit(&p2p_pgmap->ref); pgmap_free: devm_kfree(&pdev->dev, p2p_pgmap); return error; -- cgit v1.2.3 From 234f223d63d8f7db64a682ccf02871d40d38db52 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 7 Jun 2019 00:30:58 +0200 Subject: PCI: PM: Avoid resuming devices in D3hot during system suspend The current code resumes devices in D3hot during system suspend if the target power state for them is D3cold, but that is not necessary in general. It only is necessary to do that if the platform firmware requires the device to be resumed, but that should be covered by the platform_pci_need_resume() check anyway, so rework pci_dev_keep_suspended() to avoid returning 'false' for devices in D3hot which need not be resumed due to platform firmware requirements. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg --- drivers/pci/pci.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8abc843b1615..324175509e94 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2474,10 +2474,20 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev) { struct device *dev = &pci_dev->dev; bool wakeup = device_may_wakeup(dev); + pci_power_t target_state; + + if (!pm_runtime_suspended(dev) || platform_pci_need_resume(pci_dev)) + return false; - if (!pm_runtime_suspended(dev) - || pci_target_state(pci_dev, wakeup) != pci_dev->current_state - || platform_pci_need_resume(pci_dev)) + target_state = pci_target_state(pci_dev, wakeup); + + /* + * If the earlier platform check has not triggered, D3cold is just power + * removal on top of D3hot, so no need to resume the device in that + * case. + */ + if (target_state != pci_dev->current_state && + target_state != PCI_D3cold && pci_dev->current_state != PCI_D3hot) return false; /* -- cgit v1.2.3 From 0c7376ada9508141becec9b897d73b65ce66a15a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 7 Jun 2019 00:32:31 +0200 Subject: PCI: PM: Replace pci_dev_keep_suspended() with two functions The code in pci_dev_keep_suspended() is relatively hard to follow due to the negative checks in it and in its callers and the function has a possible side-effect (disabling the PME) which doesn't really match its role. For this reason, move the PME disabling from pci_dev_keep_suspended() to a separate function and change the semantics (and name) of the rest of it, so that 'true' is returned when the device needs to be resumed (and not the other way around). Change the callers of pci_dev_keep_suspended() accordingly. While at it, make the code flow in pci_pm_poweroff() reflect the pci_pm_suspend() more closely to avoid arbitrary differences between them. This is a cosmetic change with no intention to alter behavior. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg --- drivers/pci/pci-driver.c | 22 +++++++++++++++---- drivers/pci/pci.c | 55 ++++++++++++++++++++++++------------------------ drivers/pci/pci.h | 3 ++- 3 files changed, 48 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 5eadbc3d0969..dd656cf19a12 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -679,6 +679,7 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) static int pci_pm_prepare(struct device *dev) { struct device_driver *drv = dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); if (drv && drv->pm && drv->pm->prepare) { int error = drv->pm->prepare(dev); @@ -688,7 +689,15 @@ static int pci_pm_prepare(struct device *dev) if (!error && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE)) return 0; } - return pci_dev_keep_suspended(to_pci_dev(dev)); + if (pci_dev_need_resume(pci_dev)) + return 0; + + /* + * The PME setting needs to be adjusted here in case the direct-complete + * optimization is used with respect to this device. + */ + pci_dev_adjust_pme(pci_dev); + return 1; } static void pci_pm_complete(struct device *dev) @@ -758,9 +767,11 @@ static int pci_pm_suspend(struct device *dev) * better to resume the device from runtime suspend here. */ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || - !pci_dev_keep_suspended(pci_dev)) { + pci_dev_need_resume(pci_dev)) { pm_runtime_resume(dev); pci_dev->state_saved = false; + } else { + pci_dev_adjust_pme(pci_dev); } if (pm->suspend) { @@ -1108,10 +1119,13 @@ static int pci_pm_poweroff(struct device *dev) /* The reason to do that is the same as in pci_pm_suspend(). */ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || - !pci_dev_keep_suspended(pci_dev)) + pci_dev_need_resume(pci_dev)) { pm_runtime_resume(dev); + pci_dev->state_saved = false; + } else { + pci_dev_adjust_pme(pci_dev); + } - pci_dev->state_saved = false; if (pm->poweroff) { int error; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 324175509e94..24d86bd1bba1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2459,55 +2459,56 @@ bool pci_dev_run_wake(struct pci_dev *dev) EXPORT_SYMBOL_GPL(pci_dev_run_wake); /** - * pci_dev_keep_suspended - Check if the device can stay in the suspended state. + * pci_dev_need_resume - Check if it is necessary to resume the device. * @pci_dev: Device to check. * - * Return 'true' if the device is runtime-suspended, it doesn't have to be + * Return 'true' if the device is not runtime-suspended or it has to be * reconfigured due to wakeup settings difference between system and runtime - * suspend and the current power state of it is suitable for the upcoming - * (system) transition. - * - * If the device is not configured for system wakeup, disable PME for it before - * returning 'true' to prevent it from waking up the system unnecessarily. + * suspend, or the current power state of it is not suitable for the upcoming + * (system-wide) transition. */ -bool pci_dev_keep_suspended(struct pci_dev *pci_dev) +bool pci_dev_need_resume(struct pci_dev *pci_dev) { struct device *dev = &pci_dev->dev; - bool wakeup = device_may_wakeup(dev); pci_power_t target_state; if (!pm_runtime_suspended(dev) || platform_pci_need_resume(pci_dev)) - return false; + return true; - target_state = pci_target_state(pci_dev, wakeup); + target_state = pci_target_state(pci_dev, device_may_wakeup(dev)); /* * If the earlier platform check has not triggered, D3cold is just power * removal on top of D3hot, so no need to resume the device in that * case. */ - if (target_state != pci_dev->current_state && - target_state != PCI_D3cold && pci_dev->current_state != PCI_D3hot) - return false; + return target_state != pci_dev->current_state && + target_state != PCI_D3cold && + pci_dev->current_state != PCI_D3hot; +} + +/** + * pci_dev_adjust_pme - Adjust PME setting for a suspended device. + * @pci_dev: Device to check. + * + * If the device is suspended and it is not configured for system wakeup, + * disable PME for it to prevent it from waking up the system unnecessarily. + * + * Note that if the device's power state is D3cold and the platform check in + * pci_dev_need_resume() has not triggered, the device's configuration need not + * be changed. + */ +void pci_dev_adjust_pme(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; - /* - * At this point the device is good to go unless it's been configured - * to generate PME at the runtime suspend time, but it is not supposed - * to wake up the system. In that case, simply disable PME for it - * (it will have to be re-enabled on exit from system resume). - * - * If the device's power state is D3cold and the platform check above - * hasn't triggered, the device's configuration is suitable and we don't - * need to manipulate it at all. - */ spin_lock_irq(&dev->power.lock); - if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold && - !wakeup) + if (pm_runtime_suspended(dev) && !device_may_wakeup(dev) && + pci_dev->current_state < PCI_D3cold) __pci_pme_active(pci_dev, false); spin_unlock_irq(&dev->power.lock); - return true; } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9cb99380c61e..e04fa7fd3e2f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -82,7 +82,8 @@ int pci_finish_runtime_suspend(struct pci_dev *dev); void pcie_clear_root_pme_status(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); void pci_pme_restore(struct pci_dev *dev); -bool pci_dev_keep_suspended(struct pci_dev *dev); +bool pci_dev_need_resume(struct pci_dev *dev); +void pci_dev_adjust_pme(struct pci_dev *dev); void pci_dev_complete_resume(struct pci_dev *pci_dev); void pci_config_pm_runtime_get(struct pci_dev *dev); void pci_config_pm_runtime_put(struct pci_dev *dev); -- cgit v1.2.3 From c2bf1fc212f7e6f25ace1af8f0b3ac061ea48ba5 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 12 Jun 2019 13:57:38 +0300 Subject: PCI: Add missing link delays required by the PCIe spec Currently Linux does not follow PCIe spec regarding the required delays after reset. A concrete example is a Thunderbolt add-in-card that consists of a PCIe switch and two PCIe endpoints: +-1b.0-[01-6b]----00.0-[02-6b]--+-00.0-[03]----00.0 TBT controller +-01.0-[04-36]-- DS hotplug port +-02.0-[37]----00.0 xHCI controller \-04.0-[38-6b]-- DS hotplug port The root port (1b.0) and the PCIe switch downstream ports are all PCIe gen3 so they support 8GT/s link speeds. We wait for the PCIe hierarchy to enter D3cold (runtime): pcieport 0000:00:1b.0: power state changed by ACPI to D3cold When it wakes up from D3cold, according to the PCIe 4.0 section 5.8 the PCIe switch is put to reset and its power is re-applied. This means that we must follow the rules in PCIe 4.0 section 6.6.1. For the PCIe gen3 ports we are dealing with here, the following applies: With a Downstream Port that supports Link speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms after Link training completes before sending a Configuration Request to the device immediately below that Port. Software can determine when Link training completes by polling the Data Link Layer Link Active bit or by setting up an associated interrupt (see Section 6.7.3.3). Translating this into the above topology we would need to do this (DLLLA stands for Data Link Layer Link Active): pcieport 0000:00:1b.0: wait for 100ms after DLLLA is set before access to 0000:01:00.0 pcieport 0000:02:00.0: wait for 100ms after DLLLA is set before access to 0000:03:00.0 pcieport 0000:02:02.0: wait for 100ms after DLLLA is set before access to 0000:37:00.0 I've instrumented the kernel with additional logging so we can see the actual delays the kernel performs: pcieport 0000:00:1b.0: power state changed by ACPI to D0 pcieport 0000:00:1b.0: waiting for D3cold delay of 100 ms pcieport 0000:00:1b.0: waking up bus pcieport 0000:00:1b.0: waiting for D3hot delay of 10 ms pcieport 0000:00:1b.0: restoring config space at offset 0x2c (was 0x60, writing 0x60) ... pcieport 0000:00:1b.0: PME# disabled pcieport 0000:01:00.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) ... pcieport 0000:01:00.0: PME# disabled pcieport 0000:02:00.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) ... pcieport 0000:02:00.0: PME# disabled pcieport 0000:02:01.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) ... pcieport 0000:02:01.0: restoring config space at offset 0x4 (was 0x100000, writing 0x100407) pcieport 0000:02:01.0: PME# disabled pcieport 0000:02:02.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) ... pcieport 0000:02:02.0: PME# disabled pcieport 0000:02:04.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) ... pcieport 0000:02:04.0: PME# disabled pcieport 0000:02:01.0: PME# enabled pcieport 0000:02:01.0: waiting for D3hot delay of 10 ms pcieport 0000:02:04.0: PME# enabled pcieport 0000:02:04.0: waiting for D3hot delay of 10 ms thunderbolt 0000:03:00.0: restoring config space at offset 0x14 (was 0x0, writing 0x8a040000) ... thunderbolt 0000:03:00.0: PME# disabled xhci_hcd 0000:37:00.0: restoring config space at offset 0x10 (was 0x0, writing 0x73f00000) ... xhci_hcd 0000:37:00.0: PME# disabled For the switch upstream port (01:00.0) we wait for 100ms but not taking into account the DLLLA requirement. We then wait 10ms for D3hot -> D0 transition of the root port and the two downstream hotplug ports. This means that we deviate from what the spec requires. Performing the same check for system sleep (s2idle) transitions we can see following when resuming from s2idle: pcieport 0000:00:1b.0: power state changed by ACPI to D0 pcieport 0000:00:1b.0: restoring config space at offset 0x2c (was 0x60, writing 0x60) ... pcieport 0000:01:00.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) ... pcieport 0000:02:02.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) pcieport 0000:02:02.0: restoring config space at offset 0x2c (was 0x0, writing 0x0) pcieport 0000:02:01.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) pcieport 0000:02:04.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) pcieport 0000:02:02.0: restoring config space at offset 0x28 (was 0x0, writing 0x0) pcieport 0000:02:00.0: restoring config space at offset 0x3c (was 0x1ff, writing 0x201ff) pcieport 0000:02:02.0: restoring config space at offset 0x24 (was 0x10001, writing 0x1fff1) pcieport 0000:02:01.0: restoring config space at offset 0x2c (was 0x0, writing 0x60) pcieport 0000:02:02.0: restoring config space at offset 0x20 (was 0x0, writing 0x73f073f0) pcieport 0000:02:04.0: restoring config space at offset 0x2c (was 0x0, writing 0x60) pcieport 0000:02:01.0: restoring config space at offset 0x28 (was 0x0, writing 0x60) pcieport 0000:02:00.0: restoring config space at offset 0x2c (was 0x0, writing 0x0) pcieport 0000:02:02.0: restoring config space at offset 0x1c (was 0x101, writing 0x1f1) pcieport 0000:02:04.0: restoring config space at offset 0x28 (was 0x0, writing 0x60) pcieport 0000:02:01.0: restoring config space at offset 0x24 (was 0x10001, writing 0x1ff10001) pcieport 0000:02:00.0: restoring config space at offset 0x28 (was 0x0, writing 0x0) pcieport 0000:02:02.0: restoring config space at offset 0x18 (was 0x0, writing 0x373702) pcieport 0000:02:04.0: restoring config space at offset 0x24 (was 0x10001, writing 0x49f12001) pcieport 0000:02:01.0: restoring config space at offset 0x20 (was 0x0, writing 0x73e05c00) pcieport 0000:02:00.0: restoring config space at offset 0x24 (was 0x10001, writing 0x1fff1) pcieport 0000:02:04.0: restoring config space at offset 0x20 (was 0x0, writing 0x89f07400) pcieport 0000:02:01.0: restoring config space at offset 0x1c (was 0x101, writing 0x5151) pcieport 0000:02:00.0: restoring config space at offset 0x20 (was 0x0, writing 0x8a008a00) pcieport 0000:02:02.0: restoring config space at offset 0xc (was 0x10000, writing 0x10020) pcieport 0000:02:04.0: restoring config space at offset 0x1c (was 0x101, writing 0x6161) pcieport 0000:02:01.0: restoring config space at offset 0x18 (was 0x0, writing 0x360402) pcieport 0000:02:00.0: restoring config space at offset 0x1c (was 0x101, writing 0x1f1) pcieport 0000:02:04.0: restoring config space at offset 0x18 (was 0x0, writing 0x6b3802) pcieport 0000:02:02.0: restoring config space at offset 0x4 (was 0x100000, writing 0x100407) pcieport 0000:02:00.0: restoring config space at offset 0x18 (was 0x0, writing 0x30302) pcieport 0000:02:01.0: restoring config space at offset 0xc (was 0x10000, writing 0x10020) pcieport 0000:02:04.0: restoring config space at offset 0xc (was 0x10000, writing 0x10020) pcieport 0000:02:00.0: restoring config space at offset 0xc (was 0x10000, writing 0x10020) pcieport 0000:02:01.0: restoring config space at offset 0x4 (was 0x100000, writing 0x100407) pcieport 0000:02:04.0: restoring config space at offset 0x4 (was 0x100000, writing 0x100407) pcieport 0000:02:00.0: restoring config space at offset 0x4 (was 0x100000, writing 0x100407) xhci_hcd 0000:37:00.0: restoring config space at offset 0x10 (was 0x0, writing 0x73f00000) ... thunderbolt 0000:03:00.0: restoring config space at offset 0x14 (was 0x0, writing 0x8a040000) This is even worse. None of the mandatory delays are performed. If this would be S3 instead of s2idle then according to PCI FW spec 3.2 section 4.6.8. there is a specific _DSM that allows the OS to skip the delays but this platform does not provide the _DSM and does not go to S3 anyway so no firmware is involved that could already handle these delays. In this particular Intel Coffee Lake platform these delays are not actually needed because there is an additional delay as part of the ACPI power resource that is used to turn on power to the hierarchy but since that additional delay is not required by any of standards (PCIe, ACPI) it is not present in the Intel Ice Lake, for example where missing the mandatory delays causes pciehp to start tearing down the stack too early (links are not yet trained). For this reason, change the PCIe portdrv PM resume hooks so that they perform the mandatory delays before the downstream component gets resumed. We perform the delays before port services are resumed because otherwise pciehp might find that the link is not up (even if it is just training) and tears-down the hierarchy. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/pci/pci.c | 29 +++++++++++------- drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv_core.c | 66 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 24d86bd1bba1..9839d6f9bcb5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1004,15 +1004,10 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) if (state == PCI_D0) { pci_platform_power_transition(dev, PCI_D0); /* - * Mandatory power management transition delays, see - * PCI Express Base Specification Revision 2.0 Section - * 6.6.1: Conventional Reset. Do not delay for - * devices powered on/off by corresponding bridge, - * because have already delayed for the bridge. + * Mandatory power management transition delays are + * handled in the PCIe portdrv resume hooks. */ if (dev->runtime_d3cold) { - if (dev->d3cold_delay && !dev->imm_ready) - msleep(dev->d3cold_delay); /* * When powering on a bridge from D3cold, the * whole hierarchy may be powered on into @@ -4579,14 +4574,16 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS); } + /** - * pcie_wait_for_link - Wait until link is active or inactive + * pcie_wait_for_link_delay - Wait until link is active or inactive * @pdev: Bridge device * @active: waiting for active or inactive? + * @delay: Delay to wait after link has become active (in ms) * * Use this to wait till link becomes active or inactive. */ -bool pcie_wait_for_link(struct pci_dev *pdev, bool active) +bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay) { int timeout = 1000; bool ret; @@ -4623,13 +4620,25 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active) timeout -= 10; } if (active && ret) - msleep(100); + msleep(delay); else if (ret != active) pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", active ? "set" : "cleared"); return ret == active; } +/** + * pcie_wait_for_link - Wait until link is active or inactive + * @pdev: Bridge device + * @active: waiting for active or inactive? + * + * Use this to wait till link becomes active or inactive. + */ +bool pcie_wait_for_link(struct pci_dev *pdev, bool active) +{ + return pcie_wait_for_link_delay(pdev, active, 100); +} + void pci_reset_secondary_bus(struct pci_dev *dev) { u16 ctrl; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e04fa7fd3e2f..7f9fd93270ed 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -494,6 +494,7 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, u32 service); +bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay); bool pcie_wait_for_link(struct pci_dev *pdev, bool active); #ifdef CONFIG_PCIEASPM void pcie_aspm_init_link_state(struct pci_dev *pdev); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 1b330129089f..308c3e0c4a34 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -378,6 +379,67 @@ static int pm_iter(struct device *dev, void *data) return 0; } +static int get_downstream_delay(struct pci_bus *bus) +{ + struct pci_dev *pdev; + int min_delay = 100; + int max_delay = 0; + + list_for_each_entry(pdev, &bus->devices, bus_list) { + if (!pdev->imm_ready) + min_delay = 0; + else if (pdev->d3cold_delay < min_delay) + min_delay = pdev->d3cold_delay; + if (pdev->d3cold_delay > max_delay) + max_delay = pdev->d3cold_delay; + } + + return max(min_delay, max_delay); +} + +/* + * wait_for_downstream_link - Wait for downstream link to establish + * @pdev: PCIe port whose downstream link is waited + * + * Handle delays according to PCIe 4.0 section 6.6.1 before configuration + * access to the downstream component is permitted. + * + * This blocks PCI core resume of the hierarchy below this port until the + * link is trained. Should be called before resuming port services to + * prevent pciehp from starting to tear-down the hierarchy too soon. + */ +static void wait_for_downstream_link(struct pci_dev *pdev) +{ + int delay; + + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT && + pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) + return; + + if (pci_dev_is_disconnected(pdev)) + return; + + if (!pdev->subordinate || list_empty(&pdev->subordinate->devices) || + !pdev->bridge_d3) + return; + + delay = get_downstream_delay(pdev->subordinate); + if (!delay) + return; + + dev_dbg(&pdev->dev, "waiting downstream link for %d ms\n", delay); + + /* + * If downstream port does not support speeds greater than 5 GT/s + * need to wait 100ms. For higher speeds (gen3) we need to wait + * first for the data link layer to become active. + */ + if (pcie_get_speed_cap(pdev) <= PCIE_SPEED_5_0GT) + msleep(delay); + else + pcie_wait_for_link_delay(pdev, true, delay); +} + /** * pcie_port_device_suspend - suspend port services associated with a PCIe port * @dev: PCI Express port to handle @@ -391,6 +453,8 @@ int pcie_port_device_suspend(struct device *dev) int pcie_port_device_resume_noirq(struct device *dev) { size_t off = offsetof(struct pcie_port_service_driver, resume_noirq); + + wait_for_downstream_link(to_pci_dev(dev)); return device_for_each_child(dev, &off, pm_iter); } @@ -421,6 +485,8 @@ int pcie_port_device_runtime_suspend(struct device *dev) int pcie_port_device_runtime_resume(struct device *dev) { size_t off = offsetof(struct pcie_port_service_driver, runtime_resume); + + wait_for_downstream_link(to_pci_dev(dev)); return device_for_each_child(dev, &off, pm_iter); } #endif /* PM */ -- cgit v1.2.3 From 000dd5316e1c756a1c028f22e01d06a38249dd4d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 12 Jun 2019 13:57:39 +0300 Subject: PCI: Do not poll for PME if the device is in D3cold PME polling does not take into account that a device that is directly connected to the host bridge may go into D3cold as well. This leads to a situation where the PME poll thread reads from a config space of a device that is in D3cold and gets incorrect information because the config space is not accessible. Here is an example from Intel Ice Lake system where two PCIe root ports are in D3cold (I've instrumented the kernel to log the PMCSR register contents): [ 62.971442] pcieport 0000:00:07.1: Check PME status, PMCSR=0xffff [ 62.971504] pcieport 0000:00:07.0: Check PME status, PMCSR=0xffff Since 0xffff is interpreted so that PME is pending, the root ports will be runtime resumed. This repeats over and over again essentially blocking all runtime power management. Prevent this from happening by checking whether the device is in D3cold before its PME status is read. Fixes: 71a83bd727cc ("PCI/PM: add runtime PM support to PCIe port") Signed-off-by: Mika Westerberg Reviewed-by: Lukas Wunner Cc: 3.6+ # v3.6+ Signed-off-by: Rafael J. Wysocki --- drivers/pci/pci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9839d6f9bcb5..e34fb2b3c466 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2060,6 +2060,13 @@ static void pci_pme_list_scan(struct work_struct *work) */ if (bridge && bridge->current_state != PCI_D0) continue; + /* + * If the device is in D3cold it should not be + * polled either. + */ + if (pme_dev->dev->current_state == PCI_D3cold) + continue; + pci_pme_wakeup(pme_dev->dev, NULL); } else { list_del(&pme_dev->list); -- cgit v1.2.3