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

net: ipa: program metadata mask differently

The way the mask value is programmed for QMAP RX endpoints was based
on some wrong assumptions about the way metadata containing the QMAP
mux_id value is formatted. The metadata value supplied by the
modem is *not* in QMAP format, and in fact contains the mux_id we
want in its (big endian) low-order byte. That byte must be written
by the IPA into offset 1 of the QMAP header it inserts before the
received packet.

QMAP TX endpoints *do* use a QMAP header as the metadata sent with
each packet. The modem assumes this, and based on that assumes the
mux_id is in the second byte. To match those assumptions we must
program the modem TX (QMAP) endpoint HDR register to indicate the
metadata will be found at offset 0 in the message header.

The previous configuration managed to work, but it was not working
correctly. This patch fixes a bug whose symptom was receipt of
messages containing the wrong QMAP mux_id.

In fixing this, get rid of ipa_rmnet_mux_id_metadata_mask(), which
was more or less defined so there was a separate place to explain
what was happening as we generated the mask value. Instead, put a
longer description of how this works above ipa_endpoint_init_hdr(),
and define the metadata mask to use as a simple constant.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Alex Elder and committed by
David S. Miller
8730f45d c25cba36

+40 -34
+40 -34
drivers/net/ipa/ipa_endpoint.c
··· 32 32 /* The amount of RX buffer space consumed by standard skb overhead */ 33 33 #define IPA_RX_BUFFER_OVERHEAD (PAGE_SIZE - SKB_MAX_ORDER(NET_SKB_PAD, 0)) 34 34 35 + /* Where to find the QMAP mux_id for a packet within modem-supplied metadata */ 36 + #define IPA_ENDPOINT_QMAP_METADATA_MASK 0x000000ff /* host byte order */ 37 + 35 38 #define IPA_ENDPOINT_RESET_AGGR_RETRY_MAX 3 36 39 #define IPA_AGGR_TIME_LIMIT_DEFAULT 1000 /* microseconds */ 37 40 ··· 436 433 iowrite32(val, endpoint->ipa->reg_virt + offset); 437 434 } 438 435 436 + /** 437 + * We program QMAP endpoints so each packet received is preceded by a QMAP 438 + * header structure. The QMAP header contains a 1-byte mux_id and 2-byte 439 + * packet size field, and we have the IPA hardware populate both for each 440 + * received packet. The header is configured (in the HDR_EXT register) 441 + * to use big endian format. 442 + * 443 + * The packet size is written into the QMAP header's pkt_len field. That 444 + * location is defined here using the HDR_OFST_PKT_SIZE field. 445 + * 446 + * The mux_id comes from a 4-byte metadata value supplied with each packet 447 + * by the modem. It is *not* a QMAP header, but it does contain the mux_id 448 + * value that we want, in its low-order byte. A bitmask defined in the 449 + * endpoint's METADATA_MASK register defines which byte within the modem 450 + * metadata contains the mux_id. And the OFST_METADATA field programmed 451 + * here indicates where the extracted byte should be placed within the QMAP 452 + * header. 453 + */ 439 454 static void ipa_endpoint_init_hdr(struct ipa_endpoint *endpoint) 440 455 { 441 456 u32 offset = IPA_REG_ENDP_INIT_HDR_N_OFFSET(endpoint->endpoint_id); ··· 462 441 if (endpoint->data->qmap) { 463 442 size_t header_size = sizeof(struct rmnet_map_header); 464 443 444 + /* We might supply a checksum header after the QMAP header */ 465 445 if (endpoint->toward_ipa && endpoint->data->checksum) 466 446 header_size += sizeof(struct rmnet_map_ul_csum_header); 467 - 468 447 val |= u32_encode_bits(header_size, HDR_LEN_FMASK); 469 - /* metadata is the 4 byte rmnet_map header itself */ 470 - val |= HDR_OFST_METADATA_VALID_FMASK; 471 - val |= u32_encode_bits(0, HDR_OFST_METADATA_FMASK); 472 - /* HDR_ADDITIONAL_CONST_LEN is 0; (IPA->AP only) */ 473 - if (!endpoint->toward_ipa) { 474 - u32 size_offset = offsetof(struct rmnet_map_header, 475 - pkt_len); 476 448 449 + /* Define how to fill mux_id in a received QMAP header */ 450 + if (!endpoint->toward_ipa) { 451 + u32 off; /* Field offset within header */ 452 + 453 + /* Where IPA will write the metadata value */ 454 + off = offsetof(struct rmnet_map_header, mux_id); 455 + val |= u32_encode_bits(off, HDR_OFST_METADATA_FMASK); 456 + 457 + /* Where IPA will write the length */ 458 + off = offsetof(struct rmnet_map_header, pkt_len); 477 459 val |= HDR_OFST_PKT_SIZE_VALID_FMASK; 478 - val |= u32_encode_bits(size_offset, 479 - HDR_OFST_PKT_SIZE_FMASK); 460 + val |= u32_encode_bits(off, HDR_OFST_PKT_SIZE_FMASK); 480 461 } 462 + /* For QMAP TX, metadata offset is 0 (modem assumes this) */ 463 + val |= HDR_OFST_METADATA_VALID_FMASK; 464 + 465 + /* HDR_ADDITIONAL_CONST_LEN is 0; (RX only) */ 481 466 /* HDR_A5_MUX is 0 */ 482 467 /* HDR_LEN_INC_DEAGG_HDR is 0 */ 483 - /* HDR_METADATA_REG_VALID is 0; (AP->IPA only) */ 468 + /* HDR_METADATA_REG_VALID is 0 (TX only) */ 484 469 } 485 470 486 471 iowrite32(val, endpoint->ipa->reg_virt + offset); ··· 509 482 iowrite32(val, endpoint->ipa->reg_virt + offset); 510 483 } 511 484 512 - /** 513 - * Generate a metadata mask value that will select only the mux_id 514 - * field in an rmnet_map header structure. The mux_id is at offset 515 - * 1 byte from the beginning of the structure, but the metadata 516 - * value is treated as a 4-byte unit. So this mask must be computed 517 - * with endianness in mind. Note that ipa_endpoint_init_hdr_metadata_mask() 518 - * will convert this value to the proper byte order. 519 - * 520 - * Marked __always_inline because this is really computing a 521 - * constant value. 522 - */ 523 - static __always_inline __be32 ipa_rmnet_mux_id_metadata_mask(void) 524 - { 525 - size_t mux_id_offset = offsetof(struct rmnet_map_header, mux_id); 526 - u32 mux_id_mask = 0; 527 - u8 *bytes; 528 - 529 - bytes = (u8 *)&mux_id_mask; 530 - bytes[mux_id_offset] = 0xff; /* mux_id is 1 byte */ 531 - 532 - return cpu_to_be32(mux_id_mask); 533 - } 534 485 535 486 static void ipa_endpoint_init_hdr_metadata_mask(struct ipa_endpoint *endpoint) 536 487 { ··· 518 513 519 514 offset = IPA_REG_ENDP_INIT_HDR_METADATA_MASK_N_OFFSET(endpoint_id); 520 515 516 + /* Note that HDR_ENDIANNESS indicates big endian header fields */ 521 517 if (!endpoint->toward_ipa && endpoint->data->qmap) 522 - val = ipa_rmnet_mux_id_metadata_mask(); 518 + val = cpu_to_be32(IPA_ENDPOINT_QMAP_METADATA_MASK); 523 519 524 520 iowrite32(val, endpoint->ipa->reg_virt + offset); 525 521 }