1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Core Source for: 4 * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. 5 * For use with Cypress Txx3xx parts. 6 * Supported parts include: 7 * CY8CTST341 8 * CY8CTMA340 9 * 10 * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. 11 * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> 12 * 13 * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> 14 */ 15 16 #include <linux/delay.h> 17 #include <linux/input.h> 18 #include <linux/input/mt.h> 19 #include <linux/input/touchscreen.h> 20 #include <linux/gpio.h> 21 #include <linux/interrupt.h> 22 #include <linux/slab.h> 23 #include <linux/property.h> 24 #include <linux/gpio/consumer.h> 25 #include <linux/regulator/consumer.h> 26 27 #include "cyttsp_core.h" 28 29 /* Bootloader number of command keys */ 30 #define CY_NUM_BL_KEYS 8 31 32 /* helpers */ 33 #define GET_NUM_TOUCHES(x) ((x) & 0x0F) 34 #define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) 35 #define IS_BAD_PKT(x) ((x) & 0x20) 36 #define IS_VALID_APP(x) ((x) & 0x01) 37 #define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) 38 #define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) 39 #define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) 40 41 #define CY_REG_BASE 0x00 42 #define CY_REG_ACT_DIST 0x1E 43 #define CY_REG_ACT_INTRVL 0x1D 44 #define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) 45 #define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) 46 #define CY_MAXZ 255 47 #define CY_DELAY_DFLT 20 /* ms */ 48 #define CY_DELAY_MAX 500 49 #define CY_ACT_DIST_DFLT 0xF8 50 #define CY_ACT_DIST_MASK 0x0F 51 #define CY_HNDSHK_BIT 0x80 52 /* device mode bits */ 53 #define CY_OPERATE_MODE 0x00 54 #define CY_SYSINFO_MODE 0x10 55 /* power mode select bits */ 56 #define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ 57 #define CY_DEEP_SLEEP_MODE 0x02 58 #define CY_LOW_POWER_MODE 0x04 59 60 /* Slots management */ 61 #define CY_MAX_FINGER 4 62 #define CY_MAX_ID 16 63 64 static const u8 bl_command[] = { 65 0x00, /* file offset */ 66 0xFF, /* command */ 67 0xA5, /* exit bootloader command */ 68 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ 69 }; 70 71 static int ttsp_read_block_data(struct cyttsp *ts, u8 command, 72 u8 length, void *buf) 73 { 74 int error; 75 int tries; 76 77 for (tries = 0; tries < CY_NUM_RETRY; tries++) { 78 error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command, 79 length, buf); 80 if (!error) 81 return 0; 82 83 msleep(CY_DELAY_DFLT); 84 } 85 86 return -EIO; 87 } 88 89 static int ttsp_write_block_data(struct cyttsp *ts, u8 command, 90 u8 length, void *buf) 91 { 92 int error; 93 int tries; 94 95 for (tries = 0; tries < CY_NUM_RETRY; tries++) { 96 error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command, 97 length, buf); 98 if (!error) 99 return 0; 100 101 msleep(CY_DELAY_DFLT); 102 } 103 104 return -EIO; 105 } 106 107 static int ttsp_send_command(struct cyttsp *ts, u8 cmd) 108 { 109 return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); 110 } 111 112 static int cyttsp_handshake(struct cyttsp *ts) 113 { 114 if (ts->use_hndshk) 115 return ttsp_send_command(ts, 116 ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); 117 118 return 0; 119 } 120 121 static int cyttsp_load_bl_regs(struct cyttsp *ts) 122 { 123 memset(&ts->bl_data, 0, sizeof(ts->bl_data)); 124 ts->bl_data.bl_status = 0x10; 125 126 return ttsp_read_block_data(ts, CY_REG_BASE, 127 sizeof(ts->bl_data), &ts->bl_data); 128 } 129 130 static int cyttsp_exit_bl_mode(struct cyttsp *ts) 131 { 132 int error; 133 u8 bl_cmd[sizeof(bl_command)]; 134 135 memcpy(bl_cmd, bl_command, sizeof(bl_command)); 136 if (ts->bl_keys) 137 memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], 138 ts->bl_keys, CY_NUM_BL_KEYS); 139 140 error = ttsp_write_block_data(ts, CY_REG_BASE, 141 sizeof(bl_cmd), bl_cmd); 142 if (error) 143 return error; 144 145 /* wait for TTSP Device to complete the operation */ 146 msleep(CY_DELAY_DFLT); 147 148 error = cyttsp_load_bl_regs(ts); 149 if (error) 150 return error; 151 152 if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) 153 return -EIO; 154 155 return 0; 156 } 157 158 static int cyttsp_set_operational_mode(struct cyttsp *ts) 159 { 160 int error; 161 162 error = ttsp_send_command(ts, CY_OPERATE_MODE); 163 if (error) 164 return error; 165 166 /* wait for TTSP Device to complete switch to Operational mode */ 167 error = ttsp_read_block_data(ts, CY_REG_BASE, 168 sizeof(ts->xy_data), &ts->xy_data); 169 if (error) 170 return error; 171 172 error = cyttsp_handshake(ts); 173 if (error) 174 return error; 175 176 return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; 177 } 178 179 static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) 180 { 181 int error; 182 183 memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); 184 185 /* switch to sysinfo mode */ 186 error = ttsp_send_command(ts, CY_SYSINFO_MODE); 187 if (error) 188 return error; 189 190 /* read sysinfo registers */ 191 msleep(CY_DELAY_DFLT); 192 error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), 193 &ts->sysinfo_data); 194 if (error) 195 return error; 196 197 error = cyttsp_handshake(ts); 198 if (error) 199 return error; 200 201 if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) 202 return -EIO; 203 204 return 0; 205 } 206 207 static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) 208 { 209 int retval = 0; 210 211 if (ts->act_intrvl != CY_ACT_INTRVL_DFLT || 212 ts->tch_tmout != CY_TCH_TMOUT_DFLT || 213 ts->lp_intrvl != CY_LP_INTRVL_DFLT) { 214 215 u8 intrvl_ray[] = { 216 ts->act_intrvl, 217 ts->tch_tmout, 218 ts->lp_intrvl 219 }; 220 221 /* set intrvl registers */ 222 retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, 223 sizeof(intrvl_ray), intrvl_ray); 224 msleep(CY_DELAY_DFLT); 225 } 226 227 return retval; 228 } 229 230 static void cyttsp_hard_reset(struct cyttsp *ts) 231 { 232 if (ts->reset_gpio) { 233 /* 234 * According to the CY8CTMA340 datasheet page 21, the external 235 * reset pulse width should be >= 1 ms. The datasheet does not 236 * specify how long we have to wait after reset but a vendor 237 * tree specifies 5 ms here. 238 */ 239 gpiod_set_value_cansleep(ts->reset_gpio, 1); 240 usleep_range(1000, 2000); 241 gpiod_set_value_cansleep(ts->reset_gpio, 0); 242 usleep_range(5000, 6000); 243 } 244 } 245 246 static int cyttsp_soft_reset(struct cyttsp *ts) 247 { 248 int retval; 249 250 /* wait for interrupt to set ready completion */ 251 reinit_completion(&ts->bl_ready); 252 ts->state = CY_BL_STATE; 253 254 enable_irq(ts->irq); 255 256 retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); 257 if (retval) { 258 dev_err(ts->dev, "failed to send soft reset\n"); 259 goto out; 260 } 261 262 if (!wait_for_completion_timeout(&ts->bl_ready, 263 msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX))) { 264 dev_err(ts->dev, "timeout waiting for soft reset\n"); 265 retval = -EIO; 266 } 267 268 out: 269 ts->state = CY_IDLE_STATE; 270 disable_irq(ts->irq); 271 return retval; 272 } 273 274 static int cyttsp_act_dist_setup(struct cyttsp *ts) 275 { 276 u8 act_dist_setup = ts->act_dist; 277 278 /* Init gesture; active distance setup */ 279 return ttsp_write_block_data(ts, CY_REG_ACT_DIST, 280 sizeof(act_dist_setup), &act_dist_setup); 281 } 282 283 static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) 284 { 285 ids[0] = xy_data->touch12_id >> 4; 286 ids[1] = xy_data->touch12_id & 0xF; 287 ids[2] = xy_data->touch34_id >> 4; 288 ids[3] = xy_data->touch34_id & 0xF; 289 } 290 291 static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, 292 int idx) 293 { 294 switch (idx) { 295 case 0: 296 return &xy_data->tch1; 297 case 1: 298 return &xy_data->tch2; 299 case 2: 300 return &xy_data->tch3; 301 case 3: 302 return &xy_data->tch4; 303 default: 304 return NULL; 305 } 306 } 307 308 static void cyttsp_report_tchdata(struct cyttsp *ts) 309 { 310 struct cyttsp_xydata *xy_data = &ts->xy_data; 311 struct input_dev *input = ts->input; 312 int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); 313 const struct cyttsp_tch *tch; 314 int ids[CY_MAX_ID]; 315 int i; 316 DECLARE_BITMAP(used, CY_MAX_ID); 317 318 if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { 319 /* terminate all active tracks */ 320 num_tch = 0; 321 dev_dbg(ts->dev, "%s: Large area detected\n", __func__); 322 } else if (num_tch > CY_MAX_FINGER) { 323 /* terminate all active tracks */ 324 num_tch = 0; 325 dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); 326 } else if (IS_BAD_PKT(xy_data->tt_mode)) { 327 /* terminate all active tracks */ 328 num_tch = 0; 329 dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); 330 } 331 332 cyttsp_extract_track_ids(xy_data, ids); 333 334 bitmap_zero(used, CY_MAX_ID); 335 336 for (i = 0; i < num_tch; i++) { 337 tch = cyttsp_get_tch(xy_data, i); 338 339 input_mt_slot(input, ids[i]); 340 input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 341 input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); 342 input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); 343 input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); 344 345 __set_bit(ids[i], used); 346 } 347 348 for (i = 0; i < CY_MAX_ID; i++) { 349 if (test_bit(i, used)) 350 continue; 351 352 input_mt_slot(input, i); 353 input_mt_report_slot_inactive(input); 354 } 355 356 input_sync(input); 357 } 358 359 static irqreturn_t cyttsp_irq(int irq, void *handle) 360 { 361 struct cyttsp *ts = handle; 362 int error; 363 364 if (unlikely(ts->state == CY_BL_STATE)) { 365 complete(&ts->bl_ready); 366 goto out; 367 } 368 369 /* Get touch data from CYTTSP device */ 370 error = ttsp_read_block_data(ts, CY_REG_BASE, 371 sizeof(struct cyttsp_xydata), &ts->xy_data); 372 if (error) 373 goto out; 374 375 /* provide flow control handshake */ 376 error = cyttsp_handshake(ts); 377 if (error) 378 goto out; 379 380 if (unlikely(ts->state == CY_IDLE_STATE)) 381 goto out; 382 383 if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { 384 /* 385 * TTSP device has reset back to bootloader mode. 386 * Restore to operational mode. 387 */ 388 error = cyttsp_exit_bl_mode(ts); 389 if (error) { 390 dev_err(ts->dev, 391 "Could not return to operational mode, err: %d\n", 392 error); 393 ts->state = CY_IDLE_STATE; 394 } 395 } else { 396 cyttsp_report_tchdata(ts); 397 } 398 399 out: 400 return IRQ_HANDLED; 401 } 402 403 static int cyttsp_power_on(struct cyttsp *ts) 404 { 405 int error; 406 407 error = cyttsp_soft_reset(ts); 408 if (error) 409 return error; 410 411 error = cyttsp_load_bl_regs(ts); 412 if (error) 413 return error; 414 415 if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && 416 IS_VALID_APP(ts->bl_data.bl_status)) { 417 error = cyttsp_exit_bl_mode(ts); 418 if (error) { 419 dev_err(ts->dev, "failed to exit bootloader mode\n"); 420 return error; 421 } 422 } 423 424 if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || 425 IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { 426 return -ENODEV; 427 } 428 429 error = cyttsp_set_sysinfo_mode(ts); 430 if (error) 431 return error; 432 433 error = cyttsp_set_sysinfo_regs(ts); 434 if (error) 435 return error; 436 437 error = cyttsp_set_operational_mode(ts); 438 if (error) 439 return error; 440 441 /* init active distance */ 442 error = cyttsp_act_dist_setup(ts); 443 if (error) 444 return error; 445 446 ts->state = CY_ACTIVE_STATE; 447 448 return 0; 449 } 450 451 static int cyttsp_enable(struct cyttsp *ts) 452 { 453 int error; 454 455 /* 456 * The device firmware can wake on an I2C or SPI memory slave 457 * address match. So just reading a register is sufficient to 458 * wake up the device. The first read attempt will fail but it 459 * will wake it up making the second read attempt successful. 460 */ 461 error = ttsp_read_block_data(ts, CY_REG_BASE, 462 sizeof(ts->xy_data), &ts->xy_data); 463 if (error) 464 return error; 465 466 if (GET_HSTMODE(ts->xy_data.hst_mode)) 467 return -EIO; 468 469 enable_irq(ts->irq); 470 471 return 0; 472 } 473 474 static int cyttsp_disable(struct cyttsp *ts) 475 { 476 int error; 477 478 error = ttsp_send_command(ts, CY_LOW_POWER_MODE); 479 if (error) 480 return error; 481 482 disable_irq(ts->irq); 483 484 return 0; 485 } 486 487 static int __maybe_unused cyttsp_suspend(struct device *dev) 488 { 489 struct cyttsp *ts = dev_get_drvdata(dev); 490 int retval = 0; 491 492 mutex_lock(&ts->input->mutex); 493 494 if (input_device_enabled(ts->input)) { 495 retval = cyttsp_disable(ts); 496 if (retval == 0) 497 ts->suspended = true; 498 } 499 500 mutex_unlock(&ts->input->mutex); 501 502 return retval; 503 } 504 505 static int __maybe_unused cyttsp_resume(struct device *dev) 506 { 507 struct cyttsp *ts = dev_get_drvdata(dev); 508 509 mutex_lock(&ts->input->mutex); 510 511 if (input_device_enabled(ts->input)) 512 cyttsp_enable(ts); 513 514 ts->suspended = false; 515 516 mutex_unlock(&ts->input->mutex); 517 518 return 0; 519 } 520 521 SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); 522 EXPORT_SYMBOL_GPL(cyttsp_pm_ops); 523 524 static int cyttsp_open(struct input_dev *dev) 525 { 526 struct cyttsp *ts = input_get_drvdata(dev); 527 int retval = 0; 528 529 if (!ts->suspended) 530 retval = cyttsp_enable(ts); 531 532 return retval; 533 } 534 535 static void cyttsp_close(struct input_dev *dev) 536 { 537 struct cyttsp *ts = input_get_drvdata(dev); 538 539 if (!ts->suspended) 540 cyttsp_disable(ts); 541 } 542 543 static int cyttsp_parse_properties(struct cyttsp *ts) 544 { 545 struct device *dev = ts->dev; 546 u32 dt_value; 547 int ret; 548 549 ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL); 550 if (!ts->bl_keys) 551 return -ENOMEM; 552 553 /* Set some default values */ 554 ts->use_hndshk = false; 555 ts->act_dist = CY_ACT_DIST_DFLT; 556 ts->act_intrvl = CY_ACT_INTRVL_DFLT; 557 ts->tch_tmout = CY_TCH_TMOUT_DFLT; 558 ts->lp_intrvl = CY_LP_INTRVL_DFLT; 559 560 ret = device_property_read_u8_array(dev, "bootloader-key", 561 ts->bl_keys, CY_NUM_BL_KEYS); 562 if (ret) { 563 dev_err(dev, 564 "bootloader-key property could not be retrieved\n"); 565 return ret; 566 } 567 568 ts->use_hndshk = device_property_present(dev, "use-handshake"); 569 570 if (!device_property_read_u32(dev, "active-distance", &dt_value)) { 571 if (dt_value > 15) { 572 dev_err(dev, "active-distance (%u) must be [0-15]\n", 573 dt_value); 574 return -EINVAL; 575 } 576 ts->act_dist &= ~CY_ACT_DIST_MASK; 577 ts->act_dist |= dt_value; 578 } 579 580 if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) { 581 if (dt_value > 255) { 582 dev_err(dev, "active-interval-ms (%u) must be [0-255]\n", 583 dt_value); 584 return -EINVAL; 585 } 586 ts->act_intrvl = dt_value; 587 } 588 589 if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) { 590 if (dt_value > 2550) { 591 dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n", 592 dt_value); 593 return -EINVAL; 594 } 595 /* Register value is expressed in 0.01s / bit */ 596 ts->lp_intrvl = dt_value / 10; 597 } 598 599 if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) { 600 if (dt_value > 2550) { 601 dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n", 602 dt_value); 603 return -EINVAL; 604 } 605 /* Register value is expressed in 0.01s / bit */ 606 ts->tch_tmout = dt_value / 10; 607 } 608 609 return 0; 610 } 611 612 static void cyttsp_disable_regulators(void *_ts) 613 { 614 struct cyttsp *ts = _ts; 615 616 regulator_bulk_disable(ARRAY_SIZE(ts->regulators), 617 ts->regulators); 618 } 619 620 struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, 621 struct device *dev, int irq, size_t xfer_buf_size) 622 { 623 struct cyttsp *ts; 624 struct input_dev *input_dev; 625 int error; 626 627 ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL); 628 if (!ts) 629 return ERR_PTR(-ENOMEM); 630 631 input_dev = devm_input_allocate_device(dev); 632 if (!input_dev) 633 return ERR_PTR(-ENOMEM); 634 635 ts->dev = dev; 636 ts->input = input_dev; 637 ts->bus_ops = bus_ops; 638 ts->irq = irq; 639 640 /* 641 * VCPIN is the analog voltage supply 642 * VDD is the digital voltage supply 643 */ 644 ts->regulators[0].supply = "vcpin"; 645 ts->regulators[1].supply = "vdd"; 646 error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators), 647 ts->regulators); 648 if (error) { 649 dev_err(dev, "Failed to get regulators: %d\n", error); 650 return ERR_PTR(error); 651 } 652 653 error = regulator_bulk_enable(ARRAY_SIZE(ts->regulators), 654 ts->regulators); 655 if (error) { 656 dev_err(dev, "Cannot enable regulators: %d\n", error); 657 return ERR_PTR(error); 658 } 659 660 error = devm_add_action_or_reset(dev, cyttsp_disable_regulators, ts); 661 if (error) { 662 dev_err(dev, "failed to install chip disable handler\n"); 663 return ERR_PTR(error); 664 } 665 666 ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 667 if (IS_ERR(ts->reset_gpio)) { 668 error = PTR_ERR(ts->reset_gpio); 669 dev_err(dev, "Failed to request reset gpio, error %d\n", error); 670 return ERR_PTR(error); 671 } 672 673 error = cyttsp_parse_properties(ts); 674 if (error) 675 return ERR_PTR(error); 676 677 init_completion(&ts->bl_ready); 678 679 input_dev->name = "Cypress TTSP TouchScreen"; 680 input_dev->id.bustype = bus_ops->bustype; 681 input_dev->dev.parent = ts->dev; 682 683 input_dev->open = cyttsp_open; 684 input_dev->close = cyttsp_close; 685 686 input_set_drvdata(input_dev, ts); 687 688 input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); 689 input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); 690 /* One byte for width 0..255 so this is the limit */ 691 input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 692 693 touchscreen_parse_properties(input_dev, true, NULL); 694 695 error = input_mt_init_slots(input_dev, CY_MAX_ID, INPUT_MT_DIRECT); 696 if (error) { 697 dev_err(dev, "Unable to init MT slots.\n"); 698 return ERR_PTR(error); 699 } 700 701 error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, 702 IRQF_ONESHOT | IRQF_NO_AUTOEN, 703 "cyttsp", ts); 704 if (error) { 705 dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", 706 ts->irq, error); 707 return ERR_PTR(error); 708 } 709 710 cyttsp_hard_reset(ts); 711 712 error = cyttsp_power_on(ts); 713 if (error) 714 return ERR_PTR(error); 715 716 error = input_register_device(input_dev); 717 if (error) { 718 dev_err(ts->dev, "failed to register input device: %d\n", 719 error); 720 return ERR_PTR(error); 721 } 722 723 return ts; 724 } 725 EXPORT_SYMBOL_GPL(cyttsp_probe); 726 727 MODULE_LICENSE("GPL"); 728 MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); 729 MODULE_AUTHOR("Cypress"); 730