Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'qcom-drivers-for-6.18-2' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into soc/drivers

More Qualcomm device driver updates for v6.18

Introduce support for loading firmware into the QUP serial engines from
Linux, which allows deferring selection of which protocol (uart, i2c,
spi, etc) a given SE should have until the OS loads.

Also introduce the "object invoke" interface in the SCM driver, to
provide interface to the Qualcomm TEE driver.

* tag 'qcom-drivers-for-6.18-2' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux:
serial: qcom-geni: Load UART qup Firmware from linux side
spi: geni-qcom: Load spi qup Firmware from linux side
i2c: qcom-geni: Load i2c qup Firmware from linux side
soc: qcom: geni-se: Add support to load QUP SE Firmware via Linux subsystem
soc: qcom: geni-se: Cleanup register defines and update copyright
dt-bindings: qcom: se-common: Add QUP Peripheral-specific properties for I2C, SPI, and SERIAL bus
firmware: qcom: scm: add support for object invocation
firmware: qcom: tzmem: export shm_bridge create/delete

Link: https://lore.kernel.org/r/20250921020225.595403-1-andersson@kernel.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

+739 -32
+1
Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml
··· 75 75 76 76 allOf: 77 77 - $ref: /schemas/i2c/i2c-controller.yaml# 78 + - $ref: /schemas/soc/qcom/qcom,se-common-props.yaml# 78 79 - if: 79 80 properties: 80 81 compatible:
+1
Documentation/devicetree/bindings/serial/qcom,serial-geni-qcom.yaml
··· 12 12 13 13 allOf: 14 14 - $ref: /schemas/serial/serial.yaml# 15 + - $ref: /schemas/soc/qcom/qcom,se-common-props.yaml# 15 16 16 17 properties: 17 18 compatible:
+26
Documentation/devicetree/bindings/soc/qcom/qcom,se-common-props.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/soc/qcom/qcom,se-common-props.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: QUP Peripheral-specific properties for I2C, SPI and SERIAL bus 8 + 9 + description: 10 + The Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) is 11 + a programmable module that supports a wide range of serial interfaces 12 + such as UART, SPI, I2C, I3C, etc. This defines the common properties used 13 + across QUP-supported peripherals. 14 + 15 + maintainers: 16 + - Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com> 17 + - Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com> 18 + 19 + properties: 20 + qcom,enable-gsi-dma: 21 + $ref: /schemas/types.yaml#/definitions/flag 22 + description: 23 + Configure the Serial Engine (SE) to transfer data in QCOM GPI DMA mode. 24 + By default, FIFO mode (PIO/CPU DMA) will be selected. 25 + 26 + additionalProperties: true
+1
Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
··· 25 25 26 26 allOf: 27 27 - $ref: /schemas/spi/spi-controller.yaml# 28 + - $ref: /schemas/soc/qcom/qcom,se-common-props.yaml# 28 29 29 30 properties: 30 31 compatible:
+119
drivers/firmware/qcom/qcom_scm.c
··· 2098 2098 #endif /* CONFIG_QCOM_QSEECOM */ 2099 2099 2100 2100 /** 2101 + * qcom_scm_qtee_invoke_smc() - Invoke a QTEE object. 2102 + * @inbuf: start address of memory area used for inbound buffer. 2103 + * @inbuf_size: size of the memory area used for inbound buffer. 2104 + * @outbuf: start address of memory area used for outbound buffer. 2105 + * @outbuf_size: size of the memory area used for outbound buffer. 2106 + * @result: result of QTEE object invocation. 2107 + * @response_type: response type returned by QTEE. 2108 + * 2109 + * @response_type determines how the contents of @inbuf and @outbuf 2110 + * should be processed. 2111 + * 2112 + * Return: On success, return 0 or <0 on failure. 2113 + */ 2114 + int qcom_scm_qtee_invoke_smc(phys_addr_t inbuf, size_t inbuf_size, 2115 + phys_addr_t outbuf, size_t outbuf_size, 2116 + u64 *result, u64 *response_type) 2117 + { 2118 + struct qcom_scm_desc desc = { 2119 + .svc = QCOM_SCM_SVC_SMCINVOKE, 2120 + .cmd = QCOM_SCM_SMCINVOKE_INVOKE, 2121 + .owner = ARM_SMCCC_OWNER_TRUSTED_OS, 2122 + .args[0] = inbuf, 2123 + .args[1] = inbuf_size, 2124 + .args[2] = outbuf, 2125 + .args[3] = outbuf_size, 2126 + .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, 2127 + QCOM_SCM_RW, QCOM_SCM_VAL), 2128 + }; 2129 + struct qcom_scm_res res; 2130 + int ret; 2131 + 2132 + ret = qcom_scm_call(__scm->dev, &desc, &res); 2133 + if (ret) 2134 + return ret; 2135 + 2136 + if (response_type) 2137 + *response_type = res.result[0]; 2138 + 2139 + if (result) 2140 + *result = res.result[1]; 2141 + 2142 + return 0; 2143 + } 2144 + EXPORT_SYMBOL(qcom_scm_qtee_invoke_smc); 2145 + 2146 + /** 2147 + * qcom_scm_qtee_callback_response() - Submit response for callback request. 2148 + * @buf: start address of memory area used for outbound buffer. 2149 + * @buf_size: size of the memory area used for outbound buffer. 2150 + * @result: Result of QTEE object invocation. 2151 + * @response_type: Response type returned by QTEE. 2152 + * 2153 + * @response_type determines how the contents of @buf should be processed. 2154 + * 2155 + * Return: On success, return 0 or <0 on failure. 2156 + */ 2157 + int qcom_scm_qtee_callback_response(phys_addr_t buf, size_t buf_size, 2158 + u64 *result, u64 *response_type) 2159 + { 2160 + struct qcom_scm_desc desc = { 2161 + .svc = QCOM_SCM_SVC_SMCINVOKE, 2162 + .cmd = QCOM_SCM_SMCINVOKE_CB_RSP, 2163 + .owner = ARM_SMCCC_OWNER_TRUSTED_OS, 2164 + .args[0] = buf, 2165 + .args[1] = buf_size, 2166 + .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL), 2167 + }; 2168 + struct qcom_scm_res res; 2169 + int ret; 2170 + 2171 + ret = qcom_scm_call(__scm->dev, &desc, &res); 2172 + if (ret) 2173 + return ret; 2174 + 2175 + if (response_type) 2176 + *response_type = res.result[0]; 2177 + 2178 + if (result) 2179 + *result = res.result[1]; 2180 + 2181 + return 0; 2182 + } 2183 + EXPORT_SYMBOL(qcom_scm_qtee_callback_response); 2184 + 2185 + static void qcom_scm_qtee_free(void *data) 2186 + { 2187 + struct platform_device *qtee_dev = data; 2188 + 2189 + platform_device_unregister(qtee_dev); 2190 + } 2191 + 2192 + static void qcom_scm_qtee_init(struct qcom_scm *scm) 2193 + { 2194 + struct platform_device *qtee_dev; 2195 + u64 result, response_type; 2196 + int ret; 2197 + 2198 + /* 2199 + * Probe for smcinvoke support. This will fail due to invalid buffers, 2200 + * but first, it checks whether the call is supported in QTEE syscall 2201 + * handler. If it is not supported, -EIO is returned. 2202 + */ 2203 + ret = qcom_scm_qtee_invoke_smc(0, 0, 0, 0, &result, &response_type); 2204 + if (ret == -EIO) 2205 + return; 2206 + 2207 + /* Setup QTEE interface device. */ 2208 + qtee_dev = platform_device_register_data(scm->dev, "qcomtee", 2209 + PLATFORM_DEVID_NONE, NULL, 0); 2210 + if (IS_ERR(qtee_dev)) 2211 + return; 2212 + 2213 + devm_add_action_or_reset(scm->dev, qcom_scm_qtee_free, qtee_dev); 2214 + } 2215 + 2216 + /** 2101 2217 * qcom_scm_is_available() - Checks if SCM is available 2102 2218 */ 2103 2219 bool qcom_scm_is_available(void) ··· 2444 2328 */ 2445 2329 ret = qcom_scm_qseecom_init(scm); 2446 2330 WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); 2331 + 2332 + /* Initialize the QTEE object interface. */ 2333 + qcom_scm_qtee_init(scm); 2447 2334 2448 2335 return 0; 2449 2336 }
+7
drivers/firmware/qcom/qcom_scm.h
··· 156 156 #define QCOM_SCM_SVC_GPU 0x28 157 157 #define QCOM_SCM_SVC_GPU_INIT_REGS 0x01 158 158 159 + /* ARM_SMCCC_OWNER_TRUSTED_OS calls */ 160 + 161 + #define QCOM_SCM_SVC_SMCINVOKE 0x06 162 + #define QCOM_SCM_SMCINVOKE_INVOKE_LEGACY 0x00 163 + #define QCOM_SCM_SMCINVOKE_CB_RSP 0x01 164 + #define QCOM_SCM_SMCINVOKE_INVOKE 0x02 165 + 159 166 /* common error codes */ 160 167 #define QCOM_SCM_V2_EBUSY -12 161 168 #define QCOM_SCM_ENOMEM -5
+52 -11
drivers/firmware/qcom/qcom_tzmem.c
··· 110 110 return 0; 111 111 } 112 112 113 - static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) 113 + /** 114 + * qcom_tzmem_shm_bridge_create() - Create a SHM bridge. 115 + * @paddr: Physical address of the memory to share. 116 + * @size: Size of the memory to share. 117 + * @handle: Handle to the SHM bridge. 118 + * 119 + * On platforms that support SHM bridge, this function creates a SHM bridge 120 + * for the given memory region with QTEE. The handle returned by this function 121 + * must be passed to qcom_tzmem_shm_bridge_delete() to free the SHM bridge. 122 + * 123 + * Return: On success, returns 0; on failure, returns < 0. 124 + */ 125 + int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, size_t size, u64 *handle) 114 126 { 115 127 u64 pfn_and_ns_perm, ipfn_and_s_perm, size_and_flags; 116 128 int ret; ··· 130 118 if (!qcom_tzmem_using_shm_bridge) 131 119 return 0; 132 120 133 - pfn_and_ns_perm = (u64)area->paddr | QCOM_SCM_PERM_RW; 134 - ipfn_and_s_perm = (u64)area->paddr | QCOM_SCM_PERM_RW; 135 - size_and_flags = area->size | (1 << QCOM_SHM_BRIDGE_NUM_VM_SHIFT); 121 + pfn_and_ns_perm = paddr | QCOM_SCM_PERM_RW; 122 + ipfn_and_s_perm = paddr | QCOM_SCM_PERM_RW; 123 + size_and_flags = size | (1 << QCOM_SHM_BRIDGE_NUM_VM_SHIFT); 124 + 125 + ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, 126 + size_and_flags, QCOM_SCM_VMID_HLOS, 127 + handle); 128 + if (ret) { 129 + dev_err(qcom_tzmem_dev, 130 + "SHM Bridge failed: ret %d paddr 0x%pa, size %zu\n", 131 + ret, &paddr, size); 132 + 133 + return ret; 134 + } 135 + 136 + return 0; 137 + } 138 + EXPORT_SYMBOL_GPL(qcom_tzmem_shm_bridge_create); 139 + 140 + /** 141 + * qcom_tzmem_shm_bridge_delete() - Delete a SHM bridge. 142 + * @handle: Handle to the SHM bridge. 143 + * 144 + * On platforms that support SHM bridge, this function deletes the SHM bridge 145 + * for the given memory region. The handle must be the same as the one 146 + * returned by qcom_tzmem_shm_bridge_create(). 147 + */ 148 + void qcom_tzmem_shm_bridge_delete(u64 handle) 149 + { 150 + if (qcom_tzmem_using_shm_bridge) 151 + qcom_scm_shm_bridge_delete(handle); 152 + } 153 + EXPORT_SYMBOL_GPL(qcom_tzmem_shm_bridge_delete); 154 + 155 + static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) 156 + { 157 + int ret; 136 158 137 159 u64 *handle __free(kfree) = kzalloc(sizeof(*handle), GFP_KERNEL); 138 160 if (!handle) 139 161 return -ENOMEM; 140 162 141 - ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, 142 - size_and_flags, QCOM_SCM_VMID_HLOS, 143 - handle); 163 + ret = qcom_tzmem_shm_bridge_create(area->paddr, area->size, handle); 144 164 if (ret) 145 165 return ret; 146 166 ··· 185 141 { 186 142 u64 *handle = area->priv; 187 143 188 - if (!qcom_tzmem_using_shm_bridge) 189 - return; 190 - 191 - qcom_scm_shm_bridge_delete(*handle); 144 + qcom_tzmem_shm_bridge_delete(*handle); 192 145 kfree(handle); 193 146 } 194 147
+7 -1
drivers/i2c/busses/i2c-qcom-geni.c
··· 870 870 goto err_clk; 871 871 } 872 872 proto = geni_se_read_proto(&gi2c->se); 873 - if (proto != GENI_SE_I2C) { 873 + if (proto == GENI_SE_INVALID_PROTO) { 874 + ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C); 875 + if (ret) { 876 + dev_err_probe(dev, ret, "i2c firmware load failed ret: %d\n", ret); 877 + goto err_resources; 878 + } 879 + } else if (proto != GENI_SE_I2C) { 874 880 ret = dev_err_probe(dev, -ENXIO, "Invalid proto %d\n", proto); 875 881 goto err_resources; 876 882 }
+487 -19
drivers/soc/qcom/qcom-geni-se.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 - // Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. 2 + /* 3 + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. 4 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 5 + */ 3 6 4 7 /* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ 5 8 #define __DISABLE_TRACE_MMIO__ 6 9 7 10 #include <linux/acpi.h> 11 + #include <linux/bitfield.h> 8 12 #include <linux/clk.h> 13 + #include <linux/firmware.h> 9 14 #include <linux/slab.h> 10 15 #include <linux/dma-mapping.h> 11 16 #include <linux/io.h> ··· 115 110 static const char * const icc_path_names[] = {"qup-core", "qup-config", 116 111 "qup-memory"}; 117 112 118 - #define QUP_HW_VER_REG 0x4 113 + static const char * const protocol_name[] = { "None", "SPI", "UART", "I2C", "I3C", "SPI SLAVE" }; 114 + 115 + /** 116 + * struct se_fw_hdr - Serial Engine firmware configuration header 117 + * 118 + * This structure defines the SE firmware header, which together with the 119 + * firmware payload is stored in individual ELF segments. 120 + * 121 + * @magic: Set to 'SEFW'. 122 + * @version: Structure version number. 123 + * @core_version: QUPV3 hardware version. 124 + * @serial_protocol: Encoded in GENI_FW_REVISION. 125 + * @fw_version: Firmware version, from GENI_FW_REVISION. 126 + * @cfg_version: Configuration version, from GENI_INIT_CFG_REVISION. 127 + * @fw_size_in_items: Number of 32-bit words in GENI_FW_RAM. 128 + * @fw_offset: Byte offset to GENI_FW_RAM array. 129 + * @cfg_size_in_items: Number of GENI_FW_CFG index/value pairs. 130 + * @cfg_idx_offset: Byte offset to GENI_FW_CFG index array. 131 + * @cfg_val_offset: Byte offset to GENI_FW_CFG values array. 132 + */ 133 + struct se_fw_hdr { 134 + __le32 magic; 135 + __le32 version; 136 + __le32 core_version; 137 + __le16 serial_protocol; 138 + __le16 fw_version; 139 + __le16 cfg_version; 140 + __le16 fw_size_in_items; 141 + __le16 fw_offset; 142 + __le16 cfg_size_in_items; 143 + __le16 cfg_idx_offset; 144 + __le16 cfg_val_offset; 145 + }; 146 + 147 + /*Magic numbers*/ 148 + #define SE_MAGIC_NUM 0x57464553 149 + 150 + #define MAX_GENI_CFG_RAMn_CNT 455 151 + 152 + #define MI_PBT_NON_PAGED_SEGMENT 0x0 153 + #define MI_PBT_HASH_SEGMENT 0x2 154 + #define MI_PBT_NOTUSED_SEGMENT 0x3 155 + #define MI_PBT_SHARED_SEGMENT 0x4 156 + 157 + #define MI_PBT_FLAG_PAGE_MODE BIT(20) 158 + #define MI_PBT_FLAG_SEGMENT_TYPE GENMASK(26, 24) 159 + #define MI_PBT_FLAG_ACCESS_TYPE GENMASK(23, 21) 160 + 161 + #define MI_PBT_PAGE_MODE_VALUE(x) FIELD_GET(MI_PBT_FLAG_PAGE_MODE, x) 162 + 163 + #define MI_PBT_SEGMENT_TYPE_VALUE(x) FIELD_GET(MI_PBT_FLAG_SEGMENT_TYPE, x) 164 + 165 + #define MI_PBT_ACCESS_TYPE_VALUE(x) FIELD_GET(MI_PBT_FLAG_ACCESS_TYPE, x) 166 + 167 + #define M_COMMON_GENI_M_IRQ_EN (GENMASK(6, 1) | \ 168 + M_IO_DATA_DEASSERT_EN | \ 169 + M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \ 170 + M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \ 171 + M_TX_FIFO_WR_ERR_EN) 172 + 173 + /* Common QUPV3 registers */ 174 + #define QUPV3_HW_VER_REG 0x4 175 + #define QUPV3_SE_AHB_M_CFG 0x118 176 + #define QUPV3_COMMON_CFG 0x120 177 + #define QUPV3_COMMON_CGC_CTRL 0x21c 178 + 179 + /* QUPV3_COMMON_CFG fields */ 180 + #define FAST_SWITCH_TO_HIGH_DISABLE BIT(0) 181 + 182 + /* QUPV3_SE_AHB_M_CFG fields */ 183 + #define AHB_M_CLK_CGC_ON BIT(0) 184 + 185 + /* QUPV3_COMMON_CGC_CTRL fields */ 186 + #define COMMON_CSR_SLV_CLK_CGC_ON BIT(0) 119 187 120 188 /* Common SE registers */ 121 - #define GENI_INIT_CFG_REVISION 0x0 122 - #define GENI_S_INIT_CFG_REVISION 0x4 123 - #define GENI_OUTPUT_CTRL 0x24 124 - #define GENI_CGC_CTRL 0x28 125 - #define GENI_CLK_CTRL_RO 0x60 126 - #define GENI_FW_S_REVISION_RO 0x6c 189 + #define SE_GENI_INIT_CFG_REVISION 0x0 190 + #define SE_GENI_S_INIT_CFG_REVISION 0x4 191 + #define SE_GENI_CGC_CTRL 0x28 192 + #define SE_GENI_CLK_CTRL_RO 0x60 193 + #define SE_GENI_FW_S_REVISION_RO 0x6c 194 + #define SE_GENI_CFG_REG0 0x100 127 195 #define SE_GENI_BYTE_GRAN 0x254 128 196 #define SE_GENI_TX_PACKING_CFG0 0x260 129 197 #define SE_GENI_TX_PACKING_CFG1 0x264 130 198 #define SE_GENI_RX_PACKING_CFG0 0x284 131 199 #define SE_GENI_RX_PACKING_CFG1 0x288 132 - #define SE_GENI_M_GP_LENGTH 0x910 133 - #define SE_GENI_S_GP_LENGTH 0x914 200 + #define SE_GENI_S_IRQ_ENABLE 0x644 134 201 #define SE_DMA_TX_PTR_L 0xc30 135 202 #define SE_DMA_TX_PTR_H 0xc34 136 203 #define SE_DMA_TX_ATTR 0xc38 ··· 219 142 #define SE_DMA_RX_IRQ_EN 0xd48 220 143 #define SE_DMA_RX_IRQ_EN_SET 0xd4c 221 144 #define SE_DMA_RX_IRQ_EN_CLR 0xd50 222 - #define SE_DMA_RX_LEN_IN 0xd54 223 145 #define SE_DMA_RX_MAX_BURST 0xd5c 224 146 #define SE_DMA_RX_FLUSH 0xd60 225 147 #define SE_GSI_EVENT_EN 0xe18 226 148 #define SE_IRQ_EN 0xe1c 227 149 #define SE_DMA_GENERAL_CFG 0xe30 150 + #define SE_GENI_FW_REVISION 0x1000 151 + #define SE_GENI_S_FW_REVISION 0x1004 152 + #define SE_GENI_CFG_RAMN 0x1010 153 + #define SE_GENI_CLK_CTRL 0x2000 154 + #define SE_DMA_IF_EN 0x2004 155 + #define SE_FIFO_IF_DISABLE 0x2008 156 + 157 + /* GENI_FW_REVISION_RO fields */ 158 + #define FW_REV_VERSION_MSK GENMASK(7, 0) 228 159 229 160 /* GENI_OUTPUT_CTRL fields */ 230 161 #define DEFAULT_IO_OUTPUT_CTRL_MSK GENMASK(6, 0) ··· 264 179 /* SE_DMA_GENERAL_CFG */ 265 180 #define DMA_RX_CLK_CGC_ON BIT(0) 266 181 #define DMA_TX_CLK_CGC_ON BIT(1) 267 - #define DMA_AHB_SLV_CFG_ON BIT(2) 182 + #define DMA_AHB_SLV_CLK_CGC_ON BIT(2) 268 183 #define AHB_SEC_SLV_CLK_CGC_ON BIT(3) 269 184 #define DUMMY_RX_NON_BUFFERABLE BIT(4) 270 185 #define RX_DMA_ZERO_PADDING_EN BIT(5) 271 186 #define RX_DMA_IRQ_DELAY_MSK GENMASK(8, 6) 272 187 #define RX_DMA_IRQ_DELAY_SHFT 6 188 + 189 + /* GENI_CLK_CTRL fields */ 190 + #define SER_CLK_SEL BIT(0) 191 + 192 + /* GENI_DMA_IF_EN fields */ 193 + #define DMA_IF_EN BIT(0) 194 + 195 + #define geni_setbits32(_addr, _v) writel(readl(_addr) | (_v), _addr) 196 + #define geni_clrbits32(_addr, _v) writel(readl(_addr) & ~(_v), _addr) 273 197 274 198 /** 275 199 * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version ··· 290 196 { 291 197 struct geni_wrapper *wrapper = se->wrapper; 292 198 293 - return readl_relaxed(wrapper->base + QUP_HW_VER_REG); 199 + return readl_relaxed(wrapper->base + QUPV3_HW_VER_REG); 294 200 } 295 201 EXPORT_SYMBOL_GPL(geni_se_get_qup_hw_version); 296 202 ··· 314 220 { 315 221 u32 val; 316 222 317 - val = readl_relaxed(base + GENI_CGC_CTRL); 223 + val = readl_relaxed(base + SE_GENI_CGC_CTRL); 318 224 val |= DEFAULT_CGC_EN; 319 - writel_relaxed(val, base + GENI_CGC_CTRL); 225 + writel_relaxed(val, base + SE_GENI_CGC_CTRL); 320 226 321 227 val = readl_relaxed(base + SE_DMA_GENERAL_CFG); 322 - val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON; 228 + val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CLK_CGC_ON; 323 229 val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON; 324 230 writel_relaxed(val, base + SE_DMA_GENERAL_CFG); 325 231 ··· 752 658 } 753 659 EXPORT_SYMBOL_GPL(geni_se_clk_freq_match); 754 660 755 - #define GENI_SE_DMA_DONE_EN BIT(0) 756 - #define GENI_SE_DMA_EOT_EN BIT(1) 757 - #define GENI_SE_DMA_AHB_ERR_EN BIT(2) 661 + #define GENI_SE_DMA_DONE_EN BIT(0) 662 + #define GENI_SE_DMA_EOT_EN BIT(1) 663 + #define GENI_SE_DMA_AHB_ERR_EN BIT(2) 664 + #define GENI_SE_DMA_RESET_DONE_EN BIT(3) 665 + #define GENI_SE_DMA_FLUSH_DONE BIT(4) 666 + 758 667 #define GENI_SE_DMA_EOT_BUF BIT(0) 759 668 760 669 /** ··· 987 890 return 0; 988 891 } 989 892 EXPORT_SYMBOL_GPL(geni_icc_disable); 893 + 894 + /** 895 + * geni_find_protocol_fw() - Locate and validate SE firmware for a protocol. 896 + * @dev: Pointer to the device structure. 897 + * @fw: Pointer to the firmware image. 898 + * @protocol: Expected serial engine protocol type. 899 + * 900 + * Identifies the appropriate firmware image or configuration required for a 901 + * specific communication protocol instance running on a Qualcomm GENI 902 + * controller. 903 + * 904 + * Return: pointer to a valid 'struct se_fw_hdr' if found, or NULL otherwise. 905 + */ 906 + static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct firmware *fw, 907 + enum geni_se_protocol_type protocol) 908 + { 909 + const struct elf32_hdr *ehdr; 910 + const struct elf32_phdr *phdrs; 911 + const struct elf32_phdr *phdr; 912 + struct se_fw_hdr *sefw; 913 + u32 fw_end, cfg_idx_end, cfg_val_end; 914 + u16 fw_size; 915 + int i; 916 + 917 + if (!fw || fw->size < sizeof(struct elf32_hdr)) 918 + return NULL; 919 + 920 + ehdr = (const struct elf32_hdr *)fw->data; 921 + phdrs = (const struct elf32_phdr *)(fw->data + ehdr->e_phoff); 922 + 923 + /* 924 + * The firmware is expected to have at least two program headers (segments). 925 + * One for metadata and the other for the actual protocol-specific firmware. 926 + */ 927 + if (ehdr->e_phnum < 2) { 928 + dev_err(dev, "Invalid firmware: less than 2 program headers\n"); 929 + return NULL; 930 + } 931 + 932 + for (i = 0; i < ehdr->e_phnum; i++) { 933 + phdr = &phdrs[i]; 934 + 935 + if (fw->size < phdr->p_offset + phdr->p_filesz) { 936 + dev_err(dev, "Firmware size (%zu) < expected offset (%u) + size (%u)\n", 937 + fw->size, phdr->p_offset, phdr->p_filesz); 938 + return NULL; 939 + } 940 + 941 + if (phdr->p_type != PT_LOAD || !phdr->p_memsz) 942 + continue; 943 + 944 + if (MI_PBT_PAGE_MODE_VALUE(phdr->p_flags) != MI_PBT_NON_PAGED_SEGMENT || 945 + MI_PBT_SEGMENT_TYPE_VALUE(phdr->p_flags) == MI_PBT_HASH_SEGMENT || 946 + MI_PBT_ACCESS_TYPE_VALUE(phdr->p_flags) == MI_PBT_NOTUSED_SEGMENT || 947 + MI_PBT_ACCESS_TYPE_VALUE(phdr->p_flags) == MI_PBT_SHARED_SEGMENT) 948 + continue; 949 + 950 + if (phdr->p_filesz < sizeof(struct se_fw_hdr)) 951 + continue; 952 + 953 + sefw = (struct se_fw_hdr *)(fw->data + phdr->p_offset); 954 + fw_size = le16_to_cpu(sefw->fw_size_in_items); 955 + fw_end = le16_to_cpu(sefw->fw_offset) + fw_size * sizeof(u32); 956 + cfg_idx_end = le16_to_cpu(sefw->cfg_idx_offset) + 957 + le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u8); 958 + cfg_val_end = le16_to_cpu(sefw->cfg_val_offset) + 959 + le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u32); 960 + 961 + if (le32_to_cpu(sefw->magic) != SE_MAGIC_NUM || le32_to_cpu(sefw->version) != 1) 962 + continue; 963 + 964 + if (le32_to_cpu(sefw->serial_protocol) != protocol) 965 + continue; 966 + 967 + if (fw_size % 2 != 0) { 968 + fw_size++; 969 + sefw->fw_size_in_items = cpu_to_le16(fw_size); 970 + } 971 + 972 + if (fw_size >= MAX_GENI_CFG_RAMn_CNT) { 973 + dev_err(dev, 974 + "Firmware size (%u) exceeds max allowed RAMn count (%u)\n", 975 + fw_size, MAX_GENI_CFG_RAMn_CNT); 976 + continue; 977 + } 978 + 979 + if (fw_end > phdr->p_filesz || cfg_idx_end > phdr->p_filesz || 980 + cfg_val_end > phdr->p_filesz) { 981 + dev_err(dev, "Truncated or corrupt SE FW segment found at index %d\n", i); 982 + continue; 983 + } 984 + 985 + return sefw; 986 + } 987 + 988 + dev_err(dev, "Failed to get %s protocol firmware\n", protocol_name[protocol]); 989 + return NULL; 990 + } 991 + 992 + /** 993 + * geni_configure_xfer_mode() - Set the transfer mode. 994 + * @se: Pointer to the concerned serial engine. 995 + * @mode: SE data transfer mode. 996 + * 997 + * Set the transfer mode to either FIFO or DMA according to the mode specified 998 + * by the protocol driver. 999 + * 1000 + * Return: 0 if successful, otherwise return an error value. 1001 + */ 1002 + static int geni_configure_xfer_mode(struct geni_se *se, enum geni_se_xfer_mode mode) 1003 + { 1004 + /* Configure SE FIFO, DMA or GSI mode. */ 1005 + switch (mode) { 1006 + case GENI_GPI_DMA: 1007 + geni_setbits32(se->base + SE_GENI_DMA_MODE_EN, GENI_DMA_MODE_EN); 1008 + writel(0x0, se->base + SE_IRQ_EN); 1009 + writel(DMA_RX_EVENT_EN | DMA_TX_EVENT_EN | GENI_M_EVENT_EN | GENI_S_EVENT_EN, 1010 + se->base + SE_GSI_EVENT_EN); 1011 + break; 1012 + 1013 + case GENI_SE_FIFO: 1014 + geni_clrbits32(se->base + SE_GENI_DMA_MODE_EN, GENI_DMA_MODE_EN); 1015 + writel(DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN | GENI_S_IRQ_EN, 1016 + se->base + SE_IRQ_EN); 1017 + writel(0x0, se->base + SE_GSI_EVENT_EN); 1018 + break; 1019 + 1020 + case GENI_SE_DMA: 1021 + geni_setbits32(se->base + SE_GENI_DMA_MODE_EN, GENI_DMA_MODE_EN); 1022 + writel(DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN | GENI_S_IRQ_EN, 1023 + se->base + SE_IRQ_EN); 1024 + writel(0x0, se->base + SE_GSI_EVENT_EN); 1025 + break; 1026 + 1027 + default: 1028 + dev_err(se->dev, "Invalid geni-se transfer mode: %d\n", mode); 1029 + return -EINVAL; 1030 + } 1031 + return 0; 1032 + } 1033 + 1034 + /** 1035 + * geni_enable_interrupts() - Enable interrupts. 1036 + * @se: Pointer to the concerned serial engine. 1037 + * 1038 + * Enable the required interrupts during the firmware load process. 1039 + */ 1040 + static void geni_enable_interrupts(struct geni_se *se) 1041 + { 1042 + u32 val; 1043 + 1044 + /* Enable required interrupts. */ 1045 + writel(M_COMMON_GENI_M_IRQ_EN, se->base + SE_GENI_M_IRQ_EN); 1046 + 1047 + val = S_CMD_OVERRUN_EN | S_ILLEGAL_CMD_EN | S_CMD_CANCEL_EN | S_CMD_ABORT_EN | 1048 + S_GP_IRQ_0_EN | S_GP_IRQ_1_EN | S_GP_IRQ_2_EN | S_GP_IRQ_3_EN | 1049 + S_RX_FIFO_WR_ERR_EN | S_RX_FIFO_RD_ERR_EN; 1050 + writel(val, se->base + SE_GENI_S_IRQ_ENABLE); 1051 + 1052 + /* DMA mode configuration. */ 1053 + val = GENI_SE_DMA_RESET_DONE_EN | GENI_SE_DMA_AHB_ERR_EN | GENI_SE_DMA_DONE_EN; 1054 + writel(val, se->base + SE_DMA_TX_IRQ_EN_SET); 1055 + val = GENI_SE_DMA_FLUSH_DONE | GENI_SE_DMA_RESET_DONE_EN | GENI_SE_DMA_AHB_ERR_EN | 1056 + GENI_SE_DMA_DONE_EN; 1057 + writel(val, se->base + SE_DMA_RX_IRQ_EN_SET); 1058 + } 1059 + 1060 + /** 1061 + * geni_write_fw_revision() - Write the firmware revision. 1062 + * @se: Pointer to the concerned serial engine. 1063 + * @serial_protocol: serial protocol type. 1064 + * @fw_version: QUP firmware version. 1065 + * 1066 + * Write the firmware revision and protocol into the respective register. 1067 + */ 1068 + static void geni_write_fw_revision(struct geni_se *se, u16 serial_protocol, u16 fw_version) 1069 + { 1070 + u32 reg; 1071 + 1072 + reg = FIELD_PREP(FW_REV_PROTOCOL_MSK, serial_protocol); 1073 + reg |= FIELD_PREP(FW_REV_VERSION_MSK, fw_version); 1074 + 1075 + writel(reg, se->base + SE_GENI_FW_REVISION); 1076 + writel(reg, se->base + SE_GENI_S_FW_REVISION); 1077 + } 1078 + 1079 + /** 1080 + * geni_load_se_fw() - Load Serial Engine specific firmware. 1081 + * @se: Pointer to the concerned serial engine. 1082 + * @fw: Pointer to the firmware structure. 1083 + * @mode: SE data transfer mode. 1084 + * @protocol: Protocol type to be used with the SE (e.g., UART, SPI, I2C). 1085 + * 1086 + * Load the protocol firmware into the IRAM of the Serial Engine. 1087 + * 1088 + * Return: 0 if successful, otherwise return an error value. 1089 + */ 1090 + static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, 1091 + enum geni_se_xfer_mode mode, enum geni_se_protocol_type protocol) 1092 + { 1093 + const u32 *fw_data, *cfg_val_arr; 1094 + const u8 *cfg_idx_arr; 1095 + u32 i, reg_value; 1096 + int ret; 1097 + struct se_fw_hdr *hdr; 1098 + 1099 + hdr = geni_find_protocol_fw(se->dev, fw, protocol); 1100 + if (!hdr) 1101 + return -EINVAL; 1102 + 1103 + fw_data = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->fw_offset)); 1104 + cfg_idx_arr = (const u8 *)hdr + le16_to_cpu(hdr->cfg_idx_offset); 1105 + cfg_val_arr = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->cfg_val_offset)); 1106 + 1107 + ret = geni_icc_set_bw(se); 1108 + if (ret) 1109 + return ret; 1110 + 1111 + ret = geni_icc_enable(se); 1112 + if (ret) 1113 + return ret; 1114 + 1115 + ret = geni_se_resources_on(se); 1116 + if (ret) 1117 + goto out_icc_disable; 1118 + 1119 + /* 1120 + * Disable high-priority interrupts until all currently executing 1121 + * low-priority interrupts have been fully handled. 1122 + */ 1123 + geni_setbits32(se->wrapper->base + QUPV3_COMMON_CFG, FAST_SWITCH_TO_HIGH_DISABLE); 1124 + 1125 + /* Set AHB_M_CLK_CGC_ON to indicate hardware controls se-wrapper cgc clock. */ 1126 + geni_setbits32(se->wrapper->base + QUPV3_SE_AHB_M_CFG, AHB_M_CLK_CGC_ON); 1127 + 1128 + /* Let hardware to control common cgc. */ 1129 + geni_setbits32(se->wrapper->base + QUPV3_COMMON_CGC_CTRL, COMMON_CSR_SLV_CLK_CGC_ON); 1130 + 1131 + /* 1132 + * Setting individual bits in GENI_OUTPUT_CTRL activates corresponding output lines, 1133 + * allowing the hardware to drive data as configured. 1134 + */ 1135 + writel(0x0, se->base + GENI_OUTPUT_CTRL); 1136 + 1137 + /* Set SCLK and HCLK to program RAM */ 1138 + geni_setbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); 1139 + writel(0x0, se->base + SE_GENI_CLK_CTRL); 1140 + geni_clrbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); 1141 + 1142 + /* Enable required clocks for DMA CSR, TX and RX. */ 1143 + reg_value = AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CLK_CGC_ON | 1144 + DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON; 1145 + geni_setbits32(se->base + SE_DMA_GENERAL_CFG, reg_value); 1146 + 1147 + /* Let hardware control CGC by default. */ 1148 + writel(DEFAULT_CGC_EN, se->base + SE_GENI_CGC_CTRL); 1149 + 1150 + /* Set version of the configuration register part of firmware. */ 1151 + writel(le16_to_cpu(hdr->cfg_version), se->base + SE_GENI_INIT_CFG_REVISION); 1152 + writel(le16_to_cpu(hdr->cfg_version), se->base + SE_GENI_S_INIT_CFG_REVISION); 1153 + 1154 + /* Configure GENI primitive table. */ 1155 + for (i = 0; i < le16_to_cpu(hdr->cfg_size_in_items); i++) 1156 + writel(cfg_val_arr[i], 1157 + se->base + SE_GENI_CFG_REG0 + (cfg_idx_arr[i] * sizeof(u32))); 1158 + 1159 + /* Configure condition for assertion of RX_RFR_WATERMARK condition. */ 1160 + reg_value = geni_se_get_rx_fifo_depth(se); 1161 + writel(reg_value - 2, se->base + SE_GENI_RX_RFR_WATERMARK_REG); 1162 + 1163 + /* Let hardware control CGC */ 1164 + geni_setbits32(se->base + GENI_OUTPUT_CTRL, DEFAULT_IO_OUTPUT_CTRL_MSK); 1165 + 1166 + ret = geni_configure_xfer_mode(se, mode); 1167 + if (ret) 1168 + goto out_resources_off; 1169 + 1170 + geni_enable_interrupts(se); 1171 + 1172 + geni_write_fw_revision(se, le16_to_cpu(hdr->serial_protocol), le16_to_cpu(hdr->fw_version)); 1173 + 1174 + /* Program RAM address space. */ 1175 + memcpy_toio(se->base + SE_GENI_CFG_RAMN, fw_data, 1176 + le16_to_cpu(hdr->fw_size_in_items) * sizeof(u32)); 1177 + 1178 + /* Put default values on GENI's output pads. */ 1179 + writel_relaxed(0x1, se->base + GENI_FORCE_DEFAULT_REG); 1180 + 1181 + /* Toggle SCLK/HCLK from high to low to finalize RAM programming and apply config. */ 1182 + geni_setbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); 1183 + geni_setbits32(se->base + SE_GENI_CLK_CTRL, SER_CLK_SEL); 1184 + geni_clrbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); 1185 + 1186 + /* Serial engine DMA interface is enabled. */ 1187 + geni_setbits32(se->base + SE_DMA_IF_EN, DMA_IF_EN); 1188 + 1189 + /* Enable or disable FIFO interface of the serial engine. */ 1190 + if (mode == GENI_SE_FIFO) 1191 + geni_clrbits32(se->base + SE_FIFO_IF_DISABLE, FIFO_IF_DISABLE); 1192 + else 1193 + geni_setbits32(se->base + SE_FIFO_IF_DISABLE, FIFO_IF_DISABLE); 1194 + 1195 + out_resources_off: 1196 + geni_se_resources_off(se); 1197 + 1198 + out_icc_disable: 1199 + geni_icc_disable(se); 1200 + return ret; 1201 + } 1202 + 1203 + /** 1204 + * geni_load_se_firmware() - Load firmware for SE based on protocol 1205 + * @se: Pointer to the concerned serial engine. 1206 + * @protocol: Protocol type to be used with the SE (e.g., UART, SPI, I2C). 1207 + * 1208 + * Retrieves the firmware name from device properties and sets the transfer mode 1209 + * (FIFO or GSI DMA) based on device tree configuration. Enforces FIFO mode for 1210 + * UART protocol due to lack of GSI DMA support. Requests the firmware and loads 1211 + * it into the SE. 1212 + * 1213 + * Return: 0 on success, negative error code on failure. 1214 + */ 1215 + int geni_load_se_firmware(struct geni_se *se, enum geni_se_protocol_type protocol) 1216 + { 1217 + const char *fw_name; 1218 + const struct firmware *fw; 1219 + enum geni_se_xfer_mode mode = GENI_SE_FIFO; 1220 + int ret; 1221 + 1222 + if (protocol >= ARRAY_SIZE(protocol_name)) { 1223 + dev_err(se->dev, "Invalid geni-se protocol: %d", protocol); 1224 + return -EINVAL; 1225 + } 1226 + 1227 + ret = device_property_read_string(se->wrapper->dev, "firmware-name", &fw_name); 1228 + if (ret) { 1229 + dev_err(se->dev, "Failed to read firmware-name property: %d\n", ret); 1230 + return -EINVAL; 1231 + } 1232 + 1233 + if (of_property_read_bool(se->dev->of_node, "qcom,enable-gsi-dma")) 1234 + mode = GENI_GPI_DMA; 1235 + 1236 + /* GSI mode is not supported by the UART driver; therefore, setting FIFO mode */ 1237 + if (protocol == GENI_SE_UART) 1238 + mode = GENI_SE_FIFO; 1239 + 1240 + ret = request_firmware(&fw, fw_name, se->dev); 1241 + if (ret) { 1242 + if (ret == -ENOENT) 1243 + return -EPROBE_DEFER; 1244 + 1245 + dev_err(se->dev, "Failed to request firmware '%s' for protocol %d: ret: %d\n", 1246 + fw_name, protocol, ret); 1247 + return ret; 1248 + } 1249 + 1250 + ret = geni_load_se_fw(se, fw, mode, protocol); 1251 + release_firmware(fw); 1252 + 1253 + if (ret) { 1254 + dev_err(se->dev, "Failed to load SE firmware for protocol %d: ret: %d\n", 1255 + protocol, ret); 1256 + return ret; 1257 + } 1258 + 1259 + dev_dbg(se->dev, "Firmware load for %s protocol is successful for xfer mode: %d\n", 1260 + protocol_name[protocol], mode); 1261 + return 0; 1262 + } 1263 + EXPORT_SYMBOL_GPL(geni_load_se_firmware); 990 1264 991 1265 static int geni_se_probe(struct platform_device *pdev) 992 1266 {
+6
drivers/spi/spi-geni-qcom.c
··· 671 671 goto out_pm; 672 672 } 673 673 spi_slv_setup(mas); 674 + } else if (proto == GENI_SE_INVALID_PROTO) { 675 + ret = geni_load_se_firmware(se, GENI_SE_SPI); 676 + if (ret) { 677 + dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret); 678 + goto out_pm; 679 + } 674 680 } else if (proto != GENI_SE_SPI) { 675 681 dev_err(mas->dev, "Invalid proto %d\n", proto); 676 682 goto out_pm;
+7 -1
drivers/tty/serial/qcom_geni_serial.c
··· 1200 1200 int ret; 1201 1201 1202 1202 proto = geni_se_read_proto(&port->se); 1203 - if (proto != GENI_SE_UART) { 1203 + if (proto == GENI_SE_INVALID_PROTO) { 1204 + ret = geni_load_se_firmware(&port->se, GENI_SE_UART); 1205 + if (ret) { 1206 + dev_err(uport->dev, "UART firmware load failed ret: %d\n", ret); 1207 + return ret; 1208 + } 1209 + } else if (proto != GENI_SE_UART) { 1204 1210 dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); 1205 1211 return -ENXIO; 1206 1212 }
+6
include/linux/firmware/qcom/qcom_scm.h
··· 175 175 176 176 #endif /* CONFIG_QCOM_QSEECOM */ 177 177 178 + int qcom_scm_qtee_invoke_smc(phys_addr_t inbuf, size_t inbuf_size, 179 + phys_addr_t outbuf, size_t outbuf_size, 180 + u64 *result, u64 *response_type); 181 + int qcom_scm_qtee_callback_response(phys_addr_t buf, size_t buf_size, 182 + u64 *result, u64 *response_type); 183 + 178 184 #endif
+15
include/linux/firmware/qcom/qcom_tzmem.h
··· 53 53 54 54 phys_addr_t qcom_tzmem_to_phys(void *ptr); 55 55 56 + #if IS_ENABLED(CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE) 57 + int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, size_t size, u64 *handle); 58 + void qcom_tzmem_shm_bridge_delete(u64 handle); 59 + #else 60 + static inline int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, 61 + size_t size, u64 *handle) 62 + { 63 + return 0; 64 + } 65 + 66 + static inline void qcom_tzmem_shm_bridge_delete(u64 handle) 67 + { 68 + } 69 + #endif 70 + 56 71 #endif /* __QCOM_TZMEM */
+4
include/linux/soc/qcom/geni-se.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* 3 3 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. 4 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4 5 */ 5 6 6 7 #ifndef _LINUX_QCOM_GENI_SE ··· 37 36 GENI_SE_I2C, 38 37 GENI_SE_I3C, 39 38 GENI_SE_SPI_SLAVE, 39 + GENI_SE_INVALID_PROTO = 255, 40 40 }; 41 41 42 42 struct geni_wrapper; ··· 533 531 int geni_icc_enable(struct geni_se *se); 534 532 535 533 int geni_icc_disable(struct geni_se *se); 534 + 535 + int geni_load_se_firmware(struct geni_se *se, enum geni_se_protocol_type protocol); 536 536 #endif 537 537 #endif