The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/pc/devlm78.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #include "u.h"
    2 #include "../port/lib.h"
    3 #include "mem.h"
    4 #include "dat.h"
    5 #include "fns.h"
    6 #include "io.h"
    7 #include "ureg.h"
    8 #include "../port/error.h"
    9 
   10 /* this driver doesn't implement the management interrupts.  we
   11  * leave the LM78 interrupts set to whatever the BIOS did.  we do
   12  * allow reading and writing the the readouts and alarm values.
   13  * Read(2)ing or write(2)ing at offset 0x0-0x1f, is
   14  * equivalent to reading or writing lm78 registers 0x20-0x3f.
   15  */
   16 enum
   17 {
   18         /*  address of chip on serial interface */
   19         Serialaddr=     0x2d,
   20 
   21         /*  parallel access registers */
   22         Rpaddr=         0x5,
   23         Bbusy=           (1<<7),
   24         Rpdata=         0x6,
   25 
   26         /*  internal register addresses */
   27         Rconfig=        0x40,
   28         Bstart=          (1<<0),
   29         Bsmiena=         (1<<1),
   30         Birqena=         (1<<2),
   31         Bintclr=         (1<<3),
   32         Breset=          (1<<4),
   33         Bnmi=            (1<<5),        /*  if set, use nmi, else irq */
   34         Bpowbypass=      (1<<6),
   35         Binit=           (1<<7),
   36         Ristat1=        0x41,
   37         Ristat2=        0x42,
   38         Rsmimask1=      0x43,
   39         Rsmimask2=      0x44,
   40         Rnmimask1=      0x45,
   41         Rnmimask2=      0x46,
   42         Rvidfan=        0x47,           /*  set fan counter, and read voltage level */
   43         Mvid=            0x0f,
   44         Mfan=            0xf0,
   45         Raddr=          0x48,           /*  address used on serial bus */
   46         Rresetid=       0x49,           /*  chip reset and ID register */
   47         Rpost=          0x00,           /*  start of post ram */
   48         Rvalue=         0x20,           /*  start of value ram */
   49 
   50         VRsize=         0x20,           /*  size of value ram */
   51 };
   52 
   53 enum
   54 {
   55         Qdir,
   56         Qlm78vram,
   57 };
   58 
   59 static Dirtab lm78dir[] = {
   60         ".",                    { Qdir, 0, QTDIR},      0,      0555,
   61         "lm78vram",     { Qlm78vram, 0 },       0,      0444,
   62 };
   63 
   64 /*  interface type */
   65 enum
   66 {
   67         None=   0,
   68         Smbus,
   69         Parallel,
   70 };
   71 
   72 static struct {
   73         QLock;
   74         int     probed;
   75         int     ifc;    /*  which interface is connected */
   76         SMBus   *smbus; /*  serial interface */
   77         int     port;   /*  parallel interface */
   78 } lm78;
   79 
   80 extern SMBus*   piix4smbus(void);
   81 
   82 /*  wait for device to become quiescent and then set the */
   83 /*  register address */
   84 static void
   85 setreg(int reg)
   86 {
   87         int tries;
   88 
   89         for(tries = 0; tries < 1000000; tries++)
   90                 if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
   91                         outb(lm78.port+Rpaddr, reg);
   92                         return;
   93                 }
   94         error("lm78 broken");
   95 }
   96 
   97 /*  routines that actually touch the device */
   98 static void
   99 lm78wrreg(int reg, uchar val)
  100 {
  101         if(waserror()){
  102                 qunlock(&lm78);
  103                 nexterror();
  104         }
  105         qlock(&lm78);
  106 
  107         switch(lm78.ifc){
  108         case Smbus:
  109                 lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
  110                 break;
  111         case Parallel:
  112                 setreg(reg);
  113                 outb(lm78.port+Rpdata, val);
  114                 break;
  115         default:
  116                 error(Enodev);
  117                 break;
  118         }
  119 
  120         qunlock(&lm78);
  121         poperror();
  122 }
  123 
  124 static int
  125 lm78rdreg(int reg)
  126 {
  127         uchar val;
  128 
  129         if(waserror()){
  130                 qunlock(&lm78);
  131                 nexterror();
  132         }
  133         qlock(&lm78);
  134 
  135         switch(lm78.ifc){
  136         case Smbus:
  137                 lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
  138                 lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
  139                 break;
  140         case Parallel:
  141                 setreg(reg);
  142                 val = inb(lm78.port+Rpdata);
  143                 break;
  144         default:
  145                 error(Enodev);
  146                 break;
  147         }
  148 
  149         qunlock(&lm78);
  150         poperror();
  151         return val;
  152 }
  153 
  154 /*  start the chip monitoring but don't change any smi 
  155  *  interrupts and/or alarms that the BIOS may have set up. 
  156  *  this isn't locked because it's thought to be idempotent 
  157  */
  158 static void
  159 lm78enable(void)
  160 {
  161         uchar config;
  162 
  163         if(lm78.ifc == None)
  164                 error(Enodev);
  165 
  166         if(lm78.probed == 0){
  167                 /*  make sure its really there */
  168                 if(lm78rdreg(Raddr) != Serialaddr){
  169                         lm78.ifc = None;
  170                         error(Enodev);
  171                 } else {
  172                         /*  start the sampling */
  173                         config = lm78rdreg(Rconfig);
  174                         config = (config | Bstart) & ~(Bintclr|Binit);
  175                         lm78wrreg(Rconfig, config);
  176 pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
  177                 }
  178                 lm78.probed = 1;
  179         }
  180 }
  181 
  182 enum
  183 {
  184         IntelVendID=    0x8086,
  185         PiixID=         0x122E,
  186         Piix3ID=        0x7000,
  187 
  188         Piix4PMID=      0x7113,         /*  PIIX4 power management function */
  189 
  190         PCSC=           0x78,           /*  programmable chip select control register */
  191         PCSC8bytes=     0x01,
  192 };
  193 
  194 /*  figure out what kind of interface we could have */
  195 void
  196 lm78reset(void)
  197 {
  198         int pcs;
  199         Pcidev *p;
  200 
  201         lm78.ifc = None;
  202         p = nil;
  203         while((p = pcimatch(p, IntelVendID, 0)) != nil){
  204                 switch(p->did){
  205                 /*  these bridges use the PCSC to map the lm78 into port space. */
  206                 /*  for this case the lm78's CS# select is connected to the PIIX's */
  207                 /*  PCS# output and the bottom 3 bits of address are passed to the */
  208                 /*  LM78's A0-A2 inputs. */
  209                 case PiixID:
  210                 case Piix3ID:
  211                         pcs = pcicfgr16(p, PCSC);
  212                         if(pcs & 3) {
  213                                 /* already enabled */
  214                                 lm78.port = pcs & ~3;
  215                                 lm78.ifc = Parallel;
  216                                 return; 
  217                         }
  218 
  219                         /*  enable the chip, use default address 0x50 */
  220                         pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
  221                         pcs = pcicfgr16(p, PCSC);
  222                         lm78.port = pcs & ~3;
  223                         lm78.ifc = Parallel;
  224                         return;
  225 
  226                 /*  this bridge puts the lm78's serial interface on the smbus */
  227                 case Piix4PMID:
  228                         lm78.smbus = piix4smbus();
  229                         if(lm78.smbus == nil)
  230                                 continue;
  231                         print("found piix4 smbus, base %lud\n", lm78.smbus->base);
  232                         lm78.ifc = Smbus;
  233                         return;
  234                 }
  235         }
  236 }
  237 
  238 Walkqid *
  239 lm78walk(Chan* c, Chan *nc, char** name, int nname)
  240 {
  241         return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen);
  242 }
  243 
  244 static int
  245 lm78stat(Chan* c, uchar* dp, int n)
  246 {
  247         return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen);
  248 }
  249 
  250 static Chan*
  251 lm78open(Chan* c, int omode)
  252 {
  253         return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
  254 }
  255 
  256 static void
  257 lm78close(Chan*)
  258 {
  259 }
  260 
  261 enum
  262 {
  263         Linelen= 25,
  264 };
  265 
  266 static long
  267 lm78read(Chan *c, void *a, long n, vlong offset)
  268 {
  269         uchar *va = a;
  270         int off, e;
  271 
  272         off = offset;
  273 
  274         switch((ulong)c->qid.path){
  275         case Qdir:
  276                 return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
  277 
  278         case Qlm78vram:
  279                 if(off >=  VRsize)
  280                         return 0;
  281                 e = off + n;
  282                 if(e > VRsize)
  283                         e = VRsize;
  284                 for(; off < e; off++)
  285                         *va++ = lm78rdreg(Rvalue+off);
  286                 return (int)(va - (uchar*)a);
  287         }
  288         return 0;
  289 }
  290 
  291 static long
  292 lm78write(Chan *c, void *a, long n, vlong offset)
  293 {
  294         uchar *va = a;
  295         int off, e;
  296 
  297         off = offset;
  298 
  299         switch((ulong)c->qid.path){
  300         default:
  301                 error(Eperm);
  302 
  303         case Qlm78vram:
  304                 if(off >=  VRsize)
  305                         return 0;
  306                 e = off + n;
  307                 if(e > VRsize)
  308                         e = VRsize;
  309                 for(; off < e; off++)
  310                         lm78wrreg(Rvalue+off, *va++);
  311                 return va - (uchar*)a;
  312         }
  313         return 0;
  314 }
  315 
  316 extern Dev lm78devtab;
  317 
  318 static Chan*
  319 lm78attach(char* spec)
  320 {
  321         lm78enable();
  322 
  323         return devattach(lm78devtab.dc, spec);
  324 }
  325 
  326 Dev lm78devtab = {
  327         'T',
  328         "lm78",
  329 
  330         lm78reset,
  331         devinit,
  332         devshutdown,
  333         lm78attach,
  334         lm78walk,
  335         lm78stat,
  336         lm78open,
  337         devcreate,
  338         lm78close,
  339         lm78read,
  340         devbread,
  341         lm78write,
  342         devbwrite,
  343         devremove,
  344         devwstat,
  345 };
  346 

Cache object: f47eb28fdc8df7cebf856c7ea0952f7f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.