Rework the tegra slave controller init to look more like in tegra-i2c.c. This makes the nvec init reliable. Also add de-init of the slave to be used during suspend. Signed-off-by: Marc Dietrich <marvin24@xxxxxx> --- drivers/staging/nvec/nvec.c | 207 ++++++++++++++++++++++--------------------- 1 files changed, 105 insertions(+), 102 deletions(-) diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 2d99f8a..b2f03bb 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -28,65 +28,68 @@ #include <linux/input.h> #include <linux/workqueue.h> #include <linux/clk.h> -#include <mach/iomap.h> -#include <mach/clk.h> + #include <linux/semaphore.h> #include <linux/list.h> #include <linux/notifier.h> #include <linux/platform_device.h> #include <linux/mfd/core.h> + +#include <mach/iomap.h> +#include <mach/clk.h> + #include "nvec.h" -static unsigned char EC_DISABLE_EVENT_REPORTING[] = {'\x04','\x00','\x00'}; -static unsigned char EC_ENABLE_EVENT_REPORTING[] = {'\x04','\x00','\x01'}; -static unsigned char EC_GET_FIRMWARE_VERSION[] = {'\x07','\x15'}; +static const unsigned char EC_DISABLE_EVENT_REPORTING[] = { '\x04', '\x00', '\x00' }; +static const unsigned char EC_ENABLE_EVENT_REPORTING[] = { '\x04', '\x00', '\x01' }; +static const unsigned char EC_GET_FIRMWARE_VERSION[] = { '\x07', '\x15' }; static struct nvec_chip *nvec_power_handle; static struct mfd_cell nvec_devices[] = { { - .name = "nvec-kbd", - .id = 1, + .name = "nvec-kbd", + .id = 1, }, { - .name = "nvec-mouse", - .id = 1, + .name = "nvec-mouse", + .id = 1, }, { - .name = "nvec-power", - .id = 1, + .name = "nvec-power", + .id = 1, }, { - .name = "nvec-power", - .id = 2, + .name = "nvec-power", + .id = 2, }, }; int nvec_register_notifier(struct nvec_chip *nvec, struct notifier_block *nb, - unsigned int events) + unsigned int events) { return atomic_notifier_chain_register(&nvec->notifier_list, nb); } + EXPORT_SYMBOL_GPL(nvec_register_notifier); -static int nvec_status_notifier(struct notifier_block *nb, unsigned long event_type, - void *data) +static int nvec_status_notifier(struct notifier_block *nb, + unsigned long event_type, void *data) { unsigned char *msg = (unsigned char *)data; - int i; - if(event_type != NVEC_CNTL) + if (event_type != NVEC_CNTL) return NOTIFY_DONE; - printk("unhandled msg type %ld, payload: ", event_type); - for (i = 0; i < msg[1]; i++) - printk("%0x ", msg[i+2]); - printk("\n"); + printk(KERN_WARNING "unhandled msg type %ld\n", event_type); + print_hex_dump(KERN_WARNING, "payload: ", DUMP_PREFIX_NONE, 16, 1, + msg, msg[1] + 2, true); return NOTIFY_OK; } -void nvec_write_async(struct nvec_chip *nvec, unsigned char *data, short size) +void nvec_write_async(struct nvec_chip *nvec, const unsigned char *data, + short size) { struct nvec_msg *msg = kzalloc(sizeof(struct nvec_msg), GFP_NOWAIT); @@ -101,48 +104,47 @@ void nvec_write_async(struct nvec_chip *nvec, unsigned char *data, short size) gpio_set_value(nvec->gpio, 0); } + EXPORT_SYMBOL(nvec_write_async); static void nvec_request_master(struct work_struct *work) { struct nvec_chip *nvec = container_of(work, struct nvec_chip, tx_work); - if(!list_empty(&nvec->tx_data)) { + if (!list_empty(&nvec->tx_data)) { gpio_set_value(nvec->gpio, 0); } } static int parse_msg(struct nvec_chip *nvec, struct nvec_msg *msg) { - int i; - - if((msg->data[0] & 1<<7) == 0 && msg->data[3]) { - dev_err(nvec->dev, "ec responded %02x %02x %02x %02x\n", msg->data[0], - msg->data[1], msg->data[2], msg->data[3]); + if ((msg->data[0] & 1 << 7) == 0 && msg->data[3]) { + dev_err(nvec->dev, "ec responded %02x %02x %02x %02x\n", + msg->data[0], msg->data[1], msg->data[2], msg->data[3]); return -EINVAL; } - if ((msg->data[0] >> 7 ) == 1 && (msg->data[0] & 0x0f) == 5) - { - dev_warn(nvec->dev, "ec system event "); - for (i=0; i < msg->data[1]; i++) - dev_warn(nvec->dev, "%02x ", msg->data[2+i]); - dev_warn(nvec->dev, "\n"); + if ((msg->data[0] >> 7) == 1 && (msg->data[0] & 0x0f) == 5) { + print_hex_dump(KERN_WARNING, "ec system event ", DUMP_PREFIX_NONE, + 16, 1, msg->data, msg->data[1] + 2, true); } - atomic_notifier_call_chain(&nvec->notifier_list, msg->data[0] & 0x8f, msg->data); + atomic_notifier_call_chain(&nvec->notifier_list, msg->data[0] & 0x8f, + msg->data); return 0; } -static struct nvec_msg *nvec_write_sync(struct nvec_chip *nvec, unsigned char *data, short size) +static struct nvec_msg *nvec_write_sync(struct nvec_chip *nvec, + const unsigned char *data, short size) { down(&nvec->sync_write_mutex); nvec->sync_write_pending = (data[1] << 8) + data[0]; nvec_write_async(nvec, data, size); - dev_dbg(nvec->dev, "nvec_sync_write: 0x%04x\n", nvec->sync_write_pending); + dev_dbg(nvec->dev, "nvec_sync_write: 0x%04x\n", + nvec->sync_write_pending); wait_for_completion(&nvec->sync_write); dev_dbg(nvec->dev, "nvec_sync_write: pong!\n"); @@ -157,21 +159,21 @@ static void nvec_dispatch(struct work_struct *work) struct nvec_chip *nvec = container_of(work, struct nvec_chip, rx_work); struct nvec_msg *msg; - while(!list_empty(&nvec->rx_data)) - { + while (!list_empty(&nvec->rx_data)) { msg = list_first_entry(&nvec->rx_data, struct nvec_msg, node); list_del_init(&msg->node); - if(nvec->sync_write_pending == (msg->data[2] << 8) + msg->data[0]) - { + if (nvec->sync_write_pending == + (msg->data[2] << 8) + msg->data[0]) { dev_dbg(nvec->dev, "sync write completed!\n"); nvec->sync_write_pending = 0; nvec->last_sync_msg = msg; complete(&nvec->sync_write); } else { parse_msg(nvec, msg); - if((!msg) || (!msg->data)) - dev_warn(nvec->dev, "attempt access zero pointer\n"); + if ((!msg) || (!msg->data)) + dev_warn(nvec->dev, + "attempt access zero pointer\n"); else { kfree(msg->data); kfree(msg); @@ -191,20 +193,13 @@ static irqreturn_t nvec_interrupt(int irq, void *dev) status = readl(base + I2C_SL_STATUS); - if(!(status & I2C_SL_IRQ)) - { + if (!(status & I2C_SL_IRQ)) { dev_warn(nvec->dev, "nvec Spurious IRQ\n"); - //Yup, handled. ahum. goto handled; } - if(status & END_TRANS && !(status & RCVD)) - { - //Reenable IRQ only when even has been sent - //printk("Write sequence ended !\n"); - //parse_msg(nvec); + if (status & END_TRANS && !(status & RCVD)) { nvec->state = NVEC_WAIT; - if(nvec->rx->size > 1) - { + if (nvec->rx->size > 1) { list_add_tail(&nvec->rx->node, &nvec->rx_data); schedule_work(&nvec->rx_work); } else { @@ -212,41 +207,33 @@ static irqreturn_t nvec_interrupt(int irq, void *dev) kfree(nvec->rx); } return IRQ_HANDLED; - } else if(status & RNW) - { - // Work around for AP20 New Slave Hw Bug. Give 1us extra. - // nvec/smbus/nvec_i2c_transport.c in NV`s crap for reference - if(status & RCVD) + } else if (status & RNW) { + if (status & RCVD) udelay(3); - if(status & RCVD) - { + if (status & RCVD) { nvec->state = NVEC_WRITE; - //Master wants something from us. New communication -// dev_dbg(nvec->dev, "New read comm!\n"); } else { - //Master wants something from us from a communication we've already started -// dev_dbg(nvec->dev, "Read comm cont !\n"); +/* dev_dbg(nvec->dev, "Read comm cont !\n"); */ } - //if(msg_pos<msg_size) { - if(list_empty(&nvec->tx_data)) - { + if (list_empty(&nvec->tx_data)) { dev_err(nvec->dev, "nvec empty tx - sending no-op\n"); to_send = 0x8a; nvec_write_async(nvec, "\x07\x02", 2); -// to_send = 0x01; } else { - msg = list_first_entry(&nvec->tx_data, struct nvec_msg, node); - if(msg->pos < msg->size) { + msg = + list_first_entry(&nvec->tx_data, struct nvec_msg, + node); + if (msg->pos < msg->size) { to_send = msg->data[msg->pos]; msg->pos++; } else { - dev_err(nvec->dev, "nvec crap! %d\n", msg->size); + dev_err(nvec->dev, "nvec crap! %d\n", + msg->size); to_send = 0x01; } - if(msg->pos >= msg->size) - { + if (msg->pos >= msg->size) { list_del_init(&msg->node); kfree(msg->data); kfree(msg); @@ -263,14 +250,13 @@ static irqreturn_t nvec_interrupt(int irq, void *dev) goto handled; } else { received = readl(base + I2C_SL_RCVD); - //Workaround? - if(status & RCVD) { + + if (status & RCVD) { writel(0, base + I2C_SL_RCVD); goto handled; } - if (nvec->state == NVEC_WAIT) - { + if (nvec->state == NVEC_WAIT) { nvec->state = NVEC_READ; msg = kzalloc(sizeof(struct nvec_msg), GFP_NOWAIT); msg->data = kzalloc(32, GFP_NOWAIT); @@ -284,7 +270,8 @@ static irqreturn_t nvec_interrupt(int irq, void *dev) msg->data[msg->pos] = received; msg->pos++; msg->size = msg->pos; - dev_dbg(nvec->dev, "Got %02lx from Master (pos: %d)!\n", received, msg->pos); + dev_dbg(nvec->dev, "Got %02lx from Master (pos: %d)!\n", + received, msg->pos); } handled: return IRQ_HANDLED; @@ -300,18 +287,30 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec) udelay(2); tegra_periph_reset_deassert(nvec->i2c_clk); - writel(nvec->i2c_addr>>1, nvec->base + I2C_SL_ADDR1); - writel(0, nvec->base + I2C_SL_ADDR2); - - writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT); val = I2C_CNFG_NEW_MASTER_SFM | I2C_CNFG_PACKET_MODE_EN | - (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); + (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); writel(val, nvec->base + I2C_CNFG); + + clk_set_rate(nvec->i2c_clk, 8 * 80000); + writel(I2C_SL_NEWL, nvec->base + I2C_SL_CNFG); + writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT); + + writel(nvec->i2c_addr>>1, nvec->base + I2C_SL_ADDR1); + writel(0, nvec->base + I2C_SL_ADDR2); + + enable_irq(nvec->irq); clk_disable(nvec->i2c_clk); } +static void nvec_disable_i2c_slave(struct nvec_chip *nvec) +{ + disable_irq(nvec->irq); + writel(I2C_SL_NEWL | I2C_SL_NACK, nvec->base + I2C_SL_CNFG); + clk_disable(nvec->i2c_clk); +} + static void nvec_power_off(void) { nvec_write_async(nvec_power_handle, EC_DISABLE_EVENT_REPORTING, 3); @@ -330,7 +329,7 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev) void __iomem *base; nvec = kzalloc(sizeof(struct nvec_chip), GFP_KERNEL); - if(nvec == NULL) { + if (nvec == NULL) { dev_err(&pdev->dev, "failed to reserve memory\n"); return -ENOMEM; } @@ -370,16 +369,13 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev) goto err_iounmap; } - clk_enable(i2c_clk); - clk_set_rate(i2c_clk, 8*80000); - nvec->base = base; nvec->irq = res->start; nvec->i2c_clk = i2c_clk; /* Set the gpio to low when we've got something to say */ err = gpio_request(nvec->gpio, "nvec gpio"); - if(err < 0) + if (err < 0) dev_err(nvec->dev, "couldn't request gpio\n"); ATOMIC_INIT_NOTIFIER_HEAD(&nvec->notifier_list); @@ -396,15 +392,18 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev) dev_err(nvec->dev, "couldn't request irq\n"); goto failed; } + disable_irq(nvec->irq); tegra_init_i2c_slave(nvec); + clk_enable(i2c_clk); + gpio_direction_output(nvec->gpio, 1); gpio_set_value(nvec->gpio, 1); /* enable event reporting */ nvec_write_async(nvec, EC_ENABLE_EVENT_REPORTING, - sizeof(EC_ENABLE_EVENT_REPORTING)); + sizeof(EC_ENABLE_EVENT_REPORTING)); nvec->nvec_status_notifier.notifier_call = nvec_status_notifier; nvec_register_notifier(nvec, &nvec->nvec_status_notifier, 0); @@ -414,17 +413,17 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev) /* Get Firmware Version */ msg = nvec_write_sync(nvec, EC_GET_FIRMWARE_VERSION, - sizeof(EC_GET_FIRMWARE_VERSION)); + sizeof(EC_GET_FIRMWARE_VERSION)); dev_warn(nvec->dev, "ec firmware version %02x.%02x.%02x / %02x\n", - msg->data[4], msg->data[5], msg->data[6], msg->data[7]); + msg->data[4], msg->data[5], msg->data[6], msg->data[7]); kfree(msg->data); kfree(msg); ret = mfd_add_devices(nvec->dev, -1, nvec_devices, - ARRAY_SIZE(nvec_devices), base, 0); - if(ret) + ARRAY_SIZE(nvec_devices), base, 0); + if (ret) dev_err(nvec->dev, "error adding subdevices\n"); /* unmute speakers? */ @@ -468,12 +467,13 @@ static int tegra_nvec_suspend(struct platform_device *pdev, pm_message_t state) dev_dbg(nvec->dev, "suspending\n"); nvec_write_async(nvec, EC_DISABLE_EVENT_REPORTING, 3); nvec_write_async(nvec, "\x04\x02", 2); + nvec_disable_i2c_slave(nvec); return 0; } -static int tegra_nvec_resume(struct platform_device *pdev) { - +static int tegra_nvec_resume(struct platform_device *pdev) +{ struct nvec_chip *nvec = platform_get_drvdata(pdev); dev_dbg(nvec->dev, "resuming\n"); @@ -488,13 +488,12 @@ static int tegra_nvec_resume(struct platform_device *pdev) { #define tegra_nvec_resume NULL #endif -static struct platform_driver nvec_device_driver = -{ - .probe = tegra_nvec_probe, - .remove = __devexit_p(tegra_nvec_remove), +static struct platform_driver nvec_device_driver = { + .probe = tegra_nvec_probe, + .remove = __devexit_p(tegra_nvec_remove), .suspend = tegra_nvec_suspend, - .resume = tegra_nvec_resume, - .driver = { + .resume = tegra_nvec_resume, + .driver = { .name = "nvec", .owner = THIS_MODULE, } @@ -506,4 +505,8 @@ static int __init tegra_nvec_init(void) } module_init(tegra_nvec_init); + MODULE_ALIAS("platform:nvec"); +MODULE_DESCRIPTION("NVIDIA compliant embedded controller interface"); +MODULE_AUTHOR("Marc Dietrich <marvin24@xxxxxx>"); +MODULE_LICENSE("GPL"); -- 1.7.4.1 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/devel