From 12f9a5753f0393455f739e4ef1efc1a5e0d40faf Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Fri, 11 Aug 2023 21:38:08 +0300 Subject: [PATCH 01/10] regulator: qcom-rpmh: Add PM8350b LDO1 --- drivers/regulator/qcom-rpmh-regulator.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index cf502eec0915..be3709027fa5 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1013,6 +1013,11 @@ static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm8350b_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + {} +}; + static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps515, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), @@ -1474,6 +1479,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm8350-rpmh-regulators", .data = pm8350_vreg_data, }, + { + .compatible = "qcom,pm8350b-rpmh-regulators", + .data = pm8350b_vreg_data, + }, { .compatible = "qcom,pm8350c-rpmh-regulators", .data = pm8350c_vreg_data, From ecba5e4fc87ac9c5a8b67267c69867dea597bde1 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Sun, 12 Nov 2023 16:47:18 +0100 Subject: [PATCH 02/10] dt-bindings: net: qualcomm: Add QCA6490 Bluetooth --- .../net/bluetooth/qualcomm-bluetooth.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index eba2f3026ab0..7d55b48579a2 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -18,6 +18,7 @@ properties: enum: - qcom,qca2066-bt - qcom,qca6174-bt + - qcom,qca6490-bt - qcom,qca9377-bt - qcom,wcn3988-bt - qcom,wcn3990-bt @@ -37,6 +38,15 @@ properties: description: gpio specifier is used to find status of clock supply to SoC + wlan-gpios: + maxItems: 1 + description: gpio specifier used to enable the WLAN + chip, on QCA6490 only + + power-domains: + maxItems: 1 + description: power domain associated with the chip + clocks: maxItems: 1 description: clock provided to the controller (SUSCLK_32KHZ) @@ -114,6 +124,17 @@ allOf: - enable-gpios - clocks + - if: + properties: + compatible: + contains: + enum: + - qcom,qca6490-bt + then: + required: + - enable-gpios + - wlan-gpios + - if: properties: compatible: From ff19be52ff0b8d348069fee132e9180e6ffb4fdc Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Wed, 6 Sep 2023 08:36:48 +0200 Subject: [PATCH 03/10] UNFINISHED: bluetooth: Add support for qca6490 --- drivers/bluetooth/btqca.c | 3 + drivers/bluetooth/btqca.h | 1 + drivers/bluetooth/hci_qca.c | 50 ++++++- drivers/net/wireless/ath/ath11k/core.c | 197 ++++++++++++++++++------- drivers/net/wireless/ath/ath11k/core.h | 1 + drivers/net/wireless/ath/ath11k/mhi.c | 1 + drivers/net/wireless/ath/ath11k/pci.c | 6 + drivers/net/wireless/ath/ath11k/pcic.c | 11 ++ 8 files changed, 208 insertions(+), 62 deletions(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index fdb0fae88d1c..b89060a315f0 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -687,6 +687,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, snprintf(config.fwname, sizeof(config.fwname), "qca/msbtfw%02x.mbn", rom_ver); break; + case QCA_QCA6490: case QCA_WCN6855: snprintf(config.fwname, sizeof(config.fwname), "qca/hpbtfw%02x.tlv", rom_ver); @@ -746,6 +747,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, snprintf(config.fwname, sizeof(config.fwname), "qca/msnv%02x.bin", rom_ver); break; + case QCA_QCA6490: case QCA_WCN6855: snprintf(config.fwname, sizeof(config.fwname), "qca/hpnv%02x.bin", rom_ver); @@ -771,6 +773,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, case QCA_WCN3991: case QCA_QCA2066: case QCA_QCA6390: + case QCA_QCA6490: case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index dc31984f71dc..107fe8785316 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -150,6 +150,7 @@ enum qca_btsoc_type { QCA_WCN3991, QCA_QCA2066, QCA_QCA6390, + QCA_QCA6490, QCA_WCN6750, QCA_WCN6855, QCA_WCN7850, diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 067e248e3599..11df067f0f25 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -219,6 +219,7 @@ struct qca_power { struct qca_serdev { struct hci_uart serdev_hu; struct gpio_desc *bt_en; + struct gpio_desc *wl_en; struct gpio_desc *sw_ctrl; struct clk *susclk; enum qca_btsoc_type btsoc_type; @@ -1112,13 +1113,14 @@ static void qca_controller_memdump(struct work_struct *work) * the controller. In such cases let us store the dummy * packets in the buffer. */ - /* For QCA6390, controller does not lost packets but + /* For QCA6(3,4)90, controller does not lost packets but * sequence number field of packet sometimes has error * bits, so skip this checking for missing packet. */ while ((seq_no > qca_memdump->current_seq_no + 1) && - (soc_type != QCA_QCA6390) && - seq_no != QCA_LAST_SEQUENCE_NUM) { + (soc_type != QCA_QCA6390) && + (soc_type != QCA_QCA6490) && + seq_no != QCA_LAST_SEQUENCE_NUM) { bt_dev_err(hu->hdev, "QCA controller missed packet:%d", qca_memdump->current_seq_no); rx_size = qca_memdump->received_dump; @@ -1725,8 +1727,13 @@ static int qca_regulator_init(struct hci_uart *hu) if (qcadev->bt_en) { gpiod_set_value_cansleep(qcadev->bt_en, 0); msleep(50); + /* For qca6490 also need to enable wlan gpio */ + if (qcadev->wl_en) + gpiod_set_value_cansleep(qcadev->wl_en, 0); gpiod_set_value_cansleep(qcadev->bt_en, 1); msleep(50); + if (qcadev->wl_en) + gpiod_set_value_cansleep(qcadev->wl_en, 1); if (qcadev->sw_ctrl) { sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl); bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state); @@ -1788,6 +1795,7 @@ static int qca_power_on(struct hci_dev *hdev) case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: + case QCA_QCA6490: ret = qca_regulator_init(hu); break; @@ -1864,6 +1872,10 @@ static int qca_setup(struct hci_uart *hu) soc_name = "wcn7850"; break; + case QCA_QCA6490: + soc_name = "qca6490"; + break; + default: soc_name = "ROME/QCA6390"; } @@ -1886,6 +1898,7 @@ retry: case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: + case QCA_QCA6490: // XXX: Is this needed? set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); hci_set_aosp_capable(hdev); @@ -1916,6 +1929,7 @@ retry: case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: + case QCA_QCA6490: // XXX: TEMPORARY break; default: @@ -1965,7 +1979,7 @@ out: } /* Setup bdaddr */ - if (soc_type == QCA_ROME) + if (soc_type == QCA_ROME) // XXX: Test here? hu->hdev->set_bdaddr = qca_set_bdaddr_rome; else hu->hdev->set_bdaddr = qca_set_bdaddr; @@ -2046,6 +2060,20 @@ static const struct qca_device_data qca_soc_data_qca6390 __maybe_unused = { .num_vregs = 0, }; +static const struct qca_device_data qca_soc_data_qca6490 __maybe_unused = { + .soc_type = QCA_QCA6490, // QCA_WCN6855 + /*.vregs = (struct qca_vreg []) { + { "vddio", 1800000 }, + { "vddaon", 950000 }, + { "vdddig", 950000 }, + { "vddrfa1", 1880000 }, + { "vddrfa2", 1350000 }, + { "vddasd", 2800000 }, + },*/ + .num_vregs = 0, + //.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, +}; + static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = { .soc_type = QCA_WCN6750, .vregs = (struct qca_vreg []) { @@ -2300,6 +2328,15 @@ static int qca_serdev_probe(struct serdev_device *serdev) power_ctrl_enabled = false; } + /* QCA6490 needs wlan and bluetooth enabled together */ + qcadev->wl_en = devm_gpiod_get_optional(&serdev->dev, "wlan", + GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(qcadev->wl_en) && + data->soc_type == QCA_QCA6490) { + dev_err(&serdev->dev, "failed to acquire WL_EN gpio\n"); + power_ctrl_enabled = false; + } + qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl", GPIOD_IN); if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && @@ -2411,7 +2448,8 @@ static void qca_serdev_shutdown(struct device *dev) const u8 ibs_wake_cmd[] = { 0xFD }; const u8 edl_reset_soc_cmd[] = { 0x01, 0x00, 0xFC, 0x01, 0x05 }; - if (qcadev->btsoc_type == QCA_QCA6390) { + if ((qcadev->btsoc_type == QCA_QCA6390) || + (qcadev->btsoc_type == QCA_QCA6490)) { if (test_bit(QCA_BT_OFF, &qca->flags) || !test_bit(HCI_RUNNING, &hdev->flags)) return; @@ -2571,6 +2609,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = { { .compatible = "qcom,qca2066-bt", .data = &qca_soc_data_qca2066}, { .compatible = "qcom,qca6174-bt" }, { .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390}, + { .compatible = "qcom,qca6490-bt", .data = &qca_soc_data_qca6490}, { .compatible = "qcom,qca9377-bt" }, { .compatible = "qcom,wcn3988-bt", .data = &qca_soc_data_wcn3988}, { .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990}, @@ -2591,6 +2630,7 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = { { "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 }, { "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 }, { "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 }, + { "QCOM6490", (kernel_ulong_t)&qca_soc_data_qca6490 }, { }, }; MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match); diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 0c6ecbb9a066..664af4984bc3 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -292,86 +292,169 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .support_fw_mac_sequence = true, }, { - .name = "qcn9074 hw1.0", - .hw_rev = ATH11K_HW_QCN9074_HW10, - .fw = { - .dir = "QCN9074/hw1.0", - .board_size = 256 * 1024, - .cal_offset = 128 * 1024, - }, - .max_radios = 1, - .single_pdev_only = false, - .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074, - .hw_ops = &qcn9074_ops, - .ring_mask = &ath11k_hw_ring_mask_qcn9074, - .internal_sleep_clock = false, - .regs = &qcn9074_regs, - .host_ce_config = ath11k_host_ce_config_qcn9074, - .ce_count = 6, - .target_ce_config = ath11k_target_ce_config_wlan_qcn9074, + .name = "qca6490 hw1.0", + .hw_rev = ATH11K_HW_QCA6490_HW10, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &qca6390_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &qca6390_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, + .target_ce_config = ath11k_target_ce_config_wlan_qca6390, .target_ce_count = 9, - .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qcn9074, - .svc_to_ce_map_len = 18, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, - .rxdma1_enable = true, - .num_rxmda_per_pdev = 1, - .rx_mac_buf_ring = false, - .vdev_start_delay = false, - .htt_peer_map_v2 = true, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, .spectral = { - .fft_sz = 2, + .fft_sz = 0, .fft_pad_sz = 0, - .summary_pad_sz = 16, - .fft_hdr_len = 24, - .max_fft_bins = 1024, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, .fragment_160mhz = false, }, .interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT), - .supports_monitor = true, - .full_monitor_mode = true, - .supports_shadow_regs = false, - .idle_ps = false, - .supports_sta_ps = false, + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, + .full_monitor_mode = false, + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, .coldboot_cal_mm = false, - .coldboot_cal_ftm = true, - .cbcal_restart_fw = true, - .fw_mem_mode = 2, - .num_vdevs = 8, - .num_peers = 128, - .supports_suspend = false, - .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .coldboot_cal_ftm = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), .supports_regdb = false, .fix_l1ss = true, - .credit_flow = false, - .max_tx_ring = DP_TCL_NUM_RING_MAX, - .hal_params = &ath11k_hw_hal_params_ipq8074, - .supports_dynamic_smps_6ghz = true, - .alloc_cacheable_memory = true, - .supports_rssi_stats = false, - .fw_wmi_diag_event = false, - .current_cc_support = false, - .dbr_debug_support = true, - .global_reset = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, .bios_sar_capa = NULL, .m3_fw_support = true, .fixed_bdf_addr = false, .fixed_mem_region = false, - .static_window_map = true, + .static_window_map = false, .hybrid_bus_type = false, .fixed_fw_mem = false, - .support_off_channel_tx = false, - .supports_multi_bssid = false, + .support_off_channel_tx = true, + .supports_multi_bssid = true, - .sram_dump = {}, + .sram_dump = { + .start = 0x01400000, + .end = 0x0171ffff, + }, .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, - .support_fw_mac_sequence = false, + .support_fw_mac_sequence = true, + }, + { + .name = "qcn9074 hw1.0", + .hw_rev = ATH11K_HW_QCN9074_HW10, + .fw = { + .dir = "WCN6855/hw2.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &wcn6855_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &wcn6855_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, + .target_ce_config = ath11k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, + + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + .fragment_160mhz = false, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, + .full_monitor_mode = false, + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .coldboot_cal_mm = false, + .coldboot_cal_ftm = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, + .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, + .support_off_channel_tx = true, + .supports_multi_bssid = true, + + .sram_dump = { + .start = 0x01400000, + .end = 0x0177ffff, + }, + + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "wcn6855 hw2.0", diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index f12b606e2d2e..462c592677fb 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -141,6 +141,7 @@ struct ath11k_skb_rxcb { enum ath11k_hw_rev { ATH11K_HW_IPQ8074, ATH11K_HW_QCA6390_HW20, + ATH11K_HW_QCA6490_HW10, ATH11K_HW_IPQ6018_HW10, ATH11K_HW_QCN9074_HW10, ATH11K_HW_WCN6855_HW20, diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index afeabd6ecc67..da48546b6c4f 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -441,6 +441,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) ath11k_mhi_config = &ath11k_mhi_config_qcn9074; break; case ATH11K_HW_QCA6390_HW20: + case ATH11K_HW_QCA6490_HW10: case ATH11K_HW_WCN6855_HW20: case ATH11K_HW_WCN6855_HW21: ath11k_mhi_config = &ath11k_mhi_config_qca6390; diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 09e65c5e55c4..817340177373 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -801,6 +801,12 @@ static int ath11k_pci_probe(struct pci_dev *pdev, ath11k_pci_read_hw_version(ab, &soc_hw_version_major, &soc_hw_version_minor); switch (soc_hw_version_major) { + case 1: + switch (soc_hw_version_minor) { + case 0x10: + ab->hw_rev = ATH11K_HW_QCA6490_HW10; + } + break; case 2: switch (soc_hw_version_minor) { case 0x00: diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index 16d1e332193f..b6d3d5420b37 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -74,6 +74,17 @@ static const struct ath11k_msi_config ath11k_msi_config[] = { }, .hw_rev = ATH11K_HW_QCA6390_HW20, }, + /*{ + .total_vectors = 32, + .total_users = 4, + .users = (struct ath11k_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, + .hw_rev = ATH11K_HW_QCA6490_HW10, + },*/ { .total_vectors = 16, .total_users = 3, From cb877d00b37584af862297911a7bb77db9cc2ff3 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Mon, 4 Sep 2023 09:18:52 +0200 Subject: [PATCH 04/10] UNFINISHED: firmware: Add synaptics tcm oncell fw loader driver --- drivers/firmware/Kconfig | 9 ++++ drivers/firmware/Makefile | 1 + drivers/firmware/syna_tcm_loader.c | 80 ++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 drivers/firmware/syna_tcm_loader.c diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 4a98a859d44d..8c16b458a562 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -220,6 +220,15 @@ config SYSFB_SIMPLEFB If unsure, say Y. +config SYNA_TCM_FW + tristate "Synaptics TCM Firmware Loader" + depends on RMI4_I2C + help + Say yes here to add support for loading firmware images on Synaptics + TCM on-cell touch controllers. + + If unsure, say N. + config TI_SCI_PROTOCOL tristate "TI System Control Interface (TISCI) Message Protocol" depends on TI_MESSAGE_MANAGER diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 5f9dab82e1a0..3b458a198863 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o +obj-$(CONFIG_SYNA_TCM_FW) += syna_tcm_loader.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o obj-$(CONFIG_SYSFB) += sysfb.o diff --git a/drivers/firmware/syna_tcm_loader.c b/drivers/firmware/syna_tcm_loader.c new file mode 100644 index 000000000000..4b36ad5be85d --- /dev/null +++ b/drivers/firmware/syna_tcm_loader.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +struct fw_block { + unsigned char *data; + unsigned int size; + unsigned int addr; +}; + +struct tcm_firmware { + struct fw_block boot_cfg; + struct fw_block app_fw; + struct fw_block app_cfg; + struct fw_block disp_cfg; +}; + +enum tcm_mode { + MODE_BL, + MODE_APP, +}; + +static int rmi_is_tcm(struct rmi_device *rmi) { + /* TODO */ + return 0; +} + +static int syna_tcm_switch_mode(struct rmi_device *rmi, enum tcm_mode mode) +{ + unsigned char *response; + unsigned int + int ret; + + mutex_lock(rmi->dev->mutex); + /* TODO */ + mutex_unlock(rmi->dev->mutex); + +} + +static int syna_tcm_fw_load(struct rmi_device *rmi, + const struct tcm_firmware *fw) +{ + int ret; + + ret = syna_tcm_switch_mode(struct rmi_device *rmi, MODE_BL); + if (!ret) { + dev_err(dev, "Failed to switch into bootloader mode: %d\n", ret); + return ret; + } + + /* XXX: Write the 4 fw imgs */ +} + +static int syna_tcm_fw_init(struct rmi_device *rmi) +{ + struct tcm_firmware *fw; + struct device *dev = rmi->dev; + int ret; + + ret = rmi_is_tcm(rmi); + if (ret) { + dev_err(dev, "RMI device is not TCM; aborting fw load: %d\n", ret); + return -ENODEV; + } + + fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + ret = syna_tcm_fw_load(rmi, const struct tcm_firmware *fw) +} + +static const struct of_device_id syna_tcm_fw_match { + /* XXX: See if this is actually generic before upstreaming */ + { + .compatible = "syna,tcm-fw-loader", + }, + {}, +}; From 3ee4455e33bf8bce22147b0f6be773d116b48392 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Fri, 3 Nov 2023 18:29:53 +0100 Subject: [PATCH 05/10] UNTESTED: arm64: dts: qcom: sm8350: Add 4pin config for uart18 node --- arch/arm64/boot/dts/qcom/sm8350.dtsi | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index a72f3c470089..1f686f63d4f2 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -2971,6 +2971,38 @@ bias-disable; }; + qup_uart18_4pin: qup-uart18-4pin-state { + cts-pins { + pins = "gpio68"; + function = "qup18"; + drive-strength = <2>; + bias-bus-hold; + }; + + rts-pins { + pins = "gpio69"; + function = "qup18"; + drive-strength = <2>; + bias-disable; + }; + + tx-pins { + pins = "gpio70"; + function = "qup18"; + drive-strength = <2>; + bias-disable; + output-enable; + }; + + rx-pins { + pins = "gpio71"; + function = "qup18"; + drive-strength = <2>; + bias-disable; + output-disable; + }; + }; + qup_i2c0_default: qup-i2c0-default-state { pins = "gpio4", "gpio5"; function = "qup0"; From 1c9bc9bcf65cc9192702f42c04f6cd975299138b Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Sat, 11 Nov 2023 23:46:47 +0100 Subject: [PATCH 06/10] UNFINISHED: arm64: dts: qcom: sm8350-lemonade(p): More feature bringup --- .../boot/dts/qcom/sm8350-oneplus-common.dtsi | 294 +++++++++++++++++- .../boot/dts/qcom/sm8350-oneplus-lemonade.dts | 26 +- .../dts/qcom/sm8350-oneplus-lemonadep.dts | 28 ++ 3 files changed, 326 insertions(+), 22 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sm8350-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sm8350-oneplus-common.dtsi index 3dc0a98409c4..a9227d6764fb 100644 --- a/arch/arm64/boot/dts/qcom/sm8350-oneplus-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350-oneplus-common.dtsi @@ -24,6 +24,12 @@ chassis-type = "handset"; interrupt-parent = <&intc>; + aliases { + bluetooth = &bluetooth; + serial0 = &uart2; + serial1 = &uart18; + }; + bat: battery { compatible = "simple-battery"; device-chemistry = "lithium-ion"; @@ -54,6 +60,33 @@ }; }; + cnss_supply: cnss-supply { + compatible = "regulator-fixed"; + regulator-name = "wcnss_regulator"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <1900000>; + + enable-active-high; + + pinctrl-0 = <&cnss_regulator_active>; + pinctrl-names = "default"; + + gpio = <&tlmm 201 GPIO_ACTIVE_HIGH>; + }; + + /* + gpio_i2c4: gpio-i2c { + compatible = "i2c-gpio"; + sda-gpios = <&tlmm 20 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + scl-gpios = <&tlmm 21 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + //#address-cells = <1>; + //#size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&qup_i2c4_default>, <&tp_enable_2v8>; + }; + */ + gpio-keys { compatible = "gpio-keys"; @@ -106,6 +139,27 @@ }; }; + qca6490_pd: qca6490 { + compatible = "qcom,qca6490"; + #power-domain-cells = <0>; + + vddaonbt-supply = <&pm8350_s11>; + vddaonwl-supply = <&pmr735a_s2>; + vddasd-supply = <&pmr735a_l7>; + vddrfa1-supply = <&pm8350c_s1>; + vddrfa2-supply = <&pm8350_s12>; + vddrfa3-supply = <&pm8350c_s1>; + vddpcie1-supply = <&cnss_supply>; + vddpcie2-supply = <&pm8350c_s1>; + vddio-supply = <&pm8350_s10>; + + pinctrl-0 = <&cnss_wlan_sleep>; + pinctrl-1 = <&cnss_wlan_active>; + pinctrl-names = "default", "active"; + + status = "disabled"; + }; + display_panel_avdd: regulator-display { compatible = "regulator-fixed"; regulator-name = "display_panel_avdd"; @@ -403,9 +457,23 @@ }; }; - /* pm8350b has one regulator here; unused for now */ - regulators-2 { + compatible = "qcom,pm8350b-rpmh-regulators"; + qcom,pmic-id = "d"; + + vdd-l1-supply = <&pm8350_s12>; + + pm8350b_l1: ldo1 { + regulator-name = "pm8350b_l1"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allowed-modes = ; + }; + }; + + regulators-3 { compatible = "qcom,pmr735a-rpmh-regulators"; qcom,pmic-id = "e"; @@ -486,6 +554,24 @@ status = "okay"; }; +&dispcc { + //status = "okay"; +}; + +&gmu { + /* FIXME: check_dtbs complains */ + //firmware-name = "qcom/sm8350/lemonade/a660_gmu.bin"; +}; + +&gpu { + //status = "okay"; + + zap-shader { + memory-region = <&pil_gpu_mem>; + firmware-name = "qcom/sm8350/lemonade/a660_zap.mbn"; + }; +}; + &i2c2 { clock-frequency = <100000>; status = "okay"; @@ -497,6 +583,7 @@ }; }; + &i2c4 { clock-frequency = <400000>; status = "okay"; @@ -554,14 +641,74 @@ status = "okay"; }; +&mdss { + //status = "okay"; +}; + +&mdss_dp { + //status = "okay"; + + ports { + port@1 { + reg = <1>; + + endpoint { + }; + }; + }; +}; + +&mdss_dsi0 { + vdda-supply = <&pm8350_l6>; + + //status = "okay"; + + /* Common properties for OnePlus 9 and 9 Pro */ + display_panel: panel-1@0 { + reg = <0>; + + pinctrl-0 = <&sde_dsi_active &sde_te_active>; + pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + pinctrl-names = "default", "sleep"; + + reset-gpios = <&tlmm 24 GPIO_ACTIVE_LOW>; + + vdd-supply = <&pm8350c_l13>; + vddio-supply = <&pm8350c_l12>; + avdd-supply = <&display_panel_avdd>; + //clocks = <&rpmhcc RPMH_DIV_CLK1>; + //clock-names = "div_clk"; + //clocks = <&mdss_dsi0_phy DISP_CC_MDSS_> + + status = "disabled"; + + port { + panel_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + }; +}; + +&mdss_dsi0_out { + remote-endpoint = <&panel_in>; + data-lanes = <0 1 2 3>; +}; + +&mdss_dsi0_phy { + vdds-supply = <&pm8350_l5>; + //status = "okay"; +}; + &mpss { firmware-name = "qcom/sm8350/OnePlus/lemonade/modem.mbn"; - status = "okay"; }; &pcie0 { - pinctrl-0 = <&pcie0_default_state>; - pinctrl-names = "default"; + pinctrl-0 = <&pcie0_default_state>;//, <&pcie0_sleep_state>; + pinctrl-names = "default";//, "sleep"; + + //power-domains = <&qca6490_pd>; perst-gpios = <&tlmm 94 GPIO_ACTIVE_HIGH>; wake-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>; @@ -573,6 +720,8 @@ vdda-phy-supply = <&pm8350_l5>; vdda-pll-supply = <&pm8350_l6>; + power-domains = <&qca6490_pd>; + status = "okay"; }; @@ -865,10 +1014,15 @@ }; &pon_resin { - linux,code = ; + linux,code = ; status = "okay"; }; - +/* +&qup_i2c4_default { + function = "gpio"; + bias-disable; +}; +*/ &qupv3_id_0 { status = "okay"; }; @@ -919,6 +1073,74 @@ /* Fingerprint sensor and NFC-related pins shouldn't be touched by us */ gpio-reserved-ranges = <52 8>; + bt_active: bt-active-state { + pins = "gpio65"; + function = "gpio"; + drive-strength = <16>; + output-high; + bias-pull-up; + }; + + bt_sleep: bt-sleep-state { + pins = "gpio65"; + function = "gpio"; + drive-strength = <2>; + output-low; + bias-pull-down; + }; + + cnss_regulator_active: cnss-regulator-active-state { + pins = "gpio201"; + function = "gpio"; + drive-strength = <16>; + bias-pull-down; + output-low; + }; + + cnss_wlan_active: cnss-wlan-active-state { + pins = "gpio64"; + function = "gpio"; + drive-strength = <16>; + output-high; + bias-pull-up; + }; + + cnss_wlan_sleep: cnss-wlan-sleep-state { + pins = "gpio64"; + function = "gpio"; + drive-strength = <2>; + output-low; + bias-pull-down; + }; + + sde_dsi_active: sde-dsi-active-state { + pins = "gpio24"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + sde_dsi_suspend: sde-dsi-sleep-state { + pins = "gpio24"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + sde_te_active: sde-te-active-state { + pins = "gpio82"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + sde_te_suspend: sde-te-sleep-state { + pins = "gpio82"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + pcie0_default_state: pcie0-default-state { perst-pins { pins = "gpio94"; @@ -940,13 +1162,13 @@ drive-strength = <2>; bias-pull-up; }; + }; - sleep-pins { - pins = "gpio95"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; + pcie0_sleep_state: pcie0-sleep-state { + pins = "gpio95"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; }; pcie1_default_state: pcie1-default-state { @@ -969,10 +1191,17 @@ function = "gpio"; drive-strength = <2>; bias-pull-up; - /* wil6210_refclk_en_pin lives here also */ }; }; + /* XXX: Is this correct? */ + pcie1_sleep_state: pcie1-sleep-state { + pins = "gpio98"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + /* Modem-related pin common on both devices */ rf_cable_ant0_active: rf-cable-ant0-active-state { pins = "gpio165"; @@ -1005,6 +1234,43 @@ }; }; +&uart2 { + status = "okay"; +}; + +&uart18 { + pinctrl-0 = <&qup_uart18_4pin>; + status = "okay"; + + bluetooth: bluetooth { + compatible = "qcom,qca6490-bt"; + + pinctrl-0 = <&bt_active>; + pinctrl-1 = <&bt_sleep>; + pinctrl-names = "default", "sleep"; + + /* QCA6490 requires wlan and bluetooth be enabled simultaneously */ + enable-gpios = <&tlmm 65 GPIO_ACTIVE_HIGH>; + wlan-gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>; + swctrl-gpios = <&tlmm 153 GPIO_ACTIVE_HIGH>; + + power-domains = <&qca6490_pd>; + + //vddio-supply = <&pm8350_s10>; + //vddaon-supply = <&pm8350_s11>; + //vddasd-supply = <&pmr735a_l7>; + //vdddig-supply = <&pm8350_s11>; + //vddrfa1-supply = <&pm8350c_s1>; + //vddrfa2-supply = <&pm8350_s12>; + //vddpcie1-supply = <&pm8350_s12>; + //vddpcie2-supply = <&pm8350c_s1>; + + max-speed = <3200000>; + + //status = "disabled"; + }; +}; + &ufs_mem_hc { reset-gpios = <&tlmm 203 GPIO_ACTIVE_LOW>; diff --git a/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonade.dts b/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonade.dts index da2dc7e55c4f..a22ea07b6fb7 100644 --- a/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonade.dts +++ b/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonade.dts @@ -36,6 +36,16 @@ <&rf_cable_ant3_active>, <&rf_cable_ant7_active>; pinctrl-names = "default"; + status = "okay"; +}; + +&qca6490_pd { + rf-gpios = <&tlmm 165 0>, + <&tlmm 27 0>, + <&tlmm 92 0>, + <&tlmm 44 0>, + <&tlmm 155 0>; + status = "okay"; }; &tlmm { @@ -68,18 +78,18 @@ bias-pull-up; }; - tp_rst_suspend: tp-rst-suspend-state { - pins = "gpio22"; - function = "gpio"; - drive-strength = <2>; - bias-pull-down; - }; - - tp_enable_2v8: tp-enable-2v8-state { + tp_enable_2v8: tp-2v8-enable-state { pins = "gpio74"; function = "gpio"; drive-strength = <8>; bias-pull-up; output-high; }; + + tp_rst_suspend: tp-rst-suspend-state { + pins = "gpio22"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonadep.dts b/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonadep.dts index 713dc08e696c..244611cbb69b 100644 --- a/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonadep.dts +++ b/arch/arm64/boot/dts/qcom/sm8350-oneplus-lemonadep.dts @@ -14,7 +14,35 @@ model = "OnePlus 9 Pro"; }; +&display_panel { + compatible = "samsung,amb670yf01"; + status = "fail"; +}; + &mpss { pinctrl-0 = <&rf_cable_ant0_active>; pinctrl-names = "default"; + status = "okay"; +}; + +&qca6490_pd { + rf-gpios = <&tlmm 165 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&tlmm { + tp_enable_2v8: tp-2v8-enable-state { + pins = "gpio31", "gpio197"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + output-high; + }; + + tp_rst_suspend: tp-rst-suspend-state { + pins = "gpio22"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; }; From dc758a448b70dab6bdf38b976ef8fc81b1d0e169 Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Sat, 2 Dec 2023 23:41:57 +0100 Subject: [PATCH 07/10] Revert "usb: gadget: u_ether: Replace netif_stop_queue with netif_device_detach" This reverts commit f49449fbc21e7e9550a5203902d69c8ae7dfd918. --- drivers/usb/gadget/function/u_ether.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 9d1c40c152d8..4bb0553da658 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1200,7 +1200,7 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); - netif_device_detach(dev->net); + netif_stop_queue(dev->net); netif_carrier_off(dev->net); /* disable endpoints, forcing (synchronous) completion From 5b29df495c4aab187855e2a7c6195bf3dfc1336f Mon Sep 17 00:00:00 2001 From: Piecuuu <56731916+Piecuuu@users.noreply.github.com> Date: Sun, 28 Jun 2026 18:33:58 +0200 Subject: [PATCH 08/10] drivers: gpu: drm: Add driver for the Samsung amb641zr01 dsi cmd mode panel --- drivers/gpu/drm/panel/Kconfig | 8 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-samsung-amb641zr01.c | 478 ++++++++++++++++++ 3 files changed, 487 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-samsung-amb641zr01.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 07e51b16fa41..60455f88274d 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -551,6 +551,14 @@ config DRM_PANEL_SAMSUNG_AMB670YF01 Say Y here if you want to enable support for the Samsung AMB670YF01 DPI 1440x3216 panel found in OnePlus 9 Pro smartphones. +config DRM_PANEL_SAMSUNG_AMB641ZR01 + tristate "Samsung AMB641ZR01 DSI command mode panel" + depends on OF && DRM_MIPI_DSI && BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS + help + Say Y here if you want to enable support for the Samsung AMB641ZR01 DPI + 1080x2340 panel found in Samsung Galaxy S21 FE smartphones. + config DRM_PANEL_SAMSUNG_ATNA33XC20 tristate "Samsung ATNA33XC20 eDP panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index c3e206014cdf..f7bbe11ea704 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMB670YF01) += panel-samsung-amb670yf01.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMB641ZR01) += panel-samsung-amb641zr01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o diff --git a/drivers/gpu/drm/panel/panel-samsung-amb641zr01.c b/drivers/gpu/drm/panel/panel-samsung-amb641zr01.c new file mode 100644 index 000000000000..58f38f9d757d --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-amb641zr01.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Panel driver for the Samsung AMB641ZR01. + * Found in the Samsung Galaxy S21 FE phone. + * + * Copyright (c) 2026 <> + */ + +#include +#include +#include +#include +#include +#include +#include + +#include