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/i386/isa/pcf.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 /*-
    2  * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/5.2/sys/i386/isa/pcf.c 116599 2003-06-20 07:22:54Z jmg $");
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/kernel.h>
   33 #include <sys/module.h>
   34 #include <sys/bus.h>
   35 
   36 #include <machine/bus.h>
   37 #include <machine/resource.h>
   38 #include <sys/rman.h>
   39 
   40 #include <isa/isareg.h>
   41 #include <isa/isavar.h>
   42 
   43 #include <i386/isa/isa_device.h>
   44 
   45 #include <dev/iicbus/iiconf.h>
   46 #include "iicbus_if.h"
   47 
   48 #define IO_PCFSIZE      2
   49 
   50 #define TIMEOUT 9999                                    /* XXX */
   51 
   52 /* Status bits of S1 register (read only) */
   53 #define nBB     0x01            /* busy when low set/reset by STOP/START*/
   54 #define LAB     0x02            /* lost arbitration bit in multi-master mode */
   55 #define AAS     0x04            /* addressed as slave */
   56 #define LRB     0x08            /* last received byte when not AAS */
   57 #define AD0     0x08            /* general call received when AAS */
   58 #define BER     0x10            /* bus error, misplaced START or STOP */
   59 #define STS     0x20            /* STOP detected in slave receiver mode */
   60 #define PIN     0x80            /* pending interrupt not (r/w) */
   61 
   62 /* Control bits of S1 register (write only) */
   63 #define ACK     0x01
   64 #define STO     0x02
   65 #define STA     0x04
   66 #define ENI     0x08
   67 #define ES2     0x10
   68 #define ES1     0x20
   69 #define ES0     0x40
   70 
   71 #define BUFSIZE 2048
   72 
   73 #define SLAVE_TRANSMITTER       0x1
   74 #define SLAVE_RECEIVER          0x2
   75 
   76 #define PCF_DEFAULT_ADDR        0xaa
   77 
   78 struct pcf_softc {
   79 
   80         int pcf_base;                   /* isa port */
   81         int pcf_flags;
   82         u_char pcf_addr;                /* interface I2C address */
   83 
   84         int pcf_slave_mode;             /* receiver or transmitter */
   85         int pcf_started;                /* 1 if start condition sent */
   86 
   87         device_t iicbus;                /* the corresponding iicbus */
   88 
   89         int rid_irq, rid_ioport;
   90         struct resource *res_irq, *res_ioport;
   91         void *intr_cookie;
   92 };
   93 
   94 static int pcf_probe(device_t);
   95 static int pcf_attach(device_t);
   96 static void pcfintr(void *arg);
   97 
   98 static int pcf_print_child(device_t, device_t);
   99 
  100 static int pcf_repeated_start(device_t, u_char, int);
  101 static int pcf_start(device_t, u_char, int);
  102 static int pcf_stop(device_t);
  103 static int pcf_write(device_t, char *, int, int *, int);
  104 static int pcf_read(device_t, char *, int, int *, int, int);
  105 static int pcf_rst_card(device_t, u_char, u_char, u_char *);
  106 
  107 static device_method_t pcf_methods[] = {
  108         /* device interface */
  109         DEVMETHOD(device_probe,         pcf_probe),
  110         DEVMETHOD(device_attach,        pcf_attach),
  111 
  112         /* bus interface */
  113         DEVMETHOD(bus_print_child,      pcf_print_child),
  114 
  115         /* iicbus interface */
  116         DEVMETHOD(iicbus_callback,      iicbus_null_callback),
  117         DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
  118         DEVMETHOD(iicbus_start,         pcf_start),
  119         DEVMETHOD(iicbus_stop,          pcf_stop),
  120         DEVMETHOD(iicbus_write,         pcf_write),
  121         DEVMETHOD(iicbus_read,          pcf_read),
  122         DEVMETHOD(iicbus_reset,         pcf_rst_card),
  123 
  124         { 0, 0 }
  125 };
  126 
  127 static driver_t pcf_driver = {
  128         "pcf",
  129         pcf_methods,
  130         sizeof(struct pcf_softc),
  131 };
  132 
  133 static devclass_t pcf_devclass;
  134 
  135 #define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
  136 
  137 static int
  138 pcf_probe(device_t pcfdev)
  139 {
  140         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  141         device_t parent = device_get_parent(pcfdev);
  142         uintptr_t base;
  143 
  144         device_set_desc(pcfdev, "PCF8584 I2C bus controller");
  145 
  146         pcf = DEVTOSOFTC(pcfdev);
  147         bzero(pcf, sizeof(struct pcf_softc));
  148 
  149         pcf->rid_irq = pcf->rid_ioport = 0;
  150         pcf->res_irq = pcf->res_ioport = 0;
  151 
  152         /* IO port is mandatory */
  153         pcf->res_ioport = bus_alloc_resource(pcfdev, SYS_RES_IOPORT,
  154                                              &pcf->rid_ioport, 0ul, ~0ul, 
  155                                              IO_PCFSIZE, RF_ACTIVE);
  156         if (pcf->res_ioport == 0) {
  157                 device_printf(pcfdev, "cannot reserve I/O port range\n");
  158                 goto error;
  159         }
  160         BUS_READ_IVAR(parent, pcfdev, ISA_IVAR_PORT, &base);
  161         pcf->pcf_base = base;
  162 
  163         pcf->pcf_flags = device_get_flags(pcfdev);
  164 
  165         if (!(pcf->pcf_flags & IIC_POLLED)) {
  166                 pcf->res_irq = bus_alloc_resource(pcfdev, SYS_RES_IRQ, &pcf->rid_irq,
  167                                                   0ul, ~0ul, 1, RF_ACTIVE);
  168                 if (pcf->res_irq == 0) {
  169                         device_printf(pcfdev, "can't reserve irq, polled mode.\n");
  170                         pcf->pcf_flags |= IIC_POLLED;
  171                 }
  172         }
  173 
  174         /* reset the chip */
  175         pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
  176 
  177         return (0);
  178 error:
  179         if (pcf->res_ioport != 0) {
  180                 bus_deactivate_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
  181                                         pcf->res_ioport);
  182                 bus_release_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
  183                                      pcf->res_ioport);
  184         }
  185         return (ENXIO);
  186 }
  187 
  188 static int
  189 pcf_attach(device_t pcfdev)
  190 {
  191         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  192         device_t parent = device_get_parent(pcfdev);
  193         int error = 0;
  194 
  195         if (pcf->res_irq) {
  196                 /* default to the tty mask for registration */  /* XXX */
  197                 error = BUS_SETUP_INTR(parent, pcfdev, pcf->res_irq, INTR_TYPE_NET,
  198                                             pcfintr, pcfdev, &pcf->intr_cookie);
  199                 if (error)
  200                         return (error);
  201         }
  202 
  203         pcf->iicbus = device_add_child(pcfdev, "iicbus", -1);
  204 
  205         /* probe and attach the iicbus */
  206         bus_generic_attach(pcfdev);
  207 
  208         return (0);
  209 }
  210 
  211 static int
  212 pcf_print_child(device_t bus, device_t dev)
  213 {
  214         struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
  215         int retval = 0;
  216 
  217         retval += bus_print_child_header(bus, dev);
  218         retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus),
  219                          (int)pcf->pcf_addr);
  220 
  221         return (retval);
  222 }
  223 
  224 /*
  225  * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
  226  * 6 clocks cycles must be left between two consecutives access
  227  */
  228 #define pcf_nops()      DELAY(10)
  229 
  230 #define dummy_read(pcf)         PCF_GET_S0(pcf)
  231 #define dummy_write(pcf)        PCF_SET_S0(pcf, 0)
  232 
  233 /*
  234  * Specific register access to PCF8584
  235  */
  236 static void PCF_SET_S0(struct pcf_softc *pcf, int data)
  237 {
  238         outb(pcf->pcf_base, data);
  239         pcf_nops();
  240 }
  241 
  242 static void PCF_SET_S1(struct pcf_softc *pcf, int data)
  243 {
  244         outb(pcf->pcf_base+1, data);
  245         pcf_nops();
  246 }
  247 
  248 static char PCF_GET_S0(struct pcf_softc *pcf)
  249 {
  250         char data;
  251 
  252         data = inb(pcf->pcf_base);
  253         pcf_nops();
  254 
  255         return (data);
  256 }
  257 
  258 static char PCF_GET_S1(struct pcf_softc *pcf)
  259 {
  260         char data;
  261 
  262         data = inb(pcf->pcf_base+1);
  263         pcf_nops();
  264 
  265         return (data);
  266 }
  267 
  268 /*
  269  * Polling mode for master operations wait for a new
  270  * byte incomming or outgoing
  271  */
  272 static int pcf_wait_byte(struct pcf_softc *pcf)
  273 {
  274         int counter = TIMEOUT;
  275 
  276         while (counter--) {
  277 
  278                 if ((PCF_GET_S1(pcf) & PIN) == 0)
  279                         return (0);
  280         }
  281 
  282         return (IIC_ETIMEOUT);
  283 }
  284 
  285 static int pcf_stop(device_t pcfdev)
  286 {
  287         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  288 
  289         /*
  290          * Send STOP condition iff the START condition was previously sent.
  291          * STOP is sent only once even if an iicbus_stop() is called after
  292          * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
  293          * before the last char is read.
  294          */
  295         if (pcf->pcf_started) {
  296                 /* set stop condition and enable IT */
  297                 PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
  298 
  299                 pcf->pcf_started = 0;
  300         }
  301 
  302         return (0);
  303 }
  304 
  305 
  306 static int pcf_noack(struct pcf_softc *pcf, int timeout)
  307 {
  308         int noack;
  309         int k = timeout/10;
  310 
  311         do {
  312                 noack = PCF_GET_S1(pcf) & LRB;
  313                 if (!noack)
  314                         break;
  315                 DELAY(10);                              /* XXX wait 10 us */
  316         } while (k--);
  317 
  318         return (noack);
  319 }
  320 
  321 static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
  322 {
  323         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  324         int error = 0;
  325 
  326         /* repeated start */
  327         PCF_SET_S1(pcf, ES0|STA|STO|ACK);
  328 
  329         /* set slave address to PCF. Last bit (LSB) must be set correctly
  330          * according to transfer direction */
  331         PCF_SET_S0(pcf, slave);
  332 
  333         /* wait for address sent, polling */
  334         if ((error = pcf_wait_byte(pcf)))
  335                 goto error;
  336 
  337         /* check for ack */
  338         if (pcf_noack(pcf, timeout)) {
  339                 error = IIC_ENOACK;
  340                 goto error;
  341         }
  342 
  343         return (0);
  344 
  345 error:
  346         pcf_stop(pcfdev);
  347         return (error);
  348 }
  349 
  350 static int pcf_start(device_t pcfdev, u_char slave, int timeout)
  351 {
  352         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  353         int error = 0;
  354 
  355         if ((PCF_GET_S1(pcf) & nBB) == 0)
  356                 return (IIC_EBUSBSY);
  357 
  358         /* set slave address to PCF. Last bit (LSB) must be set correctly
  359          * according to transfer direction */
  360         PCF_SET_S0(pcf, slave);
  361 
  362         /* START only */
  363         PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
  364 
  365         pcf->pcf_started = 1;
  366 
  367         /* wait for address sent, polling */
  368         if ((error = pcf_wait_byte(pcf)))
  369                 goto error;
  370 
  371         /* check for ACK */
  372         if (pcf_noack(pcf, timeout)) {
  373                 error = IIC_ENOACK;
  374                 goto error;
  375         }
  376 
  377         return (0);
  378 
  379 error:
  380         pcf_stop(pcfdev);
  381         return (error);
  382 }
  383 
  384 static void
  385 pcfintr(void *arg)
  386 {
  387         device_t pcfdev = (device_t)arg;
  388         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  389 
  390         char data, status, addr;
  391         char error = 0;
  392 
  393         status = PCF_GET_S1(pcf);
  394 
  395         if (status & PIN) {
  396                 device_printf(pcfdev, "spurious interrupt, status=0x%x\n", status & 0xff);
  397 
  398                 goto error;
  399         }       
  400 
  401         if (status & LAB)
  402                 device_printf(pcfdev, "bus arbitration lost!\n");
  403 
  404         if (status & BER) {
  405                 error = IIC_EBUSERR;
  406                 iicbus_intr(pcf->iicbus, INTR_ERROR, &error);
  407 
  408                 goto error;
  409         }
  410 
  411         do {
  412                 status = PCF_GET_S1(pcf);
  413 
  414                 switch(pcf->pcf_slave_mode) {
  415 
  416                 case SLAVE_TRANSMITTER:
  417                         if (status & LRB) {
  418                                 /* ack interrupt line */
  419                                 dummy_write(pcf);
  420 
  421                                 /* no ack, don't send anymore */
  422                                 pcf->pcf_slave_mode = SLAVE_RECEIVER;
  423 
  424                                 iicbus_intr(pcf->iicbus, INTR_NOACK, NULL);
  425                                 break;
  426                         }
  427 
  428                         /* get data from upper code */
  429                         iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
  430 
  431                         PCF_SET_S0(pcf, data);  
  432                         break;  
  433                 
  434                 case SLAVE_RECEIVER:
  435                         if (status & AAS) {
  436                                 addr = PCF_GET_S0(pcf);
  437 
  438                                 if (status & AD0)
  439                                         iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr);
  440                                 else
  441                                         iicbus_intr(pcf->iicbus, INTR_START, &addr);
  442 
  443                                 if (addr & LSB) {
  444                                         pcf->pcf_slave_mode = SLAVE_TRANSMITTER;
  445 
  446                                         /* get the first char from upper code */
  447                                         iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
  448 
  449                                         /* send first data byte */
  450                                         PCF_SET_S0(pcf, data);
  451                                 }
  452 
  453                                 break;
  454                         }
  455 
  456                         /* stop condition received? */
  457                         if (status & STS) {
  458                                 /* ack interrupt line */
  459                                 dummy_read(pcf);
  460 
  461                                 /* emulate intr stop condition */
  462                                 iicbus_intr(pcf->iicbus, INTR_STOP, NULL);
  463 
  464                         } else {
  465                                 /* get data, ack interrupt line */
  466                                 data = PCF_GET_S0(pcf);
  467 
  468                                 /* deliver the character */
  469                                 iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data);
  470                         }
  471                         break;
  472 
  473                     default:
  474                         panic("%s: unknown slave mode (%d)!", __func__,
  475                                 pcf->pcf_slave_mode);
  476                     }
  477 
  478         } while ((PCF_GET_S1(pcf) & PIN) == 0);
  479 
  480         return;
  481 
  482 error:
  483         /* unknown event on bus...reset PCF */
  484         PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
  485 
  486         pcf->pcf_slave_mode = SLAVE_RECEIVER;
  487 
  488         return;
  489 }
  490 
  491 static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
  492 {
  493         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  494 
  495         if (oldaddr)
  496                 *oldaddr = pcf->pcf_addr;
  497 
  498         /* retrieve own address from bus level */
  499         if (!addr)
  500                 pcf->pcf_addr = PCF_DEFAULT_ADDR;
  501         else
  502                 pcf->pcf_addr = addr;
  503         
  504         PCF_SET_S1(pcf, PIN);                           /* initialize S1 */
  505 
  506         /* own address S'O<>0 */
  507         PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
  508 
  509         /* select clock register */
  510         PCF_SET_S1(pcf, PIN|ES1);
  511 
  512         /* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
  513         switch (speed) {
  514         case IIC_SLOW:
  515                 PCF_SET_S0(pcf,  0x1b);
  516                 break;
  517 
  518         case IIC_FAST:
  519                 PCF_SET_S0(pcf,  0x19);
  520                 break;
  521 
  522         case IIC_UNKNOWN:
  523         case IIC_FASTEST:
  524         default:
  525                 PCF_SET_S0(pcf,  0x18);
  526                 break;
  527         }
  528 
  529         /* set bus on, ack=yes, INT=yes */
  530         PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
  531 
  532         pcf->pcf_slave_mode = SLAVE_RECEIVER;
  533 
  534         return (0);
  535 }
  536 
  537 static int
  538 pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
  539 {
  540         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  541         int bytes, error = 0;
  542 
  543 #ifdef PCFDEBUG
  544         printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len);
  545 #endif
  546 
  547         bytes = 0;
  548         while (len) {
  549 
  550                 PCF_SET_S0(pcf, *buf++);
  551 
  552                 /* wait for the byte to be send */
  553                 if ((error = pcf_wait_byte(pcf)))
  554                         goto error;
  555 
  556                 /* check if ack received */
  557                 if (pcf_noack(pcf, timeout)) {
  558                         error = IIC_ENOACK;
  559                         goto error;
  560                 }
  561 
  562                 len --;
  563                 bytes ++;
  564         }
  565 
  566 error:
  567         *sent = bytes;
  568 
  569 #ifdef PCFDEBUG
  570         printf("pcf%d: >> %d bytes written (%d)\n",
  571                 device_get_unit(pcfdev), bytes, error);
  572 #endif
  573 
  574         return (error);
  575 }
  576 
  577 static int
  578 pcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
  579                                                         int delay /* us */)
  580 {
  581         struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
  582         int bytes, error = 0;
  583 
  584 #ifdef PCFDEBUG
  585         printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len);
  586 #endif
  587 
  588         /* trig the bus to get the first data byte in S0 */
  589         if (len) {
  590                 if (len == 1 && last)
  591                         /* just one byte to read */
  592                         PCF_SET_S1(pcf, ES0);           /* no ack */
  593 
  594                 dummy_read(pcf);
  595         }
  596 
  597         bytes = 0;
  598         while (len) {
  599 
  600                 /* XXX delay needed here */
  601 
  602                 /* wait for trigged byte */
  603                 if ((error = pcf_wait_byte(pcf))) {
  604                         pcf_stop(pcfdev);
  605                         goto error;
  606                 }
  607 
  608                 if (len == 1 && last)
  609                         /* ok, last data byte already in S0, no I2C activity
  610                          * on next PCF_GET_S0() */
  611                         pcf_stop(pcfdev);
  612 
  613                 else if (len == 2 && last)
  614                         /* next trigged byte with no ack */
  615                         PCF_SET_S1(pcf, ES0);
  616 
  617                 /* receive byte, trig next byte */
  618                 *buf++ = PCF_GET_S0(pcf);
  619 
  620                 len --;
  621                 bytes ++;
  622         };
  623 
  624 error:
  625         *read = bytes;
  626 
  627 #ifdef PCFDEBUG
  628         printf("pcf%d: << %d bytes read (%d)\n",
  629                 device_get_unit(pcfdev), bytes, error);
  630 #endif
  631 
  632         return (error);
  633 }
  634 
  635 DRIVER_MODULE(pcf, isa, pcf_driver, pcf_devclass, 0, 0);

Cache object: 58252768d4f9950de0f91b7130d63b70


[ 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.