LCOV - code coverage report
Current view: top level - dev/isa - asmc.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 289 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 21 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: asmc.c,v 1.32 2017/08/22 11:00:39 jsg Exp $   */
       2             : /*
       3             :  * Copyright (c) 2015 Joerg Jung <jung@openbsd.org>
       4             :  *
       5             :  * Permission to use, copy, modify, and distribute this software for any
       6             :  * purpose with or without fee is hereby granted, provided that the above
       7             :  * copyright notice and this permission notice appear in all copies.
       8             :  *
       9             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      10             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      11             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      12             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      13             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      14             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      15             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      16             :  */
      17             : 
      18             : /*
      19             :  * Driver for Apple's System Management Controller (SMC) an H8S/2117 chip
      20             :  */
      21             : 
      22             : #include <sys/param.h>
      23             : #include <sys/systm.h>
      24             : #include <sys/device.h>
      25             : #include <sys/kernel.h>
      26             : #include <sys/rwlock.h>
      27             : #include <sys/task.h>
      28             : #include <sys/sensors.h>
      29             : 
      30             : #include <machine/bus.h>
      31             : 
      32             : #include <dev/isa/isavar.h>
      33             : #include <dev/wscons/wsconsio.h>
      34             : 
      35             : #define ASMC_BASE       0x300   /* SMC base address */
      36             : #define ASMC_IOSIZE     32      /* I/O region size 0x300-0x31f */
      37             : 
      38             : #define ASMC_DATA       0x00    /* SMC data port offset */
      39             : #define ASMC_COMMAND    0x04    /* SMC command port offset */
      40             : #define ASMC_STATUS     0x1e    /* SMC status port offset */
      41             : #define ASMC_INTERRUPT  0x1f    /* SMC interrupt port offset */
      42             : 
      43             : #define ASMC_READ       0x10    /* SMC read command */
      44             : #define ASMC_WRITE      0x11    /* SMC write command */
      45             : #define ASMC_INFO       0x13    /* SMC info/type command */
      46             : 
      47             : #define ASMC_OBF        0x01    /* Output buffer full */
      48             : #define ASMC_IBF        0x02    /* Input buffer full */
      49             : #define ASMC_ACCEPT     0x04
      50             : 
      51             : #define ASMC_RETRY      3
      52             : #define ASMC_MAXLEN     32      /* SMC maximum data size len */
      53             : #define ASMC_NOTFOUND   0x84    /* SMC status key not found */
      54             : 
      55             : #define ASMC_MAXTEMP    101     /* known asmc_prods temperature sensor keys */
      56             : #define ASMC_MAXFAN     10      /* fan keys with digits 0-9 */
      57             : #define ASMC_MAXLIGHT   2       /* left and right light sensor */
      58             : #define ASMC_MAXMOTION  3       /* x y z axis motion sensors */
      59             : 
      60             : struct asmc_prod {
      61             :         const char      *pr_name;
      62             :         uint8_t          pr_light;
      63             :         const char      *pr_temp[ASMC_MAXTEMP];
      64             : };
      65             : 
      66             : struct asmc_softc {
      67             :         struct device            sc_dev;
      68             : 
      69             :         bus_space_tag_t          sc_iot;
      70             :         bus_space_handle_t       sc_ioh;
      71             : 
      72             :         struct asmc_prod        *sc_prod;
      73             :         uint8_t                  sc_nfans;      /* number of fans */
      74             :         uint8_t                  sc_lightlen;   /* light data len */
      75             :         uint8_t                  sc_backlight;  /* keyboard backlight value */
      76             : 
      77             :         struct rwlock            sc_lock;
      78             :         struct task              sc_task_backlight;
      79             : 
      80             :         struct ksensor           sc_sensor_temp[ASMC_MAXTEMP];
      81             :         struct ksensor           sc_sensor_fan[ASMC_MAXFAN];
      82             :         struct ksensor           sc_sensor_light[ASMC_MAXLIGHT];
      83             :         struct ksensor           sc_sensor_motion[ASMC_MAXMOTION];
      84             :         struct ksensordev        sc_sensor_dev;
      85             :         struct sensor_task      *sc_sensor_task;
      86             : };
      87             : 
      88             : int     asmc_try(struct asmc_softc *, int, const char *, uint8_t *, uint8_t);
      89             : void    asmc_init(struct asmc_softc *);
      90             : void    asmc_update(void *);
      91             : 
      92             : int     asmc_match(struct device *, void *, void *);
      93             : void    asmc_attach(struct device *, struct device *, void *);
      94             : int     asmc_detach(struct device *, int);
      95             : int     asmc_activate(struct device *, int);
      96             : 
      97             : /* wskbd hook functions */
      98             : void    asmc_backlight(void *);
      99             : int     asmc_get_backlight(struct wskbd_backlight *);
     100             : int     asmc_set_backlight(struct wskbd_backlight *);
     101             : extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
     102             : extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
     103             : 
     104             : const struct cfattach asmc_ca = {
     105             :         sizeof(struct asmc_softc), asmc_match, asmc_attach, NULL, asmc_activate
     106             : };
     107             : 
     108             : struct cfdriver asmc_cd = {
     109             :         NULL, "asmc", DV_DULL
     110             : };
     111             : 
     112             : static struct asmc_prod asmc_prods[] = {
     113             :         { "MacBookAir", 1, {
     114             :                 "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TBXT", "TC0C", "TC0D",
     115             :                 "TC0E", "TC0F", "TC0P", "TC1C", "TC1E", "TC2C", "TCFP", "TCGC",
     116             :                 "TCHP", "TCMX", "TCSA", "TCXC", "TCZ3", "TCZ4", "TCZ5", "TG0E",
     117             :                 "TG1E", "TG2E", "TGZ3", "TGZ4", "TGZ5", "TH0A", "TH0B", "TH0V",
     118             :                 "TH0a", "TH0b", "THSP", "TM0P", "TN0D", "TPCD", "TS2P", "TTF0",
     119             :                 "TV0P", "TVFP", "TW0P", "Ta0P", "Th0H", "Th0P", "Th1H", "Tm0P",
     120             :                 "Tm1P", "Tp0P", "Tp1P", "TpFP", "Ts0P", "Ts0S", NULL }
     121             :         },
     122             :         { "MacBookPro", 1, {
     123             :                 "TA0P", "TA1P", "TALP", "TB0T", "TB1T", "TB2T", "TB3T", "TBXT",
     124             :                 "TC0C", "TC0D", "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", "TC3C",
     125             :                 "TC4C", "TCGC", "TCSA", "TCXC", "TG0D", "TG0F", "TG0H", "TG0P",
     126             :                 "TG0T", "TG1D", "TG1F", "TG1H", "TG1d", "TH0A", "TH0B", "TH0F",
     127             :                 "TH0R", "TH0V", "TH0a", "TH0b", "TH0c", "TH0x", "THSP", "TM0P",
     128             :                 "TM0S", "TMCD", "TN0D", "TN0P", "TN0S", "TN1D", "TN1F", "TN1G",
     129             :                 "TN1S", "TP0P", "TPCD", "TTF0", "TW0P", "Ta0P", "TaSP", "Th0H",
     130             :                 "Th1H", "Th2H", "Tm0P", "Ts0P", "Ts0S", "Ts1P", "Ts1S", NULL }
     131             :         },
     132             :         { "MacBook", 0, {
     133             :                 "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TM0P", "TN0D",
     134             :                 "TN0P", "TN1P", "TTF0", "TW0P", "Th0H", "Th0S", "Th1H", "ThFH",
     135             :                 "Ts0P", "Ts0S", NULL }
     136             :         },
     137             :         { "MacPro", 0, {
     138             :                 "TA0P", "TC0C", "TC0D", "TC0P", "TC1C", "TC1D", "TC2C", "TC2D",
     139             :                 "TC3C", "TC3D", "TCAC", "TCAD", "TCAG", "TCAH", "TCAS", "TCBC",
     140             :                 "TCBD", "TCBG", "TCBH", "TCBS", "TH0P", "TH1F", "TH1P", "TH1V",
     141             :                 "TH2F", "TH2P", "TH2V", "TH3F", "TH3P", "TH3V", "TH4F", "TH4P",
     142             :                 "TH4V", "THPS", "THTG", "TM0P", "TM0S", "TM1P", "TM1S", "TM2P",
     143             :                 "TM2S", "TM2V", "TM3P", "TM3S", "TM3V", "TM4P", "TM5P", "TM6P",
     144             :                 "TM6V", "TM7P", "TM7V", "TM8P", "TM8S", "TM8V", "TM9P", "TM9S",
     145             :                 "TM9V", "TMA1", "TMA2", "TMA3", "TMA4", "TMAP", "TMAS", "TMB1",
     146             :                 "TMB2", "TMB3", "TMB4", "TMBS", "TMHS", "TMLS", "TMPS", "TMPV",
     147             :                 "TMTG", "TN0C", "TN0D", "TN0H", "TNTG", "TS0C", "Te1F", "Te1P",
     148             :                 "Te1S", "Te2F", "Te2S", "Te3F", "Te3S", "Te4F", "Te4S", "Te5F",
     149             :                 "Te5S", "TeGG", "TeGP", "TeRG", "TeRP", "TeRV", "Tp0C", "Tp1C",
     150             :                 "TpPS", "TpTG", "Tv0S", "Tv1S", NULL }
     151             :         },
     152             :         { "MacMini", 0, {
     153             :                 "TC0D", "TC0H", "TC0P", "TH0P", "TN0D", "TN0P", "TN1P", "TW0P",
     154             :                 NULL }
     155             :         },
     156             :         { "iMac", 0, {
     157             :                 "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
     158             :                 "TL0P", "TN0D", "TN0H", "TN0P", "TO0P", "TW0P", "Tm0P", "Tp0C",
     159             :                 "Tp0P", NULL }
     160             :         },
     161             :         { NULL, 0, { NULL } }
     162             : };
     163             : 
     164             : static const char *asmc_temp_desc[][2] = {
     165             :         { "TA0P", "ambient" }, { "TA0P", "hdd bay 1" },
     166             :         { "TA0S", "pci slot 1 pos 1" }, { "TA1P", "ambient 2" },
     167             :         { "TA1S", "pci slot 1 pos 2" }, { "TA2S", "pci slot 2 pos 1" },
     168             :         { "TA3S", "pci slot 2 pos 2" },
     169             :         { "TB0T", "enclosure bottom" }, { "TB1T", "enclosure bottom 2" },
     170             :         { "TB2T", "enclosure bottom 3" }, { "TB3T", "enclosure bottom 4" },
     171             :         { "TC0D", "cpu0 die core" }, { "TC0H", "cpu0 heatsink" },
     172             :         { "TC0P", "cpu0 proximity" },
     173             :         { "TC1D", "cpu1" }, { "TC2D", "cpu2" }, { "TC3D", "cpu3" },
     174             :         { "TCAH", "cpu0" }, { "TCBH", "cpu1" }, { "TCCH", "cpu2" },
     175             :         { "TCDH", "cpu3" },
     176             :         { "TG0D", "gpu0 diode" }, { "TG0H", "gpu0 heatsink" },
     177             :         { "TG0P", "gpu0 proximity" },
     178             :         { "TG1H", "gpu heatsink 2" },
     179             :         { "TH0P", "hdd bay 1" }, { "TH1P", "hdd bay 2" },
     180             :         { "TH2P", "hdd bay 3" }, { "TH3P", "hdd bay 4" },
     181             :         { "TL0P", "lcd proximity"},
     182             :         { "TM0P", "mem bank a1" }, { "TM0S", "mem module a1" },
     183             :         { "TM1P", "mem bank a2" }, { "TM1S", "mem module a2" },
     184             :         { "TM2P", "mem bank a3" }, { "TM2S", "mem module a3" },
     185             :         { "TM3P", "mem bank a4" }, { "TM3S", "mem module a4" },
     186             :         { "TM4P", "mem bank a5" }, { "TM4S", "mem module a5" },
     187             :         { "TM5P", "mem bank a6" }, { "TM5S", "mem module a6" },
     188             :         { "TM6P", "mem bank a7" }, { "TM6S", "mem module a7" },
     189             :         { "TM7P", "mem bank a8" }, { "TM7S", "mem module a8" },
     190             :         { "TM8P", "mem bank b1" }, { "TM8S", "mem module b1" },
     191             :         { "TM9P", "mem bank b2" }, { "TM9S", "mem module b2" },
     192             :         { "TMA1", "ram a1" }, { "TMA2", "ram a2" },
     193             :         { "TMA3", "ram a3" }, { "TMA4", "ram a4" },
     194             :         { "TMB1", "ram b1" }, { "TMB2", "ram b2" },
     195             :         { "TMB3", "ram b3" }, { "TMB4", "ram b4" },
     196             :         { "TMAP", "mem bank b3" }, { "TMAS", "mem module b3" },
     197             :         { "TMBP", "mem bank b4" }, { "TMBS", "mem module b4" },
     198             :         { "TMCP", "mem bank b5" }, { "TMCS", "mem module b5" },
     199             :         { "TMDP", "mem bank b6" }, { "TMDS", "mem module b6" },
     200             :         { "TMEP", "mem bank b7" }, { "TMES", "mem module b7" },
     201             :         { "TMFP", "mem bank b8" }, { "TMFS", "mem module b8" },
     202             :         { "TN0D", "northbridge die core" }, { "TN0H", "northbridge" },
     203             :         { "TN0P", "northbridge proximity" }, { "TN1P", "northbridge 2" },
     204             :         { "TO0P", "optical drive" }, { "TS0C", "expansion slots" },
     205             :         { "TW0P", "wireless airport card" },
     206             :         { "Th0H", "main heatsink a" }, { "Th1H", "main heatsink b" },
     207             :         { "Th2H", "main heatsink c" },
     208             :         { "Tm0P", "memory controller" },
     209             :         { "Tp0C", "power supply 1" }, { "Tp0P", "power supply 1" },
     210             :         { "Tp1C", "power supply 2" }, { "Tp1P", "power supply 2" },
     211             :         { "Tp2P", "power supply 3" }, { "Tp3P", "power supply 4" },
     212             :         { "Tp4P", "power supply 5" }, { "Tp5P", "power supply 6" },
     213             :         { NULL, NULL }
     214             : };
     215             : 
     216             : static const char *asmc_fan_loc[] = {
     217             :         "left lower front", "center lower front", "right lower front",
     218             :         "left mid front",   "center mid front",   "right mid front",
     219             :         "left upper front", "center upper front", "right upper front",
     220             :         "left lower rear",  "center lower rear",  "right lower rear",
     221             :         "left mid rear",    "center mid rear",    "right mid rear",
     222             :         "left upper rear",  "center upper rear",  "right upper rear"
     223             : };
     224             : 
     225             : static const char *asmc_light_desc[ASMC_MAXLIGHT] = {
     226             :         "left", "right"
     227             : };
     228             : 
     229             : extern char *hw_vendor, *hw_prod;
     230             : 
     231             : int
     232           0 : asmc_match(struct device *parent, void *match, void *aux)
     233             : {
     234           0 :         struct asmc_softc *sc = match;
     235           0 :         struct isa_attach_args *ia = aux;
     236           0 :         bus_space_handle_t ioh;
     237             :         int i;
     238             : 
     239           0 :         if (!hw_vendor || !hw_prod || strncmp(hw_vendor, "Apple", 5))
     240           0 :                 return 0;
     241             : 
     242           0 :         for (i = 0; asmc_prods[i].pr_name && !sc->sc_prod; i++)
     243           0 :                 if (!strncasecmp(asmc_prods[i].pr_name, hw_prod,
     244           0 :                     strlen(asmc_prods[i].pr_name)))
     245           0 :                         sc->sc_prod = &asmc_prods[i];
     246           0 :         if (!sc->sc_prod)
     247           0 :                 return 0;
     248             : 
     249           0 :         if (ia->ia_iobase != ASMC_BASE ||
     250           0 :             bus_space_map(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE, 0, &ioh))
     251           0 :                 return 0;
     252             : 
     253           0 :         bus_space_unmap(ia->ia_iot, ioh, ASMC_IOSIZE);
     254           0 :         ia->ia_iosize = ASMC_IOSIZE;
     255           0 :         ia->ipa_nio = 1;
     256           0 :         ia->ipa_nmem = 0;
     257           0 :         ia->ipa_nirq = 0;
     258           0 :         ia->ipa_ndrq = 0;
     259             : 
     260           0 :         return 1;
     261           0 : }
     262             : 
     263             : void
     264           0 : asmc_attach(struct device *parent, struct device *self, void *aux)
     265             : {
     266           0 :         struct asmc_softc *sc = (struct asmc_softc *)self;
     267           0 :         struct isa_attach_args *ia = aux;
     268           0 :         uint8_t buf[6];
     269             :         int i, r;
     270             : 
     271           0 :         if (bus_space_map(ia->ia_iot, ia->ia_iobase, ia->ia_iosize, 0,
     272           0 :             &sc->sc_ioh)) {
     273           0 :                 printf(": can't map i/o space\n");
     274           0 :                 return;
     275             :         }
     276           0 :         sc->sc_iot = ia->ia_iot;
     277             : 
     278           0 :         rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
     279             : 
     280           0 :         if ((r = asmc_try(sc, ASMC_READ, "REV ", buf, 6))) {
     281           0 :                 printf(": revision failed (0x%x)\n", r);
     282           0 :                 bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
     283           0 :                 return;
     284             :         }
     285           0 :         printf(": rev %x.%x%x%x", buf[0], buf[1], buf[2],
     286           0 :             ntohs(*(uint16_t *)buf + 4));
     287             : 
     288           0 :         if ((r = asmc_try(sc, ASMC_READ, "#KEY", buf, 4))) {
     289           0 :                 printf(", no of keys failed (0x%x)\n", r);
     290           0 :                 bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
     291           0 :                 return;
     292             :         }
     293           0 :         printf(", %u key%s\n", ntohl(*(uint32_t *)buf),
     294           0 :             (ntohl(*(uint32_t *)buf) == 1) ? "" : "s");
     295             : 
     296             :         /* keyboard backlight led is optional */
     297           0 :         sc->sc_backlight = buf[0] = 127, buf[1] = 0;
     298           0 :         if ((r = asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2))) {
     299           0 :                 if (r != ASMC_NOTFOUND)
     300           0 :                         printf("%s: keyboard backlight failed (0x%x)\n",
     301             :                             sc->sc_dev.dv_xname, r);
     302             :         } else {
     303           0 :                 wskbd_get_backlight = asmc_get_backlight;
     304           0 :                 wskbd_set_backlight = asmc_set_backlight;
     305             :         }
     306           0 :         task_set(&sc->sc_task_backlight, asmc_backlight, sc);
     307             : 
     308           0 :         strlcpy(sc->sc_sensor_dev.xname, sc->sc_dev.dv_xname,
     309             :             sizeof(sc->sc_sensor_dev.xname));
     310           0 :         for (i = 0; i < ASMC_MAXTEMP; i++) {
     311           0 :                 sc->sc_sensor_temp[i].flags |= SENSOR_FINVALID;
     312           0 :                 sc->sc_sensor_temp[i].flags |= SENSOR_FUNKNOWN;
     313             :         }
     314           0 :         for (i = 0; i < ASMC_MAXFAN; i++) {
     315           0 :                 sc->sc_sensor_fan[i].flags |= SENSOR_FINVALID;
     316           0 :                 sc->sc_sensor_fan[i].flags |= SENSOR_FUNKNOWN;
     317             :         }
     318           0 :         for (i = 0; i < ASMC_MAXLIGHT; i++) {
     319           0 :                 sc->sc_sensor_light[i].flags |= SENSOR_FINVALID;
     320           0 :                 sc->sc_sensor_light[i].flags |= SENSOR_FUNKNOWN;
     321             :         }
     322           0 :         for (i = 0; i < ASMC_MAXMOTION; i++) {
     323           0 :                 sc->sc_sensor_motion[i].flags |= SENSOR_FINVALID;
     324           0 :                 sc->sc_sensor_motion[i].flags |= SENSOR_FUNKNOWN;
     325             :         }
     326           0 :         asmc_init(sc);
     327             : 
     328           0 :         if (!(sc->sc_sensor_task = sensor_task_register(sc, asmc_update, 5))) {
     329           0 :                 printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
     330           0 :                 bus_space_unmap(ia->ia_iot, ia->ia_iobase, ASMC_IOSIZE);
     331           0 :                 return;
     332             :         }
     333           0 :         sensordev_install(&sc->sc_sensor_dev);
     334           0 : }
     335             : 
     336             : int
     337           0 : asmc_detach(struct device *self, int flags)
     338             : {
     339           0 :         struct asmc_softc *sc = (struct asmc_softc *)self;
     340           0 :         uint8_t buf[2] = { (sc->sc_backlight = 0), 0 };
     341             :         int i;
     342             : 
     343           0 :         if (sc->sc_sensor_task) {
     344           0 :                 sensor_task_unregister(sc->sc_sensor_task);
     345           0 :                 sc->sc_sensor_task = NULL;
     346           0 :         }
     347           0 :         sensordev_deinstall(&sc->sc_sensor_dev);
     348           0 :         for (i = 0; i < ASMC_MAXMOTION; i++)
     349           0 :                 sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[i]);
     350           0 :         for (i = 0; i < ASMC_MAXLIGHT; i++)
     351           0 :                 sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_light[i]);
     352           0 :         for (i = 0; i < ASMC_MAXFAN; i++)
     353           0 :                 sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[i]);
     354           0 :         for (i = 0; i < ASMC_MAXTEMP; i++)
     355           0 :                 sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[i]);
     356             : 
     357           0 :         task_del(systq, &sc->sc_task_backlight);
     358           0 :         asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2);
     359           0 :         return 0;
     360           0 : }
     361             : 
     362             : int
     363           0 : asmc_activate(struct device *self, int act)
     364             : {
     365           0 :         struct asmc_softc *sc = (struct asmc_softc *)self;
     366             : 
     367           0 :         switch (act) {
     368             :         case DVACT_WAKEUP:
     369           0 :                 asmc_backlight(sc);
     370           0 :                 break;
     371             :         }
     372             : 
     373           0 :         return 0;
     374             : }
     375             : 
     376             : void
     377           0 : asmc_backlight(void *arg)
     378             : {
     379           0 :         struct asmc_softc *sc = arg;
     380           0 :         uint8_t buf[2] = { sc->sc_backlight, 0 };
     381             :         int r;
     382             : 
     383           0 :         if ((r = asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2)))
     384           0 :                 printf("%s: keyboard backlight failed (0x%x)\n",
     385           0 :                     sc->sc_dev.dv_xname, r);
     386           0 : }
     387             : 
     388             : int
     389           0 : asmc_get_backlight(struct wskbd_backlight *kbl)
     390             : {
     391           0 :         struct asmc_softc *sc = asmc_cd.cd_devs[0];
     392             : 
     393           0 :         KASSERT(sc != NULL);
     394           0 :         kbl->min = 0;
     395           0 :         kbl->max = 0xff;
     396           0 :         kbl->curval = sc->sc_backlight;
     397           0 :         return 0;
     398             : }
     399             : 
     400             : int
     401           0 : asmc_set_backlight(struct wskbd_backlight *kbl)
     402             : {
     403           0 :         struct asmc_softc *sc = asmc_cd.cd_devs[0];
     404             : 
     405           0 :         KASSERT(sc != NULL);
     406           0 :         if (kbl->curval > 0xff)
     407           0 :                 return EINVAL;
     408           0 :         sc->sc_backlight = kbl->curval;
     409           0 :         task_add(systq, &sc->sc_task_backlight);
     410           0 :         return 0;
     411           0 : }
     412             : 
     413             : static uint8_t
     414           0 : asmc_status(struct asmc_softc *sc)
     415             : {
     416           0 :         return bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_STATUS);
     417             : }
     418             : 
     419             : static int
     420           0 : asmc_wait(struct asmc_softc *sc, uint8_t mask, uint8_t val)
     421             : {
     422             :         int i;
     423             : 
     424           0 :         for (i = 0; i < 500; i++) { /* wait up to 5 ms */
     425           0 :                 if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_COMMAND) &
     426           0 :                     mask) == val)
     427           0 :                         return 0;
     428           0 :                 delay(10);
     429             :         }
     430           0 :         return ETIMEDOUT;
     431           0 : }
     432             : 
     433             : static int
     434           0 : asmc_write(struct asmc_softc *sc, uint8_t off, uint8_t val)
     435             : {
     436           0 :         if (asmc_wait(sc, ASMC_IBF, 0))
     437           0 :                 return 1;
     438           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
     439           0 :         if (asmc_wait(sc, ASMC_ACCEPT, ASMC_ACCEPT))
     440           0 :                 return 1;
     441           0 :         return 0;
     442           0 : }
     443             : 
     444             : static int
     445           0 : asmc_read(struct asmc_softc *sc, uint8_t off, uint8_t *buf)
     446             : {
     447           0 :         if (asmc_wait(sc, ASMC_OBF, ASMC_OBF))
     448           0 :                 return 1;
     449           0 :         *buf = bus_space_read_1(sc->sc_iot, sc->sc_ioh, off);
     450           0 :         return 0;
     451           0 : }
     452             : 
     453             : static int
     454           0 : asmc_command(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
     455             :     uint8_t len)
     456             : {
     457             :         int i;
     458             : 
     459           0 :         if (len > ASMC_MAXLEN)
     460           0 :                 return 1;
     461           0 :         if (asmc_write(sc, ASMC_COMMAND, cmd))
     462           0 :                 return 1;
     463           0 :         for (i = 0; i < 4; i++)
     464           0 :                 if (asmc_write(sc, ASMC_DATA, key[i]))
     465           0 :                         return 1;
     466           0 :         if (asmc_write(sc, ASMC_DATA, len))
     467           0 :                 return 1;
     468           0 :         if (cmd == ASMC_READ || cmd == ASMC_INFO) {
     469           0 :                 for (i = 0; i < len; i++)
     470           0 :                         if (asmc_read(sc, ASMC_DATA, &buf[i]))
     471           0 :                                 return 1;
     472           0 :         } else if (cmd == ASMC_WRITE) {
     473           0 :                 for (i = 0; i < len; i++)
     474           0 :                         if (asmc_write(sc, ASMC_DATA, buf[i]))
     475           0 :                                 return 1;
     476             :         } else
     477           0 :                 return 1;
     478           0 :         return 0;
     479           0 : }
     480             : 
     481             : int
     482           0 : asmc_try(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
     483             :     uint8_t len)
     484             : {
     485             :         uint8_t s;
     486             :         int i, r;
     487             : 
     488           0 :         rw_enter_write(&sc->sc_lock);
     489           0 :         for (i = 0; i < ASMC_RETRY; i++)
     490           0 :                 if (!(r = asmc_command(sc, cmd, key, buf, len)))
     491             :                         break;
     492           0 :         if (r && (s = asmc_status(sc)))
     493           0 :                 r = s;
     494           0 :         rw_exit_write(&sc->sc_lock);
     495             : 
     496           0 :         return r;
     497             : }
     498             : 
     499             : static uint32_t
     500           0 : asmc_uk(uint8_t *buf)
     501             : {
     502             :         /* spe78: floating point, signed, 7 bits exponent, 8 bits fraction */
     503           0 :         return (((int16_t)ntohs(*(uint16_t *)buf)) >> 8) * 1000000 + 273150000;
     504             : }
     505             : 
     506             : static uint16_t
     507           0 : asmc_rpm(uint8_t *buf)
     508             : {
     509             :         /* fpe2: floating point, unsigned, 14 bits exponent, 2 bits fraction */
     510           0 :         return ntohs(*(uint16_t *)buf) >> 2;
     511             : }
     512             : 
     513             : static uint32_t
     514           0 : asmc_lux(uint8_t *buf, uint8_t lightlen)
     515             : {
     516             :         /* newer macbooks report a 10 bit big endian value */
     517           0 :         return (lightlen == 10) ?
     518             :             /* fp18.14: floating point, 18 bits exponent, 14 bits fraction */
     519           0 :             (ntohl(*(uint32_t *)(buf + 6)) >> 14) * 1000000 :
     520             :             /*
     521             :              * todo: calculate lux from ADC raw data
     522             :              * buf[1] true/false for high/low gain chan reads
     523             :              * chan 0: ntohs(*(uint16_t *)(buf + 2));
     524             :              * chan 1: ntohs(*(uint16_t *)(buf + 4));
     525             :              */
     526           0 :             ntohs(*(uint16_t *)(buf + 2)) * 1000000;
     527             : }
     528             : 
     529             : static int
     530           0 : asmc_temp(struct asmc_softc *sc, uint8_t idx, int init)
     531             : {
     532           0 :         uint8_t buf[2];
     533             :         uint32_t uk;
     534             :         int i, r;
     535             : 
     536           0 :         if ((r = asmc_try(sc, ASMC_READ, sc->sc_prod->pr_temp[idx], buf, 2)))
     537           0 :                 return r;
     538           0 :         if ((uk = asmc_uk(buf)) < 253150000) /* ignore unlikely values */
     539           0 :                 return 0;
     540           0 :         sc->sc_sensor_temp[idx].value = uk;
     541           0 :         sc->sc_sensor_temp[idx].flags &= ~SENSOR_FUNKNOWN;
     542             : 
     543           0 :         if (!init)
     544           0 :                 return 0;
     545             : 
     546           0 :         strlcpy(sc->sc_sensor_temp[idx].desc, sc->sc_prod->pr_temp[idx],
     547             :             sizeof(sc->sc_sensor_temp[idx].desc));
     548           0 :         for (i = 0; asmc_temp_desc[i][0]; i++)
     549           0 :                 if (!strcmp(asmc_temp_desc[i][0], sc->sc_prod->pr_temp[idx]))
     550             :                         break;
     551           0 :         if (asmc_temp_desc[i][0]) {
     552           0 :                 strlcat(sc->sc_sensor_temp[idx].desc, " ",
     553             :                     sizeof(sc->sc_sensor_temp[idx].desc));
     554           0 :                 strlcat(sc->sc_sensor_temp[idx].desc, asmc_temp_desc[i][1],
     555             :                     sizeof(sc->sc_sensor_temp[idx].desc));
     556           0 :         }
     557           0 :         sc->sc_sensor_temp[idx].type = SENSOR_TEMP;
     558           0 :         sc->sc_sensor_temp[idx].flags &= ~SENSOR_FINVALID;
     559           0 :         sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[idx]);
     560           0 :         return 0;
     561           0 : }
     562             : 
     563             : static int
     564           0 : asmc_fan(struct asmc_softc *sc, uint8_t idx, int init)
     565             : {
     566           0 :         char key[5];
     567           0 :         uint8_t buf[17], *end;
     568             :         int r;
     569             : 
     570           0 :         snprintf(key, sizeof(key), "F%dAc", idx);
     571           0 :         if ((r = asmc_try(sc, ASMC_READ, key, buf, 2)))
     572           0 :                 return r;
     573           0 :         sc->sc_sensor_fan[idx].value = asmc_rpm(buf);
     574           0 :         sc->sc_sensor_fan[idx].flags &= ~SENSOR_FUNKNOWN;
     575             : 
     576           0 :         if (!init)
     577           0 :                 return 0;
     578             : 
     579           0 :         snprintf(key, sizeof(key), "F%dID", idx);
     580           0 :         if ((r = asmc_try(sc, ASMC_READ, key, buf, 16)))
     581           0 :                 return r;
     582           0 :         buf[16] = '\0';
     583           0 :         end = buf + 4 + strlen((char *)buf + 4) - 1;
     584           0 :         while (buf + 4 < end && *end == ' ') /* trim trailing spaces */
     585           0 :                 *end-- = '\0';
     586           0 :         strlcpy(sc->sc_sensor_fan[idx].desc, buf + 4,
     587             :             sizeof(sc->sc_sensor_fan[idx].desc));
     588           0 :         if (buf[2] < nitems(asmc_fan_loc)) {
     589           0 :                 strlcat(sc->sc_sensor_fan[idx].desc, ", ",
     590             :                     sizeof(sc->sc_sensor_fan[idx].desc));
     591           0 :                 strlcat(sc->sc_sensor_fan[idx].desc, asmc_fan_loc[buf[2]],
     592             :                     sizeof(sc->sc_sensor_fan[idx].desc));
     593           0 :         }
     594           0 :         sc->sc_sensor_fan[idx].type = SENSOR_FANRPM;
     595           0 :         sc->sc_sensor_fan[idx].flags &= ~SENSOR_FINVALID;
     596           0 :         sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[idx]);
     597           0 :         return 0;
     598           0 : }
     599             : 
     600             : static int
     601           0 : asmc_light(struct asmc_softc *sc, uint8_t idx, int init)
     602             : {
     603           0 :         char key[5];
     604           0 :         uint8_t buf[10];
     605             :         int r;
     606             : 
     607           0 :         snprintf(key, sizeof(key), "ALV%d", idx);
     608           0 :         if (!sc->sc_lightlen) {
     609           0 :                 if ((r = asmc_try(sc, ASMC_INFO, key, buf, 6)))
     610           0 :                         return r;
     611           0 :                 if ((sc->sc_lightlen = buf[0]) > 10)
     612           0 :                         return 1;
     613             :         }
     614           0 :         if ((r = asmc_try(sc, ASMC_READ, key, buf, sc->sc_lightlen)))
     615           0 :                 return r;
     616           0 :         if (!buf[0]) /* valid data? */
     617           0 :                 return 0;
     618           0 :         sc->sc_sensor_light[idx].value = asmc_lux(buf, sc->sc_lightlen);
     619           0 :         sc->sc_sensor_light[idx].flags &= ~SENSOR_FUNKNOWN;
     620             : 
     621           0 :         if (!init)
     622           0 :                 return 0;
     623             : 
     624           0 :         strlcpy(sc->sc_sensor_light[idx].desc, asmc_light_desc[idx],
     625             :             sizeof(sc->sc_sensor_light[idx].desc));
     626           0 :         sc->sc_sensor_light[idx].type = SENSOR_LUX;
     627           0 :         sc->sc_sensor_light[idx].flags &= ~SENSOR_FINVALID;
     628           0 :         sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_light[idx]);
     629           0 :         return 0;
     630           0 : }
     631             : 
     632             : #if 0 /* todo: implement motion sensors update and initialization */
     633             : static int
     634             : asmc_motion(struct asmc_softc *sc, uint8_t idx, int init)
     635             : {
     636             :         char key[5];
     637             :         uint8_t buf[2];
     638             :         int r;
     639             : 
     640             :         snprintf(key, sizeof(key), "MO_%c", 88 + idx); /* X, Y, Z */
     641             :         if ((r = asmc_try(sc, ASMC_READ, key, buf, 2)))
     642             :                 return r;
     643             :         sc->sc_sensor_motion[idx].value = 0;
     644             :         sc->sc_sensor_motion[idx].flags &= ~SENSOR_FUNKNOWN;
     645             : 
     646             :         if (!init)
     647             :                 return 0;
     648             : 
     649             :         /* todo: setup and attach sensors and description */
     650             :         strlcpy(sc->sc_sensor_motion[idx].desc, 120 + idx, /* x, y, z */
     651             :             sizeof(sc->sc_sensor_motion[idx].desc));
     652             :         strlcat(sc->sc_sensor_motion[idx].desc, "-axis",
     653             :             sizeof(sc->sc_sensor_motion[idx].desc));
     654             :         sc->sc_sensor_motion[idx].type = SENSOR_ACCEL;
     655             :         sc->sc_sensor_motion[idx].flags &= ~SENSOR_FINVALID;
     656             :         sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[idx]);
     657             :         return 0;
     658             : }
     659             : #endif
     660             : 
     661             : void
     662           0 : asmc_init(struct asmc_softc *sc)
     663             : {
     664           0 :         uint8_t buf[2];
     665             :         int i, r;
     666             : 
     667             :         /* number of temperature sensors depends on product */
     668           0 :         for (i = 0; i < ASMC_MAXTEMP && sc->sc_prod->pr_temp[i]; i++)
     669           0 :                 if ((r = asmc_temp(sc, i, 1)) && r != ASMC_NOTFOUND)
     670           0 :                         printf("%s: read temp %d failed (0x%x)\n",
     671           0 :                             sc->sc_dev.dv_xname, i, r);
     672             :         /* number of fan sensors depends on product */
     673           0 :         if ((r = asmc_try(sc, ASMC_READ, "FNum", buf, 1)))
     674           0 :                 printf("%s: read FNum failed (0x%x)\n",
     675           0 :                     sc->sc_dev.dv_xname, r);
     676             :         else
     677           0 :                 sc->sc_nfans = buf[0];
     678           0 :         for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
     679           0 :                 if ((r = asmc_fan(sc, i, 1)) && r != ASMC_NOTFOUND)
     680           0 :                         printf("%s: read fan %d failed (0x%x)\n",
     681           0 :                             sc->sc_dev.dv_xname, i, r);
     682             :         /* left and right light sensors are optional */
     683           0 :         for (i = 0; sc->sc_prod->pr_light && i < ASMC_MAXLIGHT; i++)
     684           0 :                 if ((r = asmc_light(sc, i, 1)) && r != ASMC_NOTFOUND)
     685           0 :                         printf("%s: read light %d failed (0x%x)\n",
     686           0 :                             sc->sc_dev.dv_xname, i, r);
     687             :         /* motion sensors are optional */
     688           0 :         if ((r = asmc_try(sc, ASMC_READ, "MOCN", buf, 2)) &&
     689           0 :             r != ASMC_NOTFOUND)
     690           0 :                 printf("%s: read MOCN failed (0x%x)\n",
     691           0 :                     sc->sc_dev.dv_xname, r);
     692             : #if 0 /* todo: initialize sudden motion sensors and setup interrupt handling */
     693             :         buf[0] = 0xe0, buf[1] = 0xf8;
     694             :         if ((r = asmc_try(sc, ASMC_WRITE, "MOCN", buf, 2)))
     695             :                 printf("%s write MOCN failed (0x%x)\n",
     696             :                     sc->sc_dev.dv_xname, r);
     697             :         for (i = 0; i < ASMC_MAXMOTION; i++)
     698             :                 if ((r = asmc_motion(sc, i, 1)) && r != ASMC_NOTFOUND)
     699             :                         printf("%s: read motion %d failed (0x%x)\n",
     700             :                             sc->sc_dev.dv_xname, i, r);
     701             : #endif
     702           0 : }
     703             : 
     704             : void
     705           0 : asmc_update(void *arg)
     706             : {
     707           0 :         struct asmc_softc *sc = arg;
     708             :         int i;
     709             : 
     710           0 :         for (i = 0; i < ASMC_MAXTEMP && sc->sc_prod->pr_temp[i]; i++)
     711           0 :                 if (!(sc->sc_sensor_temp[i].flags & SENSOR_FINVALID))
     712           0 :                         asmc_temp(sc, i, 0);
     713           0 :         for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
     714           0 :                 if (!(sc->sc_sensor_fan[i].flags & SENSOR_FINVALID))
     715           0 :                         asmc_fan(sc, i, 0);
     716           0 :         for (i = 0; i < ASMC_MAXLIGHT; i++)
     717           0 :                 if (!(sc->sc_sensor_light[i].flags & SENSOR_FINVALID))
     718           0 :                         asmc_light(sc, i, 0);
     719             : #if 0
     720             :         for (i = 0; i < ASMC_MAXMOTION; i++)
     721             :                 if (!(sc->sc_sensor_motion[i].flags & SENSOR_FINVALID))
     722             :                         asmc_motion(sc, i, 0);
     723             : #endif
     724           0 : }

Generated by: LCOV version 1.13