| @@ -1,33 +1,33 @@ | | | @@ -1,33 +1,33 @@ |
1 | /* $NetBSD: si70xx.c,v 1.9 2021/11/11 14:16:04 brad Exp $ */ | | 1 | /* $NetBSD: si70xx.c,v 1.10 2021/11/12 15:12:11 brad Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2017 Brad Spencer <brad@anduin.eldar.org> | | 4 | * Copyright (c) 2017 Brad Spencer <brad@anduin.eldar.org> |
5 | * | | 5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any | | 6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above | | 7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. | | 8 | * copyright notice and this permission notice appear in all copies. |
9 | * | | 9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ | | 17 | */ |
18 | | | 18 | |
19 | #include <sys/cdefs.h> | | 19 | #include <sys/cdefs.h> |
20 | __KERNEL_RCSID(0, "$NetBSD: si70xx.c,v 1.9 2021/11/11 14:16:04 brad Exp $"); | | 20 | __KERNEL_RCSID(0, "$NetBSD: si70xx.c,v 1.10 2021/11/12 15:12:11 brad Exp $"); |
21 | | | 21 | |
22 | /* | | 22 | /* |
23 | Driver for the Silicon Labs SI7013/SI7020/SI7021, HTU21D and SHT21 | | 23 | Driver for the Silicon Labs SI7013/SI7020/SI7021, HTU21D and SHT21 |
24 | */ | | 24 | */ |
25 | | | 25 | |
26 | #include <sys/param.h> | | 26 | #include <sys/param.h> |
27 | #include <sys/systm.h> | | 27 | #include <sys/systm.h> |
28 | #include <sys/kernel.h> | | 28 | #include <sys/kernel.h> |
29 | #include <sys/device.h> | | 29 | #include <sys/device.h> |
30 | #include <sys/module.h> | | 30 | #include <sys/module.h> |
31 | #include <sys/sysctl.h> | | 31 | #include <sys/sysctl.h> |
32 | #include <sys/mutex.h> | | 32 | #include <sys/mutex.h> |
33 | | | 33 | |
| @@ -599,40 +599,41 @@ si70xx_match(device_t parent, cfdata_t m | | | @@ -599,40 +599,41 @@ si70xx_match(device_t parent, cfdata_t m |
599 | } | | 599 | } |
600 | | | 600 | |
601 | static void | | 601 | static void |
602 | si70xx_attach(device_t parent, device_t self, void *aux) | | 602 | si70xx_attach(device_t parent, device_t self, void *aux) |
603 | { | | 603 | { |
604 | struct si70xx_sc *sc; | | 604 | struct si70xx_sc *sc; |
605 | struct i2c_attach_args *ia; | | 605 | struct i2c_attach_args *ia; |
606 | int error, i; | | 606 | int error, i; |
607 | int ecount = 0; | | 607 | int ecount = 0; |
608 | uint8_t buf[8]; | | 608 | uint8_t buf[8]; |
609 | uint8_t testcrcpt1[4]; | | 609 | uint8_t testcrcpt1[4]; |
610 | uint8_t testcrcpt2[4]; | | 610 | uint8_t testcrcpt2[4]; |
611 | uint8_t crc1 = 0, crc2 = 0; | | 611 | uint8_t crc1 = 0, crc2 = 0; |
| | | 612 | bool validcrcpt1, validcrcpt2; |
612 | uint8_t readcrc1 = 0, readcrc2 = 0; | | 613 | uint8_t readcrc1 = 0, readcrc2 = 0; |
613 | uint8_t fwversion = 0, model, heaterregister; | | 614 | uint8_t fwversion = 0, model, heaterregister; |
614 | | | 615 | |
615 | ia = aux; | | 616 | ia = aux; |
616 | sc = device_private(self); | | 617 | sc = device_private(self); |
617 | | | 618 | |
618 | sc->sc_dev = self; | | 619 | sc->sc_dev = self; |
619 | sc->sc_tag = ia->ia_tag; | | 620 | sc->sc_tag = ia->ia_tag; |
620 | sc->sc_addr = ia->ia_addr; | | 621 | sc->sc_addr = ia->ia_addr; |
621 | sc->sc_si70xxdebug = 0; | | 622 | sc->sc_si70xxdebug = 0; |
622 | #ifdef HAVE_I2C_EXECV | | 623 | #ifdef HAVE_I2C_EXECV |
623 | sc->sc_clockstretch = 2048; | | 624 | sc->sc_clockstretch = 2048; |
624 | #endif | | 625 | #endif |
625 | sc->sc_readattempts = 25; | | 626 | sc->sc_readattempts = 40; |
626 | sc->sc_ignorecrc = false; | | 627 | sc->sc_ignorecrc = false; |
627 | sc->sc_sme = NULL; | | 628 | sc->sc_sme = NULL; |
628 | sc->sc_noheater = false; | | 629 | sc->sc_noheater = false; |
629 | sc->sc_nofw = false; | | 630 | sc->sc_nofw = false; |
630 | | | 631 | |
631 | aprint_normal("\n"); | | 632 | aprint_normal("\n"); |
632 | | | 633 | |
633 | mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); | | 634 | mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); |
634 | sc->sc_numsensors = __arraycount(si70xx_sensors); | | 635 | sc->sc_numsensors = __arraycount(si70xx_sensors); |
635 | | | 636 | |
636 | if ((sc->sc_sme = sysmon_envsys_create()) == NULL) { | | 637 | if ((sc->sc_sme = sysmon_envsys_create()) == NULL) { |
637 | aprint_error_dev(self, | | 638 | aprint_error_dev(self, |
638 | "Unable to create sysmon structure\n"); | | 639 | "Unable to create sysmon structure\n"); |
| @@ -655,68 +656,95 @@ si70xx_attach(device_t parent, device_t | | | @@ -655,68 +656,95 @@ si70xx_attach(device_t parent, device_t |
655 | error = si70xx_cmd2(sc, SI70XX_READ_ID_PT1A, SI70XX_READ_ID_PT1B, | | 656 | error = si70xx_cmd2(sc, SI70XX_READ_ID_PT1A, SI70XX_READ_ID_PT1B, |
656 | buf, 8); | | 657 | buf, 8); |
657 | if (error) { | | 658 | if (error) { |
658 | aprint_error_dev(self, "Failed to read first part of ID: %d\n", | | 659 | aprint_error_dev(self, "Failed to read first part of ID: %d\n", |
659 | error); | | 660 | error); |
660 | ecount++; | | 661 | ecount++; |
661 | } | | 662 | } |
662 | testcrcpt1[0] = buf[0]; | | 663 | testcrcpt1[0] = buf[0]; |
663 | testcrcpt1[1] = buf[2]; | | 664 | testcrcpt1[1] = buf[2]; |
664 | testcrcpt1[2] = buf[4]; | | 665 | testcrcpt1[2] = buf[4]; |
665 | testcrcpt1[3] = buf[6]; | | 666 | testcrcpt1[3] = buf[6]; |
666 | readcrc1 = buf[7]; | | 667 | readcrc1 = buf[7]; |
667 | crc1 = si70xx_crc(testcrcpt1, 4); | | 668 | crc1 = si70xx_crc(testcrcpt1, 4); |
| | | 669 | /* A "real" SI70xx has the CRC cover the entire first part of the |
| | | 670 | * serial number. An HTU21D has the CRC broken out into each |
| | | 671 | * part of the serial number. |
| | | 672 | */ |
| | | 673 | validcrcpt1 = (readcrc1 == crc1); |
| | | 674 | if (! validcrcpt1) { |
| | | 675 | validcrcpt1 = (si70xx_crc(&testcrcpt1[0],1) == buf[1] && |
| | | 676 | si70xx_crc(&testcrcpt1[1],1) == buf[3] && |
| | | 677 | si70xx_crc(&testcrcpt1[2],1) == buf[5] && |
| | | 678 | si70xx_crc(&testcrcpt1[3],1) == buf[7]); |
| | | 679 | DPRINTF(sc, 2, ("%s: Part 1 SN CRC was not valid for real type, " |
| | | 680 | "check clone: %d\n", device_xname(sc->sc_dev), validcrcpt1)); |
| | | 681 | } |
668 | | | 682 | |
669 | DPRINTF(sc, 2, ("%s: read 1 values: %02x%02x%02x%02x%02x%02x%02x%02x " | | 683 | DPRINTF(sc, 2, ("%s: read 1 values: %02x%02x%02x%02x%02x%02x%02x%02x " |
670 | "- %02x\n", device_xname(sc->sc_dev), buf[0], buf[1], | | 684 | "- %02x -- %d\n", device_xname(sc->sc_dev), buf[0], buf[1], |
671 | buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], | | 685 | buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], |
672 | crc1)); | | 686 | crc1, validcrcpt1)); |
673 | | | 687 | |
674 | error = si70xx_cmd2(sc, SI70XX_READ_ID_PT2A, SI70XX_READ_ID_PT2B, | | 688 | error = si70xx_cmd2(sc, SI70XX_READ_ID_PT2A, SI70XX_READ_ID_PT2B, |
675 | buf, 8); | | 689 | buf, 8); |
676 | if (error != 0) { | | 690 | if (error != 0) { |
677 | aprint_error_dev(self, "Failed to read second part of ID: %d\n", | | 691 | aprint_error_dev(self, "Failed to read second part of ID: %d\n", |
678 | error); | | 692 | error); |
679 | ecount++; | | 693 | ecount++; |
680 | } | | 694 | } |
681 | model = testcrcpt2[0] = buf[0]; | | 695 | model = testcrcpt2[0] = buf[0]; |
682 | testcrcpt2[1] = buf[1]; | | 696 | testcrcpt2[1] = buf[1]; |
683 | testcrcpt2[2] = buf[3]; | | 697 | testcrcpt2[2] = buf[3]; |
684 | testcrcpt2[3] = buf[4]; | | 698 | testcrcpt2[3] = buf[4]; |
685 | readcrc2 = buf[5]; | | 699 | readcrc2 = buf[5]; |
686 | crc2 = si70xx_crc(testcrcpt2, 4); | | 700 | crc2 = si70xx_crc(testcrcpt2, 4); |
| | | 701 | /* It is even stranger for this part of the serial number. A "real" |
| | | 702 | * SI70XX will have a single CRC for the entire second part, but |
| | | 703 | * an HTU21D has a CRC for each word in this case. |
| | | 704 | * |
| | | 705 | * The datasheet actually agrees with the HTU21D case, and not the "real" |
| | | 706 | * chip. |
| | | 707 | */ |
| | | 708 | validcrcpt2 = (readcrc2 == crc2); |
| | | 709 | if (! validcrcpt2) { |
| | | 710 | validcrcpt2 = (si70xx_crc(&testcrcpt2[0],2) == buf[2] && |
| | | 711 | si70xx_crc(&testcrcpt2[2],2) == buf[5]); |
| | | 712 | DPRINTF(sc, 2, ("%s: Part 2 SN CRC was not valid for real type, " |
| | | 713 | "check clone: %d\n", device_xname(sc->sc_dev), validcrcpt2)); |
| | | 714 | } |
687 | | | 715 | |
688 | DPRINTF(sc, 2, ("%s: read 2 values: %02x%02x%02x%02x%02x%02x - %02x\n", | | 716 | DPRINTF(sc, 2, ("%s: read 2 values: %02x%02x%02x%02x%02x%02x - %02x -- %d\n", |
689 | device_xname(sc->sc_dev), buf[0], buf[1], buf[2], | | 717 | device_xname(sc->sc_dev), buf[0], buf[1], buf[2], |
690 | buf[3], buf[4], buf[5], crc2)); | | 718 | buf[3], buf[4], buf[5], crc2, validcrcpt2)); |
691 | | | 719 | |
692 | error = si70xx_cmd2(sc, SI70XX_READ_FW_VERA, SI70XX_READ_FW_VERB, | | 720 | error = si70xx_cmd2(sc, SI70XX_READ_FW_VERA, SI70XX_READ_FW_VERB, |
693 | buf, 8); | | 721 | buf, 8); |
694 | | | 722 | |
695 | if (error) { | | 723 | if (error) { |
696 | aprint_error_dev(self, "Failed to read firmware version: %d\n", | | 724 | aprint_error_dev(self, "Failed to read firmware version: Error %d\n", |
697 | error); | | 725 | error); |
698 | sc->sc_nofw = true; | | 726 | sc->sc_nofw = true; |
699 | } | | 727 | } |
700 | if (! sc->sc_nofw) { | | 728 | if (! sc->sc_nofw) { |
701 | fwversion = buf[0]; | | 729 | fwversion = buf[0]; |
702 | DPRINTF(sc, 2, ("%s: read fw values: %#x\n", device_xname(sc->sc_dev), | | 730 | DPRINTF(sc, 2, ("%s: read fw values: %#x\n", device_xname(sc->sc_dev), |
703 | fwversion)); | | 731 | fwversion)); |
704 | } | | 732 | } |
705 | | | 733 | |
706 | error = si70xx_cmd1(sc, SI70XX_READ_HEATER_REG, &heaterregister, 1); | | 734 | error = si70xx_cmd1(sc, SI70XX_READ_HEATER_REG, &heaterregister, 1); |
707 | | | 735 | |
708 | if (error) { | | 736 | if (error) { |
709 | aprint_error_dev(self, "Failed to read heater register: %d\n", | | 737 | aprint_error_dev(self, "Failed to read heater register: Error %d\n", |
710 | error); | | 738 | error); |
711 | sc->sc_noheater = true; | | 739 | sc->sc_noheater = true; |
712 | } | | 740 | } |
713 | | | 741 | |
714 | error = si70xx_update_status(sc); | | 742 | error = si70xx_update_status(sc); |
715 | | | 743 | |
716 | iic_release_bus(sc->sc_tag, 0); | | 744 | iic_release_bus(sc->sc_tag, 0); |
717 | | | 745 | |
718 | if ((error = si70xx_sysctl_init(sc)) != 0) { | | 746 | if ((error = si70xx_sysctl_init(sc)) != 0) { |
719 | aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error); | | 747 | aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error); |
720 | goto out; | | 748 | goto out; |
721 | } | | 749 | } |
722 | | | 750 | |
| @@ -749,31 +777,26 @@ si70xx_attach(device_t parent, device_t | | | @@ -749,31 +777,26 @@ si70xx_attach(device_t parent, device_t |
749 | sc->sc_sme->sme_name = device_xname(sc->sc_dev); | | 777 | sc->sc_sme->sme_name = device_xname(sc->sc_dev); |
750 | sc->sc_sme->sme_cookie = sc; | | 778 | sc->sc_sme->sme_cookie = sc; |
751 | sc->sc_sme->sme_refresh = si70xx_refresh; | | 779 | sc->sc_sme->sme_refresh = si70xx_refresh; |
752 | | | 780 | |
753 | DPRINTF(sc, 2, ("si70xx_attach: registering with envsys\n")); | | 781 | DPRINTF(sc, 2, ("si70xx_attach: registering with envsys\n")); |
754 | | | 782 | |
755 | if (sysmon_envsys_register(sc->sc_sme)) { | | 783 | if (sysmon_envsys_register(sc->sc_sme)) { |
756 | aprint_error_dev(self, | | 784 | aprint_error_dev(self, |
757 | "unable to register with sysmon\n"); | | 785 | "unable to register with sysmon\n"); |
758 | sysmon_envsys_destroy(sc->sc_sme); | | 786 | sysmon_envsys_destroy(sc->sc_sme); |
759 | sc->sc_sme = NULL; | | 787 | sc->sc_sme = NULL; |
760 | return; | | 788 | return; |
761 | } | | 789 | } |
762 | if (ecount != 0) { | | | |
763 | aprint_normal_dev(self, "Could not read model, " | | | |
764 | "probably an HTU21D\n"); | | | |
765 | return; | | | |
766 | } | | | |
767 | | | 790 | |
768 | char modelstr[64]; | | 791 | char modelstr[64]; |
769 | switch (model) { | | 792 | switch (model) { |
770 | case 0: | | 793 | case 0: |
771 | case 0xff: | | 794 | case 0xff: |
772 | snprintf(modelstr, sizeof(modelstr), "Engineering Sample"); | | 795 | snprintf(modelstr, sizeof(modelstr), "Engineering Sample"); |
773 | break; | | 796 | break; |
774 | case 13: | | 797 | case 13: |
775 | case 20: | | 798 | case 20: |
776 | case 21: | | 799 | case 21: |
777 | snprintf(modelstr, sizeof(modelstr), "SI70%d", model); | | 800 | snprintf(modelstr, sizeof(modelstr), "SI70%d", model); |
778 | break; | | 801 | break; |
779 | default: | | 802 | default: |
| @@ -790,27 +813,27 @@ si70xx_attach(device_t parent, device_t | | | @@ -790,27 +813,27 @@ si70xx_attach(device_t parent, device_t |
790 | fwversionstr = "2.0"; | | 813 | fwversionstr = "2.0"; |
791 | break; | | 814 | break; |
792 | default: | | 815 | default: |
793 | fwversionstr = "unknown"; | | 816 | fwversionstr = "unknown"; |
794 | break; | | 817 | break; |
795 | } | | 818 | } |
796 | | | 819 | |
797 | aprint_normal_dev(self, "Silicon Labs Model: %s, " | | 820 | aprint_normal_dev(self, "Silicon Labs Model: %s, " |
798 | "Firmware version: %s, " | | 821 | "Firmware version: %s, " |
799 | "Serial number: %02x%02x%02x%02x%02x%02x%02x%02x%s", | | 822 | "Serial number: %02x%02x%02x%02x%02x%02x%02x%02x%s", |
800 | modelstr, fwversionstr, testcrcpt1[0], testcrcpt1[1], | | 823 | modelstr, fwversionstr, testcrcpt1[0], testcrcpt1[1], |
801 | testcrcpt1[2], testcrcpt1[3], testcrcpt2[0], testcrcpt2[1], | | 824 | testcrcpt1[2], testcrcpt1[3], testcrcpt2[0], testcrcpt2[1], |
802 | testcrcpt2[2], testcrcpt2[3], | | 825 | testcrcpt2[2], testcrcpt2[3], |
803 | (crc1 == readcrc1 && crc2 == readcrc2) ? "\n" : " (bad crc)\n"); | | 826 | (validcrcpt1 && validcrcpt2) ? "\n" : " (bad crc)\n"); |
804 | return; | | 827 | return; |
805 | out: | | 828 | out: |
806 | sysmon_envsys_destroy(sc->sc_sme); | | 829 | sysmon_envsys_destroy(sc->sc_sme); |
807 | sc->sc_sme = NULL; | | 830 | sc->sc_sme = NULL; |
808 | } | | 831 | } |
809 | | | 832 | |
810 | static int | | 833 | static int |
811 | si70xx_exec(struct si70xx_sc *sc, uint8_t cmd, envsys_data_t *edata) | | 834 | si70xx_exec(struct si70xx_sc *sc, uint8_t cmd, envsys_data_t *edata) |
812 | { | | 835 | { |
813 | int error; | | 836 | int error; |
814 | int xdelay; | | 837 | int xdelay; |
815 | const char *name; | | 838 | const char *name; |
816 | int64_t mul, offs; | | 839 | int64_t mul, offs; |