xref: /dragonfly/sys/bus/smbus/smbacpi/smbacpi.c (revision 806343b9970349b20504d6a3a0ad77008c380d00)
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Imre Vadász <imre@vdsz.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /* Register SMBUS device with ACPICA for ACPI-5.0 GPIO functionality */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/errno.h>
42 #include <sys/lock.h>
43 #include <sys/bus.h>
44 
45 #include "opt_acpi.h"
46 #include "acpi.h"
47 #include <dev/acpica/acpivar.h>
48 #include <contrib/dev/acpica/source/include/amlcode.h>
49 
50 #include <bus/smbus/smbconf.h>
51 
52 #include "smbus_if.h"
53 
54 struct gsb_buffer {
55           UINT8 status;
56           UINT8 len;
57           UINT8 data[];
58 } __packed;
59 
60 struct acpi_i2c_handler_data {
61           struct acpi_connection_info info;
62           device_t dev;
63 };
64 
65 struct smbus_acpi_softc {
66           device_t dev;
67           device_t parent;
68           struct acpi_i2c_handler_data space_handler_data;
69 };
70 
71 static int          smbus_acpi_probe(device_t dev);
72 static int          smbus_acpi_attach(device_t dev);
73 static int          smbus_acpi_detach(device_t dev);
74 
75 /* SMBUS Address Space Handler */
76 static void                   smbus_acpi_install_address_space_handler(
77                                   struct smbus_acpi_softc *sc);
78 static void                   smbus_acpi_remove_address_space_handler(
79                                   struct smbus_acpi_softc *sc);
80 static ACPI_STATUS  smbus_acpi_space_handler(UINT32 Function,
81                                   ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
82                                   UINT64 *Value, void *HandlerContext,
83                                   void *RegionContext);
84 
85 /*
86  * SMBUS Address space handler
87  */
88 
89 static void
smbus_acpi_install_address_space_handler(struct smbus_acpi_softc * sc)90 smbus_acpi_install_address_space_handler(struct smbus_acpi_softc *sc)
91 {
92           ACPI_HANDLE handle;
93           ACPI_STATUS s;
94 
95           handle = acpi_get_handle(sc->parent);
96           sc->space_handler_data.dev = sc->parent;
97           s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
98               &smbus_acpi_space_handler, NULL, &sc->space_handler_data);
99           if (ACPI_FAILURE(s)) {
100                     device_printf(sc->dev,
101                         "Failed to install GSBUS Address Space Handler in ACPI\n");
102           }
103 }
104 
105 static void
smbus_acpi_remove_address_space_handler(struct smbus_acpi_softc * sc)106 smbus_acpi_remove_address_space_handler(struct smbus_acpi_softc *sc)
107 {
108           ACPI_HANDLE handle;
109           ACPI_STATUS s;
110 
111           handle = acpi_get_handle(sc->parent);
112           s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
113               &smbus_acpi_space_handler);
114           if (ACPI_FAILURE(s)) {
115                     device_printf(sc->dev,
116                         "Failed to remove GSBUS Address Space Handler from ACPI\n");
117           }
118 }
119 
120 static ACPI_STATUS
smbus_acpi_space_handler(UINT32 Function,ACPI_PHYSICAL_ADDRESS Address,UINT32 BitWidth,UINT64 * Value,void * HandlerContext,void * RegionContext)121 smbus_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
122     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
123 {
124           struct gsb_buffer *gsb = (struct gsb_buffer *)Value;
125           struct acpi_i2c_handler_data *data = HandlerContext;
126           device_t dev = data->dev;
127           struct acpi_connection_info *info = &data->info;
128           struct acpi_resource_i2c_serialbus *sb;
129           ACPI_RESOURCE *Resource;
130           UINT32 accessor_type = Function >> 16;
131           UINT8 action = Function & ACPI_IO_MASK;
132           ACPI_STATUS s = AE_OK;
133           int cnt, val = 0;
134           uint16_t *wdata;
135           short word;
136           char byte;
137           char buf[32];
138           u_char count;
139 
140           if (Value == NULL)
141                     return (AE_BAD_PARAMETER);
142 
143           s = AcpiBufferToResource(info->Connection, info->Length,
144               &Resource);
145           if (ACPI_FAILURE(s))
146                     return s;
147           if (Resource->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
148                     s = AE_BAD_PARAMETER;
149                     goto err;
150           }
151 
152           sb = &Resource->Data.I2cSerialBus;
153           if (sb->Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
154                     s = AE_BAD_PARAMETER;
155                     goto err;
156           }
157 
158           /* XXX Ignore 10bit addressing for now */
159           if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
160                     s = AE_BAD_PARAMETER;
161                     goto err;
162           }
163 
164           switch (accessor_type) {
165           case AML_FIELD_ATTRIB_SEND_RECEIVE:
166                     if (action == ACPI_READ) {
167                               val = SMBUS_RECVB(dev, sb->SlaveAddress, &byte);
168                               if (val == 0)
169                                         gsb->data[0] = byte;
170                     } else {
171                               val = SMBUS_SENDB(dev, sb->SlaveAddress,
172                                   gsb->data[0]);
173                     }
174                     break;
175           case AML_FIELD_ATTRIB_BYTE:
176                     if (action == ACPI_READ) {
177                               val = SMBUS_READB(dev, sb->SlaveAddress, Address,
178                                   &byte);
179                               if (val == 0)
180                                         gsb->data[0] = byte;
181                     } else {
182                               val = SMBUS_WRITEB(dev, sb->SlaveAddress, Address,
183                                   gsb->data[0]);
184                     }
185                     break;
186           case AML_FIELD_ATTRIB_WORD:
187                     wdata = (uint16_t *)gsb->data;
188                     if (action == ACPI_READ) {
189                               val = SMBUS_READW(dev, sb->SlaveAddress, Address,
190                                   &word);
191                               if (val == 0)
192                                         wdata[0] = word;
193                     } else {
194                               val = SMBUS_WRITEW(dev, sb->SlaveAddress, Address,
195                                   wdata[0]);
196                     }
197                     break;
198           case AML_FIELD_ATTRIB_BLOCK:
199                     if (action == ACPI_READ) {
200                               count = 32;
201                               val = SMBUS_BREAD(dev, sb->SlaveAddress, Address,
202                                   &count, buf);
203                               if (val == 0) {
204                                         gsb->len = count;
205                                         memcpy(gsb->data, buf, count);
206                               }
207                     } else {
208                               memcpy(buf, gsb->data, gsb->len);
209                               count = gsb->len;
210                               val = SMBUS_BWRITE(dev, sb->SlaveAddress, Address,
211                                   count, buf);
212                     }
213                     break;
214           case AML_FIELD_ATTRIB_BYTES:
215                     if (action == ACPI_READ) {
216                               cnt = 0;
217                               val = SMBUS_TRANS(dev, sb->SlaveAddress, Address,
218                                   SMB_TRANS_NOCNT | SMB_TRANS_7BIT, NULL, 0,
219                                   buf, info->AccessLength, &cnt);
220                               if (val == 0)
221                                         memcpy(gsb->data, buf, cnt);
222                     } else {
223                               memcpy(buf, gsb->data, info->AccessLength);
224                               val = SMBUS_TRANS(dev, sb->SlaveAddress, Address,
225                                   SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
226                                   buf, info->AccessLength, NULL, 0, NULL);
227                     }
228                     break;
229           default:
230                     device_printf(dev, "protocol(0x%02x) is not supported.\n",
231                         accessor_type);
232                     s = AE_BAD_PARAMETER;
233                     goto err;
234           }
235 
236           gsb->status = val;
237 
238 err:
239           ACPI_FREE(Resource);
240 
241           return (s);
242 }
243 
244 static int
smbus_acpi_probe(device_t dev)245 smbus_acpi_probe(device_t dev)
246 {
247           if (acpi_get_handle(device_get_parent(dev)) == NULL)
248                     return (ENXIO);
249 
250           device_set_desc(dev, "ACPI I2cSerialBus backend");
251 
252           return (0);
253 }
254 
255 static int
smbus_acpi_attach(device_t dev)256 smbus_acpi_attach(device_t dev)
257 {
258           struct smbus_acpi_softc *sc = device_get_softc(dev);
259 
260           sc->dev = dev;
261           sc->parent = device_get_parent(dev);
262 
263           smbus_acpi_install_address_space_handler(sc);
264 
265           return (0);
266 }
267 
268 static int
smbus_acpi_detach(device_t dev)269 smbus_acpi_detach(device_t dev)
270 {
271           struct smbus_acpi_softc *sc = device_get_softc(dev);
272 
273           smbus_acpi_remove_address_space_handler(sc);
274 
275           return (0);
276 }
277 
278 static device_method_t smbacpi_methods[] = {
279           /* Device interface */
280           DEVMETHOD(device_probe, smbus_acpi_probe),
281           DEVMETHOD(device_attach, smbus_acpi_attach),
282           DEVMETHOD(device_detach, smbus_acpi_detach),
283 
284           DEVMETHOD_END
285 };
286 
287 static driver_t smbacpi_driver = {
288           "smbacpi",
289           smbacpi_methods,
290           sizeof(struct smbus_acpi_softc)
291 };
292 
293 static devclass_t smbacpi_devclass;
294 
295 DRIVER_MODULE(smbacpi, ig4iic, smbacpi_driver, smbacpi_devclass,
296     NULL, NULL);
297 MODULE_DEPEND(smbacpi, acpi, 1, 1, 1);
298 MODULE_DEPEND(smbacpi, smbus, 1, 1, 1);
299 MODULE_VERSION(smbacpi, 1);
300