1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // mcp251xfd - Microchip MCP251xFD Family CAN controller driver 4 // 5 // Copyright (c) 2019, 2020, 2021 Pengutronix, 6 // Marc Kleine-Budde <kernel@pengutronix.de> 7 // 8 9 #include "mcp251xfd.h" 10 11 #include <asm/unaligned.h> 12 13 static const struct regmap_config mcp251xfd_regmap_crc; 14 15 static int 16 mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count) 17 { 18 struct spi_device *spi = context; 19 20 return spi_write(spi, data, count); 21 } 22 23 static int 24 mcp251xfd_regmap_nocrc_gather_write(void *context, 25 const void *reg, size_t reg_len, 26 const void *val, size_t val_len) 27 { 28 struct spi_device *spi = context; 29 struct mcp251xfd_priv *priv = spi_get_drvdata(spi); 30 struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; 31 struct spi_transfer xfer[] = { 32 { 33 .tx_buf = buf_tx, 34 .len = sizeof(buf_tx->cmd) + val_len, 35 }, 36 }; 37 38 BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); 39 40 if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && 41 reg_len != sizeof(buf_tx->cmd.cmd)) 42 return -EINVAL; 43 44 memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd)); 45 memcpy(buf_tx->data, val, val_len); 46 47 return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); 48 } 49 50 static inline bool 51 mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv, 52 unsigned int reg) 53 { 54 struct mcp251xfd_rx_ring *ring; 55 int n; 56 57 switch (reg) { 58 case MCP251XFD_REG_INT: 59 case MCP251XFD_REG_TEFCON: 60 case MCP251XFD_REG_FLTCON(0): 61 case MCP251XFD_REG_ECCSTAT: 62 case MCP251XFD_REG_CRC: 63 return false; 64 case MCP251XFD_REG_CON: 65 case MCP251XFD_REG_OSC: 66 case MCP251XFD_REG_ECCCON: 67 return true; 68 default: 69 mcp251xfd_for_each_rx_ring(priv, ring, n) { 70 if (reg == MCP251XFD_REG_FIFOCON(ring->fifo_nr)) 71 return false; 72 if (reg == MCP251XFD_REG_FIFOSTA(ring->fifo_nr)) 73 return true; 74 } 75 76 WARN(1, "Status of reg 0x%04x unknown.\n", reg); 77 } 78 79 return true; 80 } 81 82 static int 83 mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg, 84 unsigned int mask, unsigned int val) 85 { 86 struct spi_device *spi = context; 87 struct mcp251xfd_priv *priv = spi_get_drvdata(spi); 88 struct mcp251xfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx; 89 struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; 90 __le32 orig_le32 = 0, mask_le32, val_le32, tmp_le32; 91 u8 first_byte, last_byte, len; 92 int err; 93 94 BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16)); 95 BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); 96 97 if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && 98 mask == 0) 99 return -EINVAL; 100 101 first_byte = mcp251xfd_first_byte_set(mask); 102 last_byte = mcp251xfd_last_byte_set(mask); 103 len = last_byte - first_byte + 1; 104 105 if (mcp251xfd_update_bits_read_reg(priv, reg)) { 106 struct spi_transfer xfer[2] = { }; 107 struct spi_message msg; 108 109 spi_message_init(&msg); 110 spi_message_add_tail(&xfer[0], &msg); 111 112 if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { 113 xfer[0].tx_buf = buf_tx; 114 xfer[0].len = sizeof(buf_tx->cmd); 115 116 xfer[1].rx_buf = buf_rx->data; 117 xfer[1].len = len; 118 spi_message_add_tail(&xfer[1], &msg); 119 } else { 120 xfer[0].tx_buf = buf_tx; 121 xfer[0].rx_buf = buf_rx; 122 xfer[0].len = sizeof(buf_tx->cmd) + len; 123 124 if (MCP251XFD_SANITIZE_SPI) 125 memset(buf_tx->data, 0x0, len); 126 } 127 128 mcp251xfd_spi_cmd_read_nocrc(&buf_tx->cmd, reg + first_byte); 129 err = spi_sync(spi, &msg); 130 if (err) 131 return err; 132 133 memcpy(&orig_le32, buf_rx->data, len); 134 } 135 136 mask_le32 = cpu_to_le32(mask >> BITS_PER_BYTE * first_byte); 137 val_le32 = cpu_to_le32(val >> BITS_PER_BYTE * first_byte); 138 139 tmp_le32 = orig_le32 & ~mask_le32; 140 tmp_le32 |= val_le32 & mask_le32; 141 142 mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte); 143 memcpy(buf_tx->data, &tmp_le32, len); 144 145 return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len); 146 } 147 148 static int 149 mcp251xfd_regmap_nocrc_read(void *context, 150 const void *reg, size_t reg_len, 151 void *val_buf, size_t val_len) 152 { 153 struct spi_device *spi = context; 154 struct mcp251xfd_priv *priv = spi_get_drvdata(spi); 155 struct mcp251xfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx; 156 struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; 157 struct spi_transfer xfer[2] = { }; 158 struct spi_message msg; 159 int err; 160 161 BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16)); 162 BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); 163 164 if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && 165 reg_len != sizeof(buf_tx->cmd.cmd)) 166 return -EINVAL; 167 168 spi_message_init(&msg); 169 spi_message_add_tail(&xfer[0], &msg); 170 171 if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { 172 xfer[0].tx_buf = reg; 173 xfer[0].len = sizeof(buf_tx->cmd); 174 175 xfer[1].rx_buf = val_buf; 176 xfer[1].len = val_len; 177 spi_message_add_tail(&xfer[1], &msg); 178 } else { 179 xfer[0].tx_buf = buf_tx; 180 xfer[0].rx_buf = buf_rx; 181 xfer[0].len = sizeof(buf_tx->cmd) + val_len; 182 183 memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd)); 184 if (MCP251XFD_SANITIZE_SPI) 185 memset(buf_tx->data, 0x0, val_len); 186 } 187 188 err = spi_sync(spi, &msg); 189 if (err) 190 return err; 191 192 if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX)) 193 memcpy(val_buf, buf_rx->data, val_len); 194 195 return 0; 196 } 197 198 static int 199 mcp251xfd_regmap_crc_gather_write(void *context, 200 const void *reg_p, size_t reg_len, 201 const void *val, size_t val_len) 202 { 203 struct spi_device *spi = context; 204 struct mcp251xfd_priv *priv = spi_get_drvdata(spi); 205 struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; 206 struct spi_transfer xfer[] = { 207 { 208 .tx_buf = buf_tx, 209 .len = sizeof(buf_tx->cmd) + val_len + 210 sizeof(buf_tx->crc), 211 }, 212 }; 213 u16 reg = *(u16 *)reg_p; 214 u16 crc; 215 216 BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); 217 218 if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && 219 reg_len != sizeof(buf_tx->cmd.cmd) + 220 mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE) 221 return -EINVAL; 222 223 mcp251xfd_spi_cmd_write_crc(&buf_tx->cmd, reg, val_len); 224 memcpy(buf_tx->data, val, val_len); 225 226 crc = mcp251xfd_crc16_compute(buf_tx, sizeof(buf_tx->cmd) + val_len); 227 put_unaligned_be16(crc, buf_tx->data + val_len); 228 229 return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); 230 } 231 232 static int 233 mcp251xfd_regmap_crc_write(void *context, 234 const void *data, size_t count) 235 { 236 const size_t data_offset = sizeof(__be16) + 237 mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE; 238 239 return mcp251xfd_regmap_crc_gather_write(context, 240 data, data_offset, 241 data + data_offset, 242 count - data_offset); 243 } 244 245 static int 246 mcp251xfd_regmap_crc_read_check_crc(const struct mcp251xfd_map_buf_crc * const buf_rx, 247 const struct mcp251xfd_map_buf_crc * const buf_tx, 248 unsigned int data_len) 249 { 250 u16 crc_received, crc_calculated; 251 252 crc_received = get_unaligned_be16(buf_rx->data + data_len); 253 crc_calculated = mcp251xfd_crc16_compute2(&buf_tx->cmd, 254 sizeof(buf_tx->cmd), 255 buf_rx->data, 256 data_len); 257 if (crc_received != crc_calculated) 258 return -EBADMSG; 259 260 return 0; 261 } 262 263 static int 264 mcp251xfd_regmap_crc_read_one(struct mcp251xfd_priv *priv, 265 struct spi_message *msg, unsigned int data_len) 266 { 267 const struct mcp251xfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx; 268 const struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; 269 int err; 270 271 BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8)); 272 BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); 273 274 err = spi_sync(priv->spi, msg); 275 if (err) 276 return err; 277 278 return mcp251xfd_regmap_crc_read_check_crc(buf_rx, buf_tx, data_len); 279 } 280 281 static int 282 mcp251xfd_regmap_crc_read(void *context, 283 const void *reg_p, size_t reg_len, 284 void *val_buf, size_t val_len) 285 { 286 struct spi_device *spi = context; 287 struct mcp251xfd_priv *priv = spi_get_drvdata(spi); 288 struct mcp251xfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx; 289 struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; 290 struct spi_transfer xfer[2] = { }; 291 struct spi_message msg; 292 u16 reg = *(u16 *)reg_p; 293 int i, err; 294 295 BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8)); 296 BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); 297 298 if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && 299 reg_len != sizeof(buf_tx->cmd.cmd) + 300 mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE) 301 return -EINVAL; 302 303 spi_message_init(&msg); 304 spi_message_add_tail(&xfer[0], &msg); 305 306 if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { 307 xfer[0].tx_buf = buf_tx; 308 xfer[0].len = sizeof(buf_tx->cmd); 309 310 xfer[1].rx_buf = buf_rx->data; 311 xfer[1].len = val_len + sizeof(buf_tx->crc); 312 spi_message_add_tail(&xfer[1], &msg); 313 } else { 314 xfer[0].tx_buf = buf_tx; 315 xfer[0].rx_buf = buf_rx; 316 xfer[0].len = sizeof(buf_tx->cmd) + val_len + 317 sizeof(buf_tx->crc); 318 319 if (MCP251XFD_SANITIZE_SPI) 320 memset(buf_tx->data, 0x0, val_len + 321 sizeof(buf_tx->crc)); 322 } 323 324 mcp251xfd_spi_cmd_read_crc(&buf_tx->cmd, reg, val_len); 325 326 for (i = 0; i < MCP251XFD_READ_CRC_RETRIES_MAX; i++) { 327 err = mcp251xfd_regmap_crc_read_one(priv, &msg, val_len); 328 if (!err) 329 goto out; 330 if (err != -EBADMSG) 331 return err; 332 333 /* MCP251XFD_REG_TBC is the time base counter 334 * register. It increments once per SYS clock tick, 335 * which is 20 or 40 MHz. 336 * 337 * Observation shows that if the lowest byte (which is 338 * transferred first on the SPI bus) of that register 339 * is 0x00 or 0x80 the calculated CRC doesn't always 340 * match the transferred one. 341 * 342 * If the highest bit in the lowest byte is flipped 343 * the transferred CRC matches the calculated one. We 344 * assume for now the CRC calculation in the chip 345 * works on wrong data and the transferred data is 346 * correct. 347 */ 348 if (reg == MCP251XFD_REG_TBC && 349 (buf_rx->data[0] == 0x0 || buf_rx->data[0] == 0x80)) { 350 /* Flip highest bit in lowest byte of le32 */ 351 buf_rx->data[0] ^= 0x80; 352 353 /* re-check CRC */ 354 err = mcp251xfd_regmap_crc_read_check_crc(buf_rx, 355 buf_tx, 356 val_len); 357 if (!err) { 358 /* If CRC is now correct, assume 359 * transferred data was OK, flip bit 360 * back to original value. 361 */ 362 buf_rx->data[0] ^= 0x80; 363 goto out; 364 } 365 } 366 367 /* MCP251XFD_REG_OSC is the first ever reg we read from. 368 * 369 * The chip may be in deep sleep and this SPI transfer 370 * (i.e. the assertion of the CS) will wake the chip 371 * up. This takes about 3ms. The CRC of this transfer 372 * is wrong. 373 * 374 * Or there isn't a chip at all, in this case the CRC 375 * will be wrong, too. 376 * 377 * In both cases ignore the CRC and copy the read data 378 * to the caller. It will take care of both cases. 379 * 380 */ 381 if (reg == MCP251XFD_REG_OSC && val_len == sizeof(__le32)) { 382 err = 0; 383 goto out; 384 } 385 386 netdev_info(priv->ndev, 387 "CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x) retrying.\n", 388 reg, val_len, (int)val_len, buf_rx->data, 389 get_unaligned_be16(buf_rx->data + val_len)); 390 } 391 392 if (err) { 393 netdev_err(priv->ndev, 394 "CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x).\n", 395 reg, val_len, (int)val_len, buf_rx->data, 396 get_unaligned_be16(buf_rx->data + val_len)); 397 398 return err; 399 } 400 out: 401 memcpy(val_buf, buf_rx->data, val_len); 402 403 return 0; 404 } 405 406 static const struct regmap_range mcp251xfd_reg_table_yes_range[] = { 407 regmap_reg_range(0x000, 0x2ec), /* CAN FD Controller Module SFR */ 408 regmap_reg_range(0x400, 0xbfc), /* RAM */ 409 regmap_reg_range(0xe00, 0xe14), /* MCP2517/18FD SFR */ 410 }; 411 412 static const struct regmap_access_table mcp251xfd_reg_table = { 413 .yes_ranges = mcp251xfd_reg_table_yes_range, 414 .n_yes_ranges = ARRAY_SIZE(mcp251xfd_reg_table_yes_range), 415 }; 416 417 static const struct regmap_config mcp251xfd_regmap_nocrc = { 418 .name = "nocrc", 419 .reg_bits = 16, 420 .reg_stride = 4, 421 .pad_bits = 0, 422 .val_bits = 32, 423 .max_register = 0xffc, 424 .wr_table = &mcp251xfd_reg_table, 425 .rd_table = &mcp251xfd_reg_table, 426 .cache_type = REGCACHE_NONE, 427 .read_flag_mask = (__force unsigned long) 428 cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ), 429 .write_flag_mask = (__force unsigned long) 430 cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE), 431 }; 432 433 static const struct regmap_bus mcp251xfd_bus_nocrc = { 434 .write = mcp251xfd_regmap_nocrc_write, 435 .gather_write = mcp251xfd_regmap_nocrc_gather_write, 436 .reg_update_bits = mcp251xfd_regmap_nocrc_update_bits, 437 .read = mcp251xfd_regmap_nocrc_read, 438 .reg_format_endian_default = REGMAP_ENDIAN_BIG, 439 .val_format_endian_default = REGMAP_ENDIAN_LITTLE, 440 .max_raw_read = sizeof_field(struct mcp251xfd_map_buf_nocrc, data), 441 .max_raw_write = sizeof_field(struct mcp251xfd_map_buf_nocrc, data), 442 }; 443 444 static const struct regmap_config mcp251xfd_regmap_crc = { 445 .name = "crc", 446 .reg_bits = 16, 447 .reg_stride = 4, 448 .pad_bits = 16, /* keep data bits aligned */ 449 .val_bits = 32, 450 .max_register = 0xffc, 451 .wr_table = &mcp251xfd_reg_table, 452 .rd_table = &mcp251xfd_reg_table, 453 .cache_type = REGCACHE_NONE, 454 }; 455 456 static const struct regmap_bus mcp251xfd_bus_crc = { 457 .write = mcp251xfd_regmap_crc_write, 458 .gather_write = mcp251xfd_regmap_crc_gather_write, 459 .read = mcp251xfd_regmap_crc_read, 460 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 461 .val_format_endian_default = REGMAP_ENDIAN_LITTLE, 462 .max_raw_read = sizeof_field(struct mcp251xfd_map_buf_crc, data), 463 .max_raw_write = sizeof_field(struct mcp251xfd_map_buf_crc, data), 464 }; 465 466 static inline bool 467 mcp251xfd_regmap_use_nocrc(struct mcp251xfd_priv *priv) 468 { 469 return (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) || 470 (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX)); 471 } 472 473 static inline bool 474 mcp251xfd_regmap_use_crc(struct mcp251xfd_priv *priv) 475 { 476 return (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) || 477 (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX); 478 } 479 480 static int 481 mcp251xfd_regmap_init_nocrc(struct mcp251xfd_priv *priv) 482 { 483 if (!priv->map_nocrc) { 484 struct regmap *map; 485 486 map = devm_regmap_init(&priv->spi->dev, &mcp251xfd_bus_nocrc, 487 priv->spi, &mcp251xfd_regmap_nocrc); 488 if (IS_ERR(map)) 489 return PTR_ERR(map); 490 491 priv->map_nocrc = map; 492 } 493 494 if (!priv->map_buf_nocrc_rx) { 495 priv->map_buf_nocrc_rx = 496 devm_kzalloc(&priv->spi->dev, 497 sizeof(*priv->map_buf_nocrc_rx), 498 GFP_KERNEL); 499 if (!priv->map_buf_nocrc_rx) 500 return -ENOMEM; 501 } 502 503 if (!priv->map_buf_nocrc_tx) { 504 priv->map_buf_nocrc_tx = 505 devm_kzalloc(&priv->spi->dev, 506 sizeof(*priv->map_buf_nocrc_tx), 507 GFP_KERNEL); 508 if (!priv->map_buf_nocrc_tx) 509 return -ENOMEM; 510 } 511 512 if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) 513 priv->map_reg = priv->map_nocrc; 514 515 if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX)) 516 priv->map_rx = priv->map_nocrc; 517 518 return 0; 519 } 520 521 static void mcp251xfd_regmap_destroy_nocrc(struct mcp251xfd_priv *priv) 522 { 523 if (priv->map_buf_nocrc_rx) { 524 devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_rx); 525 priv->map_buf_nocrc_rx = NULL; 526 } 527 if (priv->map_buf_nocrc_tx) { 528 devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_tx); 529 priv->map_buf_nocrc_tx = NULL; 530 } 531 } 532 533 static int 534 mcp251xfd_regmap_init_crc(struct mcp251xfd_priv *priv) 535 { 536 if (!priv->map_crc) { 537 struct regmap *map; 538 539 map = devm_regmap_init(&priv->spi->dev, &mcp251xfd_bus_crc, 540 priv->spi, &mcp251xfd_regmap_crc); 541 if (IS_ERR(map)) 542 return PTR_ERR(map); 543 544 priv->map_crc = map; 545 } 546 547 if (!priv->map_buf_crc_rx) { 548 priv->map_buf_crc_rx = 549 devm_kzalloc(&priv->spi->dev, 550 sizeof(*priv->map_buf_crc_rx), 551 GFP_KERNEL); 552 if (!priv->map_buf_crc_rx) 553 return -ENOMEM; 554 } 555 556 if (!priv->map_buf_crc_tx) { 557 priv->map_buf_crc_tx = 558 devm_kzalloc(&priv->spi->dev, 559 sizeof(*priv->map_buf_crc_tx), 560 GFP_KERNEL); 561 if (!priv->map_buf_crc_tx) 562 return -ENOMEM; 563 } 564 565 if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) 566 priv->map_reg = priv->map_crc; 567 568 if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX) 569 priv->map_rx = priv->map_crc; 570 571 return 0; 572 } 573 574 static void mcp251xfd_regmap_destroy_crc(struct mcp251xfd_priv *priv) 575 { 576 if (priv->map_buf_crc_rx) { 577 devm_kfree(&priv->spi->dev, priv->map_buf_crc_rx); 578 priv->map_buf_crc_rx = NULL; 579 } 580 if (priv->map_buf_crc_tx) { 581 devm_kfree(&priv->spi->dev, priv->map_buf_crc_tx); 582 priv->map_buf_crc_tx = NULL; 583 } 584 } 585 586 int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv) 587 { 588 int err; 589 590 if (mcp251xfd_regmap_use_nocrc(priv)) { 591 err = mcp251xfd_regmap_init_nocrc(priv); 592 593 if (err) 594 return err; 595 } else { 596 mcp251xfd_regmap_destroy_nocrc(priv); 597 } 598 599 if (mcp251xfd_regmap_use_crc(priv)) { 600 err = mcp251xfd_regmap_init_crc(priv); 601 602 if (err) 603 return err; 604 } else { 605 mcp251xfd_regmap_destroy_crc(priv); 606 } 607 608 return 0; 609 } 610