2 * NVEC: NVIDIA compliant embedded controller interface
4 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
6 * Authors: Pierre-Hugues Husson <phhusson@free.fr>
7 * Ilya Petrov <ilya.muromec@gmail.com>
8 * Marc Dietrich <marvin24@gmx.de>
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file "COPYING" in the main directory of this archive
20 #include <linux/atomic.h>
21 #include <linux/completion.h>
22 #include <linux/interrupt.h>
24 #include <linux/irq.h>
25 #include <linux/slab.h>
26 #include <linux/gpio.h>
27 #include <linux/serio.h>
28 #include <linux/delay.h>
29 #include <linux/input.h>
30 #include <linux/workqueue.h>
31 #include <linux/clk.h>
33 #include <linux/semaphore.h>
34 #include <linux/list.h>
35 #include <linux/notifier.h>
36 #include <linux/platform_device.h>
37 #include <linux/mfd/core.h>
39 #include <mach/iomap.h>
44 static const unsigned char EC_DISABLE_EVENT_REPORTING[3] = "\x04\x00\x00";
45 static const unsigned char EC_ENABLE_EVENT_REPORTING[3] = "\x04\x00\x01";
46 static const unsigned char EC_GET_FIRMWARE_VERSION[2] = "\x07\x15";
48 static struct nvec_chip *nvec_power_handle;
50 static struct mfd_cell nvec_devices[] = {
73 int nvec_register_notifier(struct nvec_chip *nvec, struct notifier_block *nb,
76 return atomic_notifier_chain_register(&nvec->notifier_list, nb);
78 EXPORT_SYMBOL_GPL(nvec_register_notifier);
80 static int nvec_status_notifier(struct notifier_block *nb,
81 unsigned long event_type, void *data)
83 unsigned char *msg = (unsigned char *)data;
85 if (event_type != NVEC_CNTL)
88 printk(KERN_WARNING "unhandled msg type %ld\n", event_type);
89 print_hex_dump(KERN_WARNING, "payload: ", DUMP_PREFIX_NONE, 16, 1,
90 msg, msg[1] + 2, true);
95 static struct nvec_msg *nvec_msg_alloc(struct nvec_chip *nvec)
99 for (i = 0; i < NVEC_POOL_SIZE; i++) {
100 if (atomic_xchg(&nvec->msg_pool[i].used, 1) == 0) {
101 dev_vdbg(nvec->dev, "INFO: Allocate %i\n", i);
102 return &nvec->msg_pool[i];
106 dev_err(nvec->dev, "could not allocate buffer\n");
111 static void nvec_msg_free(struct nvec_chip *nvec, struct nvec_msg *msg)
113 dev_vdbg(nvec->dev, "INFO: Free %ti\n", msg - nvec->msg_pool);
114 atomic_set(&msg->used, 0);
118 * nvec_msg_is_event - Return %true if @msg is an event
121 static bool nvec_msg_is_event(struct nvec_msg *msg)
123 return msg->data[0] >> 7;
127 * nvec_msg_size - Get the size of a message
128 * @msg: The message to get the size for
130 * This only works for received messages, not for outgoing messages.
132 static size_t nvec_msg_size(struct nvec_msg *msg)
134 bool is_event = nvec_msg_is_event(msg);
135 int event_length = (msg->data[0] & 0x60) >> 5;
137 /* for variable size, payload size in byte 1 + count (1) + cmd (1) */
138 if (!is_event || event_length == NVEC_VAR_SIZE)
139 return (msg->pos || msg->size) ? (msg->data[1] + 2) : 0;
140 else if (event_length == NVEC_2BYTES)
142 else if (event_length == NVEC_3BYTES)
148 static void nvec_gpio_set_value(struct nvec_chip *nvec, int value)
150 dev_dbg(nvec->dev, "GPIO changed from %u to %u\n",
151 gpio_get_value(nvec->gpio), value);
152 gpio_set_value(nvec->gpio, value);
155 void nvec_write_async(struct nvec_chip *nvec, const unsigned char *data,
158 struct nvec_msg *msg = kzalloc(sizeof(struct nvec_msg), GFP_NOWAIT);
160 msg->data = kzalloc(size, GFP_NOWAIT);
162 memcpy(msg->data + 1, data, size);
163 msg->size = size + 1;
165 INIT_LIST_HEAD(&msg->node);
167 list_add_tail(&msg->node, &nvec->tx_data);
169 gpio_set_value(nvec->gpio, 0);
171 EXPORT_SYMBOL(nvec_write_async);
173 static void nvec_request_master(struct work_struct *work)
175 struct nvec_chip *nvec = container_of(work, struct nvec_chip, tx_work);
177 if (!list_empty(&nvec->tx_data))
178 gpio_set_value(nvec->gpio, 0);
181 static int parse_msg(struct nvec_chip *nvec, struct nvec_msg *msg)
183 if ((msg->data[0] & 1 << 7) == 0 && msg->data[3]) {
184 dev_err(nvec->dev, "ec responded %02x %02x %02x %02x\n",
185 msg->data[0], msg->data[1], msg->data[2], msg->data[3]);
189 if ((msg->data[0] >> 7) == 1 && (msg->data[0] & 0x0f) == 5)
190 print_hex_dump(KERN_WARNING, "ec system event ",
191 DUMP_PREFIX_NONE, 16, 1, msg->data,
192 msg->data[1] + 2, true);
194 atomic_notifier_call_chain(&nvec->notifier_list, msg->data[0] & 0x8f,
200 static struct nvec_msg *nvec_write_sync(struct nvec_chip *nvec,
201 const unsigned char *data, short size)
203 down(&nvec->sync_write_mutex);
205 nvec->sync_write_pending = (data[1] << 8) + data[0];
206 nvec_write_async(nvec, data, size);
208 dev_dbg(nvec->dev, "nvec_sync_write: 0x%04x\n",
209 nvec->sync_write_pending);
210 wait_for_completion(&nvec->sync_write);
211 dev_dbg(nvec->dev, "nvec_sync_write: pong!\n");
213 up(&nvec->sync_write_mutex);
215 return nvec->last_sync_msg;
219 static void nvec_dispatch(struct work_struct *work)
221 struct nvec_chip *nvec = container_of(work, struct nvec_chip, rx_work);
222 struct nvec_msg *msg;
224 while (!list_empty(&nvec->rx_data)) {
225 msg = list_first_entry(&nvec->rx_data, struct nvec_msg, node);
226 list_del_init(&msg->node);
228 if (nvec->sync_write_pending ==
229 (msg->data[2] << 8) + msg->data[0]) {
230 dev_dbg(nvec->dev, "sync write completed!\n");
231 nvec->sync_write_pending = 0;
232 nvec->last_sync_msg = msg;
233 complete(&nvec->sync_write);
235 parse_msg(nvec, msg);
236 if ((!msg) || (!msg->data))
238 "attempt access zero pointer\n");
247 static irqreturn_t nvec_interrupt(int irq, void *dev)
249 unsigned long status;
250 unsigned long received;
251 unsigned char to_send;
252 struct nvec_msg *msg;
253 struct nvec_chip *nvec = (struct nvec_chip *)dev;
254 void __iomem *base = nvec->base;
256 status = readl(base + I2C_SL_STATUS);
258 if (!(status & I2C_SL_IRQ)) {
259 dev_warn(nvec->dev, "nvec Spurious IRQ\n");
262 if (status & END_TRANS && !(status & RCVD)) {
263 nvec->state = NVEC_WAIT;
264 if (nvec->rx->size > 1) {
265 list_add_tail(&nvec->rx->node, &nvec->rx_data);
266 schedule_work(&nvec->rx_work);
268 kfree(nvec->rx->data);
272 } else if (status & RNW) {
277 nvec->state = NVEC_WRITE;
279 if (list_empty(&nvec->tx_data)) {
280 dev_err(nvec->dev, "nvec empty tx - sending no-op\n");
282 nvec_write_async(nvec, "\x07\x02", 2);
285 list_first_entry(&nvec->tx_data, struct nvec_msg,
287 if (msg->pos < msg->size) {
288 to_send = msg->data[msg->pos];
291 dev_err(nvec->dev, "nvec crap! %d\n",
296 if (msg->pos >= msg->size) {
297 list_del_init(&msg->node);
300 schedule_work(&nvec->tx_work);
301 nvec->state = NVEC_WAIT;
304 writel(to_send, base + I2C_SL_RCVD);
306 gpio_set_value(nvec->gpio, 1);
308 dev_dbg(nvec->dev, "nvec sent %x\n", to_send);
312 received = readl(base + I2C_SL_RCVD);
315 writel(0, base + I2C_SL_RCVD);
319 if (nvec->state == NVEC_WAIT) {
320 nvec->state = NVEC_READ;
321 msg = kzalloc(sizeof(struct nvec_msg), GFP_NOWAIT);
322 msg->data = kzalloc(32, GFP_NOWAIT);
323 INIT_LIST_HEAD(&msg->node);
328 BUG_ON(msg->pos > 32);
330 msg->data[msg->pos] = received;
332 msg->size = msg->pos;
333 dev_dbg(nvec->dev, "Got %02lx from Master (pos: %d)!\n",
340 static void tegra_init_i2c_slave(struct nvec_chip *nvec)
344 clk_enable(nvec->i2c_clk);
346 tegra_periph_reset_assert(nvec->i2c_clk);
348 tegra_periph_reset_deassert(nvec->i2c_clk);
350 val = I2C_CNFG_NEW_MASTER_SFM | I2C_CNFG_PACKET_MODE_EN |
351 (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
352 writel(val, nvec->base + I2C_CNFG);
354 clk_set_rate(nvec->i2c_clk, 8 * 80000);
356 writel(I2C_SL_NEWL, nvec->base + I2C_SL_CNFG);
357 writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT);
359 writel(nvec->i2c_addr>>1, nvec->base + I2C_SL_ADDR1);
360 writel(0, nvec->base + I2C_SL_ADDR2);
362 enable_irq(nvec->irq);
364 clk_disable(nvec->i2c_clk);
367 static void nvec_disable_i2c_slave(struct nvec_chip *nvec)
369 disable_irq(nvec->irq);
370 writel(I2C_SL_NEWL | I2C_SL_NACK, nvec->base + I2C_SL_CNFG);
371 clk_disable(nvec->i2c_clk);
374 static void nvec_power_off(void)
376 nvec_write_async(nvec_power_handle, EC_DISABLE_EVENT_REPORTING, 3);
377 nvec_write_async(nvec_power_handle, "\x04\x01", 2);
380 static int __devinit tegra_nvec_probe(struct platform_device *pdev)
384 struct nvec_platform_data *pdata = pdev->dev.platform_data;
385 struct nvec_chip *nvec;
386 struct nvec_msg *msg;
387 struct resource *res;
388 struct resource *iomem;
391 nvec = kzalloc(sizeof(struct nvec_chip), GFP_KERNEL);
393 dev_err(&pdev->dev, "failed to reserve memory\n");
396 platform_set_drvdata(pdev, nvec);
397 nvec->dev = &pdev->dev;
398 nvec->gpio = pdata->gpio;
399 nvec->i2c_addr = pdata->i2c_addr;
401 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
403 dev_err(&pdev->dev, "no mem resource?\n");
407 iomem = request_mem_region(res->start, resource_size(res), pdev->name);
409 dev_err(&pdev->dev, "I2C region already claimed\n");
413 base = ioremap(iomem->start, resource_size(iomem));
415 dev_err(&pdev->dev, "Can't ioremap I2C region\n");
419 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
421 dev_err(&pdev->dev, "no irq resource?\n");
426 i2c_clk = clk_get_sys("tegra-i2c.2", NULL);
427 if (IS_ERR(i2c_clk)) {
428 dev_err(nvec->dev, "failed to get controller clock\n");
433 nvec->irq = res->start;
434 nvec->i2c_clk = i2c_clk;
436 /* Set the gpio to low when we've got something to say */
437 err = gpio_request(nvec->gpio, "nvec gpio");
439 dev_err(nvec->dev, "couldn't request gpio\n");
441 ATOMIC_INIT_NOTIFIER_HEAD(&nvec->notifier_list);
443 init_completion(&nvec->sync_write);
444 sema_init(&nvec->sync_write_mutex, 1);
445 INIT_LIST_HEAD(&nvec->tx_data);
446 INIT_LIST_HEAD(&nvec->rx_data);
447 INIT_WORK(&nvec->rx_work, nvec_dispatch);
448 INIT_WORK(&nvec->tx_work, nvec_request_master);
450 err = request_irq(nvec->irq, nvec_interrupt, 0, "nvec", nvec);
452 dev_err(nvec->dev, "couldn't request irq\n");
455 disable_irq(nvec->irq);
457 tegra_init_i2c_slave(nvec);
461 gpio_direction_output(nvec->gpio, 1);
462 gpio_set_value(nvec->gpio, 1);
464 /* enable event reporting */
465 nvec_write_async(nvec, EC_ENABLE_EVENT_REPORTING,
466 sizeof(EC_ENABLE_EVENT_REPORTING));
468 nvec->nvec_status_notifier.notifier_call = nvec_status_notifier;
469 nvec_register_notifier(nvec, &nvec->nvec_status_notifier, 0);
471 nvec_power_handle = nvec;
472 pm_power_off = nvec_power_off;
474 /* Get Firmware Version */
475 msg = nvec_write_sync(nvec, EC_GET_FIRMWARE_VERSION,
476 sizeof(EC_GET_FIRMWARE_VERSION));
478 dev_warn(nvec->dev, "ec firmware version %02x.%02x.%02x / %02x\n",
479 msg->data[4], msg->data[5], msg->data[6], msg->data[7]);
484 ret = mfd_add_devices(nvec->dev, -1, nvec_devices,
485 ARRAY_SIZE(nvec_devices), base, 0);
487 dev_err(nvec->dev, "error adding subdevices\n");
489 /* unmute speakers? */
490 nvec_write_async(nvec, "\x0d\x10\x59\x95", 4);
492 /* enable lid switch event */
493 nvec_write_async(nvec, "\x01\x01\x01\x00\x00\x02\x00", 7);
495 /* enable power button event */
496 nvec_write_async(nvec, "\x01\x01\x01\x00\x00\x80\x00", 7);
507 static int __devexit tegra_nvec_remove(struct platform_device *pdev)
509 struct nvec_chip *nvec = platform_get_drvdata(pdev);
511 nvec_write_async(nvec, EC_DISABLE_EVENT_REPORTING, 3);
512 mfd_remove_devices(nvec->dev);
513 free_irq(nvec->irq, &nvec_interrupt);
515 gpio_free(nvec->gpio);
523 static int tegra_nvec_suspend(struct platform_device *pdev, pm_message_t state)
525 struct nvec_chip *nvec = platform_get_drvdata(pdev);
527 dev_dbg(nvec->dev, "suspending\n");
528 nvec_write_async(nvec, EC_DISABLE_EVENT_REPORTING, 3);
529 nvec_write_async(nvec, "\x04\x02", 2);
530 nvec_disable_i2c_slave(nvec);
535 static int tegra_nvec_resume(struct platform_device *pdev)
537 struct nvec_chip *nvec = platform_get_drvdata(pdev);
539 dev_dbg(nvec->dev, "resuming\n");
540 tegra_init_i2c_slave(nvec);
541 nvec_write_async(nvec, EC_ENABLE_EVENT_REPORTING, 3);
547 #define tegra_nvec_suspend NULL
548 #define tegra_nvec_resume NULL
551 static struct platform_driver nvec_device_driver = {
552 .probe = tegra_nvec_probe,
553 .remove = __devexit_p(tegra_nvec_remove),
554 .suspend = tegra_nvec_suspend,
555 .resume = tegra_nvec_resume,
558 .owner = THIS_MODULE,
562 static int __init tegra_nvec_init(void)
564 return platform_driver_register(&nvec_device_driver);
567 module_init(tegra_nvec_init);
569 MODULE_ALIAS("platform:nvec");
570 MODULE_DESCRIPTION("NVIDIA compliant embedded controller interface");
571 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
572 MODULE_LICENSE("GPL");