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/dev/iicbus/acpi_iicbus.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org>
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/endian.h>
   34 #include <sys/kernel.h>
   35 #include <sys/malloc.h>
   36 #include <sys/module.h>
   37 #include <sys/rman.h>
   38 #include <sys/sbuf.h>
   39 
   40 #include <machine/resource.h>
   41 
   42 #include <contrib/dev/acpica/include/acpi.h>
   43 #include <contrib/dev/acpica/include/accommon.h>
   44 #include <contrib/dev/acpica/include/amlcode.h>
   45 #include <dev/acpica/acpivar.h>
   46 
   47 #include <dev/iicbus/iiconf.h>
   48 #include <dev/iicbus/iicbus.h>
   49 
   50 #define ACPI_IICBUS_LOCAL_BUFSIZE       32      /* Fits max SMBUS block size */
   51 
   52 /*
   53  * Make a copy of ACPI_RESOURCE_I2C_SERIALBUS type and replace "pointer to ACPI
   54  * object name string" field with pointer to ACPI object itself.
   55  * This saves us extra strdup()/free() pair on acpi_iicbus_get_i2cres call.
   56  */
   57 typedef ACPI_RESOURCE_I2C_SERIALBUS     ACPI_IICBUS_RESOURCE_I2C_SERIALBUS;
   58 #define ResourceSource_Handle   ResourceSource.StringPtr
   59 
   60 /* Hooks for the ACPI CA debugging infrastructure. */
   61 #define _COMPONENT      ACPI_BUS
   62 ACPI_MODULE_NAME("IIC")
   63 
   64 struct gsb_buffer {
   65         UINT8 status;
   66         UINT8 len;
   67         UINT8 data[];
   68 } __packed;
   69 
   70 struct acpi_iicbus_softc {
   71         struct iicbus_softc     super_sc;
   72         ACPI_CONNECTION_INFO    space_handler_info;
   73         bool                    space_handler_installed;
   74 };
   75 
   76 struct acpi_iicbus_ivars {
   77         struct iicbus_ivar      super_ivar;
   78         ACPI_HANDLE             handle;
   79 };
   80 
   81 static int install_space_handler = 0;
   82 TUNABLE_INT("hw.iicbus.enable_acpi_space_handler", &install_space_handler);
   83 
   84 static inline bool
   85 acpi_resource_is_i2c_serialbus(ACPI_RESOURCE *res)
   86 {
   87 
   88         return (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
   89             res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C);
   90 }
   91 
   92 /*
   93  * IICBUS Address space handler
   94  */
   95 static int
   96 acpi_iicbus_sendb(device_t dev, u_char slave, char byte)
   97 {
   98         struct iic_msg msgs[] = {
   99             { slave, IIC_M_WR, 1, &byte },
  100         };
  101 
  102         return (iicbus_transfer(dev, msgs, nitems(msgs)));
  103 }
  104 
  105 static int
  106 acpi_iicbus_recvb(device_t dev, u_char slave, char *byte)
  107 {
  108         char buf;
  109         struct iic_msg msgs[] = {
  110             { slave, IIC_M_RD, 1, &buf },
  111         };
  112         int error;
  113 
  114         error = iicbus_transfer(dev, msgs, nitems(msgs));
  115         if (error == 0)
  116                 *byte = buf;
  117 
  118         return (error);
  119 }
  120 
  121 static int
  122 acpi_iicbus_write(device_t dev, u_char slave, char cmd, void *buf,
  123     uint16_t buflen)
  124 {
  125         struct iic_msg msgs[] = {
  126             { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
  127             { slave, IIC_M_WR | IIC_M_NOSTART, buflen, buf },
  128         };
  129 
  130         return (iicbus_transfer(dev, msgs, nitems(msgs)));
  131 }
  132 
  133 static int
  134 acpi_iicbus_read(device_t dev, u_char slave, char cmd, void *buf,
  135     uint16_t buflen)
  136 {
  137         uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
  138         struct iic_msg msgs[] = {
  139             { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
  140             { slave, IIC_M_RD, buflen, NULL },
  141         };
  142         int error;
  143 
  144         if (buflen <= sizeof(local_buffer))
  145                 msgs[1].buf = local_buffer;
  146         else
  147                 msgs[1].buf = malloc(buflen, M_DEVBUF, M_WAITOK);
  148         error = iicbus_transfer(dev, msgs, nitems(msgs));
  149         if (error == 0)
  150                 memcpy(buf, msgs[1].buf, buflen);
  151         if (msgs[1].buf != local_buffer)
  152                 free(msgs[1].buf, M_DEVBUF);
  153 
  154         return (error);
  155 }
  156 
  157 static int
  158 acpi_iicbus_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
  159 {
  160         uint8_t bytes[2] = { cmd, count };
  161         struct iic_msg msgs[] = {
  162             { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
  163             { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
  164         };
  165 
  166         if (count == 0)
  167                 return (errno2iic(EINVAL));
  168 
  169         return (iicbus_transfer(dev, msgs, nitems(msgs)));
  170 }
  171 
  172 static int
  173 acpi_iicbus_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
  174 {
  175         uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
  176         u_char len;
  177         struct iic_msg msgs[] = {
  178             { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
  179             { slave, IIC_M_RD | IIC_M_NOSTOP, 1, &len },
  180         };
  181         struct iic_msg block_msg[] = {
  182             { slave, IIC_M_RD | IIC_M_NOSTART, 0, NULL },
  183         };
  184         device_t parent = device_get_parent(dev);
  185         int error;
  186 
  187         /* Have to do this because the command is split in two transfers. */
  188         error = iicbus_request_bus(parent, dev, IIC_WAIT);
  189         if (error == 0)
  190                 error = iicbus_transfer(dev, msgs, nitems(msgs));
  191         if (error == 0) {
  192                 /*
  193                  * If the slave offers an empty reply,
  194                  * read one byte to generate the stop or abort.
  195                  */
  196                 if (len == 0)
  197                         block_msg[0].len = 1;
  198                 else
  199                         block_msg[0].len = len;
  200                 if (len <= sizeof(local_buffer))
  201                         block_msg[0].buf = local_buffer;
  202                 else
  203                         block_msg[0].buf = malloc(len, M_DEVBUF, M_WAITOK);
  204                 error = iicbus_transfer(dev, block_msg, nitems(block_msg));
  205                 if (len == 0)
  206                         error = errno2iic(EBADMSG);
  207                 if (error == 0) {
  208                         *count = len;
  209                         memcpy(buf, block_msg[0].buf, len);
  210                 }
  211                 if (block_msg[0].buf != local_buffer)
  212                         free(block_msg[0].buf, M_DEVBUF);
  213         }
  214         (void)iicbus_release_bus(parent, dev);
  215         return (error);
  216 }
  217 
  218 static ACPI_STATUS
  219 acpi_iicbus_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
  220     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
  221 {
  222         struct gsb_buffer *gsb;
  223         struct acpi_iicbus_softc *sc;
  224         device_t dev;
  225         ACPI_CONNECTION_INFO *info;
  226         ACPI_RESOURCE_I2C_SERIALBUS *sb;
  227         ACPI_RESOURCE *res;
  228         ACPI_STATUS s;
  229         int val;
  230 
  231         gsb = (struct gsb_buffer *)Value;
  232         if (gsb == NULL)
  233                 return (AE_BAD_PARAMETER);
  234 
  235         info = HandlerContext;
  236         s = AcpiBufferToResource(info->Connection, info->Length, &res);
  237         if (ACPI_FAILURE(s))
  238                 return (s);
  239 
  240         if (!acpi_resource_is_i2c_serialbus(res)) {
  241                 s = AE_BAD_PARAMETER;
  242                 goto err;
  243         }
  244 
  245         sb = &res->Data.I2cSerialBus;
  246 
  247         /* XXX Ignore 10bit addressing for now */
  248         if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
  249                 s = AE_BAD_PARAMETER;
  250                 goto err;
  251         }
  252 
  253 #define AML_FIELD_ATTRIB_MASK           0x0F
  254 #define AML_FIELD_ATTRIO(attr, io)      (((attr) << 16) | (io))
  255 
  256         Function &= AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_MASK, ACPI_IO_MASK);
  257         sc = __containerof(info, struct acpi_iicbus_softc, space_handler_info);
  258         dev = sc->super_sc.dev;
  259 
  260         switch (Function) {
  261         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_READ):
  262                 val = acpi_iicbus_recvb(dev, sb->SlaveAddress, gsb->data);
  263                 break;
  264 
  265         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_WRITE):
  266                 val = acpi_iicbus_sendb(dev, sb->SlaveAddress, gsb->data[0]);
  267                 break;
  268 
  269         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_READ):
  270                 val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
  271                     gsb->data, 1);
  272                 break;
  273 
  274         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_WRITE):
  275                 val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
  276                     gsb->data, 1);
  277                 break;
  278 
  279         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_READ):
  280                 val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
  281                     gsb->data, 2);
  282                 break;
  283 
  284         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_WRITE):
  285                 val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
  286                     gsb->data, 2);
  287                 break;
  288 
  289         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_READ):
  290                 val = acpi_iicbus_bread(dev, sb->SlaveAddress, Address,
  291                     &gsb->len, gsb->data);
  292                 break;
  293 
  294         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_WRITE):
  295                 val = acpi_iicbus_bwrite(dev, sb->SlaveAddress, Address,
  296                     gsb->len, gsb->data);
  297                 break;
  298 
  299         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_READ):
  300                 val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
  301                     gsb->data, info->AccessLength);
  302                 break;
  303 
  304         case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_WRITE):
  305                 val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
  306                     gsb->data, info->AccessLength);
  307                 break;
  308 
  309         default:
  310                 device_printf(dev, "protocol(0x%04x) is not supported.\n",
  311                     Function);
  312                 s = AE_BAD_PARAMETER;
  313                 goto err;
  314         }
  315 
  316         gsb->status = val;
  317 
  318 err:
  319         ACPI_FREE(res);
  320 
  321         return (s);
  322 }
  323 
  324 static int
  325 acpi_iicbus_install_address_space_handler(struct acpi_iicbus_softc *sc)
  326 {
  327         ACPI_HANDLE handle;
  328         ACPI_STATUS s;
  329 
  330         handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
  331         s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
  332             &acpi_iicbus_space_handler, NULL, &sc->space_handler_info);
  333         if (ACPI_FAILURE(s)) {
  334                 device_printf(sc->super_sc.dev,
  335                     "Failed to install GSBUS Address Space Handler in ACPI\n");
  336                 return (ENXIO);
  337         }
  338 
  339         return (0);
  340 }
  341 
  342 static int
  343 acpi_iicbus_remove_address_space_handler(struct acpi_iicbus_softc *sc)
  344 {
  345         ACPI_HANDLE handle;
  346         ACPI_STATUS s;
  347 
  348         handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
  349         s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
  350             &acpi_iicbus_space_handler);
  351         if (ACPI_FAILURE(s)) {
  352                 device_printf(sc->super_sc.dev,
  353                     "Failed to remove GSBUS Address Space Handler from ACPI\n");
  354                 return (ENXIO);
  355         }
  356 
  357         return (0);
  358 }
  359 
  360 static ACPI_STATUS
  361 acpi_iicbus_get_i2cres_cb(ACPI_RESOURCE *res, void *context)
  362 {
  363         ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb = context;
  364         ACPI_STATUS status;
  365         ACPI_HANDLE handle;
  366 
  367         if (acpi_resource_is_i2c_serialbus(res)) {
  368                 status = AcpiGetHandle(ACPI_ROOT_OBJECT,
  369                     res->Data.I2cSerialBus.ResourceSource.StringPtr, &handle);
  370                 if (ACPI_FAILURE(status))
  371                         return (status);
  372                 memcpy(sb, &res->Data.I2cSerialBus,
  373                     sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
  374                 /*
  375                  * replace "pointer to ACPI object name string" field
  376                  * with pointer to ACPI object itself.
  377                  */
  378                 sb->ResourceSource_Handle = handle;
  379                 return (AE_CTRL_TERMINATE);
  380         } else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
  381                 return (AE_NOT_FOUND);
  382 
  383         return (AE_OK);
  384 }
  385 
  386 static ACPI_STATUS
  387 acpi_iicbus_get_i2cres(ACPI_HANDLE handle, ACPI_RESOURCE_I2C_SERIALBUS *sb)
  388 {
  389 
  390         return (AcpiWalkResources(handle, "_CRS",
  391             acpi_iicbus_get_i2cres_cb, sb));
  392 }
  393 
  394 static ACPI_STATUS
  395 acpi_iicbus_parse_resources_cb(ACPI_RESOURCE *res, void *context)
  396 {
  397         device_t dev = context;
  398         struct iicbus_ivar *super_devi = device_get_ivars(dev);
  399         struct resource_list *rl = &super_devi->rl;
  400         int irq, gpio_pin;
  401 
  402         switch(res->Type) {
  403         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  404                 if (res->Data.ExtendedIrq.InterruptCount > 0) {
  405                         irq = res->Data.ExtendedIrq.Interrupts[0];
  406                         if (bootverbose)
  407                                 printf("  IRQ:               %d\n", irq);
  408                         resource_list_add_next(rl, SYS_RES_IRQ, irq, irq, 1);
  409                         return (AE_CTRL_TERMINATE);
  410                 }
  411                 break;
  412         case ACPI_RESOURCE_TYPE_GPIO:
  413                 if (res->Data.Gpio.ConnectionType ==
  414                     ACPI_RESOURCE_GPIO_TYPE_INT) {
  415                         /* Not supported by FreeBSD yet */
  416                         gpio_pin = res->Data.Gpio.PinTable[0];
  417                         if (bootverbose)
  418                                 printf("  GPIO IRQ pin:      %d\n", gpio_pin);
  419                         return (AE_CTRL_TERMINATE);
  420                 }
  421                 break;
  422         default:
  423                 break;
  424         }
  425 
  426         return (AE_OK);
  427 }
  428 
  429 static ACPI_STATUS
  430 acpi_iicbus_parse_resources(ACPI_HANDLE handle, device_t dev)
  431 {
  432 
  433         return (AcpiWalkResources(handle, "_CRS",
  434             acpi_iicbus_parse_resources_cb, dev));
  435 }
  436 
  437 static void
  438 acpi_iicbus_dump_res(device_t dev, ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb)
  439 {
  440         device_printf(dev, "found ACPI child\n");
  441         printf("  SlaveAddress:      0x%04hx\n", sb->SlaveAddress);
  442         printf("  ConnectionSpeed:   %uHz\n", sb->ConnectionSpeed);
  443         printf("  SlaveMode:         %s\n",
  444             sb->SlaveMode == ACPI_CONTROLLER_INITIATED ?
  445             "ControllerInitiated" : "DeviceInitiated");
  446         printf("  AddressingMode:    %uBit\n", sb->AccessMode == 0 ? 7 : 10);
  447         printf("  ConnectionSharing: %s\n", sb->ConnectionSharing == 0 ?
  448             "Exclusive" : "Shared");
  449 }
  450 
  451 static device_t
  452 acpi_iicbus_add_child(device_t dev, u_int order, const char *name, int unit)
  453 {
  454 
  455         return (iicbus_add_child_common(
  456             dev, order, name, unit, sizeof(struct acpi_iicbus_ivars)));
  457 }
  458 
  459 static ACPI_STATUS
  460 acpi_iicbus_enumerate_child(ACPI_HANDLE handle, UINT32 level,
  461     void *context, void **result)
  462 {
  463         device_t iicbus, child, acpi_child, acpi0;
  464         struct iicbus_softc *super_sc;
  465         ACPI_IICBUS_RESOURCE_I2C_SERIALBUS sb;
  466         ACPI_STATUS status;
  467         UINT32 sta;
  468 
  469         iicbus = context;
  470         super_sc = device_get_softc(iicbus);
  471 
  472         /*
  473          * If no _STA method or if it failed, then assume that
  474          * the device is present.
  475          */
  476         if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
  477             !ACPI_DEVICE_PRESENT(sta))
  478                 return (AE_OK);
  479 
  480         if (!acpi_has_hid(handle))
  481                 return (AE_OK);
  482 
  483         /*
  484          * Read "I2C Serial Bus Connection Resource Descriptor"
  485          * described in p.19.6.57 of ACPI specification.
  486          */
  487         bzero(&sb, sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
  488         if (ACPI_FAILURE(acpi_iicbus_get_i2cres(handle, &sb)) ||
  489             sb.SlaveAddress == 0)
  490                 return (AE_OK);
  491         if (sb.ResourceSource_Handle !=
  492             acpi_get_handle(device_get_parent(iicbus)))
  493                 return (AE_OK);
  494         if (bootverbose)
  495                 acpi_iicbus_dump_res(iicbus, &sb);
  496 
  497         /* Find out speed of the slowest slave */
  498         if (super_sc->bus_freq == 0 || super_sc->bus_freq > sb.ConnectionSpeed)
  499                 super_sc->bus_freq = sb.ConnectionSpeed;
  500 
  501         /* Delete existing child of acpi bus */
  502         acpi_child = acpi_get_device(handle);
  503         if (acpi_child != NULL) {
  504                 acpi0 = devclass_get_device(devclass_find("acpi"), 0);
  505                 if (device_get_parent(acpi_child) != acpi0)
  506                         return (AE_OK);
  507 
  508                 if (device_is_attached(acpi_child))
  509                         return (AE_OK);
  510 
  511                 if (device_delete_child(acpi0, acpi_child) != 0)
  512                         return (AE_OK);
  513         }
  514 
  515         child = BUS_ADD_CHILD(iicbus, 0, NULL, -1);
  516         if (child == NULL) {
  517                 device_printf(iicbus, "add child failed\n");
  518                 return (AE_OK);
  519         }
  520 
  521         iicbus_set_addr(child, sb.SlaveAddress);
  522         acpi_set_handle(child, handle);
  523         (void)acpi_iicbus_parse_resources(handle, child);
  524 
  525         /*
  526          * Update ACPI-CA to use the IIC enumerated device_t for this handle.
  527          */
  528         status = AcpiAttachData(handle, acpi_fake_objhandler, child);
  529         if (ACPI_FAILURE(status))
  530                 printf("WARNING: Unable to attach object data to %s - %s\n",
  531                     acpi_name(handle), AcpiFormatException(status));
  532 
  533         return (AE_OK);
  534 }
  535 
  536 static ACPI_STATUS
  537 acpi_iicbus_enumerate_children(device_t dev)
  538 {
  539 
  540         return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
  541             ACPI_UINT32_MAX, acpi_iicbus_enumerate_child, NULL, dev, NULL));
  542 }
  543 
  544 static void
  545 acpi_iicbus_set_power_children(device_t dev, int state, bool all_children)
  546 {
  547         device_t *devlist;
  548         int i, numdevs;
  549 
  550         if (device_get_children(dev, &devlist, &numdevs) != 0)
  551                 return;
  552 
  553         for (i = 0; i < numdevs; i++)
  554                 if (all_children || device_is_attached(devlist[i]) != 0)
  555                         acpi_set_powerstate(devlist[i], state);
  556 
  557         free(devlist, M_TEMP);
  558 }
  559 
  560 static int
  561 acpi_iicbus_probe(device_t dev)
  562 {
  563         ACPI_HANDLE handle;
  564         device_t controller;
  565 
  566         if (acpi_disabled("iicbus"))
  567                 return (ENXIO);
  568 
  569         controller = device_get_parent(dev);
  570         if (controller == NULL)
  571                 return (ENXIO);
  572 
  573         handle = acpi_get_handle(controller);
  574         if (handle == NULL)
  575                 return (ENXIO);
  576 
  577         device_set_desc(dev, "Philips I2C bus (ACPI-hinted)");
  578         return (BUS_PROBE_DEFAULT);
  579 }
  580 
  581 static int
  582 acpi_iicbus_attach(device_t dev)
  583 {
  584         struct acpi_iicbus_softc *sc = device_get_softc(dev);
  585         int error;
  586 
  587         if (ACPI_FAILURE(acpi_iicbus_enumerate_children(dev)))
  588                 device_printf(dev, "children enumeration failed\n");
  589 
  590         acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, true);
  591         error = iicbus_attach_common(dev, sc->super_sc.bus_freq);
  592         if (error == 0 && install_space_handler != 0 &&
  593             acpi_iicbus_install_address_space_handler(sc) == 0)
  594                 sc->space_handler_installed = true;
  595 
  596         return (error);
  597 }
  598 
  599 static int
  600 acpi_iicbus_detach(device_t dev)
  601 {
  602         struct acpi_iicbus_softc *sc = device_get_softc(dev);
  603 
  604         if (sc->space_handler_installed)
  605                 acpi_iicbus_remove_address_space_handler(sc);
  606         acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
  607 
  608         return (iicbus_detach(dev));
  609 }
  610 
  611 static int
  612 acpi_iicbus_suspend(device_t dev)
  613 {
  614         int error;
  615 
  616         error = bus_generic_suspend(dev);
  617         if (error == 0)
  618                 acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
  619 
  620         return (error);
  621 }
  622 
  623 static int
  624 acpi_iicbus_resume(device_t dev)
  625 {
  626 
  627         acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, false);
  628 
  629         return (bus_generic_resume(dev));
  630 }
  631 
  632 /*
  633  * If this device is an ACPI child but no one claimed it, attempt
  634  * to power it off.  We'll power it back up when a driver is added.
  635  */
  636 static void
  637 acpi_iicbus_probe_nomatch(device_t bus, device_t child)
  638 {
  639 
  640         iicbus_probe_nomatch(bus, child);
  641         acpi_set_powerstate(child, ACPI_STATE_D3);
  642 }
  643 
  644 /*
  645  * If a new driver has a chance to probe a child, first power it up.
  646  */
  647 static void
  648 acpi_iicbus_driver_added(device_t dev, driver_t *driver)
  649 {
  650         device_t child, *devlist;
  651         int i, numdevs;
  652 
  653         DEVICE_IDENTIFY(driver, dev);
  654         if (device_get_children(dev, &devlist, &numdevs) != 0)
  655                 return;
  656 
  657         for (i = 0; i < numdevs; i++) {
  658                 child = devlist[i];
  659                 if (device_get_state(child) == DS_NOTPRESENT) {
  660                         acpi_set_powerstate(child, ACPI_STATE_D0);
  661                         if (device_probe_and_attach(child) != 0)
  662                                 acpi_set_powerstate(child, ACPI_STATE_D3);
  663                 }
  664         }
  665         free(devlist, M_TEMP);
  666 }
  667 
  668 static void
  669 acpi_iicbus_child_deleted(device_t bus, device_t child)
  670 {
  671         struct acpi_iicbus_ivars *devi = device_get_ivars(child);
  672 
  673         if (acpi_get_device(devi->handle) == child)
  674                 AcpiDetachData(devi->handle, acpi_fake_objhandler);
  675 }
  676 
  677 static int
  678 acpi_iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *res)
  679 {
  680         struct acpi_iicbus_ivars *devi = device_get_ivars(child);
  681 
  682         switch (which) {
  683         case ACPI_IVAR_HANDLE:
  684                 *res = (uintptr_t)devi->handle;
  685                 break;
  686         default:
  687                 return (iicbus_read_ivar(bus, child, which, res));
  688         }
  689 
  690         return (0);
  691 }
  692 
  693 static int
  694 acpi_iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t val)
  695 {
  696         struct acpi_iicbus_ivars *devi = device_get_ivars(child);
  697 
  698         switch (which) {
  699         case ACPI_IVAR_HANDLE:
  700                 if (devi->handle != NULL)
  701                         return (EINVAL);
  702                 devi->handle = (ACPI_HANDLE)val;
  703                 break;
  704         default:
  705                 return (iicbus_write_ivar(bus, child, which, val));
  706         }
  707 
  708         return (0);
  709 }
  710 
  711 /* Location hint for devctl(8). Concatenate IIC and ACPI hints. */
  712 static int
  713 acpi_iicbus_child_location(device_t bus, device_t child, struct sbuf *sb)
  714 {
  715         struct acpi_iicbus_ivars *devi = device_get_ivars(child);
  716         int error;
  717 
  718         /* read IIC location hint string into the buffer. */
  719         error = iicbus_child_location(bus, child, sb);
  720         if (error != 0)
  721                 return (error);
  722 
  723         /* Place ACPI string right after IIC one's terminating NUL. */
  724         if (devi->handle != NULL)
  725                 sbuf_printf(sb, " handle=%s", acpi_name(devi->handle));
  726 
  727         return (0);
  728 }
  729 
  730 /* PnP information for devctl(8). Concatenate IIC and ACPI info strings. */
  731 static int
  732 acpi_iicbus_child_pnpinfo(device_t bus, device_t child, struct sbuf *sb)
  733 {
  734         struct acpi_iicbus_ivars *devi = device_get_ivars(child);
  735         int error;
  736 
  737         /* read IIC PnP string into the buffer. */
  738         error = iicbus_child_pnpinfo(bus, child, sb);
  739         if (error != 0)
  740                 return (error);
  741 
  742         if (devi->handle == NULL)
  743                 return (0);
  744 
  745         error = acpi_pnpinfo(devi->handle, sb);
  746 
  747         return (error);
  748 }
  749 
  750 static device_method_t acpi_iicbus_methods[] = {
  751         /* Device interface */
  752         DEVMETHOD(device_probe,         acpi_iicbus_probe),
  753         DEVMETHOD(device_attach,        acpi_iicbus_attach),
  754         DEVMETHOD(device_detach,        acpi_iicbus_detach),
  755         DEVMETHOD(device_suspend,       acpi_iicbus_suspend),
  756         DEVMETHOD(device_resume,        acpi_iicbus_resume),
  757 
  758         /* Bus interface */
  759         DEVMETHOD(bus_add_child,        acpi_iicbus_add_child),
  760         DEVMETHOD(bus_probe_nomatch,    acpi_iicbus_probe_nomatch),
  761         DEVMETHOD(bus_driver_added,     acpi_iicbus_driver_added),
  762         DEVMETHOD(bus_child_deleted,    acpi_iicbus_child_deleted),
  763         DEVMETHOD(bus_read_ivar,        acpi_iicbus_read_ivar),
  764         DEVMETHOD(bus_write_ivar,       acpi_iicbus_write_ivar),
  765         DEVMETHOD(bus_child_location,   acpi_iicbus_child_location),
  766         DEVMETHOD(bus_child_pnpinfo,    acpi_iicbus_child_pnpinfo),
  767         DEVMETHOD(bus_get_device_path,  acpi_get_acpi_device_path),
  768 
  769         DEVMETHOD_END,
  770 };
  771 
  772 DEFINE_CLASS_1(iicbus, acpi_iicbus_driver, acpi_iicbus_methods,
  773     sizeof(struct acpi_iicbus_softc), iicbus_driver);
  774 MODULE_VERSION(acpi_iicbus, 1);
  775 MODULE_DEPEND(acpi_iicbus, acpi, 1, 1, 1);

Cache object: 59c5a3623cac252189da34f997fdcbad


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