OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [acpi/] [sbshc.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * SMBus driver for ACPI Embedded Controller (v0.1)
3
 *
4
 * Copyright (c) 2007 Alexey Starikovskiy
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation version 2.
9
 */
10
 
11
#include <acpi/acpi_bus.h>
12
#include <acpi/acpi_drivers.h>
13
#include <acpi/actypes.h>
14
#include <linux/wait.h>
15
#include <linux/delay.h>
16
#include <linux/interrupt.h>
17
#include "sbshc.h"
18
 
19
#define ACPI_SMB_HC_CLASS       "smbus_host_controller"
20
#define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC"
21
 
22
struct acpi_smb_hc {
23
        struct acpi_ec *ec;
24
        struct mutex lock;
25
        wait_queue_head_t wait;
26
        u8 offset;
27
        u8 query_bit;
28
        smbus_alarm_callback callback;
29
        void *context;
30
};
31
 
32
static int acpi_smbus_hc_add(struct acpi_device *device);
33
static int acpi_smbus_hc_remove(struct acpi_device *device, int type);
34
 
35
static const struct acpi_device_id sbs_device_ids[] = {
36
        {"ACPI0001", 0},
37
        {"ACPI0005", 0},
38
        {"", 0},
39
};
40
 
41
MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
42
 
43
static struct acpi_driver acpi_smb_hc_driver = {
44
        .name = "smbus_hc",
45
        .class = ACPI_SMB_HC_CLASS,
46
        .ids = sbs_device_ids,
47
        .ops = {
48
                .add = acpi_smbus_hc_add,
49
                .remove = acpi_smbus_hc_remove,
50
                },
51
};
52
 
53
union acpi_smb_status {
54
        u8 raw;
55
        struct {
56
                u8 status:5;
57
                u8 reserved:1;
58
                u8 alarm:1;
59
                u8 done:1;
60
        } fields;
61
};
62
 
63
enum acpi_smb_status_codes {
64
        SMBUS_OK = 0,
65
        SMBUS_UNKNOWN_FAILURE = 0x07,
66
        SMBUS_DEVICE_ADDRESS_NACK = 0x10,
67
        SMBUS_DEVICE_ERROR = 0x11,
68
        SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12,
69
        SMBUS_UNKNOWN_ERROR = 0x13,
70
        SMBUS_DEVICE_ACCESS_DENIED = 0x17,
71
        SMBUS_TIMEOUT = 0x18,
72
        SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19,
73
        SMBUS_BUSY = 0x1a,
74
        SMBUS_PEC_ERROR = 0x1f,
75
};
76
 
77
enum acpi_smb_offset {
78
        ACPI_SMB_PROTOCOL = 0,   /* protocol, PEC */
79
        ACPI_SMB_STATUS = 1,    /* status */
80
        ACPI_SMB_ADDRESS = 2,   /* address */
81
        ACPI_SMB_COMMAND = 3,   /* command */
82
        ACPI_SMB_DATA = 4,      /* 32 data registers */
83
        ACPI_SMB_BLOCK_COUNT = 0x24,    /* number of data bytes */
84
        ACPI_SMB_ALARM_ADDRESS = 0x25,  /* alarm address */
85
        ACPI_SMB_ALARM_DATA = 0x26,     /* 2 bytes alarm data */
86
};
87
 
88
static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data)
89
{
90
        return ec_read(hc->offset + address, data);
91
}
92
 
93
static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data)
94
{
95
        return ec_write(hc->offset + address, data);
96
}
97
 
98
static inline int smb_check_done(struct acpi_smb_hc *hc)
99
{
100
        union acpi_smb_status status = {.raw = 0};
101
        smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw);
102
        return status.fields.done && (status.fields.status == SMBUS_OK);
103
}
104
 
105
static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout)
106
{
107
        if (wait_event_timeout(hc->wait, smb_check_done(hc),
108
                               msecs_to_jiffies(timeout)))
109
                return 0;
110
        else
111
                return -ETIME;
112
}
113
 
114
int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol, u8 address,
115
                    u8 command, u8 *data, u8 length)
116
{
117
        int ret = -EFAULT, i;
118
        u8 temp, sz = 0;
119
 
120
        mutex_lock(&hc->lock);
121
        if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
122
                goto end;
123
        if (temp) {
124
                ret = -EBUSY;
125
                goto end;
126
        }
127
        smb_hc_write(hc, ACPI_SMB_COMMAND, command);
128
        smb_hc_write(hc, ACPI_SMB_COMMAND, command);
129
        if (!(protocol & 0x01)) {
130
                smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length);
131
                for (i = 0; i < length; ++i)
132
                        smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]);
133
        }
134
        smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1);
135
        smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol);
136
        /*
137
         * Wait for completion. Save the status code, data size,
138
         * and data into the return package (if required by the protocol).
139
         */
140
        ret = wait_transaction_complete(hc, 1000);
141
        if (ret || !(protocol & 0x01))
142
                goto end;
143
        switch (protocol) {
144
        case SMBUS_RECEIVE_BYTE:
145
        case SMBUS_READ_BYTE:
146
                sz = 1;
147
                break;
148
        case SMBUS_READ_WORD:
149
                sz = 2;
150
                break;
151
        case SMBUS_READ_BLOCK:
152
                if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) {
153
                        ret = -EFAULT;
154
                        goto end;
155
                }
156
                sz &= 0x1f;
157
                break;
158
        }
159
        for (i = 0; i < sz; ++i)
160
                smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]);
161
      end:
162
        mutex_unlock(&hc->lock);
163
        return ret;
164
}
165
 
166
int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
167
                    u8 command, u8 *data)
168
{
169
        return acpi_smbus_transaction(hc, protocol, address, command, data, 0);
170
}
171
 
172
EXPORT_SYMBOL_GPL(acpi_smbus_read);
173
 
174
int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address,
175
                     u8 command, u8 *data, u8 length)
176
{
177
        return acpi_smbus_transaction(hc, protocol, address, command, data, length);
178
}
179
 
180
EXPORT_SYMBOL_GPL(acpi_smbus_write);
181
 
182
int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
183
                                 smbus_alarm_callback callback, void *context)
184
{
185
        mutex_lock(&hc->lock);
186
        hc->callback = callback;
187
        hc->context = context;
188
        mutex_unlock(&hc->lock);
189
        return 0;
190
}
191
 
192
EXPORT_SYMBOL_GPL(acpi_smbus_register_callback);
193
 
194
int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc)
195
{
196
        mutex_lock(&hc->lock);
197
        hc->callback = NULL;
198
        hc->context = NULL;
199
        mutex_unlock(&hc->lock);
200
        return 0;
201
}
202
 
203
EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback);
204
 
205
static inline void acpi_smbus_callback(void *context)
206
{
207
        struct acpi_smb_hc *hc = context;
208
        if (hc->callback)
209
                hc->callback(hc->context);
210
}
211
 
212
static int smbus_alarm(void *context)
213
{
214
        struct acpi_smb_hc *hc = context;
215
        union acpi_smb_status status;
216
        u8 address;
217
        if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw))
218
                return 0;
219
        /* Check if it is only a completion notify */
220
        if (status.fields.done)
221
                wake_up(&hc->wait);
222
        if (!status.fields.alarm)
223
                return 0;
224
        mutex_lock(&hc->lock);
225
        smb_hc_read(hc, ACPI_SMB_ALARM_ADDRESS, &address);
226
        status.fields.alarm = 0;
227
        smb_hc_write(hc, ACPI_SMB_STATUS, status.raw);
228
        /* We are only interested in events coming from known devices */
229
        switch (address >> 1) {
230
                case ACPI_SBS_CHARGER:
231
                case ACPI_SBS_MANAGER:
232
                case ACPI_SBS_BATTERY:
233
                        acpi_os_execute(OSL_GPE_HANDLER,
234
                                        acpi_smbus_callback, hc);
235
                default:;
236
        }
237
        mutex_unlock(&hc->lock);
238
        return 0;
239
}
240
 
241
typedef int (*acpi_ec_query_func) (void *data);
242
 
243
extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
244
                              acpi_handle handle, acpi_ec_query_func func,
245
                              void *data);
246
 
247
static int acpi_smbus_hc_add(struct acpi_device *device)
248
{
249
        int status;
250
        unsigned long val;
251
        struct acpi_smb_hc *hc;
252
 
253
        if (!device)
254
                return -EINVAL;
255
 
256
        status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
257
        if (ACPI_FAILURE(status)) {
258
                printk(KERN_ERR PREFIX "error obtaining _EC.\n");
259
                return -EIO;
260
        }
261
 
262
        strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
263
        strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
264
 
265
        hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
266
        if (!hc)
267
                return -ENOMEM;
268
        mutex_init(&hc->lock);
269
        init_waitqueue_head(&hc->wait);
270
 
271
        hc->ec = acpi_driver_data(device->parent);
272
        hc->offset = (val >> 8) & 0xff;
273
        hc->query_bit = val & 0xff;
274
        acpi_driver_data(device) = hc;
275
 
276
        acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
277
        printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n",
278
                hc->ec, hc->offset, hc->query_bit);
279
 
280
        return 0;
281
}
282
 
283
extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
284
 
285
static int acpi_smbus_hc_remove(struct acpi_device *device, int type)
286
{
287
        struct acpi_smb_hc *hc;
288
 
289
        if (!device)
290
                return -EINVAL;
291
 
292
        hc = acpi_driver_data(device);
293
        acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
294
        kfree(hc);
295
        return 0;
296
}
297
 
298
static int __init acpi_smb_hc_init(void)
299
{
300
        int result;
301
 
302
        result = acpi_bus_register_driver(&acpi_smb_hc_driver);
303
        if (result < 0)
304
                return -ENODEV;
305
        return 0;
306
}
307
 
308
static void __exit acpi_smb_hc_exit(void)
309
{
310
        acpi_bus_unregister_driver(&acpi_smb_hc_driver);
311
}
312
 
313
module_init(acpi_smb_hc_init);
314
module_exit(acpi_smb_hc_exit);
315
 
316
MODULE_LICENSE("GPL");
317
MODULE_AUTHOR("Alexey Starikovskiy");
318
MODULE_DESCRIPTION("ACPI SMBus HC driver");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.