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

          Line data    Source code
       1             : /*      $OpenBSD: tcpcib.c,v 1.8 2014/12/10 12:27:57 mikeb Exp $        */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2012 Matt Dainty <matt@bodgit-n-scarper.com>
       5             :  *
       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
       8             :  * copyright notice and this permission notice appear in all copies.
       9             :  *
      10             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      11             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      12             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      13             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      14             :  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
      15             :  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
      16             :  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      17             :  */
      18             : 
      19             : /*
      20             :  * Intel Atom E600 series LPC bridge also containing HPET and watchdog
      21             :  */
      22             : 
      23             : #include <sys/param.h>
      24             : #include <sys/systm.h>
      25             : #include <sys/device.h>
      26             : #include <sys/timetc.h>
      27             : 
      28             : #include <machine/bus.h>
      29             : 
      30             : #include <dev/pci/pcireg.h>
      31             : #include <dev/pci/pcivar.h>
      32             : #include <dev/pci/pcidevs.h>
      33             : 
      34             : #define E600_LPC_SMBA           0x40            /* SMBus Base Address */
      35             : #define E600_LPC_GBA            0x44            /* GPIO Base Address */
      36             : #define E600_LPC_WDTBA          0x84            /* WDT Base Address */
      37             : 
      38             : #define E600_WDT_SIZE           64              /* I/O region size */
      39             : #define E600_WDT_PV1            0x00            /* Preload Value 1 Register */
      40             : #define E600_WDT_PV2            0x04            /* Preload Value 2 Register */
      41             : #define E600_WDT_RR0            0x0c            /* Reload Register 0 */
      42             : #define E600_WDT_RR1            0x0d            /* Reload Register 1 */
      43             : #define E600_WDT_RR1_RELOAD     (1 << 0)  /* WDT Reload Flag */
      44             : #define E600_WDT_RR1_TIMEOUT    (1 << 1)  /* WDT Timeout Flag */
      45             : #define E600_WDT_WDTCR          0x10            /* WDT Configuration Register */
      46             : #define E600_WDT_WDTCR_PRE      (1 << 2)  /* WDT Prescalar Select */
      47             : #define E600_WDT_WDTCR_RESET    (1 << 3)  /* WDT Reset Select */
      48             : #define E600_WDT_WDTCR_ENABLE   (1 << 4)  /* WDT Reset Enable */
      49             : #define E600_WDT_WDTCR_TIMEOUT  (1 << 5)  /* WDT Timeout Output Enable */
      50             : #define E600_WDT_DCR            0x14            /* Down Counter Register */
      51             : #define E600_WDT_WDTLR          0x18            /* WDT Lock Register */
      52             : #define E600_WDT_WDTLR_LOCK     (1 << 0)  /* Watchdog Timer Lock */
      53             : #define E600_WDT_WDTLR_ENABLE   (1 << 1)  /* Watchdog Timer Enable */
      54             : #define E600_WDT_WDTLR_TIMEOUT  (1 << 2)  /* WDT Timeout Configuration */
      55             : 
      56             : #define E600_HPET_BASE          0xfed00000      /* HPET register base */
      57             : #define E600_HPET_SIZE          0x00000400      /* HPET register size */
      58             : 
      59             : #define E600_HPET_GCID          0x000           /* Capabilities and ID */
      60             : #define E600_HPET_GCID_WIDTH    (1 << 13) /* Counter Size */
      61             : #define E600_HPET_PERIOD        0x004           /* Counter Tick Period */
      62             : #define E600_HPET_GC            0x010           /* General Configuration */
      63             : #define E600_HPET_GC_ENABLE     (1 << 0)  /* Overall Enable */
      64             : #define E600_HPET_GIS           0x020           /* General Interrupt Status */
      65             : #define E600_HPET_MCV           0x0f0           /* Main Counter Value */
      66             : #define E600_HPET_T0C           0x100           /* Timer 0 Config and Capabilities */
      67             : #define E600_HPET_T0CV          0x108           /* Timer 0 Comparator Value */
      68             : #define E600_HPET_T1C           0x120           /* Timer 1 Config and Capabilities */
      69             : #define E600_HPET_T1CV          0x128           /* Timer 1 Comparator Value */
      70             : #define E600_HPET_T2C           0x140           /* Timer 2 Config and Capabilities */
      71             : #define E600_HPET_T2CV          0x148           /* Timer 2 Comparator Value */
      72             : 
      73             : struct tcpcib_softc {
      74             :         struct device sc_dev;
      75             : 
      76             :         /* Keep track of which parts of the hardware are active */
      77             :         int sc_active;
      78             : #define E600_WDT_ACTIVE         (1 << 0)
      79             : #define E600_HPET_ACTIVE        (1 << 1)
      80             : 
      81             :         /* Watchdog interface */
      82             :         bus_space_tag_t sc_wdt_iot;
      83             :         bus_space_handle_t sc_wdt_ioh;
      84             : 
      85             :         int sc_wdt_period;
      86             : 
      87             :         /* High Precision Event Timer */
      88             :         bus_space_tag_t sc_hpet_iot;
      89             :         bus_space_handle_t sc_hpet_ioh;
      90             : 
      91             :         struct timecounter sc_hpet_timecounter;
      92             : };
      93             : 
      94             : struct cfdriver tcpcib_cd = {
      95             :         NULL, "tcpcib", DV_DULL
      96             : };
      97             : 
      98             : int      tcpcib_match(struct device *, void *, void *);
      99             : void     tcpcib_attach(struct device *, struct device *, void *);
     100             : int      tcpcib_activate(struct device *, int);
     101             : 
     102             : int      tcpcib_wdt_cb(void *, int);
     103             : void     tcpcib_wdt_init(struct tcpcib_softc *, int);
     104             : void     tcpcib_wdt_start(struct tcpcib_softc *);
     105             : void     tcpcib_wdt_stop(struct tcpcib_softc *);
     106             : 
     107             : u_int    tcpcib_hpet_get_timecount(struct timecounter *tc);
     108             : 
     109             : struct cfattach tcpcib_ca = {
     110             :         sizeof(struct tcpcib_softc), tcpcib_match, tcpcib_attach,
     111             :         NULL, tcpcib_activate
     112             : };
     113             : 
     114             : /* from arch/<*>/pci/pcib.c */
     115             : void    pcibattach(struct device *parent, struct device *self, void *aux);
     116             : 
     117             : const struct pci_matchid tcpcib_devices[] = {
     118             :         { PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_E600_LPC }
     119             : };
     120             : 
     121             : static __inline void
     122           0 : tcpcib_wdt_unlock(struct tcpcib_softc *sc)
     123             : {
     124             :         /* Register unlocking sequence */
     125           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x80);
     126           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x86);
     127           0 : }
     128             : 
     129             : void
     130           0 : tcpcib_wdt_init(struct tcpcib_softc *sc, int period)
     131             : {
     132             :         u_int32_t preload;
     133             : 
     134             :         /* Set new timeout */
     135           0 :         preload = (period * 33000000) >> 15;
     136           0 :         preload--;
     137             : 
     138             :         /*
     139             :          * Set watchdog to perform a cold reset toggling the GPIO pin and the
     140             :          * prescaler set to 1ms-10m resolution
     141             :          */
     142           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTCR,
     143             :             E600_WDT_WDTCR_ENABLE);
     144           0 :         tcpcib_wdt_unlock(sc);
     145           0 :         bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV1, 0);
     146           0 :         tcpcib_wdt_unlock(sc);
     147           0 :         bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV2,
     148             :             preload);
     149           0 :         tcpcib_wdt_unlock(sc);
     150           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1,
     151             :             E600_WDT_RR1_RELOAD);
     152           0 : }
     153             : 
     154             : void
     155           0 : tcpcib_wdt_start(struct tcpcib_softc *sc)
     156             : {
     157             :         /* Enable watchdog */
     158           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR,
     159             :             E600_WDT_WDTLR_ENABLE);
     160           0 : }
     161             : 
     162             : void
     163           0 : tcpcib_wdt_stop(struct tcpcib_softc *sc)
     164             : {
     165             :         /* Disable watchdog, with a reload before for safety */
     166           0 :         tcpcib_wdt_unlock(sc);
     167           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1,
     168             :             E600_WDT_RR1_RELOAD);
     169           0 :         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, 0);
     170           0 : }
     171             : 
     172             : int
     173           0 : tcpcib_match(struct device *parent, void *match, void *aux)
     174             : {
     175           0 :         if (pci_matchbyid((struct pci_attach_args *)aux, tcpcib_devices,
     176             :             sizeof(tcpcib_devices) / sizeof(tcpcib_devices[0])))
     177           0 :                 return (2);
     178             : 
     179           0 :         return (0);
     180           0 : }
     181             : 
     182             : void
     183           0 : tcpcib_attach(struct device *parent, struct device *self, void *aux)
     184             : {
     185           0 :         struct tcpcib_softc *sc = (struct tcpcib_softc *)self;
     186           0 :         struct pci_attach_args *pa = aux;
     187           0 :         struct timecounter *tc = &sc->sc_hpet_timecounter;
     188             :         u_int32_t reg, wdtbase;
     189             : 
     190           0 :         sc->sc_active = 0;
     191             : 
     192             :         /* High Precision Event Timer */
     193           0 :         sc->sc_hpet_iot = pa->pa_memt;
     194           0 :         if (bus_space_map(sc->sc_hpet_iot, E600_HPET_BASE, E600_HPET_SIZE, 0,
     195           0 :             &sc->sc_hpet_ioh) == 0) {
     196           0 :                 tc->tc_get_timecount = tcpcib_hpet_get_timecount;
     197             :                 /* XXX 64-bit counter is not supported! */
     198           0 :                 tc->tc_counter_mask = 0xffffffff;
     199             : 
     200           0 :                 reg = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
     201             :                     E600_HPET_PERIOD);
     202             :                 /* femtosecs -> Hz */
     203           0 :                 tc->tc_frequency = 1000000000000000ULL / reg;
     204             : 
     205           0 :                 tc->tc_name = sc->sc_dev.dv_xname;
     206           0 :                 tc->tc_quality = 2000;
     207           0 :                 tc->tc_priv = sc;
     208           0 :                 tc_init(tc);
     209             : 
     210             :                 /* Enable counting */
     211           0 :                 bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
     212             :                     E600_HPET_GC, E600_HPET_GC_ENABLE);
     213             : 
     214           0 :                 sc->sc_active |= E600_HPET_ACTIVE;
     215             : 
     216           0 :                 printf(": %llu Hz timer", tc->tc_frequency);
     217           0 :         }
     218             : 
     219             :         /* Map Watchdog I/O space */
     220           0 :         reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA);
     221           0 :         wdtbase = reg & 0xffff;
     222           0 :         sc->sc_wdt_iot = pa->pa_iot;
     223           0 :         if (reg & (1U << 31) && wdtbase) {
     224           0 :                 if (PCI_MAPREG_IO_ADDR(wdtbase) == 0 ||
     225           0 :                     bus_space_map(sc->sc_wdt_iot, PCI_MAPREG_IO_ADDR(wdtbase),
     226           0 :                     E600_WDT_SIZE, 0, &sc->sc_wdt_ioh)) {
     227           0 :                         printf("%c can't map watchdog I/O space",
     228           0 :                             sc->sc_active ? ',' : ':');
     229           0 :                         goto corepcib;
     230             :                 }
     231           0 :                 printf("%c watchdog", sc->sc_active ? ',' : ':');
     232             : 
     233             :                 /* Check for reboot on timeout */
     234           0 :                 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
     235             :                     E600_WDT_RR1);
     236           0 :                 if (reg & E600_WDT_RR1_TIMEOUT) {
     237           0 :                         printf(", reboot on timeout");
     238             : 
     239             :                         /* Clear timeout bit */
     240           0 :                         tcpcib_wdt_unlock(sc);
     241           0 :                         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
     242             :                             E600_WDT_RR1, E600_WDT_RR1_TIMEOUT);
     243           0 :                 }
     244             : 
     245             :                 /* Check it's not locked already */
     246           0 :                 reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
     247             :                     E600_WDT_WDTLR);
     248           0 :                 if (reg & E600_WDT_WDTLR_LOCK) {
     249           0 :                         printf(", locked");
     250           0 :                         goto corepcib;
     251             :                 }
     252             : 
     253             :                 /* Disable watchdog */
     254           0 :                 tcpcib_wdt_stop(sc);
     255           0 :                 sc->sc_wdt_period = 0;
     256             : 
     257           0 :                 sc->sc_active |= E600_WDT_ACTIVE;
     258             : 
     259             :                 /* Register new watchdog */
     260           0 :                 wdog_register(tcpcib_wdt_cb, sc);
     261           0 :         }
     262             : 
     263             : corepcib:
     264             :         /* Provide core pcib(4) functionality */
     265           0 :         pcibattach(parent, self, aux);
     266           0 : }
     267             : 
     268             : int
     269           0 : tcpcib_activate(struct device *self, int act)
     270             : {
     271           0 :         struct tcpcib_softc *sc = (struct tcpcib_softc *)self;
     272             :         int rv = 0;
     273             :         
     274           0 :         switch (act) {
     275             :         case DVACT_SUSPEND:
     276           0 :                 rv = config_activate_children(self, act);
     277             :                 /* Watchdog is running, disable it */
     278           0 :                 if (sc->sc_active & E600_WDT_ACTIVE && sc->sc_wdt_period != 0)
     279           0 :                         tcpcib_wdt_stop(sc);
     280             :                 break;
     281             :         case DVACT_RESUME:
     282           0 :                 if (sc->sc_active & E600_WDT_ACTIVE) {
     283             :                         /*
     284             :                          * Watchdog was running prior to suspend so reenable
     285             :                          * it, otherwise make sure it stays disabled
     286             :                          */
     287           0 :                         if (sc->sc_wdt_period != 0) {
     288           0 :                                 tcpcib_wdt_init(sc, sc->sc_wdt_period);
     289           0 :                                 tcpcib_wdt_start(sc);
     290           0 :                         } else
     291           0 :                                 tcpcib_wdt_stop(sc);
     292             :                 }
     293           0 :                 if (sc->sc_active & E600_HPET_ACTIVE)
     294           0 :                         bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
     295             :                             E600_HPET_GC, E600_HPET_GC_ENABLE);
     296           0 :                 rv = config_activate_children(self, act);
     297           0 :                 break;
     298             :         case DVACT_POWERDOWN:
     299           0 :                 if (sc->sc_active & E600_WDT_ACTIVE)
     300           0 :                         wdog_shutdown(self);
     301           0 :                 rv = config_activate_children(self, act);
     302           0 :                 break;
     303             :         default:
     304           0 :                 rv = config_activate_children(self, act);
     305           0 :                 break;
     306             :         }
     307           0 :         return (rv);
     308             : }
     309             : 
     310             : int
     311           0 : tcpcib_wdt_cb(void *arg, int period)
     312             : {
     313           0 :         struct tcpcib_softc *sc = arg;
     314             : 
     315           0 :         if (period == 0) {
     316           0 :                 if (sc->sc_wdt_period != 0)
     317           0 :                         tcpcib_wdt_stop(sc);
     318             :         } else {
     319             :                 /* 600 seconds is the maximum supported timeout value */
     320           0 :                 if (period > 600)
     321           0 :                         period = 600;
     322           0 :                 if (sc->sc_wdt_period != period)
     323           0 :                         tcpcib_wdt_init(sc, period);
     324           0 :                 if (sc->sc_wdt_period == 0) {
     325           0 :                         tcpcib_wdt_start(sc);
     326           0 :                 } else {
     327             :                         /* Reset timer */
     328           0 :                         tcpcib_wdt_unlock(sc);
     329           0 :                         bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
     330             :                             E600_WDT_RR1, E600_WDT_RR1_RELOAD);
     331             :                 }
     332             :         }
     333           0 :         sc->sc_wdt_period = period;
     334             : 
     335           0 :         return (period);
     336             : }
     337             : 
     338             : u_int
     339           0 : tcpcib_hpet_get_timecount(struct timecounter *tc)
     340             : {
     341           0 :         struct tcpcib_softc *sc = tc->tc_priv;
     342             : 
     343             :         /* XXX 64-bit counter is not supported! */
     344           0 :         return bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
     345             :             E600_HPET_MCV);
     346             : }

Generated by: LCOV version 1.13