1 /* 2 * Power supply driver for the Active-semi ACT8945A PMIC 3 * 4 * Copyright (C) 2015 Atmel Corporation 5 * 6 * Author: Wenyou Yang <wenyou.yang@atmel.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 #include <linux/interrupt.h> 14 #include <linux/module.h> 15 #include <linux/of.h> 16 #include <linux/of_irq.h> 17 #include <linux/platform_device.h> 18 #include <linux/power_supply.h> 19 #include <linux/regmap.h> 20 #include <linux/gpio/consumer.h> 21 22 static const char *act8945a_charger_model = "ACT8945A"; 23 static const char *act8945a_charger_manufacturer = "Active-semi"; 24 25 /** 26 * ACT8945A Charger Register Map 27 */ 28 29 /* 0x70: Reserved */ 30 #define ACT8945A_APCH_CFG 0x71 31 #define ACT8945A_APCH_STATUS 0x78 32 #define ACT8945A_APCH_CTRL 0x79 33 #define ACT8945A_APCH_STATE 0x7A 34 35 /* ACT8945A_APCH_CFG */ 36 #define APCH_CFG_OVPSET (0x3 << 0) 37 #define APCH_CFG_OVPSET_6V6 (0x0 << 0) 38 #define APCH_CFG_OVPSET_7V (0x1 << 0) 39 #define APCH_CFG_OVPSET_7V5 (0x2 << 0) 40 #define APCH_CFG_OVPSET_8V (0x3 << 0) 41 #define APCH_CFG_PRETIMO (0x3 << 2) 42 #define APCH_CFG_PRETIMO_40_MIN (0x0 << 2) 43 #define APCH_CFG_PRETIMO_60_MIN (0x1 << 2) 44 #define APCH_CFG_PRETIMO_80_MIN (0x2 << 2) 45 #define APCH_CFG_PRETIMO_DISABLED (0x3 << 2) 46 #define APCH_CFG_TOTTIMO (0x3 << 4) 47 #define APCH_CFG_TOTTIMO_3_HOUR (0x0 << 4) 48 #define APCH_CFG_TOTTIMO_4_HOUR (0x1 << 4) 49 #define APCH_CFG_TOTTIMO_5_HOUR (0x2 << 4) 50 #define APCH_CFG_TOTTIMO_DISABLED (0x3 << 4) 51 #define APCH_CFG_SUSCHG (0x1 << 7) 52 53 #define APCH_STATUS_CHGDAT BIT(0) 54 #define APCH_STATUS_INDAT BIT(1) 55 #define APCH_STATUS_TEMPDAT BIT(2) 56 #define APCH_STATUS_TIMRDAT BIT(3) 57 #define APCH_STATUS_CHGSTAT BIT(4) 58 #define APCH_STATUS_INSTAT BIT(5) 59 #define APCH_STATUS_TEMPSTAT BIT(6) 60 #define APCH_STATUS_TIMRSTAT BIT(7) 61 62 #define APCH_CTRL_CHGEOCOUT BIT(0) 63 #define APCH_CTRL_INDIS BIT(1) 64 #define APCH_CTRL_TEMPOUT BIT(2) 65 #define APCH_CTRL_TIMRPRE BIT(3) 66 #define APCH_CTRL_CHGEOCIN BIT(4) 67 #define APCH_CTRL_INCON BIT(5) 68 #define APCH_CTRL_TEMPIN BIT(6) 69 #define APCH_CTRL_TIMRTOT BIT(7) 70 71 #define APCH_STATE_ACINSTAT (0x1 << 1) 72 #define APCH_STATE_CSTATE (0x3 << 4) 73 #define APCH_STATE_CSTATE_SHIFT 4 74 #define APCH_STATE_CSTATE_DISABLED 0x00 75 #define APCH_STATE_CSTATE_EOC 0x01 76 #define APCH_STATE_CSTATE_FAST 0x02 77 #define APCH_STATE_CSTATE_PRE 0x03 78 79 struct act8945a_charger { 80 struct power_supply *psy; 81 struct power_supply_desc desc; 82 struct regmap *regmap; 83 struct work_struct work; 84 85 bool init_done; 86 struct gpio_desc *lbo_gpio; 87 struct gpio_desc *chglev_gpio; 88 }; 89 90 static int act8945a_get_charger_state(struct regmap *regmap, int *val) 91 { 92 int ret; 93 unsigned int status, state; 94 95 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); 96 if (ret < 0) 97 return ret; 98 99 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); 100 if (ret < 0) 101 return ret; 102 103 state &= APCH_STATE_CSTATE; 104 state >>= APCH_STATE_CSTATE_SHIFT; 105 106 switch (state) { 107 case APCH_STATE_CSTATE_PRE: 108 case APCH_STATE_CSTATE_FAST: 109 *val = POWER_SUPPLY_STATUS_CHARGING; 110 break; 111 case APCH_STATE_CSTATE_EOC: 112 if (status & APCH_STATUS_CHGDAT) 113 *val = POWER_SUPPLY_STATUS_FULL; 114 else 115 *val = POWER_SUPPLY_STATUS_CHARGING; 116 break; 117 case APCH_STATE_CSTATE_DISABLED: 118 default: 119 if (!(status & APCH_STATUS_INDAT)) 120 *val = POWER_SUPPLY_STATUS_DISCHARGING; 121 else 122 *val = POWER_SUPPLY_STATUS_NOT_CHARGING; 123 break; 124 } 125 126 return 0; 127 } 128 129 static int act8945a_get_charge_type(struct regmap *regmap, int *val) 130 { 131 int ret; 132 unsigned int status, state; 133 134 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); 135 if (ret < 0) 136 return ret; 137 138 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); 139 if (ret < 0) 140 return ret; 141 142 state &= APCH_STATE_CSTATE; 143 state >>= APCH_STATE_CSTATE_SHIFT; 144 145 switch (state) { 146 case APCH_STATE_CSTATE_PRE: 147 *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 148 break; 149 case APCH_STATE_CSTATE_FAST: 150 *val = POWER_SUPPLY_CHARGE_TYPE_FAST; 151 break; 152 case APCH_STATE_CSTATE_EOC: 153 *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 154 break; 155 case APCH_STATE_CSTATE_DISABLED: 156 default: 157 if (!(status & APCH_STATUS_INDAT)) 158 *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 159 else 160 *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 161 break; 162 } 163 164 return 0; 165 } 166 167 static int act8945a_get_battery_health(struct regmap *regmap, int *val) 168 { 169 int ret; 170 unsigned int status, state, config; 171 172 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); 173 if (ret < 0) 174 return ret; 175 176 ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config); 177 if (ret < 0) 178 return ret; 179 180 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); 181 if (ret < 0) 182 return ret; 183 184 state &= APCH_STATE_CSTATE; 185 state >>= APCH_STATE_CSTATE_SHIFT; 186 187 switch (state) { 188 case APCH_STATE_CSTATE_DISABLED: 189 if (config & APCH_CFG_SUSCHG) { 190 *val = POWER_SUPPLY_HEALTH_UNKNOWN; 191 } else if (status & APCH_STATUS_INDAT) { 192 if (!(status & APCH_STATUS_TEMPDAT)) 193 *val = POWER_SUPPLY_HEALTH_OVERHEAT; 194 else if (status & APCH_STATUS_TIMRDAT) 195 *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; 196 else 197 *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 198 } else { 199 *val = POWER_SUPPLY_HEALTH_GOOD; 200 } 201 break; 202 case APCH_STATE_CSTATE_PRE: 203 case APCH_STATE_CSTATE_FAST: 204 case APCH_STATE_CSTATE_EOC: 205 default: 206 *val = POWER_SUPPLY_HEALTH_GOOD; 207 break; 208 } 209 210 return 0; 211 } 212 213 static int act8945a_get_capacity_level(struct act8945a_charger *charger, 214 struct regmap *regmap, int *val) 215 { 216 int ret; 217 unsigned int status, state, config; 218 int lbo_level = gpiod_get_value(charger->lbo_gpio); 219 220 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); 221 if (ret < 0) 222 return ret; 223 224 ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config); 225 if (ret < 0) 226 return ret; 227 228 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); 229 if (ret < 0) 230 return ret; 231 232 state &= APCH_STATE_CSTATE; 233 state >>= APCH_STATE_CSTATE_SHIFT; 234 235 switch (state) { 236 case APCH_STATE_CSTATE_PRE: 237 *val = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 238 break; 239 case APCH_STATE_CSTATE_FAST: 240 if (lbo_level) 241 *val = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; 242 else 243 *val = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 244 break; 245 case APCH_STATE_CSTATE_EOC: 246 if (status & APCH_STATUS_CHGDAT) 247 *val = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 248 else 249 *val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 250 break; 251 case APCH_STATE_CSTATE_DISABLED: 252 default: 253 if (config & APCH_CFG_SUSCHG) { 254 *val = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 255 } else { 256 *val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 257 if (!(status & APCH_STATUS_INDAT)) { 258 if (!lbo_level) 259 *val = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 260 } 261 } 262 break; 263 } 264 265 return 0; 266 } 267 268 #define MAX_CURRENT_USB_HIGH 450000 269 #define MAX_CURRENT_USB_LOW 90000 270 #define MAX_CURRENT_USB_PRE 45000 271 /* 272 * Riset(K) = 2336 * (1V/Ichg(mA)) - 0.205 273 * Riset = 2.43K 274 */ 275 #define MAX_CURRENT_AC_HIGH 886527 276 #define MAX_CURRENT_AC_LOW 117305 277 #define MAX_CURRENT_AC_HIGH_PRE 88653 278 #define MAX_CURRENT_AC_LOW_PRE 11731 279 280 static int act8945a_get_current_max(struct act8945a_charger *charger, 281 struct regmap *regmap, int *val) 282 { 283 int ret; 284 unsigned int status, state; 285 unsigned int acin_state; 286 int chgin_level = gpiod_get_value(charger->chglev_gpio); 287 288 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); 289 if (ret < 0) 290 return ret; 291 292 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); 293 if (ret < 0) 294 return ret; 295 296 acin_state = (state & APCH_STATE_ACINSTAT) >> 1; 297 298 state &= APCH_STATE_CSTATE; 299 state >>= APCH_STATE_CSTATE_SHIFT; 300 301 switch (state) { 302 case APCH_STATE_CSTATE_PRE: 303 if (acin_state) { 304 if (chgin_level) 305 *val = MAX_CURRENT_AC_HIGH_PRE; 306 else 307 *val = MAX_CURRENT_AC_LOW_PRE; 308 } else { 309 *val = MAX_CURRENT_USB_PRE; 310 } 311 break; 312 case APCH_STATE_CSTATE_FAST: 313 if (acin_state) { 314 if (chgin_level) 315 *val = MAX_CURRENT_AC_HIGH; 316 else 317 *val = MAX_CURRENT_AC_LOW; 318 } else { 319 if (chgin_level) 320 *val = MAX_CURRENT_USB_HIGH; 321 else 322 *val = MAX_CURRENT_USB_LOW; 323 } 324 break; 325 case APCH_STATE_CSTATE_EOC: 326 case APCH_STATE_CSTATE_DISABLED: 327 default: 328 *val = 0; 329 break; 330 } 331 332 return 0; 333 } 334 335 static enum power_supply_property act8945a_charger_props[] = { 336 POWER_SUPPLY_PROP_STATUS, 337 POWER_SUPPLY_PROP_CHARGE_TYPE, 338 POWER_SUPPLY_PROP_TECHNOLOGY, 339 POWER_SUPPLY_PROP_HEALTH, 340 POWER_SUPPLY_PROP_CAPACITY_LEVEL, 341 POWER_SUPPLY_PROP_CURRENT_MAX, 342 POWER_SUPPLY_PROP_MODEL_NAME, 343 POWER_SUPPLY_PROP_MANUFACTURER 344 }; 345 346 static int act8945a_charger_get_property(struct power_supply *psy, 347 enum power_supply_property prop, 348 union power_supply_propval *val) 349 { 350 struct act8945a_charger *charger = power_supply_get_drvdata(psy); 351 struct regmap *regmap = charger->regmap; 352 int ret = 0; 353 354 switch (prop) { 355 case POWER_SUPPLY_PROP_STATUS: 356 ret = act8945a_get_charger_state(regmap, &val->intval); 357 break; 358 case POWER_SUPPLY_PROP_CHARGE_TYPE: 359 ret = act8945a_get_charge_type(regmap, &val->intval); 360 break; 361 case POWER_SUPPLY_PROP_TECHNOLOGY: 362 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 363 break; 364 case POWER_SUPPLY_PROP_HEALTH: 365 ret = act8945a_get_battery_health(regmap, &val->intval); 366 break; 367 case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 368 ret = act8945a_get_capacity_level(charger, 369 regmap, &val->intval); 370 break; 371 case POWER_SUPPLY_PROP_CURRENT_MAX: 372 ret = act8945a_get_current_max(charger, 373 regmap, &val->intval); 374 break; 375 case POWER_SUPPLY_PROP_MODEL_NAME: 376 val->strval = act8945a_charger_model; 377 break; 378 case POWER_SUPPLY_PROP_MANUFACTURER: 379 val->strval = act8945a_charger_manufacturer; 380 break; 381 default: 382 return -EINVAL; 383 } 384 385 return ret; 386 } 387 388 static int act8945a_enable_interrupt(struct act8945a_charger *charger) 389 { 390 struct regmap *regmap = charger->regmap; 391 unsigned char ctrl; 392 int ret; 393 394 ctrl = APCH_CTRL_CHGEOCOUT | APCH_CTRL_CHGEOCIN | 395 APCH_CTRL_INDIS | APCH_CTRL_INCON | 396 APCH_CTRL_TEMPOUT | APCH_CTRL_TEMPIN | 397 APCH_CTRL_TIMRPRE | APCH_CTRL_TIMRTOT; 398 ret = regmap_write(regmap, ACT8945A_APCH_CTRL, ctrl); 399 if (ret) 400 return ret; 401 402 ctrl = APCH_STATUS_CHGSTAT | APCH_STATUS_INSTAT | 403 APCH_STATUS_TEMPSTAT | APCH_STATUS_TIMRSTAT; 404 ret = regmap_write(regmap, ACT8945A_APCH_STATUS, ctrl); 405 if (ret) 406 return ret; 407 408 return 0; 409 } 410 411 static unsigned int act8945a_set_supply_type(struct act8945a_charger *charger, 412 unsigned int *type) 413 { 414 unsigned int status, state; 415 int ret; 416 417 ret = regmap_read(charger->regmap, ACT8945A_APCH_STATUS, &status); 418 if (ret < 0) 419 return ret; 420 421 ret = regmap_read(charger->regmap, ACT8945A_APCH_STATE, &state); 422 if (ret < 0) 423 return ret; 424 425 if (status & APCH_STATUS_INDAT) { 426 if (state & APCH_STATE_ACINSTAT) 427 *type = POWER_SUPPLY_TYPE_MAINS; 428 else 429 *type = POWER_SUPPLY_TYPE_USB; 430 } else { 431 *type = POWER_SUPPLY_TYPE_BATTERY; 432 } 433 434 return 0; 435 } 436 437 static void act8945a_work(struct work_struct *work) 438 { 439 struct act8945a_charger *charger = 440 container_of(work, struct act8945a_charger, work); 441 442 act8945a_set_supply_type(charger, &charger->desc.type); 443 444 power_supply_changed(charger->psy); 445 } 446 447 static irqreturn_t act8945a_status_changed(int irq, void *dev_id) 448 { 449 struct act8945a_charger *charger = dev_id; 450 451 if (charger->init_done) 452 schedule_work(&charger->work); 453 454 return IRQ_HANDLED; 455 } 456 457 #define DEFAULT_TOTAL_TIME_OUT 3 458 #define DEFAULT_PRE_TIME_OUT 40 459 #define DEFAULT_INPUT_OVP_THRESHOLD 6600 460 461 static int act8945a_charger_config(struct device *dev, 462 struct act8945a_charger *charger) 463 { 464 struct device_node *np = dev->of_node; 465 struct regmap *regmap = charger->regmap; 466 467 u32 total_time_out; 468 u32 pre_time_out; 469 u32 input_voltage_threshold; 470 int err, ret; 471 472 unsigned int tmp; 473 unsigned int value = 0; 474 475 if (!np) { 476 dev_err(dev, "no charger of node\n"); 477 return -EINVAL; 478 } 479 480 ret = regmap_read(regmap, ACT8945A_APCH_CFG, &tmp); 481 if (ret) 482 return ret; 483 484 if (tmp & APCH_CFG_SUSCHG) { 485 value |= APCH_CFG_SUSCHG; 486 dev_info(dev, "have been suspended\n"); 487 } 488 489 charger->lbo_gpio = devm_gpiod_get_optional(dev, "active-semi,lbo", 490 GPIOD_IN); 491 if (IS_ERR(charger->lbo_gpio)) { 492 err = PTR_ERR(charger->lbo_gpio); 493 dev_err(dev, "unable to claim gpio \"lbo\": %d\n", err); 494 return err; 495 } 496 497 ret = devm_request_irq(dev, gpiod_to_irq(charger->lbo_gpio), 498 act8945a_status_changed, 499 (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), 500 "act8945a_lbo_detect", charger); 501 if (ret) 502 dev_info(dev, "failed to request gpio \"lbo\" IRQ\n"); 503 504 charger->chglev_gpio = devm_gpiod_get_optional(dev, 505 "active-semi,chglev", 506 GPIOD_IN); 507 if (IS_ERR(charger->chglev_gpio)) { 508 err = PTR_ERR(charger->chglev_gpio); 509 dev_err(dev, "unable to claim gpio \"chglev\": %d\n", err); 510 return err; 511 } 512 513 if (of_property_read_u32(np, 514 "active-semi,input-voltage-threshold-microvolt", 515 &input_voltage_threshold)) 516 input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD; 517 518 if (of_property_read_u32(np, 519 "active-semi,precondition-timeout", 520 &pre_time_out)) 521 pre_time_out = DEFAULT_PRE_TIME_OUT; 522 523 if (of_property_read_u32(np, "active-semi,total-timeout", 524 &total_time_out)) 525 total_time_out = DEFAULT_TOTAL_TIME_OUT; 526 527 switch (input_voltage_threshold) { 528 case 8000: 529 value |= APCH_CFG_OVPSET_8V; 530 break; 531 case 7500: 532 value |= APCH_CFG_OVPSET_7V5; 533 break; 534 case 7000: 535 value |= APCH_CFG_OVPSET_7V; 536 break; 537 case 6600: 538 default: 539 value |= APCH_CFG_OVPSET_6V6; 540 break; 541 } 542 543 switch (pre_time_out) { 544 case 60: 545 value |= APCH_CFG_PRETIMO_60_MIN; 546 break; 547 case 80: 548 value |= APCH_CFG_PRETIMO_80_MIN; 549 break; 550 case 0: 551 value |= APCH_CFG_PRETIMO_DISABLED; 552 break; 553 case 40: 554 default: 555 value |= APCH_CFG_PRETIMO_40_MIN; 556 break; 557 } 558 559 switch (total_time_out) { 560 case 4: 561 value |= APCH_CFG_TOTTIMO_4_HOUR; 562 break; 563 case 5: 564 value |= APCH_CFG_TOTTIMO_5_HOUR; 565 break; 566 case 0: 567 value |= APCH_CFG_TOTTIMO_DISABLED; 568 break; 569 case 3: 570 default: 571 value |= APCH_CFG_TOTTIMO_3_HOUR; 572 break; 573 } 574 575 return regmap_write(regmap, ACT8945A_APCH_CFG, value); 576 } 577 578 static int act8945a_charger_probe(struct platform_device *pdev) 579 { 580 struct act8945a_charger *charger; 581 struct power_supply_config psy_cfg = {}; 582 int irq, ret; 583 584 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); 585 if (!charger) 586 return -ENOMEM; 587 588 charger->regmap = dev_get_regmap(pdev->dev.parent, NULL); 589 if (!charger->regmap) { 590 dev_err(&pdev->dev, "Parent did not provide regmap\n"); 591 return -EINVAL; 592 } 593 594 ret = act8945a_charger_config(&pdev->dev, charger); 595 if (ret) 596 return ret; 597 598 irq = of_irq_get(pdev->dev.of_node, 0); 599 if (irq == -EPROBE_DEFER) { 600 dev_err(&pdev->dev, "failed to find IRQ number\n"); 601 return -EPROBE_DEFER; 602 } 603 604 ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed, 605 IRQF_TRIGGER_FALLING, "act8945a_interrupt", 606 charger); 607 if (ret) { 608 dev_err(&pdev->dev, "failed to request nIRQ pin IRQ\n"); 609 return ret; 610 } 611 612 charger->desc.name = "act8945a-charger"; 613 charger->desc.get_property = act8945a_charger_get_property; 614 charger->desc.properties = act8945a_charger_props; 615 charger->desc.num_properties = ARRAY_SIZE(act8945a_charger_props); 616 617 ret = act8945a_set_supply_type(charger, &charger->desc.type); 618 if (ret) 619 return -EINVAL; 620 621 psy_cfg.of_node = pdev->dev.of_node; 622 psy_cfg.drv_data = charger; 623 624 charger->psy = devm_power_supply_register(&pdev->dev, 625 &charger->desc, 626 &psy_cfg); 627 if (IS_ERR(charger->psy)) { 628 dev_err(&pdev->dev, "failed to register power supply\n"); 629 return PTR_ERR(charger->psy); 630 } 631 632 platform_set_drvdata(pdev, charger); 633 634 INIT_WORK(&charger->work, act8945a_work); 635 636 ret = act8945a_enable_interrupt(charger); 637 if (ret) 638 return -EIO; 639 640 charger->init_done = true; 641 642 return 0; 643 } 644 645 static int act8945a_charger_remove(struct platform_device *pdev) 646 { 647 struct act8945a_charger *charger = platform_get_drvdata(pdev); 648 649 charger->init_done = false; 650 cancel_work_sync(&charger->work); 651 652 return 0; 653 } 654 655 static struct platform_driver act8945a_charger_driver = { 656 .driver = { 657 .name = "act8945a-charger", 658 }, 659 .probe = act8945a_charger_probe, 660 .remove = act8945a_charger_remove, 661 }; 662 module_platform_driver(act8945a_charger_driver); 663 664 MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver"); 665 MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>"); 666 MODULE_LICENSE("GPL"); 667