diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-09-10 14:53:27 +0200 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-09-10 15:04:19 +0200 |
commit | d5bbf34613a877dbe3da847fa0432da8c6721e73 (patch) | |
tree | 902a90fd7eda61aad7abae9c35b0da2e7a786995 /drivers | |
parent | c6c1f7a2c194f1a2291a15c6691c0d6785f8976e (diff) | |
parent | 336961dd3cf9c39456dd9657e8f205718740c797 (diff) |
Merge branch 'l4t/l4t-r16' into colibri
Merge with latest NVIDIA L4T R16.
Only real conflict concerning inverted VBUS gpio support.
Diffstat (limited to 'drivers')
373 files changed, 41048 insertions, 12066 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 4cbb7ce0a0f9..d7f4b63904ac 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -5,6 +5,7 @@ # Rewritten to use lists instead of if-statements. # +obj-$(CONFIG_CPUQUIET_FRAMEWORK)+= cpuquiet/ obj-y += gpio/ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PARISC) += parisc/ diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index cc7b4f764d63..2691907757c4 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -26,10 +26,11 @@ struct regmap_irq_chip_data { int irq_base; - void *status_reg_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; + + unsigned int irq_reg_stride; }; static inline const @@ -59,7 +60,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) */ for (i = 0; i < d->chip->num_regs; i++) { ret = regmap_update_bits(d->map, d->chip->mask_base + - (i * map->reg_stride), + (i * map->reg_stride * + d->irq_reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", @@ -101,18 +103,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; int ret, i; - u8 *buf8 = data->status_reg_buf; - u16 *buf16 = data->status_reg_buf; - u32 *buf32 = data->status_reg_buf; bool handled = false; - ret = regmap_bulk_read(map, chip->status_base, data->status_reg_buf, - chip->num_regs); - if (ret != 0) { - dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); - return IRQ_NONE; - } - /* * Ignore masked IRQs and ack if we need to; we ack early so * there is no race between handling and acknowleding the @@ -121,18 +113,13 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) * doing a write per register. */ for (i = 0; i < data->chip->num_regs; i++) { - switch (map->format.val_bytes) { - case 1: - data->status_buf[i] = buf8[i]; - break; - case 2: - data->status_buf[i] = buf16[i]; - break; - case 4: - data->status_buf[i] = buf32[i]; - break; - default: - BUG(); + ret = regmap_read(map, chip->mask_base + (i * map->reg_stride + * data->irq_reg_stride), + &data->status_buf[i]); + + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); return IRQ_NONE; } @@ -140,7 +127,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) if (data->status_buf[i] && chip->ack_base) { ret = regmap_write(map, chip->ack_base + - (i * map->reg_stride), + (i * map->reg_stride * + data->irq_reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", @@ -210,11 +198,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d->status_buf) goto err_alloc; - d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs, - GFP_KERNEL); - if (!d->status_reg_buf) - goto err_alloc; - d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->mask_buf) @@ -228,6 +211,12 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->map = map; d->chip = chip; d->irq_base = irq_base; + + if (chip->irq_reg_stride) + d->irq_reg_stride = chip->irq_reg_stride; + else + d->irq_reg_stride = 1; + mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) @@ -237,7 +226,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride + * d->irq_reg_stride), d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", @@ -276,7 +266,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, err_alloc: kfree(d->mask_buf_def); kfree(d->mask_buf); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); return ret; @@ -297,7 +286,6 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) free_irq(irq, d); kfree(d->mask_buf_def); kfree(d->mask_buf); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); } diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index e8d11b6630ee..0240f01714a1 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -97,12 +97,16 @@ void syscore_resume(void) list_for_each_entry(ops, &syscore_ops_list, node) if (ops->resume) { - if (initcall_debug) - pr_info("PM: Calling %pF\n", ops->resume); ops->resume(); WARN_ONCE(!irqs_disabled(), "Interrupts enabled after %pF\n", ops->resume); } + if (initcall_debug) { + list_for_each_entry(ops, &syscore_ops_list, node) + if (ops->resume) { + pr_info("PM: Called %pF\n", ops->resume); + } + } } EXPORT_SYMBOL_GPL(syscore_resume); #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/bluetooth/bluesleep.c b/drivers/bluetooth/bluesleep.c index 0e2ec0befbe3..910e510e0b7e 100644 --- a/drivers/bluetooth/bluesleep.c +++ b/drivers/bluetooth/bluesleep.c @@ -277,13 +277,28 @@ static int bluesleep_hci_event(struct notifier_block *this, case HCI_DEV_REG: if (!bluesleep_hdev) { bluesleep_hdev = hdev; - hu = (struct hci_uart *) hdev->driver_data; - state = (struct uart_state *) hu->tty->driver_data; - bsi->uport = state->uart_port; + if (bsi->has_ext_wake == 1) { + hu = (struct hci_uart *) hdev->driver_data; + state = (struct uart_state *) \ + hu->tty->driver_data; + bsi->uport = state->uart_port; + } /* if bluetooth started, start bluesleep*/ bluesleep_start(); } break; + case HCI_DEV_UP: +#if BT_ENABLE_IRQ_WAKE + if (enable_irq_wake(bsi->host_wake_irq)) + BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup interrupt"); +#endif + break; + case HCI_DEV_DOWN: +#if BT_ENABLE_IRQ_WAKE + if (disable_irq_wake(bsi->host_wake_irq)) + BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n"); +#endif + break; case HCI_DEV_UNREG: bluesleep_stop(); bluesleep_hdev = NULL; @@ -291,7 +306,8 @@ static int bluesleep_hci_event(struct notifier_block *this, /* if bluetooth stopped, stop bluesleep also */ break; case HCI_DEV_WRITE: - bluesleep_outgoing_data(); + if (bsi->has_ext_wake == 1) + bluesleep_outgoing_data(); break; } @@ -337,7 +353,8 @@ static void bluesleep_tx_timer_expire(unsigned long data) static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) { /* schedule a tasklet to handle the change in the host wake line */ - tasklet_schedule(&hostwake_task); + if (bsi->has_ext_wake == 1) + tasklet_schedule(&hostwake_task); return IRQ_HANDLED; } @@ -348,7 +365,6 @@ static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) */ static int bluesleep_start(void) { - int retval; unsigned long irq_flags; spin_lock_irqsave(&rw_lock, irq_flags); @@ -363,28 +379,23 @@ static int bluesleep_start(void) return -EBUSY; } - /* start the timer */ - mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); - /* assert BT_WAKE */ - if (bsi->has_ext_wake == 1) + if (bsi->has_ext_wake == 1) { + /* start the timer */ + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); gpio_set_value(bsi->ext_wake, 1); - set_bit(BT_EXT_WAKE, &flags); -#if BT_ENABLE_IRQ_WAKE - retval = enable_irq_wake(bsi->host_wake_irq); - if (retval < 0) { - BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup interrupt"); - goto fail; + wake_lock(&bsi->wake_lock); + set_bit(BT_EXT_WAKE, &flags); } -#endif + set_bit(BT_PROTO, &flags); - wake_lock(&bsi->wake_lock); return 0; fail: - del_timer(&tx_timer); + if (bsi->has_ext_wake == 1) + del_timer(&tx_timer); atomic_inc(&open_count); - return retval; + return 0; } /** @@ -400,25 +411,20 @@ static void bluesleep_stop(void) return; } /* assert BT_WAKE */ - if (bsi->has_ext_wake == 1) + if (bsi->has_ext_wake == 1) { gpio_set_value(bsi->ext_wake, 1); - set_bit(BT_EXT_WAKE, &flags); - del_timer(&tx_timer); - clear_bit(BT_PROTO, &flags); - - if (test_bit(BT_ASLEEP, &flags)) { - clear_bit(BT_ASLEEP, &flags); - hsuart_power(1); + set_bit(BT_EXT_WAKE, &flags); + del_timer(&tx_timer); + wake_lock_timeout(&bsi->wake_lock, HZ / 2); + if (test_bit(BT_ASLEEP, &flags)) { + clear_bit(BT_ASLEEP, &flags); + hsuart_power(1); + } } + clear_bit(BT_PROTO, &flags); atomic_inc(&open_count); spin_unlock_irqrestore(&rw_lock, irq_flags); - -#if BT_ENABLE_IRQ_WAKE - if (disable_irq_wake(bsi->host_wake_irq)) - BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n"); -#endif - wake_lock_timeout(&bsi->wake_lock, HZ / 2); } /** * Read the <code>BT_WAKE</code> GPIO pin value via the proc interface. @@ -796,18 +802,19 @@ static int __init bluesleep_init(void) /* Initialize spinlock. */ spin_lock_init(&rw_lock); - /* Initialize timer */ - init_timer(&tx_timer); - tx_timer.function = bluesleep_tx_timer_expire; - tx_timer.data = 0; + /* assert bt wake */ + if (bsi->has_ext_wake == 1) { + /* Initialize timer */ + init_timer(&tx_timer); + tx_timer.function = bluesleep_tx_timer_expire; + tx_timer.data = 0; - /* initialize host wake tasklet */ - tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0); + /* initialize host wake tasklet */ + tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0); - /* assert bt wake */ - if (bsi->has_ext_wake == 1) gpio_set_value(bsi->ext_wake, 1); - set_bit(BT_EXT_WAKE, &flags); + set_bit(BT_EXT_WAKE, &flags); + } hci_register_notifier(&hci_event_nblock); return 0; @@ -831,16 +838,17 @@ static void __exit bluesleep_exit(void) return; /* assert bt wake */ - if (bsi->has_ext_wake == 1) + if (bsi->has_ext_wake == 1) { gpio_set_value(bsi->ext_wake, 1); - set_bit(BT_EXT_WAKE, &flags); + del_timer(&tx_timer); + if (test_bit(BT_ASLEEP, &flags)) + hsuart_power(1); + set_bit(BT_EXT_WAKE, &flags); + } if (test_bit(BT_PROTO, &flags)) { if (disable_irq_wake(bsi->host_wake_irq)) BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n"); free_irq(bsi->host_wake_irq, NULL); - del_timer(&tx_timer); - if (test_bit(BT_ASLEEP, &flags)) - hsuart_power(1); } hci_unregister_notifier(&hci_event_nblock); diff --git a/drivers/bluetooth/ti_bluesleep.c b/drivers/bluetooth/ti_bluesleep.c index d86fd261e676..2ab532410c72 100644 --- a/drivers/bluetooth/ti_bluesleep.c +++ b/drivers/bluetooth/ti_bluesleep.c @@ -31,37 +31,17 @@ */ #include <linux/module.h> /* kernel module definitions */ -#include <linux/errno.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/notifier.h> -#include <linux/proc_fs.h> -#include <linux/spinlock.h> -#include <linux/timer.h> -#include <linux/uaccess.h> -#include <linux/version.h> -#include <linux/workqueue.h> #include <linux/platform_device.h> -#include <linux/irq.h> -#include <linux/ioport.h> -#include <linux/param.h> -#include <linux/bitops.h> -#include <linux/termios.h> -#include <linux/wakelock.h> #include <mach/gpio.h> -#include <linux/serial_core.h> -#include <linux/tegra_uart.h> #include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> /* event notifications */ -#include "hci_uart.h" /* * Defines */ - #define VERSION "1.1" #define POLARITY_LOW 0 @@ -70,9 +50,7 @@ struct bluesleep_info { unsigned host_wake_irq; struct uart_port *uport; - struct wake_lock wake_lock; int irq_polarity; - int has_ext_wake; }; @@ -81,16 +59,6 @@ struct bluesleep_info { #define BT_ACTIVE 0x02 #define BT_SUSPEND 0x04 - -/* work function */ -static void hostwake_sleep_work(struct work_struct *work); - -/* work queue */ -DECLARE_DELAYED_WORK(ti_sleep_workqueue, hostwake_sleep_work); - -/* Macros for handling sleep work */ -#define hostwake_workqueue() schedule_delayed_work(&ti_sleep_workqueue, 0) - static struct bluesleep_info *bsi; /* module usage */ @@ -102,66 +70,6 @@ static atomic_t open_count = ATOMIC_INIT(1); /** Global state flags */ static unsigned long flags; -/** Tasklet to respond to change in hostwake line */ -static struct tasklet_struct hostwake_task; - -/** Lock for state transitions */ -static spinlock_t rw_lock; - -/* - * Local functions - */ -static void hsuart_power(int on) -{ - pr_debug("%s", __func__); - - if (on) { - tegra_uart_request_clock_on(bsi->uport); - tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS); - } else { - tegra_uart_set_mctrl(bsi->uport, 0); - tegra_uart_request_clock_off(bsi->uport); - } -} - - - -/** - * @brief@ main sleep work handling function which update the flags - * and activate and deactivate UART . - */ - -static void hostwake_sleep_work(struct work_struct *work) -{ - pr_debug("%s", __func__); - free_irq(bsi->host_wake_irq, "tibluesleep"); - /*Activating UART */ - if (test_bit(BT_SUSPEND, &flags)) { - BT_DBG("Activate UART"); - hsuart_power(1); - - } - bsi->has_ext_wake = 0; - clear_bit(BT_SUSPEND, &flags); - set_bit(BT_ACTIVE, &flags); - -} - - -/** - * A tasklet function that runs in tasklet context - * @param data Not used. - */ -static void bluesleep_hostwake_task(unsigned long data) -{ - pr_debug("%s", __func__); - disable_irq(bsi->host_wake_irq); - spin_lock(&rw_lock); - hostwake_workqueue(); - spin_unlock(&rw_lock); -} - - /** * Schedules a tasklet to run when receiving an interrupt on the * <code>HOST_WAKE</code> GPIO pin. @@ -170,11 +78,8 @@ static void bluesleep_hostwake_task(unsigned long data) */ static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) { - pr_debug("%s", __func__); - /* schedule a tasklet to handle the change in the host wake line */ - bsi->has_ext_wake = 1; - tasklet_schedule(&hostwake_task); + disable_irq_nosync(bsi->host_wake_irq); return IRQ_HANDLED; } @@ -183,8 +88,7 @@ static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) * @return On success, 0. On error, -1, and <code>errno</code> is set * appropriately. */ - - int bluesleep_start(struct uart_port *uport) +int bluesleep_start(struct uart_port *uport) { int retval; bsi->uport = uport; @@ -224,9 +128,8 @@ fail: /** * Stops the Sleep-Mode Protocol on the Host. */ - void bluesleep_stop(void) +void bluesleep_stop(void) { - pr_debug("%s", __func__); if (disable_irq_wake(bsi->host_wake_irq)) @@ -264,7 +167,6 @@ static int bluesleep_probe(struct platform_device *pdev) else bsi->irq_polarity = POLARITY_HIGH;/*anything else*/ - wake_lock_init(&bsi->wake_lock, WAKE_LOCK_SUSPEND, "bluesleep"); clear_bit(BT_SUSPEND, &flags); set_bit(BT_ACTIVE, &flags); @@ -282,17 +184,11 @@ static int bluesleep_remove(struct platform_device *pdev) return 0; } - static int bluesleep_resume(struct platform_device *pdev) { - pr_debug("%s", __func__); if (test_bit(BT_SUSPEND, &flags)) { - - if ((bsi->uport != NULL) && (bsi->has_ext_wake)) { - tegra_uart_request_clock_on(bsi->uport); - tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS); - } + free_irq(bsi->host_wake_irq, "tibluesleep"); clear_bit(BT_SUSPEND, &flags); set_bit(BT_ACTIVE, &flags); } @@ -317,6 +213,7 @@ static struct platform_driver bluesleep_driver = { .owner = THIS_MODULE, }, }; + /** * Initializes the module. * @return On success, 0. On error, -1, and <code>errno</code> is set @@ -337,12 +234,6 @@ static int __init bluesleep_init(void) flags = FLAG_RESET; /* clear all status bits */ - /* Initialize spinlock. */ - spin_lock_init(&rw_lock); - - /* initialize host wake tasklet */ - tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0); - return 0; fail: return retval; @@ -351,7 +242,6 @@ fail: /** * Cleans up the module. */ - static void __exit bluesleep_exit(void) { if (bsi == NULL) diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 6db161f64ae0..a9a113782821 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -89,6 +89,51 @@ void clk_put(struct clk *clk) } EXPORT_SYMBOL(clk_put); +static void devm_clk_release(struct device *dev, void *res) +{ + clk_put(*(struct clk **)res); +} + +struct clk *devm_clk_get(struct device *dev, const char *id) +{ + struct clk **ptr, *clk; + + ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + clk = clk_get(dev, id); + if (!IS_ERR(clk)) { + *ptr = clk; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return clk; +} +EXPORT_SYMBOL(devm_clk_get); + +static int devm_clk_match(struct device *dev, void *res, void *data) +{ + struct clk **c = res; + if (!c || !*c) { + WARN_ON(!c || !*c); + return 0; + } + return *c == data; +} + +void devm_clk_put(struct device *dev, struct clk *clk) +{ + int ret; + + ret = devres_destroy(dev, devm_clk_release, devm_clk_match, clk); + + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_clk_put); + void clkdev_add(struct clk_lookup *cl) { mutex_lock(&clocks_mutex); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 3acc42805e21..02e8c9120d6b 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1640,9 +1640,9 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, unsigned int pmax = policy->max; qmin = min((unsigned int)pm_qos_request(PM_QOS_CPU_FREQ_MIN), - data->max); + data->user_policy.max); qmax = max((unsigned int)pm_qos_request(PM_QOS_CPU_FREQ_MAX), - data->min); + data->user_policy.min); pr_debug("setting new policy for CPU %u: %u - %u (%u - %u) kHz\n", policy->cpu, pmin, pmax, qmin, qmax); @@ -1654,7 +1654,8 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo)); - if (policy->min > data->max || policy->max < data->min) { + if (policy->min > data->user_policy.max || + policy->max < data->user_policy.min) { ret = -EINVAL; goto error_out; } @@ -1785,6 +1786,70 @@ no_policy: } EXPORT_SYMBOL(cpufreq_update_policy); +/* + * cpufreq_set_gov - set governor for a cpu + * @cpu: CPU whose governor needs to be changed + * @target_gov: new governor to be set + */ +int cpufreq_set_gov(char *target_gov, unsigned int cpu) +{ + int ret = 0; + struct cpufreq_policy new_policy; + struct cpufreq_policy *cur_policy; + + if (target_gov == NULL) + return -EINVAL; + + /* Get current governer */ + cur_policy = cpufreq_cpu_get(cpu); + if (!cur_policy) + return -EINVAL; + + if (lock_policy_rwsem_read(cur_policy->cpu) < 0) { + ret = -EINVAL; + goto err_out; + } + + if (cur_policy->governor) + ret = strncmp(cur_policy->governor->name, target_gov, + strlen(target_gov)); + else { + unlock_policy_rwsem_read(cur_policy->cpu); + ret = -EINVAL; + goto err_out; + } + unlock_policy_rwsem_read(cur_policy->cpu); + + if (!ret) { + pr_debug(" Target governer & current governer is same\n"); + ret = -EINVAL; + goto err_out; + } else { + new_policy = *cur_policy; + if (cpufreq_parse_governor(target_gov, &new_policy.policy, + &new_policy.governor)) { + ret = -EINVAL; + goto err_out; + } + + if (lock_policy_rwsem_write(cur_policy->cpu) < 0) { + ret = -EINVAL; + goto err_out; + } + + ret = __cpufreq_set_policy(cur_policy, &new_policy); + + cur_policy->user_policy.policy = cur_policy->policy; + cur_policy->user_policy.governor = cur_policy->governor; + + unlock_policy_rwsem_write(cur_policy->cpu); + } +err_out: + cpufreq_cpu_put(cur_policy); + return ret; +} +EXPORT_SYMBOL(cpufreq_set_gov); + static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 2012d5a51889..baf4326e84c4 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -2,6 +2,7 @@ * drivers/cpufreq/cpufreq_interactive.c * * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -91,6 +92,13 @@ static unsigned long min_sample_time; #define DEFAULT_TIMER_RATE 20000; static unsigned long timer_rate; +/* Defines to control mid-range frequencies */ +#define DEFAULT_MID_RANGE_GO_MAXSPEED_LOAD 95 + +static unsigned long midrange_freq; +static unsigned long midrange_go_maxspeed_load; +static unsigned long midrange_max_boost; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -108,6 +116,8 @@ static unsigned int cpufreq_interactive_get_target( int cpu_load, int load_since_change, struct cpufreq_policy *policy) { unsigned int target_freq; + unsigned int maxspeed_load = go_maxspeed_load; + unsigned int mboost = max_boost; /* * Choose greater of short-term load (since last idle timer @@ -117,14 +127,19 @@ static unsigned int cpufreq_interactive_get_target( if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_maxspeed_load) { + if (midrange_freq && policy->cur > midrange_freq) { + maxspeed_load = midrange_go_maxspeed_load; + mboost = midrange_max_boost; + } + + if (cpu_load >= maxspeed_load) { if (!boost_factor) return policy->max; target_freq = policy->cur * boost_factor; - if (max_boost && target_freq > policy->cur + max_boost) - target_freq = policy->cur + max_boost; + if (mboost && target_freq > policy->cur + mboost) + target_freq = policy->cur + mboost; } else { if (!sustain_load) @@ -527,6 +542,50 @@ static ssize_t store_go_maxspeed_load(struct kobject *kobj, static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644, show_go_maxspeed_load, store_go_maxspeed_load); +static ssize_t show_midrange_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", midrange_freq); +} + +static ssize_t store_midrange_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + midrange_freq = val; + return count; +} + +static struct global_attr midrange_freq_attr = __ATTR(midrange_freq, 0644, + show_midrange_freq, store_midrange_freq); + +static ssize_t show_midrange_go_maxspeed_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", midrange_go_maxspeed_load); +} + +static ssize_t store_midrange_go_maxspeed_load(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + midrange_go_maxspeed_load = val; + return count; +} + +static struct global_attr midrange_go_maxspeed_load_attr = __ATTR(midrange_go_maxspeed_load, 0644, + show_midrange_go_maxspeed_load, store_midrange_go_maxspeed_load); + static ssize_t show_boost_factor(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -588,6 +647,27 @@ static ssize_t store_max_boost(struct kobject *kobj, static struct global_attr max_boost_attr = __ATTR(max_boost, 0644, show_max_boost, store_max_boost); +static ssize_t show_midrange_max_boost(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", midrange_max_boost); +} + +static ssize_t store_midrange_max_boost(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + midrange_max_boost = val; + return count; +} + +static struct global_attr midrange_max_boost_attr = __ATTR(midrange_max_boost, 0644, + show_midrange_max_boost, store_midrange_max_boost); static ssize_t show_sustain_load(struct kobject *kobj, struct attribute *attr, char *buf) @@ -657,8 +737,11 @@ static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, static struct attribute *interactive_attributes[] = { &go_maxspeed_load_attr.attr, + &midrange_freq_attr.attr, + &midrange_go_maxspeed_load_attr.attr, &boost_factor_attr.attr, &max_boost_attr.attr, + &midrange_max_boost_attr.attr, &io_is_busy_attr.attr, &sustain_load_attr.attr, &min_sample_time_attr.attr, @@ -787,6 +870,7 @@ static int __init cpufreq_interactive_init(void) struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; + midrange_go_maxspeed_load = DEFAULT_MID_RANGE_GO_MAXSPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; timer_rate = DEFAULT_TIMER_RATE; diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index c136b787c5af..1957eee7549e 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -355,6 +355,7 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, cpufreq_update_policy(cpu); break; case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: cpufreq_stats_free_sysfs(cpu); break; case CPU_DEAD: diff --git a/drivers/cpuquiet/Kconfig b/drivers/cpuquiet/Kconfig new file mode 100644 index 000000000000..844cd34a69b3 --- /dev/null +++ b/drivers/cpuquiet/Kconfig @@ -0,0 +1,11 @@ +menu "CPUQUIET Framework" + +config CPUQUIET_FRAMEWORK + bool "Cpuquiet framework" + default n + help + Cpuquiet implements pluggable policies for forcing cpu cores into a + quiescent state. Appropriate policies will save power without hurting + performance. + +endmenu diff --git a/drivers/cpuquiet/Makefile b/drivers/cpuquiet/Makefile new file mode 100644 index 000000000000..e438defaacdd --- /dev/null +++ b/drivers/cpuquiet/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CPUQUIET_FRAMEWORK) += cpuquiet.o driver.o sysfs.o cpuquiet_attribute.o governor.o governors/ diff --git a/drivers/cpuquiet/cpuquiet.c b/drivers/cpuquiet/cpuquiet.c new file mode 100644 index 000000000000..d902af26c8d7 --- /dev/null +++ b/drivers/cpuquiet/cpuquiet.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/cpu.h> +#include <linux/slab.h> +#include <linux/cpuquiet.h> +#include "cpuquiet.h" + +DEFINE_MUTEX(cpuquiet_lock); + +static int __init cpuquiet_init(void) +{ + return cpuquiet_add_class_sysfs(&cpu_sysdev_class); +} + +core_initcall(cpuquiet_init); diff --git a/drivers/cpuquiet/cpuquiet.h b/drivers/cpuquiet/cpuquiet.h new file mode 100644 index 000000000000..fa61946ff119 --- /dev/null +++ b/drivers/cpuquiet/cpuquiet.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __DRIVER_CPUQUIET_H +#define __DRIVER_CPUQUIET_H + +#include <linux/sysdev.h> + +extern struct mutex cpuquiet_lock; +extern struct cpuquiet_governor *cpuquiet_curr_governor; +extern struct list_head cpuquiet_governors; +int cpuquiet_add_class_sysfs(struct sysdev_class *cls); +struct cpuquiet_governor *cpuquiet_find_governor(const char *str); +int cpuquiet_switch_governor(struct cpuquiet_governor *gov); +struct cpuquiet_governor *cpuquiet_get_first_governor(void); +struct cpuquiet_driver *cpuquiet_get_driver(void); +void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu); +void cpuquiet_remove_dev(unsigned int cpu); +int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type, + char *name, int cpu); +#endif diff --git a/drivers/cpuquiet/cpuquiet_attribute.c b/drivers/cpuquiet/cpuquiet_attribute.c new file mode 100644 index 000000000000..9f1aa430149d --- /dev/null +++ b/drivers/cpuquiet/cpuquiet_attribute.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/cpuquiet.h> + +ssize_t show_int_attribute(struct cpuquiet_attribute *cattr, char *buf) +{ + return sprintf(buf, "%d\n", *((int *)cattr->param)); +} + +ssize_t store_int_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err, val; + + err = kstrtoint(buf, 0, &val); + if (err < 0) + return err; + + *((int *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t show_bool_attribute(struct cpuquiet_attribute *cattr, char *buf) +{ + return sprintf(buf, "%d\n", *((bool *)cattr->param)); +} + +ssize_t store_bool_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err, val; + + err = kstrtoint(buf, 0, &val); + if (err < 0) + return err; + + if (val < 0 || val > 1) + return -EINVAL; + + *((bool *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t show_uint_attribute(struct cpuquiet_attribute *cattr, char *buf) +{ + return sprintf(buf, "%u\n", *((unsigned int *)cattr->param)); +} + +ssize_t store_uint_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err; + unsigned int val; + + err = kstrtouint(buf, 0, &val); + if (err < 0) + return err; + + *((unsigned int *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t store_ulong_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err; + unsigned long val; + + err = kstrtoul(buf, 0, &val); + if (err < 0) + return err; + + *((unsigned long *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t show_ulong_attribute(struct cpuquiet_attribute *cattr, + char *buf) +{ + return sprintf(buf, "%lu\n", *((unsigned long *)cattr->param)); +} + +ssize_t cpuquiet_auto_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct cpuquiet_attribute *cattr = + container_of(attr, struct cpuquiet_attribute, attr); + + if (cattr->store) + return cattr->store(cattr, buf, count); + + return -EINVAL; +} + +ssize_t cpuquiet_auto_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpuquiet_attribute *cattr = + container_of(attr, struct cpuquiet_attribute, attr); + + return cattr->show(cattr, buf); +} diff --git a/drivers/cpuquiet/driver.c b/drivers/cpuquiet/driver.c new file mode 100644 index 000000000000..d9dbea76994a --- /dev/null +++ b/drivers/cpuquiet/driver.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/cpuquiet.h> +#include <linux/cpu.h> +#include <linux/jiffies.h> +#include <linux/slab.h> +#include <asm/cputime.h> + +#include "cpuquiet.h" + +struct cpuquiet_cpu_stat { + cputime64_t time_up_total; + u64 last_update; + unsigned int up_down_count; + struct kobject cpu_kobject; +}; + +struct cpu_attribute { + struct attribute attr; + enum { up_down_count, time_up_total } type; +}; + +static struct cpuquiet_driver *cpuquiet_curr_driver; +struct cpuquiet_cpu_stat *stats; + +#define CPU_ATTRIBUTE(_name) \ + static struct cpu_attribute _name ## _attr = { \ + .attr = {.name = __stringify(_name), .mode = 0444 }, \ + .type = _name, \ +} + +CPU_ATTRIBUTE(up_down_count); +CPU_ATTRIBUTE(time_up_total); + +static struct attribute *cpu_attributes[] = { + &up_down_count_attr.attr, + &time_up_total_attr.attr, + NULL, +}; + +static void stats_update(struct cpuquiet_cpu_stat *stat, bool up) +{ + u64 cur_jiffies = get_jiffies_64(); + bool was_up = stat->up_down_count & 0x1; + + if (was_up) + stat->time_up_total = cputime64_add(stat->time_up_total, + cputime64_sub(cur_jiffies, stat->last_update)); + + if (was_up != up) + stat->up_down_count++; + + stat->last_update = cur_jiffies; +} + +int cpuquiet_quiesence_cpu(unsigned int cpunumber) +{ + int err = -EPERM; + + if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu) + err = cpuquiet_curr_driver->quiesence_cpu(cpunumber); + + if (!err) + stats_update(stats + cpunumber, 0); + + return err; +} +EXPORT_SYMBOL(cpuquiet_quiesence_cpu); + +int cpuquiet_wake_cpu(unsigned int cpunumber) +{ + int err = -EPERM; + + if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu) + err = cpuquiet_curr_driver->wake_cpu(cpunumber); + + if (!err) + stats_update(stats + cpunumber, 1); + + return err; +} +EXPORT_SYMBOL(cpuquiet_wake_cpu); + +static ssize_t stats_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpu_attribute *cattr = + container_of(attr, struct cpu_attribute, attr); + struct cpuquiet_cpu_stat *stat = + container_of(kobj, struct cpuquiet_cpu_stat, cpu_kobject); + ssize_t len = 0; + bool was_up = stat->up_down_count & 0x1; + + stats_update(stat, was_up); + + switch (cattr->type) { + case up_down_count: + len = sprintf(buf, "%u\n", stat->up_down_count); + break; + case time_up_total: + len = sprintf(buf, "%llu\n", stat->time_up_total); + break; + } + + return len; +} + +static const struct sysfs_ops stats_sysfs_ops = { + .show = stats_sysfs_show, +}; + +static struct kobj_type ktype_cpu_stats = { + .sysfs_ops = &stats_sysfs_ops, + .default_attrs = cpu_attributes, +}; + +int cpuquiet_register_driver(struct cpuquiet_driver *drv) +{ + int err = -EBUSY; + unsigned int cpu; + struct sys_device *sys_dev; + u64 cur_jiffies; + + if (!drv) + return -EINVAL; + + stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL); + if (!stats) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + cur_jiffies = get_jiffies_64(); + stats[cpu].last_update = cur_jiffies; + if (cpu_online(cpu)) + stats[cpu].up_down_count = 1; + sys_dev = get_cpu_sysdev(cpu); + if (sys_dev) { + cpuquiet_add_dev(sys_dev, cpu); + cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject, + &ktype_cpu_stats, "stats", cpu); + } + } + + mutex_lock(&cpuquiet_lock); + if (!cpuquiet_curr_driver) { + err = 0; + cpuquiet_curr_driver = drv; + cpuquiet_switch_governor(cpuquiet_get_first_governor()); + } + mutex_unlock(&cpuquiet_lock); + + return err; +} +EXPORT_SYMBOL(cpuquiet_register_driver); + +struct cpuquiet_driver *cpuquiet_get_driver(void) +{ + return cpuquiet_curr_driver; +} + +void cpuquiet_unregister_driver(struct cpuquiet_driver *drv) +{ + unsigned int cpu; + + if (drv != cpuquiet_curr_driver) { + WARN(1, "invalid cpuquiet_unregister_driver(%s)\n", + drv->name); + return; + } + + /* stop current governor first */ + cpuquiet_switch_governor(NULL); + + mutex_lock(&cpuquiet_lock); + cpuquiet_curr_driver = NULL; + + for_each_possible_cpu(cpu) { + kobject_put(&stats[cpu].cpu_kobject); + cpuquiet_remove_dev(cpu); + } + + mutex_unlock(&cpuquiet_lock); +} +EXPORT_SYMBOL(cpuquiet_unregister_driver); diff --git a/drivers/cpuquiet/governor.c b/drivers/cpuquiet/governor.c new file mode 100644 index 000000000000..7895fccc7f42 --- /dev/null +++ b/drivers/cpuquiet/governor.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/cpuquiet.h> + +#include "cpuquiet.h" + +LIST_HEAD(cpuquiet_governors); +struct cpuquiet_governor *cpuquiet_curr_governor; + +struct cpuquiet_governor *cpuquiet_get_first_governor(void) +{ + if (!list_empty(&cpuquiet_governors)) + return list_entry(cpuquiet_governors.next, + struct cpuquiet_governor, + governor_list); + else + return NULL; +} + +struct cpuquiet_governor *cpuquiet_find_governor(const char *str) +{ + struct cpuquiet_governor *gov; + + list_for_each_entry(gov, &cpuquiet_governors, governor_list) + if (!strnicmp(str, gov->name, CPUQUIET_NAME_LEN)) + return gov; + + return NULL; +} + +int cpuquiet_switch_governor(struct cpuquiet_governor *gov) +{ + int err = 0; + + if (cpuquiet_curr_governor) { + if (cpuquiet_curr_governor->stop) + cpuquiet_curr_governor->stop(); + module_put(cpuquiet_curr_governor->owner); + } + + cpuquiet_curr_governor = gov; + + if (gov) { + if (!try_module_get(cpuquiet_curr_governor->owner)) + return -EINVAL; + if (gov->start) + err = gov->start(); + if (!err) + cpuquiet_curr_governor = gov; + } + + return err; +} + +int cpuquiet_register_governor(struct cpuquiet_governor *gov) +{ + int ret = -EEXIST; + + if (!gov) + return -EINVAL; + + mutex_lock(&cpuquiet_lock); + if (cpuquiet_find_governor(gov->name) == NULL) { + ret = 0; + list_add_tail(&gov->governor_list, &cpuquiet_governors); + if (!cpuquiet_curr_governor && cpuquiet_get_driver()) + cpuquiet_switch_governor(gov); + } + mutex_unlock(&cpuquiet_lock); + + return ret; +} + +void cpuquiet_unregister_governor(struct cpuquiet_governor *gov) +{ + if (!gov) + return; + + mutex_lock(&cpuquiet_lock); + if (cpuquiet_curr_governor == gov) + cpuquiet_switch_governor(NULL); + list_del(&gov->governor_list); + mutex_unlock(&cpuquiet_lock); +} diff --git a/drivers/cpuquiet/governors/Makefile b/drivers/cpuquiet/governors/Makefile new file mode 100644 index 000000000000..c70803127082 --- /dev/null +++ b/drivers/cpuquiet/governors/Makefile @@ -0,0 +1 @@ +obj-y += userspace.o balanced.o diff --git a/drivers/cpuquiet/governors/balanced.c b/drivers/cpuquiet/governors/balanced.c new file mode 100644 index 000000000000..f0d2e03ae22b --- /dev/null +++ b/drivers/cpuquiet/governors/balanced.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/cpuquiet.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <linux/pm_qos_params.h> +#include <linux/jiffies.h> +#include <linux/slab.h> +#include <linux/cpu.h> +#include <linux/sched.h> +#include <linux/tick.h> +#include <asm/cputime.h> + +#define CPUNAMELEN 8 + +typedef enum { + CPU_SPEED_BALANCED, + CPU_SPEED_BIASED, + CPU_SPEED_SKEWED, +} CPU_SPEED_BALANCE; + +typedef enum { + IDLE, + DOWN, + UP, +} BALANCED_STATE; + +struct idle_info { + u64 idle_last; + u64 last_timestamp; + u64 idle_current; + u64 timestamp; +}; + +static DEFINE_PER_CPU(struct idle_info, idleinfo); +static DEFINE_PER_CPU(unsigned int, cpu_load); + +static struct timer_list load_timer; +static bool load_timer_active; + +/* configurable parameters */ +static unsigned int balance_level = 60; +static unsigned int idle_bottom_freq; +static unsigned int idle_top_freq; +static unsigned long up_delay; +static unsigned long down_delay; +static unsigned long last_change_time; +static unsigned int load_sample_rate = 20; /* msec */ +static struct workqueue_struct *balanced_wq; +static struct delayed_work balanced_work; +static BALANCED_STATE balanced_state; +static struct kobject *balanced_kobject; + +static void calculate_load_timer(unsigned long data) +{ + int i; + u64 idle_time, elapsed_time; + + if (!load_timer_active) + return; + + for_each_online_cpu(i) { + struct idle_info *iinfo = &per_cpu(idleinfo, i); + unsigned int *load = &per_cpu(cpu_load, i); + + iinfo->idle_last = iinfo->idle_current; + iinfo->last_timestamp = iinfo->timestamp; + iinfo->idle_current = + get_cpu_idle_time_us(i, &iinfo->timestamp); + elapsed_time = iinfo->timestamp - iinfo->last_timestamp; + + idle_time = iinfo->idle_current - iinfo->idle_last; + idle_time *= 100; + do_div(idle_time, elapsed_time); + *load = 100 - idle_time; + } + mod_timer(&load_timer, jiffies + msecs_to_jiffies(load_sample_rate)); +} + +static void start_load_timer(void) +{ + int i; + + if (load_timer_active) + return; + + load_timer_active = true; + + for_each_online_cpu(i) { + struct idle_info *iinfo = &per_cpu(idleinfo, i); + + iinfo->idle_current = + get_cpu_idle_time_us(i, &iinfo->timestamp); + } + mod_timer(&load_timer, jiffies + msecs_to_jiffies(100)); +} + +static void stop_load_timer(void) +{ + if (!load_timer_active) + return; + + load_timer_active = false; + del_timer(&load_timer); +} + +static unsigned int get_slowest_cpu_n(void) +{ + unsigned int cpu = nr_cpu_ids; + unsigned long minload = ULONG_MAX; + int i; + + for_each_online_cpu(i) { + unsigned int *load = &per_cpu(cpu_load, i); + + if ((i > 0) && (minload > *load)) { + cpu = i; + minload = *load; + } + } + + return cpu; +} + +static unsigned int cpu_highest_speed(void) +{ + unsigned int maxload = 0; + int i; + + for_each_online_cpu(i) { + unsigned int *load = &per_cpu(cpu_load, i); + + maxload = max(maxload, *load); + } + + return maxload; +} + +static unsigned int count_slow_cpus(unsigned int limit) +{ + unsigned int cnt = 0; + int i; + + for_each_online_cpu(i) { + unsigned int *load = &per_cpu(cpu_load, i); + + if (*load <= limit) + cnt++; + } + + return cnt; +} + +#define NR_FSHIFT 2 +static unsigned int nr_run_thresholds[] = { +/* 1, 2, 3, 4 - on-line cpus target */ + 5, 9, 10, UINT_MAX /* avg run threads * 4 (e.g., 9 = 2.25 threads) */ +}; +static unsigned int nr_run_hysteresis = 2; /* 0.5 thread */ +static unsigned int nr_run_last; + +static CPU_SPEED_BALANCE balanced_speed_balance(void) +{ + unsigned long highest_speed = cpu_highest_speed(); + unsigned long balanced_speed = highest_speed * balance_level / 100; + unsigned long skewed_speed = balanced_speed / 2; + unsigned int nr_cpus = num_online_cpus(); + unsigned int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4; + unsigned int avg_nr_run = avg_nr_running(); + unsigned int nr_run; + + /* balanced: freq targets for all CPUs are above 50% of highest speed + biased: freq target for at least one CPU is below 50% threshold + skewed: freq targets for at least 2 CPUs are below 25% threshold */ + for (nr_run = 1; nr_run < ARRAY_SIZE(nr_run_thresholds); nr_run++) { + unsigned int nr_threshold = nr_run_thresholds[nr_run - 1]; + if (nr_run_last <= nr_run) + nr_threshold += nr_run_hysteresis; + if (avg_nr_run <= (nr_threshold << (FSHIFT - NR_FSHIFT))) + break; + } + nr_run_last = nr_run; + + if (count_slow_cpus(skewed_speed) >= 2 || nr_cpus > max_cpus || + nr_run < nr_cpus) + return CPU_SPEED_SKEWED; + + if (count_slow_cpus(balanced_speed) >= 1 || nr_cpus == max_cpus || + nr_run <= nr_cpus) + return CPU_SPEED_BIASED; + + return CPU_SPEED_BALANCED; +} + +static void balanced_work_func(struct work_struct *work) +{ + bool up = false; + unsigned int cpu = nr_cpu_ids; + unsigned long now = jiffies; + + CPU_SPEED_BALANCE balance; + + switch (balanced_state) { + case IDLE: + break; + case DOWN: + cpu = get_slowest_cpu_n(); + if (cpu < nr_cpu_ids) { + up = false; + queue_delayed_work(balanced_wq, + &balanced_work, up_delay); + } else + stop_load_timer(); + break; + case UP: + balance = balanced_speed_balance(); + switch (balance) { + + /* cpu speed is up and balanced - one more on-line */ + case CPU_SPEED_BALANCED: + cpu = cpumask_next_zero(0, cpu_online_mask); + if (cpu < nr_cpu_ids) + up = true; + break; + /* cpu speed is up, but skewed - remove one core */ + case CPU_SPEED_SKEWED: + cpu = get_slowest_cpu_n(); + if (cpu < nr_cpu_ids) + up = false; + break; + /* cpu speed is up, but under-utilized - do nothing */ + case CPU_SPEED_BIASED: + default: + break; + } + queue_delayed_work( + balanced_wq, &balanced_work, up_delay); + break; + default: + pr_err("%s: invalid cpuquiet balanced governor state %d\n", + __func__, balanced_state); + } + + if (!up && ((now - last_change_time) < down_delay)) + cpu = nr_cpu_ids; + + if (cpu < nr_cpu_ids) { + last_change_time = now; + if (up) + cpuquiet_wake_cpu(cpu); + else + cpuquiet_quiesence_cpu(cpu); + } +} + +static int balanced_cpufreq_transition(struct notifier_block *nb, + unsigned long state, void *data) +{ + struct cpufreq_freqs *freqs = data; + unsigned long cpu_freq; + + if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) { + cpu_freq = freqs->new; + + switch (balanced_state) { + case IDLE: + if (cpu_freq >= idle_top_freq) { + balanced_state = UP; + queue_delayed_work( + balanced_wq, &balanced_work, up_delay); + start_load_timer(); + } else if (cpu_freq <= idle_bottom_freq) { + balanced_state = DOWN; + queue_delayed_work( + balanced_wq, &balanced_work, + down_delay); + start_load_timer(); + } + break; + case DOWN: + if (cpu_freq >= idle_top_freq) { + balanced_state = UP; + queue_delayed_work( + balanced_wq, &balanced_work, up_delay); + start_load_timer(); + } + break; + case UP: + if (cpu_freq <= idle_bottom_freq) { + balanced_state = DOWN; + queue_delayed_work(balanced_wq, + &balanced_work, up_delay); + start_load_timer(); + } + break; + default: + pr_err("%s: invalid cpuquiet balanced governor " + "state %d\n", __func__, balanced_state); + } + } + + return NOTIFY_OK; +} + +static struct notifier_block balanced_cpufreq_nb = { + .notifier_call = balanced_cpufreq_transition, +}; + +static void delay_callback(struct cpuquiet_attribute *attr) +{ + unsigned long val; + + if (attr) { + val = (*((unsigned long *)(attr->param))); + (*((unsigned long *)(attr->param))) = msecs_to_jiffies(val); + } +} + +CPQ_BASIC_ATTRIBUTE(balance_level, 0644, uint); +CPQ_BASIC_ATTRIBUTE(idle_bottom_freq, 0644, uint); +CPQ_BASIC_ATTRIBUTE(idle_top_freq, 0644, uint); +CPQ_BASIC_ATTRIBUTE(load_sample_rate, 0644, uint); +CPQ_ATTRIBUTE(up_delay, 0644, ulong, delay_callback); +CPQ_ATTRIBUTE(down_delay, 0644, ulong, delay_callback); + +static struct attribute *balanced_attributes[] = { + &balance_level_attr.attr, + &idle_bottom_freq_attr.attr, + &idle_top_freq_attr.attr, + &up_delay_attr.attr, + &down_delay_attr.attr, + &load_sample_rate_attr.attr, + NULL, +}; + +static const struct sysfs_ops balanced_sysfs_ops = { + .show = cpuquiet_auto_sysfs_show, + .store = cpuquiet_auto_sysfs_store, +}; + +static struct kobj_type ktype_balanced = { + .sysfs_ops = &balanced_sysfs_ops, + .default_attrs = balanced_attributes, +}; + +static int balanced_sysfs(void) +{ + int err; + + balanced_kobject = kzalloc(sizeof(*balanced_kobject), + GFP_KERNEL); + + if (!balanced_kobject) + return -ENOMEM; + + err = cpuquiet_kobject_init(balanced_kobject, &ktype_balanced, + "balanced"); + + if (err) + kfree(balanced_kobject); + + return err; +} + +static void balanced_stop(void) +{ + /* + first unregister the notifiers. This ensures the governor state + can't be modified by a cpufreq transition + */ + cpufreq_unregister_notifier(&balanced_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + + /* now we can force the governor to be idle */ + balanced_state = IDLE; + cancel_delayed_work_sync(&balanced_work); + destroy_workqueue(balanced_wq); + del_timer(&load_timer); + + kobject_put(balanced_kobject); +} + +static int balanced_start(void) +{ + int err, count; + struct cpufreq_frequency_table *table; + struct cpufreq_freqs initial_freq; + + err = balanced_sysfs(); + if (err) + return err; + + balanced_wq = alloc_workqueue("cpuquiet-balanced", + WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1); + if (!balanced_wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&balanced_work, balanced_work_func); + + up_delay = msecs_to_jiffies(100); + down_delay = msecs_to_jiffies(500); + + table = cpufreq_frequency_get_table(0); + for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++); + + idle_top_freq = table[(count / 2) - 1].frequency; + idle_bottom_freq = table[(count / 2) - 2].frequency; + + cpufreq_register_notifier(&balanced_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + + init_timer(&load_timer); + load_timer.function = calculate_load_timer; + + /*FIXME: Kick start the state machine by faking a freq notification*/ + initial_freq.new = cpufreq_get(0); + if (initial_freq.new != 0) + balanced_cpufreq_transition(NULL, CPUFREQ_RESUMECHANGE, + &initial_freq); + return 0; +} + +struct cpuquiet_governor balanced_governor = { + .name = "balanced", + .start = balanced_start, + .stop = balanced_stop, + .owner = THIS_MODULE, +}; + +static int __init init_balanced(void) +{ + return cpuquiet_register_governor(&balanced_governor); +} + +static void __exit exit_balanced(void) +{ + cpuquiet_unregister_governor(&balanced_governor); +} + +MODULE_LICENSE("GPL"); +module_init(init_balanced); +module_exit(exit_balanced); + diff --git a/drivers/cpuquiet/governors/userspace.c b/drivers/cpuquiet/governors/userspace.c new file mode 100644 index 000000000000..470056c5e32a --- /dev/null +++ b/drivers/cpuquiet/governors/userspace.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/cpuquiet.h> +#include <linux/sysfs.h> + +static DEFINE_MUTEX(userspace_mutex); + +static int governor_set(unsigned int cpu, bool active) +{ + mutex_lock(&userspace_mutex); + if (active) + cpuquiet_wake_cpu(cpu); + else + cpuquiet_quiesence_cpu(cpu); + mutex_unlock(&userspace_mutex); + + return 0; +} + +struct cpuquiet_governor userspace_governor = { + .name = "userspace", + .store_active = governor_set, + .owner = THIS_MODULE, +}; + +static int __init init_usermode(void) +{ + return cpuquiet_register_governor(&userspace_governor); +} + +static void __exit exit_usermode(void) +{ + cpuquiet_unregister_governor(&userspace_governor); +} + +MODULE_LICENSE("GPL"); +module_init(init_usermode); +module_exit(exit_usermode); diff --git a/drivers/cpuquiet/sysfs.c b/drivers/cpuquiet/sysfs.c new file mode 100644 index 000000000000..0d63eee37dce --- /dev/null +++ b/drivers/cpuquiet/sysfs.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/slab.h> +#include <linux/cpuquiet.h> + +#include "cpuquiet.h" + +struct cpuquiet_dev { + unsigned int cpu; + struct kobject kobj; +}; + +struct cpuquiet_sysfs_attr { + struct attribute attr; + ssize_t (*show)(char *); + ssize_t (*store)(const char *, size_t count); +}; + +static struct kobject *cpuquiet_global_kobject; +struct cpuquiet_dev *cpuquiet_cpu_devices[CONFIG_NR_CPUS]; + +static ssize_t show_current_governor(char *buf) +{ + ssize_t ret; + + mutex_lock(&cpuquiet_lock); + + if (cpuquiet_curr_governor) + ret = sprintf(buf, "%s\n", cpuquiet_curr_governor->name); + else + ret = sprintf(buf, "none\n"); + + mutex_unlock(&cpuquiet_lock); + + return ret; + +} + +static ssize_t store_current_governor(const char *buf, size_t count) +{ + char name[CPUQUIET_NAME_LEN]; + struct cpuquiet_governor *gov; + int len = count, ret = -EINVAL; + + if (!len || len >= sizeof(name)) + return -EINVAL; + + memcpy(name, buf, count); + name[len] = '\0'; + if (name[len - 1] == '\n') + name[--len] = '\0'; + + mutex_lock(&cpuquiet_lock); + gov = cpuquiet_find_governor(name); + mutex_unlock(&cpuquiet_lock); + + if (gov) + ret = cpuquiet_switch_governor(gov); + + if (ret) + return ret; + else + return count; +} + +static ssize_t available_governors_show(char *buf) +{ + ssize_t ret = 0, len; + struct cpuquiet_governor *gov; + + mutex_lock(&cpuquiet_lock); + if (!list_empty(&cpuquiet_governors)) { + list_for_each_entry(gov, &cpuquiet_governors, governor_list) { + len = sprintf(buf, "%s ", gov->name); + buf += len; + ret += len; + } + buf--; + *buf = '\n'; + } else + ret = sprintf(buf, "none\n"); + + mutex_unlock(&cpuquiet_lock); + + return ret; +} + +struct cpuquiet_sysfs_attr attr_current_governor = __ATTR(current_governor, + 0644, show_current_governor, store_current_governor); +struct cpuquiet_sysfs_attr attr_governors = __ATTR_RO(available_governors); + + +static struct attribute *cpuquiet_default_attrs[] = { + &attr_current_governor.attr, + &attr_governors.attr, + NULL +}; + +static ssize_t cpuquiet_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpuquiet_sysfs_attr *cattr = + container_of(attr, struct cpuquiet_sysfs_attr, attr); + + return cattr->show(buf); +} + +static ssize_t cpuquiet_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct cpuquiet_sysfs_attr *cattr = + container_of(attr, struct cpuquiet_sysfs_attr, attr); + + if (cattr->store) + return cattr->store(buf, count); + + return -EINVAL; +} + +static const struct sysfs_ops cpuquiet_sysfs_ops = { + .show = cpuquiet_sysfs_show, + .store = cpuquiet_sysfs_store, +}; + +static struct kobj_type ktype_cpuquiet_sysfs = { + .sysfs_ops = &cpuquiet_sysfs_ops, + .default_attrs = cpuquiet_default_attrs, +}; + +int cpuquiet_add_group(struct attribute_group *attrs) +{ + return sysfs_create_group(cpuquiet_global_kobject, attrs); +} + +void cpuquiet_remove_group(struct attribute_group *attrs) +{ + sysfs_remove_group(cpuquiet_global_kobject, attrs); +} + +int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type, + char *name) +{ + int err; + + err = kobject_init_and_add(kobj, type, cpuquiet_global_kobject, name); + if (!err) + kobject_uevent(kobj, KOBJ_ADD); + + return err; +} + +int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type, + char *name, int cpu) +{ + int err; + + err = kobject_init_and_add(kobj, type, &cpuquiet_cpu_devices[cpu]->kobj, + name); + if (!err) + kobject_uevent(kobj, KOBJ_ADD); + + return err; +} + +int cpuquiet_add_class_sysfs(struct sysdev_class *cls) +{ + int err; + + cpuquiet_global_kobject = kzalloc(sizeof(*cpuquiet_global_kobject), + GFP_KERNEL); + if (!cpuquiet_global_kobject) + return -ENOMEM; + + err = kobject_init_and_add(cpuquiet_global_kobject, + &ktype_cpuquiet_sysfs, &cls->kset.kobj, "cpuquiet"); + if (!err) + kobject_uevent(cpuquiet_global_kobject, KOBJ_ADD); + + return err; +} + + +struct cpuquiet_attr { + struct attribute attr; + ssize_t (*show)(unsigned int, char *); + ssize_t (*store)(unsigned int, const char *, size_t count); +}; + + +static ssize_t cpuquiet_state_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpuquiet_attr *cattr = container_of(attr, + struct cpuquiet_attr, attr); + struct cpuquiet_dev *dev = container_of(kobj, + struct cpuquiet_dev, kobj); + + return cattr->show(dev->cpu, buf); +} + +static ssize_t cpuquiet_state_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct cpuquiet_attr *cattr = container_of(attr, + struct cpuquiet_attr, attr); + struct cpuquiet_dev *dev = container_of(kobj, + struct cpuquiet_dev, kobj); + + if (cattr->store) + return cattr->store(dev->cpu, buf, count); + + return -EINVAL; +} + +static ssize_t show_active(unsigned int cpu, char *buf) +{ + return sprintf(buf, "%u\n", cpu_online(cpu)); +} + +static ssize_t store_active(unsigned int cpu, const char *value, size_t count) +{ + unsigned int active; + int ret; + + if (!cpuquiet_curr_governor->store_active) + return -EINVAL; + + ret = sscanf(value, "%u", &active); + if (ret != 1) + return -EINVAL; + + cpuquiet_curr_governor->store_active(cpu, active); + + return count; +} + +struct cpuquiet_attr attr_active = __ATTR(active, 0644, show_active, + store_active); + +static struct attribute *cpuquiet_default_cpu_attrs[] = { + &attr_active.attr, + NULL +}; + +static const struct sysfs_ops cpuquiet_cpu_sysfs_ops = { + .show = cpuquiet_state_show, + .store = cpuquiet_state_store, +}; + +static struct kobj_type ktype_cpuquiet = { + .sysfs_ops = &cpuquiet_cpu_sysfs_ops, + .default_attrs = cpuquiet_default_cpu_attrs, +}; + +void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu) +{ + struct cpuquiet_dev *dev; + int err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev->cpu = cpu; + cpuquiet_cpu_devices[cpu] = dev; + err = kobject_init_and_add(&dev->kobj, &ktype_cpuquiet, + &sys_dev->kobj, "cpuquiet"); + if (!err) + kobject_uevent(&dev->kobj, KOBJ_ADD); +} + +void cpuquiet_remove_dev(unsigned int cpu) +{ + if (cpu < CONFIG_NR_CPUS && cpuquiet_cpu_devices[cpu]) + kobject_put(&cpuquiet_cpu_devices[cpu]->kobj); +} diff --git a/drivers/crypto/tegra-aes.c b/drivers/crypto/tegra-aes.c index 291cb4be5c39..85c1df63bebc 100644 --- a/drivers/crypto/tegra-aes.c +++ b/drivers/crypto/tegra-aes.c @@ -499,6 +499,10 @@ static int aes_set_key(struct tegra_aes_engine *eng, int slot_num) memset(dd->bsev.ivkey_base, 0, AES_HW_KEY_TABLE_LENGTH_BYTES); memcpy(dd->bsev.ivkey_base, eng->ctx->key, eng->ctx->keylen); + /* sync the buffer for device */ + dma_sync_single_for_device(dd->dev, dd->bsev.ivkey_phys_base, + eng->ctx->keylen, DMA_TO_DEVICE); + /* copy the key table from sdram to vram */ cmdq[0] = 0; cmdq[0] = UCQOPCODE_MEMDMAVD << ICQBITSHIFT_OPCODE | @@ -622,12 +626,20 @@ static int tegra_aes_handle_req(struct tegra_aes_engine *eng) */ memcpy(eng->buf_in, (u8 *)req->info, AES_BLOCK_SIZE); + /* sync the buffer for device */ + dma_sync_single_for_device(dd->dev, eng->dma_buf_in, + AES_HW_DMA_BUFFER_SIZE_BYTES, DMA_TO_DEVICE); + ret = aes_start_crypt(eng, (u32)eng->dma_buf_in, (u32)eng->dma_buf_out, 1, FLAGS_CBC, false); if (ret < 0) { dev_err(dd->dev, "aes_start_crypt fail(%d)\n", ret); goto out; } + + /* sync the buffer for cpu */ + dma_sync_single_for_cpu(dd->dev, eng->dma_buf_out, + AES_HW_DMA_BUFFER_SIZE_BYTES, DMA_FROM_DEVICE); } while (total) { @@ -960,6 +972,10 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata, memset(eng->buf_in, 0, AES_BLOCK_SIZE); memcpy(eng->buf_in, dt, DEFAULT_RNG_BLK_SZ); + /* sync the buffer for device */ + dma_sync_single_for_device(dd->dev, eng->dma_buf_in, + AES_HW_DMA_BUFFER_SIZE_BYTES, DMA_TO_DEVICE); + ret = aes_start_crypt(eng, (u32)eng->dma_buf_in, (u32)eng->dma_buf_out, 1, FLAGS_ENCRYPT | FLAGS_RNG, true); if (ret < 0) { @@ -967,6 +983,10 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata, dlen = ret; goto out; } + /* sync the buffer for cpu */ + dma_sync_single_for_cpu(dd->dev, eng->dma_buf_out, + AES_HW_DMA_BUFFER_SIZE_BYTES, DMA_FROM_DEVICE); + memcpy(dest, eng->buf_out, dlen); /* update the DT */ @@ -1054,12 +1074,20 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed, /* set seed to the aes hw slot */ memset(eng->buf_in, 0, AES_BLOCK_SIZE); memcpy(eng->buf_in, seed, DEFAULT_RNG_BLK_SZ); + + /* sync the buffer for device */ + dma_sync_single_for_device(dd->dev, eng->dma_buf_in, + AES_HW_DMA_BUFFER_SIZE_BYTES, DMA_TO_DEVICE); + ret = aes_start_crypt(eng, (u32)eng->dma_buf_in, (u32)eng->dma_buf_out, 1, FLAGS_CBC, false); if (ret < 0) { dev_err(dd->dev, "aes_start_crypt fail(%d)\n", ret); goto out; } + /* sync the buffer for cpu */ + dma_sync_single_for_cpu(dd->dev, eng->dma_buf_out, + AES_HW_DMA_BUFFER_SIZE_BYTES, DMA_FROM_DEVICE); if (slen >= (2 * DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128)) { dt = seed + DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128; @@ -1280,14 +1308,14 @@ static int tegra_aes_probe(struct platform_device *pdev) * - key schedule */ dd->bsea.ivkey_base = NULL; - dd->bsev.ivkey_base = dma_alloc_coherent(dev, AES_MAX_KEY_SIZE, + dd->bsev.ivkey_base = dma_alloc_coherent(dev, SZ_512, &dd->bsev.ivkey_phys_base, GFP_KERNEL); if (!dd->bsev.ivkey_base) { dev_err(dev, "can not allocate iv/key buffer for BSEV\n"); err = -ENOMEM; goto out; } - memset(dd->bsev.ivkey_base, 0, AES_MAX_KEY_SIZE); + memset(dd->bsev.ivkey_base, 0, SZ_512); dd->bsev.buf_in = dma_alloc_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES, &dd->bsev.dma_buf_in, GFP_KERNEL); diff --git a/drivers/crypto/tegra-se.c b/drivers/crypto/tegra-se.c index 3d2e9187b949..c03065356984 100644 --- a/drivers/crypto/tegra-se.c +++ b/drivers/crypto/tegra-se.c @@ -4,7 +4,7 @@ * * Support for Tegra Security Engine hardware crypto algorithms. * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -622,9 +622,12 @@ static int tegra_se_count_sgs(struct scatterlist *sl, u32 total_bytes) return 0; do { - total_bytes -= min(sl[i].length, total_bytes); + if (!sl->length) + return 0; + total_bytes -= min(sl->length, total_bytes); i++; - } while (total_bytes); + sl = sg_next(sl); + } while (total_bytes && sl); return i; } @@ -846,7 +849,7 @@ static int tegra_se_aes_queue_req(struct ablkcipher_request *req) bool idle = true; int err = 0; - if (!req->nbytes) + if (!tegra_se_count_sgs(req->src, req->nbytes)) return -EINVAL; spin_lock_irqsave(&se_dev->lock, flags); @@ -1899,14 +1902,6 @@ static int tegra_se_probe(struct platform_device *pdev) goto err_irq; } - err = request_irq(se_dev->irq, tegra_se_irq, IRQF_DISABLED, - DRIVER_NAME, se_dev); - if (err) { - dev_err(se_dev->dev, "request_irq failed - irq[%d] err[%d]\n", - se_dev->irq, err); - goto err_irq; - } - /* Initialize the clock */ se_dev->pclk = clk_get(se_dev->dev, "se"); if (IS_ERR(se_dev->pclk)) { @@ -1939,6 +1934,14 @@ static int tegra_se_probe(struct platform_device *pdev) pm_runtime_enable(se_dev->dev); tegra_se_key_read_disable_all(); + err = request_irq(se_dev->irq, tegra_se_irq, IRQF_DISABLED, + DRIVER_NAME, se_dev); + if (err) { + dev_err(se_dev->dev, "request_irq failed - irq[%d] err[%d]\n", + se_dev->irq, err); + goto clean; + } + err = tegra_se_alloc_ll_buf(se_dev, SE_MAX_SRC_SG_COUNT, SE_MAX_DST_SG_COUNT); if (err) { diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..9b304f254f98 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -271,6 +271,15 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +config GPIO_RC5T583 + bool "RICOH RC5T583 GPIO" + depends on MFD_RC5T583 + help + Select this option to enable GPIO driver for the Ricoh RC5T583 + chip family. + This driver provides the support for driving/reading the gpio pins + of RC5T583 device through standard gpio library. + config GPIO_SX150X bool "Semtech SX150x I2C GPIO expander" depends on I2C=y diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..4ef6785f446b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o +obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c new file mode 100644 index 000000000000..08428bf17718 --- /dev/null +++ b/drivers/gpio/gpio-rc5t583.c @@ -0,0 +1,180 @@ +/* + * GPIO driver for RICOH583 power management chip. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * Based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/mfd/rc5t583.h> + +struct rc5t583_gpio { + struct gpio_chip gpio_chip; + struct rc5t583 *rc5t583; +}; + +static inline struct rc5t583_gpio *to_rc5t583_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct rc5t583_gpio, gpio_chip); +} + +static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + uint8_t val = 0; + int ret; + + ret = rc5t583_read(parent, RC5T583_GPIO_MON_IOIN, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(offset)); +} + +static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + if (val) + rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + else + rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); +} + +static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + + ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); + if (ret < 0) + return ret; + + /* Set pin to gpio mode */ + return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + + rc5t583_gpio_set(gc, offset, value); + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); + if (ret < 0) + return ret; + + /* Set pin to gpio mode */ + return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int rc5t583_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + + if ((offset >= 0) && (offset < 8)) + return rc5t583_gpio->rc5t583->irq_base + + RC5T583_IRQ_GPIO0 + offset; + return -EINVAL; +} + +static void rc5t583_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + + rc5t583_set_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int __devinit rc5t583_gpio_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct rc5t583_gpio *rc5t583_gpio; + + rc5t583_gpio = devm_kzalloc(&pdev->dev, sizeof(*rc5t583_gpio), + GFP_KERNEL); + if (!rc5t583_gpio) { + dev_warn(&pdev->dev, "Mem allocation for rc5t583_gpio failed"); + return -ENOMEM; + } + + rc5t583_gpio->gpio_chip.label = "gpio-rc5t583", + rc5t583_gpio->gpio_chip.owner = THIS_MODULE, + rc5t583_gpio->gpio_chip.free = rc5t583_gpio_free, + rc5t583_gpio->gpio_chip.direction_input = rc5t583_gpio_dir_input, + rc5t583_gpio->gpio_chip.direction_output = rc5t583_gpio_dir_output, + rc5t583_gpio->gpio_chip.set = rc5t583_gpio_set, + rc5t583_gpio->gpio_chip.get = rc5t583_gpio_get, + rc5t583_gpio->gpio_chip.to_irq = rc5t583_gpio_to_irq, + rc5t583_gpio->gpio_chip.ngpio = RC5T583_MAX_GPIO, + rc5t583_gpio->gpio_chip.can_sleep = 1, + rc5t583_gpio->gpio_chip.dev = &pdev->dev; + rc5t583_gpio->gpio_chip.base = -1; + rc5t583_gpio->rc5t583 = rc5t583; + + if (pdata && pdata->gpio_base) + rc5t583_gpio->gpio_chip.base = pdata->gpio_base; + + platform_set_drvdata(pdev, rc5t583_gpio); + + return gpiochip_add(&rc5t583_gpio->gpio_chip); +} + +static int __devexit rc5t583_gpio_remove(struct platform_device *pdev) +{ + struct rc5t583_gpio *rc5t583_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&rc5t583_gpio->gpio_chip); +} + +static struct platform_driver rc5t583_gpio_driver = { + .driver = { + .name = "rc5t583-gpio", + .owner = THIS_MODULE, + }, + .probe = rc5t583_gpio_probe, + .remove = __devexit_p(rc5t583_gpio_remove), +}; + +static int __init rc5t583_gpio_init(void) +{ + return platform_driver_register(&rc5t583_gpio_driver); +} +subsys_initcall(rc5t583_gpio_init); + +static void __exit rc5t583_gpio_exit(void) +{ + platform_driver_unregister(&rc5t583_gpio_driver); +} +module_exit(rc5t583_gpio_exit); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("GPIO interface for RC5T583"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:rc5t583-gpio"); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 3a0893f9cdc3..49abdf016b26 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -32,6 +32,7 @@ #include <asm/mach/irq.h> #include <mach/iomap.h> +#include <mach/legacy_irq.h> #include <mach/pinmux.h> #include "../../../arch/arm/mach-tegra/pm-irq.h" @@ -90,6 +91,7 @@ struct tegra_gpio_bank { u32 oe[4]; u32 int_enb[4]; u32 int_lvl[4]; + u32 wake_enb[4]; #endif }; @@ -191,6 +193,7 @@ static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0); + tegra_gpio_enable(offset); return 0; } @@ -199,6 +202,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, { tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1); + tegra_gpio_enable(offset); return 0; } @@ -213,8 +217,20 @@ static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return TEGRA_GPIO_TO_IRQ(offset); } +static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return 0; +} + +static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_disable(offset); +} + static struct gpio_chip tegra_gpio_chip = { .label = "tegra-gpio", + .request = tegra_gpio_request, + .free = tegra_gpio_free, .direction_input = tegra_gpio_direction_input, .get = tegra_gpio_get, .direction_output = tegra_gpio_direction_output, @@ -381,21 +397,60 @@ static int tegra_gpio_suspend(void) return 0; } +static int tegra_update_lp1_gpio_wake(struct irq_data *d, bool enable) +{ +#ifdef CONFIG_PM_SLEEP + struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); + u8 mask; + u8 port_index; + u8 pin_index_in_bank; + u8 pin_in_port; + int gpio = d->irq - INT_GPIO_BASE; + + if (gpio < 0) + return -EIO; + pin_index_in_bank = (gpio & 0x1F); + port_index = pin_index_in_bank >> 3; + pin_in_port = (pin_index_in_bank & 0x7); + mask = BIT(pin_in_port); + if (enable) + bank->wake_enb[port_index] |= mask; + else + bank->wake_enb[port_index] &= ~mask; +#endif + + return 0; +} + static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) { struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); int ret = 0; - ret = tegra_pm_irq_set_wake(d->irq, enable); + /* + * update LP1 mask for gpio port/pin interrupt + * LP1 enable independent of LP0 wake support + */ + ret = tegra_update_lp1_gpio_wake(d, enable); + if (ret) { + pr_err("Failed gpio lp1 %s for irq=%d, error=%d\n", + (enable ? "enable" : "disable"), d->irq, ret); + goto fail; + } + /* LP1 enable for bank interrupt */ + ret = tegra_update_lp1_irq_wake(bank->irq, enable); if (ret) - return ret; - - ret = irq_set_irq_wake(bank->irq, enable); + pr_err("Failed gpio lp1 %s for irq=%d, error=%d\n", + (enable ? "enable" : "disable"), bank->irq, ret); + /* LP0 */ + ret = tegra_pm_irq_set_wake(d->irq, enable); if (ret) - tegra_pm_irq_set_wake(d->irq, !enable); + pr_err("Failed gpio lp0 %s for irq=%d, error=%d\n", + (enable ? "enable" : "disable"), d->irq, ret); +fail: return ret; } #else diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 7eef648a3351..af6dc837ffca 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -18,14 +18,26 @@ #include <linux/errno.h> #include <linux/gpio.h> #include <linux/i2c.h> +#include <linux/platform_device.h> #include <linux/mfd/tps65910.h> +struct tps65910_gpio { + struct gpio_chip gpio_chip; + struct tps65910 *tps65910; +}; + +static inline struct tps65910_gpio *to_tps65910_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tps65910_gpio, gpio_chip); +} + static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - uint8_t val; + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; + unsigned int val; - tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val); + tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val); if (val & GPIO_STS_MASK) return 1; @@ -36,83 +48,135 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; if (value) - tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_SET_MASK); else - tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_SET_MASK); } static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; /* Set the initial value */ tps65910_gpio_set(gc, offset, value); - return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); } static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; - return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); } -void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base) +static int __devinit tps65910_gpio_probe(struct platform_device *pdev) { + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct tps65910_board *pdata = dev_get_platdata(tps65910->dev); + struct tps65910_gpio *tps65910_gpio; int ret; - struct tps65910_board *board_data; + int i; + + tps65910_gpio = devm_kzalloc(&pdev->dev, + sizeof(*tps65910_gpio), GFP_KERNEL); + if (!tps65910_gpio) { + dev_err(&pdev->dev, "Could not allocate tps65910_gpio\n"); + return -ENOMEM; + } - if (!gpio_base) - return; + tps65910_gpio->tps65910 = tps65910; - tps65910->gpio.owner = THIS_MODULE; - tps65910->gpio.label = tps65910->i2c_client->name; - tps65910->gpio.dev = tps65910->dev; - tps65910->gpio.base = gpio_base; + tps65910_gpio->gpio_chip.owner = THIS_MODULE; + tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name; switch(tps65910_chip_id(tps65910)) { case TPS65910: - tps65910->gpio.ngpio = TPS65910_NUM_GPIO; + tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO; break; case TPS65911: - tps65910->gpio.ngpio = TPS65911_NUM_GPIO; + tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO; break; default: - return; + return -EINVAL; } - tps65910->gpio.can_sleep = 1; - - tps65910->gpio.direction_input = tps65910_gpio_input; - tps65910->gpio.direction_output = tps65910_gpio_output; - tps65910->gpio.set = tps65910_gpio_set; - tps65910->gpio.get = tps65910_gpio_get; - - /* Configure sleep control for gpios */ - board_data = dev_get_platdata(tps65910->dev); - if (board_data) { - int i; - for (i = 0; i < tps65910->gpio.ngpio; ++i) { - if (board_data->en_gpio_sleep[i]) { - ret = tps65910_set_bits(tps65910, - TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); - if (ret < 0) - dev_warn(tps65910->dev, - "GPIO Sleep setting failed\n"); - } - } + tps65910_gpio->gpio_chip.can_sleep = 1; + tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input; + tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output; + tps65910_gpio->gpio_chip.set = tps65910_gpio_set; + tps65910_gpio->gpio_chip.get = tps65910_gpio_get; + tps65910_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + tps65910_gpio->gpio_chip.base = pdata->gpio_base; + else + tps65910_gpio->gpio_chip.base = -1; + + if (!pdata) + goto skip_init; + + /* Configure sleep control for gpios if provided */ + for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) { + if (!pdata->en_gpio_sleep[i]) + continue; + + ret = tps65910_reg_set_bits(tps65910, + TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); + if (ret < 0) + dev_warn(tps65910->dev, + "GPIO Sleep setting failed with err %d\n", ret); + } + +skip_init: + ret = gpiochip_add(&tps65910_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; } - ret = gpiochip_add(&tps65910->gpio); + platform_set_drvdata(pdev, tps65910_gpio); - if (ret) - dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret); + return ret; } + +static int __devexit tps65910_gpio_remove(struct platform_device *pdev) +{ + struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&tps65910_gpio->gpio_chip); +} + +static struct platform_driver tps65910_gpio_driver = { + .driver.name = "tps65910-gpio", + .driver.owner = THIS_MODULE, + .probe = tps65910_gpio_probe, + .remove = __devexit_p(tps65910_gpio_remove), +}; + +static int __init tps65910_gpio_init(void) +{ + return platform_driver_register(&tps65910_gpio_driver); +} +subsys_initcall(tps65910_gpio_init); + +static void __exit tps65910_gpio_exit(void) +{ + platform_driver_unregister(&tps65910_gpio_driver); +} +module_exit(tps65910_gpio_exit); + +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_AUTHOR("Jorge Eduardo Candelaria jedu@slimlogic.co.uk>"); +MODULE_DESCRIPTION("GPIO interface for TPS65910/TPS6511 PMICs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65910-gpio"); diff --git a/drivers/gpu/ion/tegra/Makefile b/drivers/gpu/ion/tegra/Makefile index 11cd003fb08f..0fe898c6ee49 100644 --- a/drivers/gpu/ion/tegra/Makefile +++ b/drivers/gpu/ion/tegra/Makefile @@ -1 +1,3 @@ +subdir-ccflags-y := -Werror + obj-y += tegra_ion.o diff --git a/drivers/gpu/ion/tegra/tegra_ion.c b/drivers/gpu/ion/tegra/tegra_ion.c index 2252079279e8..e929ec84fc53 100644 --- a/drivers/gpu/ion/tegra/tegra_ion.c +++ b/drivers/gpu/ion/tegra/tegra_ion.c @@ -31,7 +31,7 @@ #define HEAP_FLAGS 0xFF #if !defined(CONFIG_TEGRA_NVMAP) -#include "mach/nvmap.h" +#include "linux/nvmap.h" struct nvmap_device *nvmap_dev; #endif diff --git a/drivers/hwmon/tegra-tsensor.c b/drivers/hwmon/tegra-tsensor.c index 28597ef4cc98..1c372a4bcce5 100644 --- a/drivers/hwmon/tegra-tsensor.c +++ b/drivers/hwmon/tegra-tsensor.c @@ -97,13 +97,20 @@ #define TSENSOR_SLOWDOWN_BIT 23 /* macros used for temperature calculations */ -#define get_temperature_int(X) ((X) / 100) -#define get_temperature_fraction(X) (((int)(abs(X))) % 100) -#define get_temperature_round(X) DIV_ROUND_CLOSEST(X, 100) +/* assumed get_temperature_int and get_temperature_fraction + * calculate up to 6 decimal places. print temperature + * in code assumes 6 decimal place formatting */ +#define get_temperature_int(X) ((X) / 1000000) +#define get_temperature_fraction(X) (((int)(abs(X))) % 1000000) + +#define get_temperature_round(X) DIV_ROUND_CLOSEST(X, 1000000) #define MILLICELSIUS_TO_CELSIUS(i) ((i) / 1000) #define CELSIUS_TO_MILLICELSIUS(i) ((i) * 1000) +#define TSENSOR_MILLI_CELSIUS(x) \ + DIV_ROUND_CLOSEST((x), 1000) + #define get_ts_state(data) tsensor_get_reg_field(data,\ ((data->instance << 16) | SENSOR_STATUS0), \ STATUS0_STATE_SHIFT, STATE_MASK) @@ -188,8 +195,8 @@ struct tegra_tsensor_data { unsigned int config2[TSENSOR_COUNT]; /* temperature readings from instance tsensor - 0/1 */ unsigned int instance; - int A_e_minus6; - int B_e_minus2; + s64 A_e_minus12; + int B_e_minus6; unsigned int fuse_T1; unsigned int fuse_F1; unsigned int fuse_T2; @@ -353,6 +360,8 @@ static void get_chip_tsensor_coeff(struct tegra_tsensor_data *data) data->m_e_minus6 = coeff_table[coeff_index].e_minus6_m; data->n_e_minus6 = coeff_table[coeff_index].e_minus6_n; data->p_e_minus2 = coeff_table[coeff_index].e_minus2_p; + pr_info("tsensor coeff: m=%d*10^-6,n=%d*10^-6,p=%d*10^-2\n", + data->m_e_minus6, data->n_e_minus6, data->p_e_minus2); } /* tsensor counter read function */ @@ -480,9 +489,10 @@ static ssize_t tsensor_show_counters(struct device *dev, snprintf(buf, (((LOCAL_STR_SIZE1 << 1) + 3) + strlen(fixed_str)), - "%d.%02dC\n", + "%d.%06dC %#x\n", get_temperature_int(temp), - get_temperature_fraction(temp)); + get_temperature_fraction(temp), + ((curr_avg & 0xFFFF0000) >> 16)); } return strlen(buf); error: @@ -567,8 +577,8 @@ static ssize_t show_tsensor_param(struct device *dev, err = tsensor_count_2_temp(data, val, &temp); if (err != 0) goto labelErr; - snprintf(buf, PAGE_SIZE, "%s threshold: %d.%d Celsius\n", info, - get_temperature_int(temp), + snprintf(buf, PAGE_SIZE, "%s threshold: %d.%06d Celsius\n", + info, get_temperature_int(temp), get_temperature_fraction(temp)); } return strlen(buf); @@ -644,7 +654,7 @@ int tsensor_thermal_get_temp_low(struct tegra_tsensor_data *data, long *milli_temp) { /* temp to counter below 20C seems to be inaccurate */ - *milli_temp = 20000; + *milli_temp = 0; return 0; } @@ -660,7 +670,8 @@ int tsensor_thermal_get_temp(struct tegra_tsensor_data *data, if (err) return err; - temp *= 10; + /* temperature is in milli-Celsius */ + temp = TSENSOR_MILLI_CELSIUS(temp); mutex_lock(&data->mutex); @@ -787,8 +798,9 @@ static int read_tsensor_fuse_regs(struct tegra_tsensor_data *data) */ dev_vdbg(data->hwmon_dev, "Tsensor low temp (T2) fuse :\n"); T2 = (spare_bits & 0x7F) | ((spare_bits >> 7) & 0x7F); - pr_info("Tsensor fuse calibration F1=%d, F2=%d, T1=%d, T2=%d\n" - , data->fuse_F1, data->fuse_F2, T1, T2); + pr_info("Tsensor fuse calibration F1=%d(%#x), F2=%d(%#x), T1=%d, T2=%d\n", + data->fuse_F1, data->fuse_F1, + data->fuse_F2, data->fuse_F2, T1, T2); data->fuse_T1 = T1; data->fuse_T2 = T2; return 0; @@ -798,9 +810,14 @@ errLabel: /* function to calculate interim temperature */ static int calc_interim_temp(struct tegra_tsensor_data *data, - unsigned int counter, int *p_interim_temp) + unsigned int counter, s64 *p_interim_temp) { - int val1; + s64 val1_64; + s64 val2; + u32 temp_rem; + bool is_neg; + u32 divisor; + /* * T-int = A * Counter + B * (Counter is the sensor frequency output) @@ -817,14 +834,27 @@ static int calc_interim_temp(struct tegra_tsensor_data *data, * s_B is 10^2 times and want end result to be 10^2 times * actual value */ - val1 = DIV_ROUND_CLOSEST((data->A_e_minus6 * counter) , 10000); - dev_vdbg(data->hwmon_dev, "A*counter / 100 = %d\n", - val1); - *p_interim_temp = (val1 + data->B_e_minus2); + val1_64 = (data->A_e_minus12 * counter); + dev_dbg(data->hwmon_dev, "A_e_-12*counter=%lld\n", val1_64); + val2 = (s64)data->B_e_minus6 * 1000000ULL; + dev_dbg(data->hwmon_dev, "B_e_-12=%lld\n", val2); + val2 += val1_64; + dev_dbg(data->hwmon_dev, "A_counter+B=%lld\n", val2); + is_neg = false; + if (val2 < 0) { + is_neg = true; + val2 *= -1; + } + divisor = 1000000; + temp_rem = do_div(val2, divisor); + if (temp_rem > (divisor >> 1)) + val2++; + if (is_neg) + val2 *= -1; + *p_interim_temp = val2; + dev_dbg(data->hwmon_dev, "counter=%d, interim_temp=%lld\n", + counter, *p_interim_temp); } - dev_dbg(data->hwmon_dev, "tsensor: counter=0x%x, interim " - "temp*100=%d\n", - counter, *p_interim_temp); return 0; } @@ -833,9 +863,13 @@ static int calc_interim_temp(struct tegra_tsensor_data *data, * interim temperature */ static void calc_final_temp(struct tegra_tsensor_data *data, - int interim_temp, int *p_final_temp) + s64 interim_temp, int *p_final_temp) { - int temp1, temp2, temp; + s64 temp1_64, temp2_64, temp_64, temp1_64_rem; + u32 temp_rem_32; + u32 divisor; + u64 divisor_64; + bool is_neg; /* * T-final = m * T-int ^2 + n * T-int + p * m = -0.002775 @@ -843,34 +877,51 @@ static void calc_final_temp(struct tegra_tsensor_data *data, * p = -7.3 */ - dev_vdbg(data->hwmon_dev, "interim_temp=%d\n", interim_temp); - temp1 = (DIV_ROUND_CLOSEST((interim_temp * interim_temp) , 100)); - dev_vdbg(data->hwmon_dev, "temp1=%d\n", temp1); - temp1 *= (DIV_ROUND_CLOSEST(data->m_e_minus6 , 10)); - dev_vdbg(data->hwmon_dev, "m*T-int^2=%d\n", temp1); - temp1 = (DIV_ROUND_CLOSEST(temp1, 10000)); - /* we want to keep 3 decimal point digits */ - dev_vdbg(data->hwmon_dev, "m*T-int^2 / 10000=%d\n", temp1); - dev_dbg(data->hwmon_dev, "temp1*100=%d\n", temp1); - - temp2 = (DIV_ROUND_CLOSEST(interim_temp * ( - DIV_ROUND_CLOSEST(data->n_e_minus6, 100) - ), 1000)); /* 1000 times actual */ - dev_vdbg(data->hwmon_dev, "n*T-int =%d\n", temp2); - - temp = temp1 + temp2; - dev_vdbg(data->hwmon_dev, "m*T-int^2 + n*T-int =%d\n", temp); - temp += (data->p_e_minus2 * 10); - temp = DIV_ROUND_CLOSEST(temp, 10); - /* final temperature(temp) is 100 times actual value - * to preserve 2 decimal digits and enable fixed point - * computation - */ - dev_vdbg(data->hwmon_dev, "m*T-int^2 + n*T-int + p =%d\n", - temp); - dev_dbg(data->hwmon_dev, "Final temp=%d.%d\n", - get_temperature_int(temp), get_temperature_fraction(temp)); - *p_final_temp = (int)(temp); + temp1_64 = (interim_temp * interim_temp); + /* always positive as squaring value */ + /* losing accuracy here */ + divisor = 10000; + /* temp1_64 contains quotient and returns remainder */ + temp_rem_32 = do_div(temp1_64, divisor); + if (temp_rem_32 > (divisor >> 1)) + temp1_64++; + temp1_64 *= (s64)data->m_e_minus6; + dev_dbg(data->hwmon_dev, "m_T-interim^2_e^14=%lld\n", temp1_64); + temp1_64_rem = (s64)data->m_e_minus6 * (s64)temp_rem_32; + is_neg = false; + if (temp1_64_rem < 0) { + is_neg = true; + temp1_64_rem *= -1; + } + temp_rem_32 = do_div(temp1_64_rem, divisor); + if (temp_rem_32 > (divisor >> 1)) + temp1_64_rem++; + if (is_neg) + temp1_64_rem *= -1; + /* temp1_64 is m * t-int * t-int * 10^14 */ + + temp2_64 = (s64)data->n_e_minus6 * interim_temp * 100; + dev_dbg(data->hwmon_dev, "n_T-interim_e^14=%lld\n", temp2_64); + /* temp2_64 is n * t-int * 10^14 */ + + temp_64 = ((s64)data->p_e_minus2 * (s64)1000000000000ULL); + /* temp_64 is n * 10^14 */ + temp_64 += temp1_64 + temp2_64 + temp1_64_rem; + is_neg = false; + if (temp_64 < 0) { + is_neg = true; + temp_64 *= -1; + } + divisor_64 = 100000000ULL; + temp_rem_32 = do_div(temp_64, divisor_64); + if (temp_rem_32 > (divisor_64 >> 1)) + temp_64++; + if (is_neg) + temp_64 *= -1; + /* temperature * 10^14 / 10^8 */ + /* get LS decimal digit rounding */ + *p_final_temp = (s32)temp_64; + dev_dbg(data->hwmon_dev, "T-final stage4=%d\n", *p_final_temp); } /* @@ -882,6 +933,10 @@ static void calc_final_temp(struct tegra_tsensor_data *data, static int tsensor_get_const_AB(struct tegra_tsensor_data *data) { int err; + s64 temp_val1, temp_val2; + u32 temp_rem; + bool is_neg; + u32 divisor; /* * 1. Find fusing registers for 25C (T1, F1) and 90C (T2, F2); @@ -902,17 +957,31 @@ static int tsensor_get_const_AB(struct tegra_tsensor_data *data) "computation\n", data->fuse_F2, data->fuse_F1); return -EINVAL; } else { - data->A_e_minus6 = ((data->fuse_T2 - data->fuse_T1) * - 1000000); - data->A_e_minus6 /= (data->fuse_F2 - data->fuse_F1); - data->B_e_minus2 = (data->fuse_T1 * 100) - ( - DIV_ROUND_CLOSEST((data->A_e_minus6 * - data->fuse_F1), 10000)); - /* B is 100 times now */ + temp_val1 = (s64)(data->fuse_T2 - data->fuse_T1) * + 1000000000000ULL; + /* temp_val1 always positive as fuse_T2 > fuse_T1 */ + temp_rem = do_div(temp_val1, (data->fuse_F2 - + data->fuse_F1)); + data->A_e_minus12 = temp_val1; + temp_val2 = (s64)(data->fuse_T1 * 1000000000000ULL); + temp_val2 -= (data->A_e_minus12 * data->fuse_F1); + is_neg = false; + if (temp_val2 < 0) { + is_neg = true; + temp_val2 *= -1; + } + divisor = 1000000; + temp_rem = do_div(temp_val2, divisor); + if (temp_rem > (divisor >> 1)) + temp_val2++; + if (is_neg) + temp_val2 *= -1; + data->B_e_minus6 = (s32)temp_val2; + /* B is 10^6 times now */ } } - dev_dbg(data->hwmon_dev, "A_e_minus6 = %d\n", data->A_e_minus6); - dev_dbg(data->hwmon_dev, "B_e_minus2 = %d\n", data->B_e_minus2); + dev_info(data->hwmon_dev, "A_e_minus12 = %lld\n", data->A_e_minus12); + dev_info(data->hwmon_dev, "B_e_minus6 = %d\n", data->B_e_minus6); return 0; } @@ -925,7 +994,7 @@ static int tsensor_get_const_AB(struct tegra_tsensor_data *data) static int tsensor_count_2_temp(struct tegra_tsensor_data *data, unsigned int count, int *p_temperature) { - int interim_temp; + s64 interim_temp; int err; /* @@ -944,28 +1013,16 @@ static int tsensor_count_2_temp(struct tegra_tsensor_data *data, * 3. Calculate final temperature: */ calc_final_temp(data, interim_temp, p_temperature); + /* logs counter -> temperature conversion */ + dev_dbg(data->hwmon_dev, "tsensor: counter=0x%x, interim " + "temp*10^6=%lld, Final temp=%d.%06d\n", + count, interim_temp, + get_temperature_int(*p_temperature), + get_temperature_fraction(*p_temperature)); return 0; } /* - * utility function implements ceil to power of 10 - - * e.g. given 987 it returns 1000 - */ -static int my_ceil_pow10(int num) -{ - int tmp; - int val = 1; - tmp = (num < 0) ? -num : num; - if (tmp == 0) - return 0; - while (tmp > 1) { - val *= 10; - tmp /= 10; - } - return val; -} - -/* * function to solve quadratic roots of equation * used to get counter corresponding to given temperature */ @@ -973,104 +1030,140 @@ static void get_quadratic_roots(struct tegra_tsensor_data *data, int temp, unsigned int *p_counter1, unsigned int *p_counter2) { - /* expr1 = 2 * m * B + n */ - int expr1_e_minus6; - /* expr2 = expr1^2 */ - int expr2_e_minus6; - /* expr3 = m * B^2 + n * B + p */ - int expr3_e_minus4_1; - int expr3_e_minus4_2; - int expr3_e_minus4; - int expr4_e_minus6; - int expr4_e_minus2_1; - int expr4_e_minus6_2; - int expr4_e_minus6_3; - int expr5_e_minus6, expr5_e_minus6_1, expr6, expr7; - int expr8_e_minus6, expr9_e_minus6; - int multiplier; - const int multiplier2 = 1000000; - int expr10_e_minus6, expr11_e_minus6; - int expr12, expr13; - - dev_vdbg(data->hwmon_dev, "A_e_minus6=%d, B_e_minus2=%d, " + /* + * Equation to solve: + * m * A^2 * Counter^2 + + * A * (2 * m * B + n) * Counter + + * (m * B^2 + n * B + p - Temperature) = 0 + + To calculate root - assume + b = A * (2 * m * B + n) + a = m * A^2 + c = ((m * B^2) + n * B + p - temp) + root1 = (-b + sqrt(b^2 - (4*a*c))) / (2 * a) + root2 = (-b - sqrt(b^2 - (4*a*c))) / (2 * a) + sqrt(k) = sqrt(k * 10^6) / sqrt(10^6) + + Roots are : + (-(2*m*B+n)+sqrt(((2*m*B+n)^2-4*m(m*B^2+n*B+p-temp))))/(2*m*A) + and + (-(2*m*B+n)-sqrt(((2*m*B+n)^2-4*m(m*B^2+n*B+p-temp))))/(2*m*A) + + */ + + int v_e_minus6_2mB_n; + int v_e_minus4_mB2_nB_p_minusTemp; + int v_e_minus6_b2, v_e_minus6_4ac; + int v_e_minus6_b2_minus4ac; + int v_e_minus6_sqrt_b2_minus4ac; + s64 v_e_minus12_2mA; + int root1, root2; + int temp_rem; + bool is_neg; + s64 temp_64; + + dev_dbg(data->hwmon_dev, "m_e-6=%d,n_e-6=%d,p_e-2=%d,A_e-6=%lld," + "B_e-2=%d\n", data->m_e_minus6, data->n_e_minus6, + data->p_e_minus2, data->A_e_minus12, data->B_e_minus6); + + temp_64 = (2ULL * (s64)data->m_e_minus6 * (s64)data->B_e_minus6); + is_neg = false; + if (temp_64 < 0) { + is_neg = true; + temp_64 *= -1; + } + temp_rem = do_div(temp_64, 1000000); + if (is_neg) + temp_64 *= -1; + v_e_minus6_2mB_n = (s32)temp_64 + data->n_e_minus6; + /* computed 2mB + n */ + + temp_64 = ((s64)data->m_e_minus6 * (s64)data->A_e_minus12); + temp_64 *= 2; + is_neg = false; + if (temp_64 < 0) { + temp_64 *= -1; + is_neg = true; + } + temp_rem = do_div(temp_64, 1000000); + if (is_neg) + temp_64 *= -1; + v_e_minus12_2mA = temp_64; + /* computed 2mA */ + + /* m * B^2 calculation */ + temp_64 = ((s64)data->B_e_minus6 * (s64)data->B_e_minus6); + /* squaring give positive value */ + temp_rem = do_div(temp_64, 1000000); + /* we see overflow if do not divide above */ + temp_64 *= data->m_e_minus6; + is_neg = false; + if (temp_64 < 0) { + is_neg = true; + temp_64 *= -1; + } + temp_rem = do_div(temp_64, 1000000); + temp_rem = do_div(temp_64, 100); + if (is_neg) + temp_64 *= -1; + v_e_minus4_mB2_nB_p_minusTemp = (s32)temp_64; + + /* n * B calculation */ + temp_64 = ((s64)data->B_e_minus6 * (s64)data->n_e_minus6); + is_neg = false; + if (temp_64 < 0) { + is_neg = true; + temp_64 *= -1; + } + temp_rem = do_div(temp_64, 1000000); + temp_rem = do_div(temp_64, 100); + if (is_neg) + temp_64 *= -1; + temp_rem = (s32)temp_64; + v_e_minus4_mB2_nB_p_minusTemp += temp_rem; + v_e_minus4_mB2_nB_p_minusTemp += ( + (data->p_e_minus2 * 100) - (temp * 10000)); + /* computed ((m * B^2) + n * B + p - temp) * 10^4 */ + + v_e_minus6_b2 = ((v_e_minus6_2mB_n / 1000) + * (v_e_minus6_2mB_n / 1000)); + dev_dbg(data->hwmon_dev, "v_e_minus6_b2=%d\n", v_e_minus6_b2); + + v_e_minus6_4ac = ((4 * data->m_e_minus6) / 10) + * ((v_e_minus4_mB2_nB_p_minusTemp) / 1000); + dev_dbg(data->hwmon_dev, "v_e_minus6_4ac=%d\n", v_e_minus6_4ac); + + v_e_minus6_b2_minus4ac = (v_e_minus6_b2 - v_e_minus6_4ac); + + v_e_minus6_sqrt_b2_minus4ac = DIV_ROUND_CLOSEST( + (int_sqrt(v_e_minus6_b2_minus4ac)*1000000), + int_sqrt(1000000)); + dev_dbg(data->hwmon_dev, "A_e_minus12=%lld, B_e_minus6=%d, " "m_e_minus6=%d, n_e_minus6=%d, p_e_minus2=%d, " - "temp=%d\n", data->A_e_minus6, data->B_e_minus2, + "temp=%d\n", data->A_e_minus12, data->B_e_minus6, data->m_e_minus6, data->n_e_minus6, data->p_e_minus2, (int)temp); - expr1_e_minus6 = (DIV_ROUND_CLOSEST((2 * data->m_e_minus6 * - data->B_e_minus2), 100) + data->n_e_minus6); - dev_vdbg(data->hwmon_dev, "2_m_B_plun_e_minus6=%d\n", - expr1_e_minus6); - expr2_e_minus6 = (DIV_ROUND_CLOSEST(expr1_e_minus6, 1000)) * - (DIV_ROUND_CLOSEST(expr1_e_minus6, 1000)); - dev_vdbg(data->hwmon_dev, "expr1^2=%d\n", expr2_e_minus6); - expr3_e_minus4_1 = (DIV_ROUND_CLOSEST(( - (DIV_ROUND_CLOSEST((data->m_e_minus6 * data->B_e_minus2), - 1000)) * (DIV_ROUND_CLOSEST(data->B_e_minus2, 10))), 100)); - dev_vdbg(data->hwmon_dev, "expr3_e_minus4_1=%d\n", - expr3_e_minus4_1); - expr3_e_minus4_2 = DIV_ROUND_CLOSEST( - (DIV_ROUND_CLOSEST(data->n_e_minus6, 100) * data->B_e_minus2), - 100); - dev_vdbg(data->hwmon_dev, "expr3_e_minus4_2=%d\n", - expr3_e_minus4_2); - expr3_e_minus4 = expr3_e_minus4_1 + expr3_e_minus4_2; - dev_vdbg(data->hwmon_dev, "expr3=%d\n", expr3_e_minus4); - expr4_e_minus2_1 = DIV_ROUND_CLOSEST((expr3_e_minus4 + - (data->p_e_minus2 * 100)), 100); - dev_vdbg(data->hwmon_dev, "expr4_e_minus2_1=%d\n", - expr4_e_minus2_1); - expr4_e_minus6_2 = (4 * data->m_e_minus6); - dev_vdbg(data->hwmon_dev, "expr4_e_minus6_2=%d\n", - expr4_e_minus6_2); - expr4_e_minus6 = DIV_ROUND_CLOSEST((expr4_e_minus2_1 * - expr4_e_minus6_2), 100); - dev_vdbg(data->hwmon_dev, "expr4_minus6=%d\n", expr4_e_minus6); - expr5_e_minus6_1 = expr2_e_minus6 - expr4_e_minus6; - dev_vdbg(data->hwmon_dev, "expr5_e_minus6_1=%d\n", - expr5_e_minus6_1); - expr4_e_minus6_3 = (expr4_e_minus6_2 * temp); - dev_vdbg(data->hwmon_dev, "expr4_e_minus6_3=%d\n", - expr4_e_minus6_3); - expr5_e_minus6 = (expr5_e_minus6_1 + expr4_e_minus6_3); - dev_vdbg(data->hwmon_dev, "expr5_e_minus6=%d\n", - expr5_e_minus6); - multiplier = my_ceil_pow10(expr5_e_minus6); - dev_vdbg(data->hwmon_dev, "multiplier=%d\n", multiplier); - expr6 = int_sqrt(expr5_e_minus6); - dev_vdbg(data->hwmon_dev, "sqrt top=%d\n", expr6); - expr7 = int_sqrt(multiplier); - dev_vdbg(data->hwmon_dev, "sqrt bot=%d\n", expr7); - if (expr7 == 0) { - pr_err("Error: %s line=%d, expr7=%d\n", - __func__, __LINE__, expr7); - return; - } else { - expr8_e_minus6 = (expr6 * multiplier2) / expr7; - } - dev_vdbg(data->hwmon_dev, "sqrt final=%d\n", expr8_e_minus6); - dev_vdbg(data->hwmon_dev, "2_m_B_plus_n_e_minus6=%d\n", - expr1_e_minus6); - expr9_e_minus6 = DIV_ROUND_CLOSEST((2 * data->m_e_minus6 * - data->A_e_minus6), 1000000); - dev_vdbg(data->hwmon_dev, "denominator=%d\n", expr9_e_minus6); - if (expr9_e_minus6 == 0) { - pr_err("Error: %s line=%d, expr9_e_minus6=%d\n", - __func__, __LINE__, expr9_e_minus6); - return; - } - expr10_e_minus6 = -expr1_e_minus6 - expr8_e_minus6; - dev_vdbg(data->hwmon_dev, "expr10_e_minus6=%d\n", - expr10_e_minus6); - expr11_e_minus6 = -expr1_e_minus6 + expr8_e_minus6; - dev_vdbg(data->hwmon_dev, "expr11_e_minus6=%d\n", - expr11_e_minus6); - expr12 = (expr10_e_minus6 / expr9_e_minus6); - dev_vdbg(data->hwmon_dev, "counter1=%d\n", expr12); - expr13 = (expr11_e_minus6 / expr9_e_minus6); - dev_vdbg(data->hwmon_dev, "counter2=%d\n", expr13); - *p_counter1 = expr12; - *p_counter2 = expr13; + dev_dbg(data->hwmon_dev, "2mB_n=%d, 2mA=%lld, mB2_nB_p_minusTemp=%d," + "b2_minus4ac=%d\n", v_e_minus6_2mB_n, + v_e_minus12_2mA, v_e_minus4_mB2_nB_p_minusTemp, + v_e_minus6_b2_minus4ac); + + temp_64=(s64)(-v_e_minus6_2mB_n - v_e_minus6_sqrt_b2_minus4ac) * 1000000; + root1=(s32)div64_s64(temp_64, v_e_minus12_2mA); + + temp_64=(s64)(-v_e_minus6_2mB_n + v_e_minus6_sqrt_b2_minus4ac) * 1000000; + root2=(s32)div64_s64(temp_64, v_e_minus12_2mA); + + dev_dbg(data->hwmon_dev, "new expr: temp=%d, root1=%d, root2=%d\n", + temp, root1, root2); + + *p_counter1 = root1; + *p_counter2 = root2; + /* we find that root2 is more appropriate root */ + + /* logs temperature -> counter conversion */ + dev_dbg(data->hwmon_dev, "temperature=%d, counter1=%#x, " + "counter2=%#x\n", temp, *p_counter1, *p_counter2); } /* @@ -1083,29 +1176,24 @@ static void tsensor_temp_2_count(struct tegra_tsensor_data *data, unsigned int *p_counter1, unsigned int *p_counter2) { - if (temp > 0) { - dev_dbg(data->hwmon_dev, "Trying to calculate counter" - " for requested temperature" - " threshold=%d\n", temp); - /* - * calculate the constants needed to get roots of - * following quadratic eqn: - * m * A^2 * Counter^2 + - * A * (2 * m * B + n) * Counter + - * (m * B^2 + n * B + p - Temperature) = 0 - */ - get_quadratic_roots(data, temp, p_counter1, p_counter2); - /* - * checked at current temperature=35 the counter=11418 - * for 50 deg temperature: counter1=22731, counter2=11817 - * at 35 deg temperature: counter1=23137, counter2=11411 - * hence, for above values we are assuming counter2 has - * the correct value - */ - } else { - *p_counter1 = DEFAULT_THRESHOLD_TH3; - *p_counter2 = DEFAULT_THRESHOLD_TH3; - } + dev_dbg(data->hwmon_dev, "Trying to calculate counter" + " for requested temperature" + " threshold=%d\n", temp); + /* + * calculate the constants needed to get roots of + * following quadratic eqn: + * m * A^2 * Counter^2 + + * A * (2 * m * B + n) * Counter + + * (m * B^2 + n * B + p - Temperature) = 0 + */ + get_quadratic_roots(data, temp, p_counter1, p_counter2); + /* + * checked at current temperature=35 the counter=11418 + * for 50 deg temperature: counter1=22731, counter2=11817 + * at 35 deg temperature: counter1=23137, counter2=11411 + * hence, for above values we are assuming counter2 has + * the correct value + */ } /* @@ -1129,46 +1217,80 @@ static bool cmp_counter( return true; } +/* function to print chart of counter to temperature values - + * It uses F1, F2, T1, T2 and start data gives reading + * for temperature in between the range + */ +static void print_counter_2_temperature_table( + struct tegra_tsensor_data *data) +{ + int i; + unsigned int start_counter, end_counter; + unsigned int diff; + int temperature; + const unsigned int num_readings = 40; + unsigned int index = 0; + dev_dbg(data->hwmon_dev, "***Counter and Temperature chart **********\n"); + start_counter = data->fuse_F1; + end_counter = data->fuse_F2; + diff = (end_counter - start_counter) / num_readings; + + /* We want to take num_readings counter values in between + and try to report corresponding temperature */ + for (i = start_counter; i <= (end_counter + diff); + i += diff) { + tsensor_count_2_temp(data, i, &temperature); + dev_dbg(data->hwmon_dev, "[%d]: Counter=%#x, temperature=%d.%06dC\n", + ++index, i, get_temperature_int(temperature), + get_temperature_fraction(temperature)); + } + dev_dbg(data->hwmon_dev, "\n\n"); + tsensor_count_2_temp(data, end_counter, &temperature); + dev_dbg(data->hwmon_dev, "[%d]: Counter=%#x, temperature=%d.%06dC\n", + ++index, end_counter, get_temperature_int(temperature), + get_temperature_fraction(temperature)); +} + +static bool temp_matched(int given_temp, int calc_temp) +{ + const int temp_diff_max = 4; + int diff; + + diff = given_temp - calc_temp; + if (diff < 0) + diff *= -1; + if (diff > temp_diff_max) + return false; + else + return true; +} + /* function to print chart of temperature to counter values */ static void print_temperature_2_counter_table( struct tegra_tsensor_data *data) { int i; - /* static list of temperature tested */ - int temp_list[] = { - 30, - 35, - 40, - 45, - 50, - 55, - 60, - 61, - 62, - 63, - 64, - 65, - 70, - 75, - 80, - 85, - 90, - 95, - 100, - 105, - 110, - 115, - 120 - }; + int min = -25; + int max = 120; unsigned int counter1, counter2; + int temperature; + dev_dbg(data->hwmon_dev, "Temperature and counter1 and " "counter2 chart **********\n"); - for (i = 0; i < ARRAY_SIZE(temp_list); i++) { - tsensor_temp_2_count(data, temp_list[i], + for (i = min; i <= max; i++) { + tsensor_temp_2_count(data, i, &counter1, &counter2); - dev_dbg(data->hwmon_dev, "temperature[%d]=%d, " + dev_dbg(data->hwmon_dev, "temperature=%d, " "counter1=0x%x, counter2=0x%x\n", - i, temp_list[i], counter1, counter2); + i, counter1, counter2); + /* verify the counter2 to temperature conversion */ + tsensor_count_2_temp(data, counter2, &temperature); + dev_dbg(data->hwmon_dev, "Given temp=%d: counter2=%d, conv temp=%d.%06d\n", + i, counter2, get_temperature_int(temperature), + get_temperature_fraction(temperature)); + if (!temp_matched(i, get_temperature_round(temperature))) + dev_dbg(data->hwmon_dev, "tsensor temp to counter to temp conversion failed for temp=%d\n", + i); } dev_dbg(data->hwmon_dev, "\n\n"); } @@ -1226,7 +1348,7 @@ static int test_temperature_algo(struct tegra_tsensor_data *data) /* calculate temperature */ err = tsensor_count_2_temp(data, actual_counter, &T1); dev_dbg(data->hwmon_dev, "%s actual counter=0x%x, calculated " - "temperature=%d.%d\n", __func__, + "temperature=%d.%06d\n", __func__, actual_counter, get_temperature_int(T1), get_temperature_fraction(T1)); if (err < 0) { @@ -1552,6 +1674,9 @@ static void tsensor_work_func(struct work_struct *work) data->alert_func(data->alert_data); if (!tsensor_within_limits(data)) + dev_dbg(data->hwmon_dev, + "repeated work queueing state=%d\n", + get_ts_state(data)); queue_delayed_work(data->workqueue, &data->work, HZ * DEFAULT_TSENSOR_M / DEFAULT_TSENSOR_CLK_HZ); @@ -1656,6 +1781,8 @@ static int tegra_tsensor_setup(struct platform_device *pdev) print_temperature_2_counter_table(data); + print_counter_2_temperature_table(data); + /* EDP and throttling support using tsensor enabled * based on fuse revision */ err = tegra_fuse_get_revision(®); @@ -1885,6 +2012,8 @@ static int tsensor_suspend(struct platform_device *pdev, struct tegra_tsensor_data *data = platform_get_drvdata(pdev); unsigned int config0; + disable_irq(data->irq); + cancel_delayed_work_sync(&data->work); /* set STOP bit, else OVERFLOW interrupt seen in LP1 */ config0 = tsensor_readl(data, ((data->instance << 16) | SENSOR_CFG0)); config0 |= (1 << SENSOR_CFG0_STOP_SHIFT); @@ -1915,6 +2044,7 @@ static int tsensor_resume(struct platform_device *pdev) if (data->is_edp_supported) schedule_delayed_work(&data->work, 0); + enable_irq(data->irq); return 0; } #endif diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 25ad6dca3884..1358dc70b0e4 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -331,12 +331,19 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) { u32 val; int tx_fifo_avail; - u8 *buf = i2c_dev->msg_buf; - size_t buf_remaining = i2c_dev->msg_buf_remaining; + u8 *buf; + size_t buf_remaining; int words_to_transfer; unsigned long flags; spin_lock_irqsave(&i2c_dev->fifo_lock, flags); + if (!i2c_dev->msg_buf_remaining) { + spin_unlock_irqrestore(&i2c_dev->fifo_lock, flags); + return 0; + } + + buf = i2c_dev->msg_buf; + buf_remaining = i2c_dev->msg_buf_remaining; val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >> @@ -375,7 +382,12 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) * boundary and fault. */ if (tx_fifo_avail > 0 && buf_remaining > 0) { - BUG_ON(buf_remaining > 3); + if (buf_remaining > 3) { + dev_err(i2c_dev->dev, + "Remaining buffer more than 3 %d\n", + buf_remaining); + BUG(); + } memcpy(&val, buf, buf_remaining); /* Again update before writing to FIFO to make sure isr sees. */ @@ -583,9 +595,12 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) } i2c_writel(i2c_dev, status, I2C_INT_STATUS); + i2c_readl(i2c_dev, I2C_INT_STATUS); - if (i2c_dev->is_dvc) + if (i2c_dev->is_dvc) { dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + dvc_readl(i2c_dev, DVC_STATUS); + } if (status & I2C_INT_PACKET_XFER_COMPLETE) { BUG_ON(i2c_dev->msg_buf_remaining); @@ -621,13 +636,16 @@ err: I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_TX_FIFO_OVERFLOW); i2c_writel(i2c_dev, status, I2C_INT_STATUS); + i2c_readl(i2c_dev, I2C_INT_STATUS); /* An error occured, mask dvc interrupt */ if (i2c_dev->is_dvc) dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN); - if (i2c_dev->is_dvc) + if (i2c_dev->is_dvc) { dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + dvc_readl(i2c_dev, DVC_STATUS); + } complete(&i2c_dev->msg_complete); return IRQ_HANDLED; diff --git a/drivers/input/misc/cm3217.c b/drivers/input/misc/cm3217.c index 9c9b60d69619..416d0b3806a9 100644 --- a/drivers/input/misc/cm3217.c +++ b/drivers/input/misc/cm3217.c @@ -3,6 +3,8 @@ * Copyright (C) 2011 Capella Microsystems Inc. * Author: Frank Hsieh <pengyueh@gmail.com> * + * Copyright (c) 2012, NVIDIA Corporation. + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. @@ -36,11 +38,11 @@ #include <asm/mach-types.h> #include <asm/setup.h> -#define D(x...) pr_info(x) +#define D(x...) pr_debug(x) #define I2C_RETRY_COUNT 10 -#define LS_POLLING_DELAY 500 +#define LS_POLLING_DELAY 1000 /* mSec */ static void report_do_work(struct work_struct *w); static DECLARE_DELAYED_WORK(report_work, report_do_work); @@ -382,20 +384,8 @@ static int lightsensor_enable(struct cm3217_info *lpi) if (ret < 0) pr_err("[LS][CM3217 error]%s: set auto light sensor fail\n", __func__); - else { - msleep(50); /* wait for 50 ms for the first report adc */ - - /* report an invalid value first to ensure we - * trigger an event when adc_level is zero. - */ - input_report_abs(lpi->ls_input_dev, ABS_MISC, -1); - input_sync(lpi->ls_input_dev); - /* resume, IOCTL and DEVICE_ATTR */ - report_lsensor_input_event(lpi, 1); - lpi->als_enable = 1; - } - queue_delayed_work(lpi->lp_wq, &report_work, lpi->polling_delay); + queue_work(lpi->lp_wq, &report_work); lpi->als_enable = 1; mutex_unlock(&als_enable_mutex); @@ -458,8 +448,10 @@ static int lightsensor_release(struct inode *inode, struct file *file) static long lightsensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int rc, val; + int rc = 0; + int val; struct cm3217_info *lpi = lp_info; + unsigned long delay; /* D("[CM3217] %s cmd %d\n", __func__, _IOC_NR(cmd)); */ @@ -481,6 +473,17 @@ static long lightsensor_ioctl(struct file *file, unsigned int cmd, rc = put_user(val, (unsigned long __user *)arg); break; + case LIGHTSENSOR_IOCTL_SET_DELAY: + if (get_user(delay, (unsigned long __user *)arg)) { + rc = -EFAULT; + break; + } + D("[LS][CM3217] %s LIGHTSENSOR_IOCTL_SET_DELAY, delay %ld\n", + __func__, delay); + delay = delay / 1000; + lpi->polling_delay = msecs_to_jiffies(delay); + break; + default: pr_err("[LS][CM3217 error]%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.c b/drivers/input/touchscreen/rmi4/rmi_driver.c index 6aeb3b93b2c5..78b30095b086 100644 --- a/drivers/input/touchscreen/rmi4/rmi_driver.c +++ b/drivers/input/touchscreen/rmi4/rmi_driver.c @@ -106,9 +106,9 @@ static ssize_t rmi_delay_store(struct device *dev, static struct device_attribute attrs[] = { __ATTR(hasbsr, RMI_RO_ATTR, rmi_driver_hasbsr_show, rmi_store_error), - __ATTR(bsr, RMI_RW_ATTR, + __ATTR(bsr, RMI_RO_ATTR, rmi_driver_bsr_show, rmi_driver_bsr_store), - __ATTR(enabled, RMI_RW_ATTR, + __ATTR(enabled, RMI_RO_ATTR, rmi_driver_enabled_show, rmi_driver_enabled_store), __ATTR(phys, RMI_RO_ATTR, rmi_driver_phys_show, rmi_store_error), diff --git a/drivers/input/touchscreen/rmi4/rmi_f01.c b/drivers/input/touchscreen/rmi4/rmi_f01.c index ef9adb0586b9..6ccdf2443609 100644 --- a/drivers/input/touchscreen/rmi4/rmi_f01.c +++ b/drivers/input/touchscreen/rmi4/rmi_f01.c @@ -187,13 +187,13 @@ static struct device_attribute fn_01_attrs[] = { rmi_fn_01_datecode_show, rmi_store_error), /* control register access */ - __ATTR(sleepmode, RMI_RW_ATTR, + __ATTR(sleepmode, RMI_RO_ATTR, rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store), - __ATTR(nosleep, RMI_RW_ATTR, + __ATTR(nosleep, RMI_RO_ATTR, rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store), - __ATTR(chargerinput, RMI_RW_ATTR, + __ATTR(chargerinput, RMI_RO_ATTR, rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store), - __ATTR(reportrate, RMI_RW_ATTR, + __ATTR(reportrate, RMI_RO_ATTR, rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store), /* We make report rate RO, since the driver uses that to look for * resets. We don't want someone faking us out by changing that @@ -203,7 +203,7 @@ static struct device_attribute fn_01_attrs[] = { rmi_fn_01_configured_show, rmi_store_error), /* Command register access. */ - __ATTR(reset, RMI_WO_ATTR, + __ATTR(reset, RMI_RO_ATTR, rmi_show_error, rmi_fn_01_reset_store), /* STatus register access. */ diff --git a/drivers/input/touchscreen/rmi4/rmi_f09.c b/drivers/input/touchscreen/rmi4/rmi_f09.c index 0ec980d7db07..4328d49ddcbf 100644 --- a/drivers/input/touchscreen/rmi4/rmi_f09.c +++ b/drivers/input/touchscreen/rmi4/rmi_f09.c @@ -86,13 +86,13 @@ static ssize_t rmi_f09_HostTestEn_show(struct device *dev, static ssize_t rmi_f09_HostTestEn_store(struct device *dev, struct device_attribute *attr, - char *buf, size_t count); - -static ssize_t rmi_f09_InternalLimits_show(struct device *dev, - struct device_attribute *attr, char *buf); + const char *buf, size_t count); static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev, struct device_attribute *attr, char *buf); +#if defined(RMI_SYS_ATTR) +static ssize_t rmi_f09_InternalLimits_show(struct device *dev, + struct device_attribute *attr, char *buf); static ssize_t rmi_f09_Overall_BIST_Result_show(struct device *dev, struct device_attribute *attr, char *buf); @@ -100,6 +100,7 @@ static ssize_t rmi_f09_Overall_BIST_Result_show(struct device *dev, static ssize_t rmi_f09_Overall_BIST_Result_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +#endif static struct device_attribute attrs[] = { __ATTR(Limit_Register_Count, RMI_RO_ATTR, @@ -119,7 +120,6 @@ static int rmi_f09_init(struct rmi_function_container *fc) struct rmi_fn_09_data *f09; u8 query_base_addr; int rc; - int i; int attr_count = 0; int retval = 0; @@ -171,8 +171,8 @@ static void rmi_f09_remove(struct rmi_function_container *fc) { struct rmi_fn_09_data *data = fc->data; if (data) { - kfree(data->query.Limit_Register_Count); - kfree(data->query.f09_bist_query1); + kfree(&data->query.Limit_Register_Count); + kfree(&data->query.f09_bist_query1); } kfree(fc->data); } @@ -230,7 +230,7 @@ static ssize_t rmi_f09_HostTestEn_show(struct device *dev, static ssize_t rmi_f09_HostTestEn_store(struct device *dev, struct device_attribute *attr, - char *buf, size_t count) + const char *buf, size_t count) { struct rmi_function_container *fc; struct rmi_fn_09_data *data; @@ -264,7 +264,8 @@ static ssize_t rmi_f09_HostTestEn_store(struct device *dev, } -static ssize_t rmi_f09_InternalLimits_show(struct device *dev, +#if defined(RMI_SYS_ATTR) + ssize_t rmi_f09_InternalLimits_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -276,6 +277,7 @@ static ssize_t rmi_f09_InternalLimits_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", data->query.InternalLimits); } +#endif static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev, struct device_attribute *attr, diff --git a/drivers/input/touchscreen/rmi4/rmi_f11.c b/drivers/input/touchscreen/rmi4/rmi_f11.c index 35bb945143de..7530720d61f1 100644 --- a/drivers/input/touchscreen/rmi4/rmi_f11.c +++ b/drivers/input/touchscreen/rmi4/rmi_f11.c @@ -99,15 +99,15 @@ static ssize_t rmi_f11_rezero_store(struct device *dev, static struct device_attribute attrs[] = { - __ATTR(flip, RMI_RW_ATTR, rmi_fn_11_flip_show, rmi_fn_11_flip_store), - __ATTR(clip, RMI_RW_ATTR, rmi_fn_11_clip_show, rmi_fn_11_clip_store), - __ATTR(offset, RMI_RW_ATTR, + __ATTR(flip, RMI_RO_ATTR, rmi_fn_11_flip_show, rmi_fn_11_flip_store), + __ATTR(clip, RMI_RO_ATTR, rmi_fn_11_clip_show, rmi_fn_11_clip_store), + __ATTR(offset, RMI_RO_ATTR, rmi_fn_11_offset_show, rmi_fn_11_offset_store), - __ATTR(swap, RMI_RW_ATTR, rmi_fn_11_swap_show, rmi_fn_11_swap_store), - __ATTR(relreport, RMI_RW_ATTR, + __ATTR(swap, RMI_RO_ATTR, rmi_fn_11_swap_show, rmi_fn_11_swap_store), + __ATTR(relreport, RMI_RO_ATTR, rmi_fn_11_relreport_show, rmi_fn_11_relreport_store), __ATTR(maxPos, RMI_RO_ATTR, rmi_fn_11_maxPos_show, rmi_store_error), - __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, rmi_f11_rezero_store) + __ATTR(rezero, RMI_RO_ATTR, rmi_show_error, rmi_f11_rezero_store) }; diff --git a/drivers/input/touchscreen/rmi4/rmi_f34.c b/drivers/input/touchscreen/rmi4/rmi_f34.c index 33e84d2cfb24..abade244b834 100644 --- a/drivers/input/touchscreen/rmi4/rmi_f34.c +++ b/drivers/input/touchscreen/rmi4/rmi_f34.c @@ -124,9 +124,9 @@ static struct device_attribute attrs[] = { rmi_fn_34_status_show, rmi_store_error), /* Also, sysfs will need to have a file set up to distinguish * between commands - like Config write/read, Image write/verify. */ - __ATTR(cmd, RMI_RW_ATTR, + __ATTR(cmd, RMI_RO_ATTR, rmi_fn_34_cmd_show, rmi_fn_34_cmd_store), - __ATTR(bootloaderid, RMI_RW_ATTR, + __ATTR(bootloaderid, RMI_RO_ATTR, rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store), __ATTR(blocksize, RMI_RO_ATTR, rmi_fn_34_blocksize_show, rmi_store_error), @@ -134,16 +134,16 @@ static struct device_attribute attrs[] = { rmi_fn_34_imageblockcount_show, rmi_store_error), __ATTR(configblockcount, RMI_RO_ATTR, rmi_fn_34_configblockcount_show, rmi_store_error), - __ATTR(blocknum, RMI_RW_ATTR, + __ATTR(blocknum, RMI_RO_ATTR, rmi_fn_34_blocknum_show, rmi_fn_34_blocknum_store), - __ATTR(rescanPDT, RMI_WO_ATTR, + __ATTR(rescanPDT, RMI_RO_ATTR, rmi_show_error, rmi_fn_34_rescanPDT_store) }; struct bin_attribute dev_attr_data = { .attr = { .name = "data", - .mode = 0666}, + .mode = 0644}, .size = 0, .read = rmi_fn_34_data_read, .write = rmi_fn_34_data_write, diff --git a/drivers/input/touchscreen/rmi4/rmi_f54.c b/drivers/input/touchscreen/rmi4/rmi_f54.c index 11bb0b934bef..f4f2e3ac2903 100644 --- a/drivers/input/touchscreen/rmi4/rmi_f54.c +++ b/drivers/input/touchscreen/rmi4/rmi_f54.c @@ -303,11 +303,11 @@ static ssize_t rmi_fn_54_fifoindex_store(struct device *dev, const char *buf, size_t count); static struct device_attribute attrs[] = { - __ATTR(report_type, RMI_RW_ATTR, + __ATTR(report_type, RMI_RO_ATTR, rmi_fn_54_report_type_show, rmi_fn_54_report_type_store), - __ATTR(get_report, RMI_WO_ATTR, + __ATTR(get_report, RMI_RO_ATTR, rmi_show_error, rmi_fn_54_get_report_store), - __ATTR(force_cal, RMI_WO_ATTR, + __ATTR(force_cal, RMI_RO_ATTR, rmi_show_error, rmi_fn_54_force_cal_store), __ATTR(status, RMI_RO_ATTR, rmi_fn_54_status_show, rmi_store_error), @@ -358,9 +358,9 @@ static struct device_attribute attrs[] = { rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error), __ATTR(number_of_sensing_frequencies, RMI_RO_ATTR, rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error), - __ATTR(no_auto_cal, RMI_RW_ATTR, + __ATTR(no_auto_cal, RMI_RO_ATTR, rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store), - __ATTR(fifoindex, RMI_RW_ATTR, + __ATTR(fifoindex, RMI_RO_ATTR, rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store), }; diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index bf33c0302257..7b5ef3ffb7d0 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -38,6 +38,9 @@ #include <mach/smmu.h> #include <mach/tegra_smmu.h> +/* REVISIT: With new configurations for t114/124/148 passed from DT */ +#define SKIP_SWGRP_CHECK + /* bitmap of the page sizes currently supported */ #define SMMU_IOMMU_PGSIZES (SZ_4K) @@ -316,11 +319,15 @@ static int __smmu_client_set_hwgrp(struct smmu_client *c, offs = HWGRP_ASID_REG(i); val = smmu_read(smmu, offs); if (on) { +#if !defined(SKIP_SWGRP_CHECK) if (WARN_ON(val & mask)) goto err_hw_busy; +#endif val |= mask; } else { +#if !defined(SKIP_SWGRP_CHECK) WARN_ON((val & mask) == mask); +#endif val &= ~mask; } smmu_write(smmu, val, offs); @@ -701,9 +708,15 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain, return -ENOMEM; client->dev = dev; client->as = as; + +#ifdef SKIP_SWGRP_CHECK + /* Enable all SWGRP blindly by default */ + map = (1 << HWGRP_COUNT) - 1; +#else map = (unsigned long)dev->platform_data; if (!map) return -EINVAL; +#endif err = smmu_client_enable_hwgrp(client, map); if (err) @@ -1028,5 +1041,5 @@ static void __exit tegra_smmu_exit(void) platform_driver_unregister(&tegra_smmu_driver); } -subsys_initcall(tegra_smmu_init); +core_initcall(tegra_smmu_init); module_exit(tegra_smmu_exit); diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig index 404d771a717e..5099fe12c3e2 100644 --- a/drivers/media/video/tegra/Kconfig +++ b/drivers/media/video/tegra/Kconfig @@ -27,6 +27,13 @@ config VIDEO_OV5650 This is a driver for the Omnivision OV5650 5MP camera sensor for use with the tegra isp. +config VIDEO_OV5640 + tristate "OV5640 camera sensor support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the Omnivision OV5640 5MP camera sensor + for use with the tegra isp. + config VIDEO_OV14810 tristate "OV14810 camera sensor support" depends on I2C && ARCH_TEGRA @@ -88,3 +95,10 @@ config VIDEO_AD5820 ---help--- This is a driver for the AD5820 focuser for use with the tegra isp. + +config VIDEO_AD5816 + tristate "AD5816 focuser support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the AD5816 focuser + for use with the tegra isp. diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile index 5d404e44fd20..7d7285e06fb7 100644 --- a/drivers/media/video/tegra/Makefile +++ b/drivers/media/video/tegra/Makefile @@ -1,4 +1,6 @@ GCOV_PROFILE := y + +subdir-ccflags-y := -Werror # # Makefile for the video capture/playback device drivers. # @@ -9,6 +11,7 @@ obj-$(CONFIG_TEGRA_DTV) += tegra_dtv.o obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o obj-$(CONFIG_VIDEO_AR0832) += ar0832_main.o obj-$(CONFIG_VIDEO_OV5650) += ov5650.o +obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV14810) += ov14810.o obj-$(CONFIG_VIDEO_OV9726) += ov9726.o obj-$(CONFIG_VIDEO_OV2710) += ov2710.o @@ -17,4 +20,5 @@ obj-$(CONFIG_TORCH_SSL3250A) += ssl3250a.o obj-$(CONFIG_TORCH_TPS61050) += tps61050.o obj-$(CONFIG_VIDEO_SH532U) += sh532u.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o +obj-$(CONFIG_VIDEO_AD5816) += ad5816.o diff --git a/drivers/media/video/tegra/ad5816.c b/drivers/media/video/tegra/ad5816.c new file mode 100644 index 000000000000..9e31acc33cc3 --- /dev/null +++ b/drivers/media/video/tegra/ad5816.c @@ -0,0 +1,1188 @@ +/* Copyright (C) 2011-2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +/* This is a NVC kernel driver for a focuser device called + * ad5816. + */ +/* Implementation + * -------------- + * The board level details about the device need to be provided in the board + * file with the <device>_platform_data structure. + * Standard among NVC kernel drivers in this structure is: + * .cfg = Use the NVC_CFG_ defines that are in nvc.h. + * Descriptions of the configuration options are with the defines. + * This value is typically 0. + * .num = The number of the instance of the device. This should start at 1 and + * and increment for each device on the board. This number will be + * appended to the MISC driver name, Example: /dev/focuser.1 + * If not used or 0, then nothing is appended to the name. + * .sync = If there is a need to synchronize two devices, then this value is + * the number of the device instance (.num above) this device is to + * sync to. For example: + * Device 1 platform entries = + * .num = 1, + * .sync = 2, + * Device 2 platfrom entries = + * .num = 2, + * .sync = 1, + * The above example sync's device 1 and 2. + * To disable sync, set .sync = 0. Note that the .num = 0 device is not + * allowed to be synced to. + * This is typically used for stereo applications. + * .dev_name = The MISC driver name the device registers as. If not used, + * then the part number of the device is used for the driver name. + * If using the NVC user driver then use the name found in this + * driver under _default_pdata. + * .gpio_count = The ARRAY_SIZE of the nvc_gpio_pdata table. + * .gpio = A pointer to the nvc_gpio_pdata structure's platform GPIO data. + * The GPIO mechanism works by cross referencing the .gpio_type key + * among the nvc_gpio_pdata GPIO data and the driver's nvc_gpio_init + * GPIO data to build a GPIO table the driver can use. The GPIO's + * defined in the device header file's _gpio_type enum are the + * gpio_type keys for the nvc_gpio_pdata and nvc_gpio_init structures. + * These need to be present in the board file's nvc_gpio_pdata + * structure for the GPIO's that are used. + * The driver's GPIO logic uses assert/deassert throughout until the + * low level _gpio_wr/rd calls where the .assert_high is used to + * convert the value to the correct signal level. + * See the GPIO notes in nvc.h for additional information. + * + * The following is specific to NVC kernel focus drivers: + * .nvc = Pointer to the nvc_focus_nvc structure. This structure needs to + * be defined and populated if overriding the driver defaults. + * .cap = Pointer to the nvc_focus_cap structure. This structure needs to + * be defined and populated if overriding the driver defaults. + * + * The following is specific to this NVC kernel focus driver: + * .info = Pointer to the ad5816_pdata_info structure. This structure does + * not need to be defined and populated unless overriding ROM data. + * + * Power Requirements: + * The device's header file defines the voltage regulators needed with the + * enumeration <device>_vreg. The order these are enumerated is the order + * the regulators will be enabled when powering on the device. When the + * device is powered off the regulators are disabled in descending order. + * The <device>_vregs table in this driver uses the nvc_regulator_init + * structure to define the regulator ID strings that go with the regulators + * defined with <device>_vreg. These regulator ID strings (or supply names) + * will be used in the regulator_get function in the _vreg_init function. + * The board power file and <device>_vregs regulator ID strings must match. + */ + +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <media/ad5816.h> + +#define AD5816_ID 0x04 +#define AD5816_FOCAL_LENGTH (4.570f) +#define AD5816_FNUMBER (2.8f) +#define AD5816_ACTUATOR_RANGE 680 +#define AD5816_SETTLETIME 110 +#define AD5816_FOCUS_MACRO 810 +#define AD5816_FOCUS_INFINITY 50 /* Exact value needs to be decided */ +#define AD5816_POS_LOW_DEFAULT 220 +#define AD5816_POS_HIGH_DEFAULT 900 +/* Need to decide exact value of VCM_THRESHOLD and its use */ +/* define AD5816_VCM_THRESHOLD 20 */ + +static u8 ad5816_ids[] = { + 0x04, +}; + +static struct nvc_gpio_init ad5816_gpios[] = { + { AD5816_GPIO_RESET, GPIOF_OUT_INIT_LOW, "reset", false, true, }, + { AD5816_GPIO_I2CMUX, 0, "i2c_mux", 0, false}, + { AD5816_GPIO_GP1, 0, "gp1", 0, false}, + { AD5816_GPIO_GP2, 0, "gp2", 0, false}, + { AD5816_GPIO_GP3, 0, "gp3", 0, false}, +}; + +static struct nvc_regulator_init ad5816_vregs[] = { + { AD5816_VREG_VDD, "vdd"}, + { AD5816_VREG_VDD_AF, "vdd_af"}, + { AD5816_VREG_VDD_I2C, "vdd_i2c"}, +}; + +struct ad5816_info { + atomic_t in_use; + struct i2c_client *i2c_client; + struct ad5816_platform_data *pdata; + struct miscdevice miscdev; + struct list_head list; + struct nvc_gpio gpio[ARRAY_SIZE(ad5816_gpios)]; + struct nvc_regulator vreg[ARRAY_SIZE(ad5816_vregs)]; + int pwr_api; + int pwr_dev; + int id_minor; + u32 pos; + u8 s_mode; + bool reset_flag; + struct ad5816_info *s_info; + struct nvc_focus_nvc nvc; + struct nvc_focus_cap cap; + struct ad5816_pdata_info config; +}; + +/** + * The following are default values + */ + +static struct ad5816_pdata_info ad5816_default_info = { + .pos_low = AD5816_POS_LOW_DEFAULT, + .pos_high = AD5816_POS_HIGH_DEFAULT, +}; + +static struct nvc_focus_cap ad5816_default_cap = { + .version = NVC_FOCUS_CAP_VER2, + .actuator_range = AD5816_ACTUATOR_RANGE, + .settle_time = AD5816_SETTLETIME, + .focus_macro = AD5816_FOCUS_MACRO, + .focus_infinity = AD5816_FOCUS_INFINITY, +}; + +static struct nvc_focus_nvc ad5816_default_nvc = { + .focal_length = AD5816_FOCAL_LENGTH, + .fnumber = AD5816_FNUMBER, +}; + +static struct ad5816_platform_data ad5816_default_pdata = { + .cfg = 0, + .num = 0, + .sync = 0, + .dev_name = "focuser", +}; +static LIST_HEAD(ad5816_info_list); +static DEFINE_SPINLOCK(ad5816_spinlock); + +static int ad5816_i2c_rd8(struct ad5816_info *info, u8 addr, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[2]; + buf[0] = reg; + if (addr) { + msg[0].addr = addr; + msg[1].addr = addr; + } else { + msg[0].addr = info->i2c_client->addr; + msg[1].addr = info->i2c_client->addr; + } + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf[0]; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &buf[1]; + *val = 0; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; + *val = buf[1]; + return 0; +} + +static int ad5816_i2c_wr8(struct ad5816_info *info, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + buf[0] = reg; + buf[1] = val; + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int ad5816_i2c_rd16(struct ad5816_info *info, u8 reg, u16 *val) +{ + struct i2c_msg msg[2]; + u8 buf[3]; + buf[0] = reg; + msg[0].addr = info->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf[0]; + msg[1].addr = info->i2c_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = &buf[1]; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; + *val = (((u16)buf[1] << 8) | (u16)buf[2]); + return 0; +} + +static int ad5816_i2c_wr16(struct ad5816_info *info, u8 reg, u16 val) +{ + struct i2c_msg msg; + u8 buf[3]; + buf[0] = reg; + buf[1] = (u8)(val >> 8); + buf[2] = (u8)(val & 0xff); + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int ad5816_gpio_wr(struct ad5816_info *info, ad5816_gpio_types i, + int val) /* val: 0=deassert, 1=assert */ +{ + int err = -EINVAL; + if (info->gpio[i].valid) { + val = !!val; + if (!info->gpio[i].active_high) + val = !val; + err = val; + gpio_set_value_cansleep(info->gpio[i].gpio, val); + dev_dbg(&info->i2c_client->dev, "%s %u %d\n", __func__, info->gpio[i].gpio, val); + } + return err; /* return value written or error */ +} + +static int ad5816_gpio_reset(struct ad5816_info *info, int val) +{ + int err = 0; + + if (val) { + if(!info->reset_flag) { + info->reset_flag = true; + err = ad5816_gpio_wr(info, AD5816_GPIO_RESET, 1); + if (err < 0) + return 0; /* flag no reset */ + + mdelay(1); + ad5816_gpio_wr(info, AD5816_GPIO_RESET, 0); + mdelay(10); /* startup delay needs to be modified*/ + err = 1; /* flag that a reset was done */ + } + } else { + info->reset_flag = false; + } + return err; +} + +static void ad5816_gpio_able(struct ad5816_info *info, int val) +{ + /** + * This is a feature that allows driver to control GPIOs + * that may be needed for the board (not the device). + * */ + if (val) { + ad5816_gpio_wr(info, AD5816_GPIO_GP1, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP2, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP3, val); + } else { + ad5816_gpio_wr(info, AD5816_GPIO_GP3, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP2, val); + ad5816_gpio_wr(info, AD5816_GPIO_GP1, val); + } +} +static void ad5816_gpio_exit(struct ad5816_info *info) +{ + unsigned i; + for (i = 0; i <= ARRAY_SIZE(ad5816_gpios); i++) { + if (info->gpio[i].flag && info->gpio[i].own) { + gpio_free(info->gpio[i].gpio); + info->gpio[i].own = false; + } + } +} + +static void ad5816_gpio_init(struct ad5816_info *info) +{ + char label[32]; + unsigned long flags; + unsigned type; + unsigned i; + unsigned j; + int err; + for (i = 0; i < ARRAY_SIZE(ad5816_gpios); i++) + info->gpio[i].flag = false; + + if (!info->pdata->gpio_count || !info->pdata->gpio) + return; + + for (i = 0; i < ARRAY_SIZE(ad5816_gpios); i++) { + type = ad5816_gpios[i].gpio_type; + + for (j = 0; j < info->pdata->gpio_count; j++) { + if (type == info->pdata->gpio[j].gpio_type) + break; + } + + if (j == info->pdata->gpio_count) + continue; + info->gpio[type].gpio = info->pdata->gpio[j].gpio; + info->gpio[type].flag = true; + + if (ad5816_gpios[i].use_flags) { + flags = ad5816_gpios[i].flags; + info->gpio[type].active_high = ad5816_gpios[i].active_high; + } else { + info->gpio[type].active_high = info->pdata->gpio[j].active_high; + if (info->gpio[type].active_high) + flags = GPIOF_OUT_INIT_LOW; + else + flags = GPIOF_OUT_INIT_HIGH; + } + + if (!info->pdata->gpio[j].init_en) + continue; + snprintf(label, sizeof(label), "ad5816_%u_%s", + info->pdata->num, ad5816_gpios[i].label); + err = gpio_request_one(info->gpio[type].gpio, flags, label); + if (err) { + dev_err(&info->i2c_client->dev, "%s ERR %s %u\n", + __func__, label, info->gpio[type].gpio); + } else { + info->gpio[type].own = true; + dev_dbg(&info->i2c_client->dev, "%s %s %u\n", + __func__, label, info->gpio[type].gpio); + } + } +} + +static int ad5816_vreg_dis(struct ad5816_info *info, ad5816_vreg i) +{ + int err = 0; + if (info->vreg[i].vreg_flag && (info->vreg[i].vreg != NULL)) { + err = regulator_disable(info->vreg[i].vreg); + if (!err) + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[i].vreg_name); + else + dev_err(&info->i2c_client->dev, "%s %s ERR\n", + __func__, info->vreg[i].vreg_name); + } + info->vreg[i].vreg_flag = false; + return err; +} + +static int ad5816_vreg_dis_all(struct ad5816_info *info) +{ + unsigned i; + int err = 0; + for (i = ARRAY_SIZE(ad5816_vregs); i > 0; i--) + err |= ad5816_vreg_dis(info, (i - 1)); + return err; +} + +static int ad5816_vreg_en(struct ad5816_info *info, ad5816_vreg i) +{ + int err = 0; + if (!info->vreg[i].vreg_flag && (info->vreg[i].vreg != NULL)) { + err = regulator_enable(info->vreg[i].vreg); + + if (!err) { + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[i].vreg_name); + info->vreg[i].vreg_flag = true; + err = 1; /* flag regulator state change */ + } else { + dev_err(&info->i2c_client->dev, "%s %s ERR\n", + __func__, info->vreg[i].vreg_name); + } + + } + return err; +} + +static int ad5816_vreg_en_all(struct ad5816_info *info) +{ + unsigned i; + int err = 0; + for (i = 0; i < ARRAY_SIZE(ad5816_vregs); i++) + err |= ad5816_vreg_en(info, i); + return err; +} + +static void ad5816_vreg_exit(struct ad5816_info *info) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE(ad5816_vregs); i++) { + regulator_put(info->vreg[i].vreg); + info->vreg[i].vreg = NULL; + } +} + +static int ad5816_vreg_init(struct ad5816_info *info) +{ + unsigned i; + unsigned j; + int err = 0; + for (i = 0; i < ARRAY_SIZE(ad5816_vregs); i++) { + j = ad5816_vregs[i].vreg_num; + info->vreg[j].vreg_name = ad5816_vregs[i].vreg_name; + info->vreg[j].vreg_flag = false; + info->vreg[j].vreg = regulator_get(&info->i2c_client->dev, + info->vreg[j].vreg_name); + if (IS_ERR_OR_NULL(info->vreg[j].vreg)) { + dev_err(&info->i2c_client->dev, "%s %s ERR: %d\n", + __func__, info->vreg[j].vreg_name, + (int)info->vreg[j].vreg); + err |= PTR_ERR(info->vreg[j].vreg); + info->vreg[j].vreg = NULL; + } else { + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[j].vreg_name); + } + } + return err; +} + +void ad5816_set_power_down(struct ad5816_info *info) +{ + int err; + err = ad5816_i2c_wr16(info, VCM_CODE_MSB, 0x0000); + if (err) + dev_err(&info->i2c_client->dev, " %s: failed \n", + __func__); +} + +void ad5816_set_arc_mode(struct ad5816_info *info) +{ + int err; + /* set ARC enable */ + err = ad5816_i2c_wr8(info, CONTROL, 0x02); + if (err) + dev_err(&info->i2c_client->dev, + "%s: CONTROL reg write failed \n", __func__); + + /* set the ARC RES2 */ + err = ad5816_i2c_wr8(info, MODE, 0x01); + if (err) + dev_err(&info->i2c_client->dev, + "%s: MODE reg write failed \n", __func__); + + /* set the VCM_FREQ to 12.8mS */ + err = ad5816_i2c_wr8(info, VCM_FREQ, 0x80); + if (err) + dev_err(&info->i2c_client->dev, + "%s: VCM_FREQ reg write failed \n", __func__); +} + +static int ad5816_pm_wr(struct ad5816_info *info, int pwr) +{ + int err = 0; + + if ((info->pdata->cfg & (NVC_CFG_OFF2STDBY | NVC_CFG_BOOT_INIT)) && + (pwr == NVC_PWR_OFF || + pwr == NVC_PWR_STDBY_OFF)) + pwr = NVC_PWR_STDBY; + + if (pwr == info->pwr_dev) + return 0; + + switch (pwr) { + case NVC_PWR_OFF_FORCE: + case NVC_PWR_OFF: + err = ad5816_vreg_dis_all(info); + ad5816_gpio_able(info, 0); + ad5816_gpio_reset(info, 0); + break; + case NVC_PWR_STDBY_OFF: + case NVC_PWR_STDBY: + err = ad5816_vreg_en_all(info); + ad5816_gpio_able(info, 1); + ad5816_gpio_reset(info, 1); + break; + case NVC_PWR_COMM: + case NVC_PWR_ON: + err = ad5816_vreg_en_all(info); + ad5816_gpio_able(info, 1); + ad5816_gpio_reset(info, 1); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + dev_err(&info->i2c_client->dev, "%s err %d\n", __func__, err); + pwr = NVC_PWR_ERR; + } + + info->pwr_dev = pwr; + dev_dbg(&info->i2c_client->dev, "%s pwr_dev=%d\n", __func__, info->pwr_dev); + + if (err > 0) + return 0; + + return err; +} +static int ad5816_pm_wr_s(struct ad5816_info *info, int pwr) +{ + int err1 = 0; + int err2 = 0; + if ((info->s_mode == NVC_SYNC_OFF) || + (info->s_mode == NVC_SYNC_MASTER) || + (info->s_mode == NVC_SYNC_STEREO)) + err1 = ad5816_pm_wr(info, pwr); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + err2 = ad5816_pm_wr(info->s_info, pwr); + return err1 | err2; +} + +static int ad5816_pm_api_wr(struct ad5816_info *info, int pwr) +{ + int err = 0; + if (!pwr || (pwr > NVC_PWR_ON)) + return 0; + if (pwr > info->pwr_dev) { + err = ad5816_pm_wr_s(info, pwr); + } + if (!err) { + info->pwr_api = pwr; + } else { + info->pwr_api = NVC_PWR_ERR; + } + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + return err; +} + +static int ad5816_pm_dev_wr(struct ad5816_info *info, int pwr) +{ + if (pwr < info->pwr_api) + pwr = info->pwr_api; + return ad5816_pm_wr(info, pwr); +} + +static void ad5816_pm_exit(struct ad5816_info *info) +{ + ad5816_pm_wr(info, NVC_PWR_OFF_FORCE); + ad5816_vreg_exit(info); + ad5816_gpio_exit(info); +} +static void ad5816_pm_init(struct ad5816_info *info) +{ + ad5816_gpio_init(info); + ad5816_vreg_init(info); +} + +static int ad5816_reset(struct ad5816_info *info, u32 level) +{ + int err; + if (level == NVC_RESET_SOFT) { + err = ad5816_pm_wr(info, NVC_PWR_COMM); + err |= ad5816_i2c_wr8(info, CONTROL, 0x01); /* SW reset */ + } else { + err = ad5816_pm_wr(info, NVC_PWR_OFF_FORCE); + } + err |= ad5816_pm_wr(info, info->pwr_api); + return err; +} + +static int ad5816_dev_id(struct ad5816_info *info) +{ + u16 val = 0; + unsigned i; + int err; + ad5816_pm_dev_wr(info, NVC_PWR_COMM); + err = ad5816_i2c_rd16(info, IC_INFO, &val); + if (!err) { + dev_dbg(&info->i2c_client->dev, "%s found devId: %x\n", __func__, val); + info->id_minor = 0; + val = val & 0xff; + for (i = 0; i < ARRAY_SIZE(ad5816_ids); i++) { + if (val == ad5816_ids[i]) { + info->id_minor = val; + break; + } + } + if (!info->id_minor) { + err = -ENODEV; + dev_dbg(&info->i2c_client->dev, "%s No devId match\n", __func__); + } + } + ad5816_pm_dev_wr(info, NVC_PWR_OFF); + return err; +} + +/** + * Below are device specific functions. + */ + +static int ad5816_position_rd(struct ad5816_info *info, unsigned *position) +{ + + u16 pos = 0; + u8 t1 = 0; + int err = 0; + + err = ad5816_i2c_rd8(info, 0, VCM_CODE_MSB, &t1); + pos = t1 & 0x03; + err = ad5816_i2c_rd8(info, 0, VCM_CODE_LSB, &t1); + pos = (pos << 8) | t1; + if(pos) + *position = pos - info->config.pos_low; + else + *position = info->config.pos_low; + + return 0; +} + +static int ad5816_position_wr(struct ad5816_info *info, unsigned position) +{ + u16 data; + + position = position + info->config.pos_low; + if(position > info->config.pos_high) + position = info->config.pos_high; + + data = position & 0x03ff; + + return ad5816_i2c_wr16(info, VCM_CODE_MSB, data); +} + +static int ad5816_param_rd(struct ad5816_info *info, unsigned long arg) +{ + struct nvc_param params; + const void *data_ptr = NULL; + u32 data_size = 0; + u32 position; + int err; + if (copy_from_user(¶ms, + (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", __func__, __LINE__); + return -EFAULT; + } + if (info->s_mode == NVC_SYNC_SLAVE) + info = info->s_info; + switch (params.param) { + case NVC_PARAM_LOCUS: + ad5816_pm_dev_wr(info, NVC_PWR_COMM); + err = ad5816_position_rd(info, &position); + if (err && !(info->pdata->cfg & NVC_CFG_NOERR)) + return err; + data_ptr = &position; + data_size = sizeof(position); + ad5816_pm_dev_wr(info, NVC_PWR_STDBY); + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n", + __func__, position); + break; + case NVC_PARAM_FOCAL_LEN: + info->nvc.focal_length = AD5816_FOCAL_LENGTH; + data_ptr = &info->nvc.focal_length; + data_size = sizeof(info->nvc.focal_length); + break; + case NVC_PARAM_MAX_APERTURE: + data_ptr = &info->nvc.max_aperature; + data_size = sizeof(info->nvc.max_aperature); + dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %x\n", + __func__, info->nvc.max_aperature); + break; + case NVC_PARAM_FNUMBER: + data_ptr = &info->nvc.fnumber; + data_size = sizeof(info->nvc.fnumber); + dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %u\n", + __func__, info->nvc.fnumber); + break; + case NVC_PARAM_CAPS: + data_ptr = &info->cap; + /* there are different sizes depending on the version */ + /* send back just what's requested or our max size */ + if (params.sizeofvalue < sizeof(info->cap)) + data_size = params.sizeofvalue; + else + data_size = sizeof(info->cap); + dev_err(&info->i2c_client->dev, "%s CAPS\n", __func__); + break; + case NVC_PARAM_STS: + /*data_ptr = &info->sts; + data_size = sizeof(info->sts);*/ + dev_dbg(&info->i2c_client->dev, "%s \n", __func__); + break; + case NVC_PARAM_STEREO: + data_ptr = &info->s_mode; + data_size = sizeof(info->s_mode); + dev_err(&info->i2c_client->dev, "%s STEREO: %d\n", __func__, info->s_mode); + break; + default: + dev_err(&info->i2c_client->dev, "%s unsupported parameter: %d\n", + __func__, params.param); + return -EINVAL; + } + if (params.sizeofvalue < data_size) { + dev_err(&info->i2c_client->dev, + "%s data size mismatch %d != %d Param: %d\n", + __func__, params.sizeofvalue, data_size, params.param); + return -EINVAL; + } + if (copy_to_user((void __user *)params.p_value, data_ptr, data_size)) { + dev_err(&info->i2c_client->dev, "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + return 0; +} + +static int ad5816_param_wr_s(struct ad5816_info *info, + struct nvc_param *params, u32 u32val) +{ + int err = 0; + switch (params->param) { + case NVC_PARAM_LOCUS: + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n", __func__, u32val); + err = ad5816_position_wr(info, u32val); + return err; + case NVC_PARAM_RESET: + err = ad5816_reset(info, u32val); + dev_dbg(&info->i2c_client->dev, "%s RESET: %d\n", __func__, err); + return err; + case NVC_PARAM_SELF_TEST: + err = 0; + dev_dbg(&info->i2c_client->dev, "%s SELF_TEST: %d\n", __func__, err); + return err; + default: + dev_dbg(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params->param); + return -EINVAL; + } +} + +static int ad5816_param_wr(struct ad5816_info *info, unsigned long arg) +{ + struct nvc_param params; + u8 u8val; + u32 u32val; + int err = 0; + if (copy_from_user(¶ms, (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s copy_from_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + if (copy_from_user(&u32val, (const void __user *)params.p_value, sizeof(u32val))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", __func__, __LINE__); + return -EFAULT; + } + u8val = (u8)u32val; + /* parameters independent of sync mode */ + switch (params.param) { + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", __func__, u8val); + if (u8val == info->s_mode) + return 0; + switch (u8val) { + case NVC_SYNC_OFF: + info->s_mode = u8val; + ad5816_gpio_wr(info, AD5816_GPIO_I2CMUX, 0); + if (info->s_info != NULL) { + info->s_info->s_mode = u8val; + ad5816_pm_wr(info->s_info, NVC_PWR_OFF); + } + break; + case NVC_SYNC_MASTER: + info->s_mode = u8val; + ad5816_gpio_wr(info, AD5816_GPIO_I2CMUX, 0); + if (info->s_info != NULL) + info->s_info->s_mode = u8val; + break; + case NVC_SYNC_SLAVE: + if (info->s_info != NULL) { + /* default slave lens position */ + err = ad5816_position_wr(info->s_info, + info->s_info->cap.focus_infinity); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + ad5816_gpio_wr(info, + AD5816_GPIO_I2CMUX, 0); + } + else { + if (info->s_mode != NVC_SYNC_STEREO) + ad5816_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + case NVC_SYNC_STEREO: + if (info->s_info != NULL) { + /* sync power */ + info->s_info->pwr_api = info->pwr_api; + /* move slave lens to master position */ + err = ad5816_position_wr(info->s_info, info->pos); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + ad5816_gpio_wr(info, AD5816_GPIO_I2CMUX, 1); + } + else { + if (info->s_mode != NVC_SYNC_SLAVE) + ad5816_pm_wr(info->s_info, NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + } + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + return err; + default: + /* parameters dependent on sync mode */ + switch (info->s_mode) { + case NVC_SYNC_OFF: + case NVC_SYNC_MASTER: + return ad5816_param_wr_s(info, ¶ms, u32val); + case NVC_SYNC_SLAVE: + return ad5816_param_wr_s(info->s_info, ¶ms, u32val); + case NVC_SYNC_STEREO: + err = ad5816_param_wr_s(info, ¶ms, u32val); + if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) + err |= ad5816_param_wr_s(info->s_info, + ¶ms, + u32val); + return err; + default: + dev_err(&info->i2c_client->dev, "%s %d internal err\n", + __func__, __LINE__); + return -EINVAL; + } + } +} + +static long ad5816_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ad5816_info *info = file->private_data; + int pwr; + int err = 0; + switch (cmd) { + case NVC_IOCTL_PARAM_WR: + err = ad5816_param_wr(info, arg); + return err; + case NVC_IOCTL_PARAM_RD: + err = ad5816_param_rd(info, arg); + return err; + case NVC_IOCTL_PWR_WR: + /* This is a Guaranteed Level of Service (GLOS) call */ + pwr = (int)arg * 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", + __func__, pwr); + err = ad5816_pm_api_wr(info, pwr); + return err; + case NVC_IOCTL_PWR_RD: + if (info->s_mode == NVC_SYNC_SLAVE) + pwr = info->s_info->pwr_api / 2; + else + pwr = info->pwr_api / 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", + __func__, pwr); + if (copy_to_user((void __user *)arg, (const void *)&pwr, sizeof(pwr))) { + dev_err(&info->i2c_client->dev, "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + return 0; + default: + dev_dbg(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", __func__, cmd); + } + return -EINVAL; +} + + +static void ad5816_sdata_init(struct ad5816_info *info) +{ + /* set defaults */ + memcpy(&info->config, &ad5816_default_info, sizeof(info->config)); + memcpy(&info->nvc, &ad5816_default_nvc, sizeof(info->nvc)); + memcpy(&info->cap, &ad5816_default_cap, sizeof(info->cap)); + + info->config.settle_time = AD5816_SETTLETIME; + info->config.focal_length = AD5816_FOCAL_LENGTH; + info->config.fnumber = AD5816_FNUMBER; + info->config.pos_low = AD5816_POS_LOW_DEFAULT; + info->config.pos_high = AD5816_POS_HIGH_DEFAULT; + + /* set to proper value */ + info->cap.actuator_range = info->config.pos_high - info->config.pos_low; + + /* set overrides if any */ + if (info->pdata->nvc) { + if (info->pdata->nvc->fnumber) + info->nvc.fnumber = info->pdata->nvc->fnumber; + if (info->pdata->nvc->focal_length) + info->nvc.focal_length = info->pdata->nvc->focal_length; + if (info->pdata->nvc->max_aperature) + info->nvc.max_aperature = info->pdata->nvc->max_aperature; + } + + if (info->pdata->cap) { + if (info->pdata->cap->actuator_range) + info->cap.actuator_range = info->pdata->cap->actuator_range; + if (info->pdata->cap->settle_time) + info->cap.settle_time = info->pdata->cap->settle_time; + if (info->pdata->cap->focus_macro) + info->cap.focus_macro = info->pdata->cap->focus_macro; + if (info->pdata->cap->focus_hyper) + info->cap.focus_hyper = info->pdata->cap->focus_hyper; + if (info->pdata->cap->focus_infinity) + info->cap.focus_infinity = info->pdata->cap->focus_infinity; + } +} + +static int ad5816_sync_en(unsigned num, unsigned sync) +{ + struct ad5816_info *master = NULL; + struct ad5816_info *slave = NULL; + struct ad5816_info *pos = NULL; + rcu_read_lock(); + list_for_each_entry_rcu(pos, &ad5816_info_list, list) { + if (pos->pdata->num == num) { + master = pos; + break; + } + } + pos = NULL; + list_for_each_entry_rcu(pos, &ad5816_info_list, list) { + if (pos->pdata->num == sync) { + slave = pos; + break; + } + } + rcu_read_unlock(); + if (master != NULL) + master->s_info = NULL; + if (slave != NULL) + slave->s_info = NULL; + if (!sync) + return 0; /* no err if sync disabled */ + if (num == sync) + return -EINVAL; /* err if sync instance is itself */ + if ((master != NULL) && (slave != NULL)) { + master->s_info = slave; + slave->s_info = master; + } + return 0; +} + +static int ad5816_sync_dis(struct ad5816_info *info) +{ + if (info->s_info != NULL) { + info->s_info->s_mode = 0; + info->s_info->s_info = NULL; + info->s_mode = 0; + info->s_info = NULL; + return 0; + } + return -EINVAL; +} + +static int ad5816_open(struct inode *inode, struct file *file) +{ + struct ad5816_info *info = NULL; + struct ad5816_info *pos = NULL; + int err; + rcu_read_lock(); + list_for_each_entry_rcu(pos, &ad5816_info_list, list) { + if (pos->miscdev.minor == iminor(inode)) { + info = pos; + break; + } + } + rcu_read_unlock(); + if (!info) + return -ENODEV; + err = ad5816_sync_en(info->pdata->num, info->pdata->sync); + if (err == -EINVAL) + dev_err(&info->i2c_client->dev, "%s err: invalid num (%u) and sync (%u) instance\n", + __func__, info->pdata->num, info->pdata->sync); + if (atomic_xchg(&info->in_use, 1)) + return -EBUSY; + if (info->s_info != NULL) { + if (atomic_xchg(&info->s_info->in_use, 1)) + return -EBUSY; + } + file->private_data = info; + ad5816_pm_dev_wr(info, NVC_PWR_ON); + /* set ARC Mode to ensure faster focus */ + ad5816_set_arc_mode(info); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + + return 0; +} + +static int ad5816_release(struct inode *inode, struct file *file) +{ + struct ad5816_info *info = file->private_data; + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + ad5816_pm_wr_s(info, NVC_PWR_OFF); + file->private_data = NULL; + WARN_ON(!atomic_xchg(&info->in_use, 0)); + if (info->s_info != NULL) + WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); + ad5816_sync_dis(info); + return 0; +} + +static const struct file_operations ad5816_fileops = { + .owner = THIS_MODULE, + .open = ad5816_open, + .unlocked_ioctl = ad5816_ioctl, + .release = ad5816_release, +}; + +static void ad5816_del(struct ad5816_info *info) +{ + ad5816_pm_exit(info); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + ad5816_pm_exit(info->s_info); + + ad5816_sync_dis(info); + spin_lock(&ad5816_spinlock); + list_del_rcu(&info->list); + spin_unlock(&ad5816_spinlock); + synchronize_rcu(); +} + +static int ad5816_remove(struct i2c_client *client) +{ + struct ad5816_info *info = i2c_get_clientdata(client); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + misc_deregister(&info->miscdev); + ad5816_del(info); + return 0; +} + +static int ad5816_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad5816_info *info; + char dname[16]; + int err; + + pr_info("ad5816: probing focuser.\n"); + dev_dbg(&client->dev, "%s\n", __func__); + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&client->dev, "%s: kzalloc error\n", __func__); + return -ENOMEM; + } + info->i2c_client = client; + if (client->dev.platform_data) { + info->pdata = client->dev.platform_data; + } else { + info->pdata = &ad5816_default_pdata; + dev_dbg(&client->dev,"%s No platform data. Using defaults.\n", __func__); + } + + i2c_set_clientdata(client, info); + INIT_LIST_HEAD(&info->list); + spin_lock(&ad5816_spinlock); + list_add_rcu(&info->list, &ad5816_info_list); + spin_unlock(&ad5816_spinlock); + ad5816_pm_init(info); + ad5816_sdata_init(info); + + if (info->pdata->cfg & (NVC_CFG_NODEV | NVC_CFG_BOOT_INIT)) { + err = ad5816_dev_id(info); + if (err < 0) { + dev_err(&client->dev, "%s device not found\n", __func__); + ad5816_pm_wr(info, NVC_PWR_OFF); + if (info->pdata->cfg & NVC_CFG_NODEV) { + ad5816_del(info); + return -ENODEV; + } + } else { + dev_dbg(&client->dev, "%s device found\n", __func__); + if (info->pdata->cfg & NVC_CFG_BOOT_INIT) { + /* initial move causes full initialization */ + ad5816_pm_dev_wr(info, NVC_PWR_ON); + ad5816_position_wr(info, info->cap.focus_infinity); + ad5816_pm_dev_wr(info, NVC_PWR_OFF); + } + } + } + + if (info->pdata->dev_name != 0) + strcpy(dname, info->pdata->dev_name); + else + strcpy(dname, "ad5816"); + + if (info->pdata->num) + snprintf(dname, sizeof(dname), "%s.%u", dname, info->pdata->num); + + info->miscdev.name = dname; + info->miscdev.fops = &ad5816_fileops; + info->miscdev.minor = MISC_DYNAMIC_MINOR; + if (misc_register(&info->miscdev)) { + dev_err(&client->dev, "%s unable to register misc device %s\n", + __func__, dname); + ad5816_del(info); + return -ENODEV; + } + + return 0; +} + + +static const struct i2c_device_id ad5816_id[] = { + { "ad5816", 0 }, + { }, +}; + + +MODULE_DEVICE_TABLE(i2c, ad5816_id); + +static struct i2c_driver ad5816_i2c_driver = { + .driver = { + .name = "ad5816", + .owner = THIS_MODULE, + }, + .id_table = ad5816_id, + .probe = ad5816_probe, + .remove = ad5816_remove, +}; + +static int __init ad5816_init(void) +{ + return i2c_add_driver(&ad5816_i2c_driver); +} + +static void __exit ad5816_exit(void) +{ + i2c_del_driver(&ad5816_i2c_driver); +} + +module_init(ad5816_init); +module_exit(ad5816_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tegra/ar0832_main.c b/drivers/media/video/tegra/ar0832_main.c index f3b56bbf847e..4da9f7e833b9 100644 --- a/drivers/media/video/tegra/ar0832_main.c +++ b/drivers/media/video/tegra/ar0832_main.c @@ -23,9 +23,11 @@ #include <linux/regulator/consumer.h> #include <media/ar0832_main.h> -#define POS_LOW 0 -#define POS_HIGH 1000 -#define SETTLETIME_MS 100 +#define POS_ACTUAL_LOW 0 +#define POS_ACTUAL_HIGH 255 +#define SETTLE_TIME 100 +#define SLEW_RATE_DEFAULT 1 + struct ar0832_sensor_info { int mode; @@ -33,7 +35,7 @@ struct ar0832_sensor_info { }; struct ar0832_focuser_info { - struct ar0832_focuser_config config; + struct nv_focuser_config config; int focuser_init_flag; u16 last_position; }; @@ -142,7 +144,7 @@ static struct ar0832_reg mode_3264X2448_8140[] = { {0x3E12, 0x4B24}, {0x3E14, 0xA3CF}, {0x3E16, 0x8802}, - {0x3E18, 0x8401}, + {0x3E18, 0x84FF}, {0x3E1A, 0x8601}, {0x3E1C, 0x8401}, {0x3E1E, 0x840A}, @@ -190,7 +192,6 @@ static struct ar0832_reg mode_3264X2448_8140[] = { {0x3ED4, 0xAFC4}, {0x3ED6, 0x909B}, {0x3EE0, 0x2424}, - {0x3EE2, 0x9797}, {0x3EE4, 0xC100}, {0x3EE6, 0x0540}, {0x3174, 0x8000}, @@ -246,8 +247,8 @@ static struct ar0832_reg mode_3264X2448_8141[] = { {0x306E, 0xFC80}, {0x30B2, 0xC000}, {0x30D6, 0x0800}, - {0x316C, 0xB42F}, - {0x316E, 0x869A}, + {0x316C, 0xB42A}, + {0x316E, 0x869C}, {0x3170, 0x210E}, {0x317A, 0x010E}, {0x31E0, 0x1FB9}, @@ -278,7 +279,7 @@ static struct ar0832_reg mode_3264X2448_8141[] = { {0x3E26, 0x0088}, {0x3E28, 0x2E8A}, {0x3E30, 0x0000}, - {0x3E32, 0x8801}, + {0x3E32, 0x00FF}, {0x3E34, 0x4029}, {0x3E36, 0x00FF}, {0x3E38, 0x8469}, @@ -308,15 +309,14 @@ static struct ar0832_reg mode_3264X2448_8141[] = { {0x3E98, 0x2B02}, {0x3E92, 0x2A04}, {0x3E94, 0x2509}, - {0x3E96, 0x0000}, + {0x3E96, 0xF000}, {0x3E9A, 0x2905}, {0x3E9C, 0x00FF}, {0x3ECC, 0x00EB}, {0x3ED0, 0x1E24}, - {0x3ED4, 0xAFC4}, + {0x3ED4, 0xFAA4}, {0x3ED6, 0x909B}, {0x3EE0, 0x2424}, - {0x3EE2, 0x9797}, {0x3EE4, 0xC100}, {0x3EE6, 0x0540}, {0x3174, 0x8000}, @@ -2160,7 +2160,8 @@ static long ar0832_ioctl(struct file *file, "%s AR0832_FOCUSER_IOCTL_GET_CONFIG\n", __func__); if (copy_to_user((void __user *) arg, &dev->focuser_info->config, - sizeof(dev->focuser_info->config))) { + sizeof(struct nv_focuser_config))) + { dev_err(&i2c_client->dev, "%s: AR0832_FOCUSER_IOCTL_GET_CONFIG failed\n", __func__); @@ -2168,6 +2169,25 @@ static long ar0832_ioctl(struct file *file, } return 0; + case AR0832_FOCUSER_IOCTL_SET_CONFIG: + dev_info(&i2c_client->dev, + "%s AR0832_FOCUSER_IOCTL_SET_CONFIG\n", __func__); + if (copy_from_user(&dev->focuser_info->config, + (const void __user *)arg, + sizeof(struct nv_focuser_config))) + { + dev_err(&i2c_client->dev, + "%s: AR0832_FOCUSER_IOCTL_SET_CONFIG failed\n", __func__); + return -EFAULT; + } + dev_dbg(&i2c_client->dev, + "%s AR0832_FOCUSER_IOCTL_SET_CONFIG sucess " + "slew_rate %i, pos_working_high %i, pos_working_low %i\n", + __func__, dev->focuser_info->config.slew_rate, + dev->focuser_info->config.pos_working_low, + dev->focuser_info->config.pos_working_high); + return 0; + case AR0832_FOCUSER_IOCTL_SET_POSITION: dev_dbg(&i2c_client->dev, "%s AR0832_FOCUSER_IOCTL_SET_POSITION\n", __func__); @@ -2443,9 +2463,12 @@ static int ar0832_probe(struct i2c_client *client, dev->i2c_client = client; /* focuser */ - dev->focuser_info->config.settle_time = SETTLETIME_MS; - dev->focuser_info->config.pos_low = POS_LOW; - dev->focuser_info->config.pos_high = POS_HIGH; + dev->focuser_info->config.settle_time = SETTLE_TIME; + dev->focuser_info->config.slew_rate = SLEW_RATE_DEFAULT; + dev->focuser_info->config.pos_actual_low = POS_ACTUAL_LOW; + dev->focuser_info->config.pos_actual_high = POS_ACTUAL_HIGH; + dev->focuser_info->config.pos_working_low = POS_ACTUAL_LOW; + dev->focuser_info->config.pos_working_high = POS_ACTUAL_HIGH; snprintf(dev->dname, sizeof(dev->dname), "%s-%s", id->name, dev->pdata->id); diff --git a/drivers/media/video/tegra/avp/avp.c b/drivers/media/video/tegra/avp/avp.c index 074a42f125be..fc965e4f5b53 100644 --- a/drivers/media/video/tegra/avp/avp.c +++ b/drivers/media/video/tegra/avp/avp.c @@ -42,7 +42,7 @@ #include <mach/clk.h> #include <mach/io.h> #include <mach/iomap.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <mach/legacy_irq.h> #include <mach/hardware.h> diff --git a/drivers/media/video/tegra/avp/avp_svc.c b/drivers/media/video/tegra/avp/avp_svc.c index 17c8b8535a62..4063ac17e570 100644 --- a/drivers/media/video/tegra/avp/avp_svc.c +++ b/drivers/media/video/tegra/avp/avp_svc.c @@ -29,7 +29,7 @@ #include <linux/types.h> #include <mach/clk.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "../../../../video/tegra/nvmap/nvmap.h" @@ -464,7 +464,7 @@ static void do_svc_module_clock_set(struct avp_svc_info *avp_svc, struct svc_clock_ctrl *msg = (struct svc_clock_ctrl *)_msg; struct svc_clock_ctrl_response resp; struct avp_module *mod; - struct avp_clk *aclk; + struct avp_clk *aclk = NULL; int ret = 0; mod = find_avp_module(avp_svc, msg->module_id); diff --git a/drivers/media/video/tegra/nvavp/Kconfig b/drivers/media/video/tegra/nvavp/Kconfig index 2d3af3f79fb3..294253a0de49 100644 --- a/drivers/media/video/tegra/nvavp/Kconfig +++ b/drivers/media/video/tegra/nvavp/Kconfig @@ -8,3 +8,14 @@ config TEGRA_NVAVP /dev/tegra_avpchannel. If unsure, say N + +config TEGRA_NVAVP_AUDIO + bool "Enable Audio Channel support for Tegra NVAVP driver" + depends on TEGRA_NVAVP + default n + help + Enables support for the push-buffer mechanism based driver for the Tegra + audio multimedia framework. Exports the Tegra nvavp interface on device node + /dev/tegra_audio_avpchannel. + + If unsure, say N diff --git a/drivers/media/video/tegra/nvavp/Makefile b/drivers/media/video/tegra/nvavp/Makefile index 82b4238fd085..af2659e84664 100644 --- a/drivers/media/video/tegra/nvavp/Makefile +++ b/drivers/media/video/tegra/nvavp/Makefile @@ -1,3 +1,5 @@ GCOV_PROFILE := y +EXTRA_CFLAGS += -Idrivers/video/tegra/host + obj-$(CONFIG_TEGRA_NVAVP) += nvavp_dev.o obj-$(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) += ../avp/headavp.o diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c index 3741043eb1d2..9a54f8e3d025 100644 --- a/drivers/media/video/tegra/nvavp/nvavp_dev.c +++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c @@ -40,12 +40,7 @@ #include <mach/io.h> #include <mach/iomap.h> #include <mach/legacy_irq.h> -#include <mach/nvmap.h> - -#include "../../../../video/tegra/nvmap/nvmap.h" -#include "../../../../video/tegra/host/host1x/host1x_syncpt.h" -#include "../../../../video/tegra/host/dev.h" -#include "../../../../video/tegra/host/nvhost_acm.h" +#include <linux/nvmap.h> #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) #include "../avp/headavp.h" @@ -73,6 +68,34 @@ /* AVP behavior params */ #define NVAVP_OS_IDLE_TIMEOUT 100 /* milli-seconds */ +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +/* Two control channels: Audio and Video channels */ +#define NVAVP_NUM_CHANNELS 2 + +#define NVAVP_AUDIO_CHANNEL 1 + +#define IS_AUDIO_CHANNEL_ID(channel_id) (channel_id == NVAVP_AUDIO_CHANNEL ? 1: 0) +#else +#define NVAVP_NUM_CHANNELS 1 +#endif + +/* Channel ID 0 represents the Video channel control area */ +#define NVAVP_VIDEO_CHANNEL 0 +/* Channel ID 1 represents the Audio channel control area */ + +#define IS_VIDEO_CHANNEL_ID(channel_id) (channel_id == NVAVP_VIDEO_CHANNEL ? 1: 0) + + +struct nvavp_channel { + struct mutex pushbuffer_lock; + struct nvmap_handle_ref *pushbuf_handle; + unsigned long pushbuf_phys; + u8 *pushbuf_data; + u32 pushbuf_index; + u32 pushbuf_fence; + struct nv_e276_control *os_control; +}; + struct nvavp_info { u32 clk_enabled; struct clk *bsev_clk; @@ -89,8 +112,10 @@ struct nvavp_info { struct mutex open_lock; int refcount; - int initialized; - + int video_initialized; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + int audio_initialized; +#endif struct work_struct clock_disable_work; /* os information */ @@ -102,22 +127,18 @@ struct nvavp_info { /* client for driver allocations, persistent */ struct nvmap_client *nvmap; - struct mutex pushbuffer_lock; - struct nvmap_handle_ref *pushbuf_handle; - unsigned long pushbuf_phys; - u8 *pushbuf_data; - u32 pushbuf_index; - u32 pushbuf_fence; + bool pending; - struct nv_e276_control *os_control; + struct nvavp_channel channel_info[NVAVP_NUM_CHANNELS]; - struct nvhost_syncpt *nvhost_syncpt; u32 syncpt_id; u32 syncpt_value; struct nvhost_device *nvhost_dev; - struct miscdevice misc_dev; - atomic_t clock_stay_on_refcount; + struct miscdevice video_misc_dev; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + struct miscdevice audio_misc_dev; +#endif }; struct nvavp_clientctx { @@ -127,9 +148,78 @@ struct nvavp_clientctx { struct nvmap_handle_ref *gather_mem; int num_relocs; struct nvavp_info *nvavp; - int clock_stay_on; + u32 clk_reqs; + int channel_id; }; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +static int nvavp_get_audio_init_status(struct nvavp_info *nvavp) +{ + return nvavp->audio_initialized; +} + +static void nvavp_set_audio_init_status(struct nvavp_info *nvavp, int status) +{ + nvavp->audio_initialized = status; +} +#endif + +static void nvavp_set_video_init_status(struct nvavp_info *nvavp, int status) +{ + nvavp->video_initialized = status; +} + +static int nvavp_get_video_init_status(struct nvavp_info *nvavp) +{ + return nvavp->video_initialized; +} + +static struct nvavp_channel *nvavp_get_channel_info(struct nvavp_info *nvavp, int channel_id) +{ + return &nvavp->channel_info[channel_id]; +} + +static void nvavp_set_channel_control_area(struct nvavp_info *nvavp, int channel_id) +{ + struct nv_e276_control *control; + struct nvavp_os_info *os = &nvavp->os_info; + u32 temp; + void *ptr; + struct nvavp_channel *channel_info; + + ptr = os->data + os->control_offset + (sizeof(struct nv_e276_control) * channel_id); + + channel_info = nvavp_get_channel_info(nvavp, channel_id); + channel_info->os_control = (struct nv_e276_control *)ptr; + + control = channel_info->os_control; + + /* init get and put pointers */ + writel(0x0, &control->put); + writel(0x0, &control->get); + + pr_debug("nvavp_set_channel_control_area for channel_id (%d):\ + control->put (0x%08x) control->get (0x%08x)\n", + channel_id, (u32) &control->put, (u32) &control->get); + + /* enable avp VDE clock control and disable iram clock gating */ + writel(0x0, &control->idle_clk_enable); + writel(0x0, &control->iram_clk_gating); + + /* enable avp idle timeout interrupt */ + writel(0x1, &control->idle_notify_enable); + writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay); + + /* init dma start and end pointers */ + writel(channel_info->pushbuf_phys, &control->dma_start); + writel((channel_info->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE), + &control->dma_end); + + writel(0x00, &channel_info->pushbuf_index); + temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE; + writel(temp, &channel_info->pushbuf_fence); +} + static struct clk *nvavp_clk_get(struct nvavp_info *nvavp, int id) { if (!nvavp) @@ -145,26 +235,29 @@ static struct clk *nvavp_clk_get(struct nvavp_info *nvavp, int id) return NULL; } -static void nvavp_clk_ctrl(struct nvavp_info *nvavp, u32 clk_en) +static void nvavp_clks_enable(struct nvavp_info *nvavp) { - if (clk_en && !nvavp->clk_enabled) { - nvhost_module_busy(nvhost_get_host(nvavp->nvhost_dev)->dev); + if (nvavp->clk_enabled++ == 0) { + nvhost_module_busy_ext(nvhost_get_parent(nvavp->nvhost_dev)); clk_enable(nvavp->bsev_clk); clk_enable(nvavp->vde_clk); clk_set_rate(nvavp->emc_clk, nvavp->emc_clk_rate); clk_set_rate(nvavp->sclk, nvavp->sclk_rate); - nvavp->clk_enabled = 1; dev_dbg(&nvavp->nvhost_dev->dev, "%s: setting sclk to %lu\n", __func__, nvavp->sclk_rate); dev_dbg(&nvavp->nvhost_dev->dev, "%s: setting emc_clk to %lu\n", __func__, nvavp->emc_clk_rate); - } else if (!clk_en && nvavp->clk_enabled) { + } +} + +static void nvavp_clks_disable(struct nvavp_info *nvavp) +{ + if (--nvavp->clk_enabled == 0) { clk_disable(nvavp->bsev_clk); clk_disable(nvavp->vde_clk); clk_set_rate(nvavp->emc_clk, 0); clk_set_rate(nvavp->sclk, 0); - nvhost_module_idle(nvhost_get_host(nvavp->nvhost_dev)->dev); - nvavp->clk_enabled = 0; + nvhost_module_idle_ext(nvhost_get_parent(nvavp->nvhost_dev)); dev_dbg(&nvavp->nvhost_dev->dev, "%s: resetting emc_clk " "and sclk\n", __func__); } @@ -172,21 +265,29 @@ static void nvavp_clk_ctrl(struct nvavp_info *nvavp, u32 clk_en) static u32 nvavp_check_idle(struct nvavp_info *nvavp) { - struct nv_e276_control *control = nvavp->os_control; - return ((control->put == control->get) - && (!atomic_read(&nvavp->clock_stay_on_refcount))) ? 1 : 0; + struct nvavp_channel *channel_info = nvavp_get_channel_info(nvavp, NVAVP_VIDEO_CHANNEL); + struct nv_e276_control *control = channel_info->os_control; + + return (control->put == control->get) ? 1 : 0; } static void clock_disable_handler(struct work_struct *work) { struct nvavp_info *nvavp; + struct nvavp_channel *channel_info; nvavp = container_of(work, struct nvavp_info, clock_disable_work); + channel_info = nvavp_get_channel_info(nvavp, NVAVP_VIDEO_CHANNEL); - mutex_lock(&nvavp->pushbuffer_lock); - nvavp_clk_ctrl(nvavp, !nvavp_check_idle(nvavp)); - mutex_unlock(&nvavp->pushbuffer_lock); + mutex_lock(&channel_info->pushbuffer_lock); + mutex_lock(&nvavp->open_lock); + if (nvavp_check_idle(nvavp) && nvavp->pending) { + nvavp->pending = false; + nvavp_clks_disable(nvavp); + } + mutex_unlock(&nvavp->open_lock); + mutex_unlock(&channel_info->pushbuffer_lock); } static int nvavp_service(struct nvavp_info *nvavp) @@ -199,11 +300,13 @@ static int nvavp_service(struct nvavp_info *nvavp) if (!(inbox & NVAVP_INBOX_VALID)) inbox = 0x00000000; - writel(0x00000000, NVAVP_OS_INBOX); - if (inbox & NVE276_OS_INTERRUPT_VIDEO_IDLE) schedule_work(&nvavp->clock_disable_work); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (inbox & NVE276_OS_INTERRUPT_AUDIO_IDLE) + pr_debug("nvavp_service NVE276_OS_INTERRUPT_AUDIO_IDLE\n"); +#endif if (inbox & NVE276_OS_INTERRUPT_DEBUG_STRING) { /* Should only occur with debug AVP OS builds */ debug_print = os->data; @@ -223,6 +326,7 @@ static int nvavp_service(struct nvavp_info *nvavp) dev_err(&nvavp->nvhost_dev->dev, "AVP breakpoint hit\n"); if (inbox & NVE276_OS_INTERRUPT_TIMEOUT) dev_err(&nvavp->nvhost_dev->dev, "AVP timeout\n"); + writel(inbox & NVAVP_INBOX_VALID, NVAVP_OS_INBOX); return 0; } @@ -290,27 +394,29 @@ static int nvavp_reset_avp(struct nvavp_info *nvavp, unsigned long reset_addr) static void nvavp_halt_vde(struct nvavp_info *nvavp) { - if (nvavp->clk_enabled) { - tegra_periph_reset_assert(nvavp->bsev_clk); - clk_disable(nvavp->bsev_clk); - tegra_periph_reset_assert(nvavp->vde_clk); - clk_disable(nvavp->vde_clk); - nvhost_module_idle(nvhost_get_host(nvavp->nvhost_dev)->dev); - nvavp->clk_enabled = 0; + if (nvavp->clk_enabled && !nvavp->pending) + BUG(); + + if (nvavp->pending) { + nvavp_clks_disable(nvavp); + nvavp->pending = false; } + + tegra_periph_reset_assert(nvavp->bsev_clk); + tegra_periph_reset_assert(nvavp->vde_clk); } static int nvavp_reset_vde(struct nvavp_info *nvavp) { - if (!nvavp->clk_enabled) - nvhost_module_busy(nvhost_get_host(nvavp->nvhost_dev)->dev); + if (nvavp->clk_enabled) + BUG(); + + nvavp_clks_enable(nvavp); - clk_enable(nvavp->bsev_clk); tegra_periph_reset_assert(nvavp->bsev_clk); udelay(2); tegra_periph_reset_deassert(nvavp->bsev_clk); - clk_enable(nvavp->vde_clk); tegra_periph_reset_assert(nvavp->vde_clk); udelay(2); tegra_periph_reset_deassert(nvavp->vde_clk); @@ -322,103 +428,95 @@ static int nvavp_reset_vde(struct nvavp_info *nvavp) */ clk_set_rate(nvavp->vde_clk, ULONG_MAX); - nvavp->clk_enabled = 1; + nvavp_clks_disable(nvavp); + return 0; } -static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp) +static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp, int channel_id) { int ret = 0; - nvavp->pushbuf_handle = nvmap_alloc(nvavp->nvmap, NVAVP_PUSHBUFFER_SIZE, - SZ_1M, NVMAP_HANDLE_UNCACHEABLE, 0); - if (IS_ERR(nvavp->pushbuf_handle)) { + struct nvavp_channel *channel_info = nvavp_get_channel_info( + nvavp, channel_id); + + channel_info->pushbuf_handle = nvmap_alloc(nvavp->nvmap, + NVAVP_PUSHBUFFER_SIZE, + SZ_1M, NVMAP_HANDLE_UNCACHEABLE, + 0); + if (IS_ERR(channel_info->pushbuf_handle)) { dev_err(&nvavp->nvhost_dev->dev, "cannot create pushbuffer handle\n"); - ret = PTR_ERR(nvavp->pushbuf_handle); + ret = PTR_ERR(channel_info->pushbuf_handle); goto err_pushbuf_alloc; } - nvavp->pushbuf_data = (u8 *)nvmap_mmap(nvavp->pushbuf_handle); - if (!nvavp->pushbuf_data) { + channel_info->pushbuf_data = (u8 *)nvmap_mmap( + channel_info->pushbuf_handle); + + if (!channel_info->pushbuf_data) { dev_err(&nvavp->nvhost_dev->dev, "cannot map pushbuffer handle\n"); ret = -ENOMEM; goto err_pushbuf_mmap; } - nvavp->pushbuf_phys = nvmap_pin(nvavp->nvmap, nvavp->pushbuf_handle); - if (IS_ERR((void *)nvavp->pushbuf_phys)) { + channel_info->pushbuf_phys = nvmap_pin(nvavp->nvmap, + channel_info->pushbuf_handle); + if (IS_ERR((void *)channel_info->pushbuf_phys)) { dev_err(&nvavp->nvhost_dev->dev, "cannot pin pushbuffer handle\n"); - ret = PTR_ERR((void *)nvavp->pushbuf_phys); + ret = PTR_ERR((void *)channel_info->pushbuf_phys); goto err_pushbuf_pin; } - memset(nvavp->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE); + memset(channel_info->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE); return 0; err_pushbuf_pin: - nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data); + nvmap_munmap(channel_info->pushbuf_handle, channel_info->pushbuf_data); err_pushbuf_mmap: - nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle); + nvmap_free(nvavp->nvmap, channel_info->pushbuf_handle); err_pushbuf_alloc: return ret; } static void nvavp_pushbuffer_free(struct nvavp_info *nvavp) { - nvmap_unpin(nvavp->nvmap, nvavp->pushbuf_handle); - nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data); - nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle); + int channel_id; + + for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) { + if (nvavp->channel_info[channel_id].pushbuf_data) { + nvmap_unpin(nvavp->nvmap, + nvavp->channel_info[channel_id].pushbuf_handle); + nvmap_munmap( + nvavp->channel_info[channel_id].pushbuf_handle, + nvavp->channel_info[channel_id].pushbuf_data); + nvmap_free(nvavp->nvmap, + nvavp->channel_info[channel_id].pushbuf_handle); + } + } } + static int nvavp_pushbuffer_init(struct nvavp_info *nvavp) { - void *ptr; - struct nvavp_os_info *os = &nvavp->os_info; - struct nv_e276_control *control; - u32 temp; - int ret; - - ret = nvavp_pushbuffer_alloc(nvavp); - if (ret) { - dev_err(&nvavp->nvhost_dev->dev, - "unable to alloc pushbuffer\n"); - return ret; - } + int ret, channel_id; - ptr = os->data; - ptr += os->control_offset; - nvavp->os_control = (struct nv_e276_control *)ptr; - - control = nvavp->os_control; - memset(control, 0, sizeof(struct nvavp_os_info)); - - /* init get and put pointers */ - writel(0x0, &control->put); - writel(0x0, &control->get); - - /* enable avp VDE clock control and disable iram clock gating */ - writel(0x0, &control->idle_clk_enable); - writel(0x0, &control->iram_clk_gating); - - /* enable avp idle timeout interrupt */ - writel(0x1, &control->idle_notify_enable); - writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay); - - /* init dma start and end pointers */ - writel(nvavp->pushbuf_phys, &control->dma_start); - writel((nvavp->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE), - &control->dma_end); - - writel(0x00, &nvavp->pushbuf_index); - temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE; - writel(temp, &nvavp->pushbuf_fence); - - nvavp->syncpt_id = NVSYNCPT_AVP_0; - nvavp->syncpt_value = nvhost_syncpt_read(nvavp->nvhost_syncpt, - nvavp->syncpt_id); + for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) { + ret = nvavp_pushbuffer_alloc(nvavp, channel_id); + if (ret) { + dev_err(&nvavp->nvhost_dev->dev, + "unable to alloc pushbuffer\n"); + return ret; + } + nvavp_set_channel_control_area(nvavp, channel_id); + if (IS_VIDEO_CHANNEL_ID(channel_id)) { + nvavp->syncpt_id = NVSYNCPT_AVP_0; + nvavp->syncpt_value = nvhost_syncpt_read_ext( + nvavp->nvhost_dev, nvavp->syncpt_id); + } + } return 0; } @@ -429,37 +527,47 @@ static void nvavp_pushbuffer_deinit(struct nvavp_info *nvavp) static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, u32 gather_count, struct nvavp_syncpt *syncpt, - u32 ext_ucode_flag) + u32 ext_ucode_flag, int channel_id) { - struct nv_e276_control *control = nvavp->os_control; + struct nvavp_channel *channel_info; + struct nv_e276_control *control; u32 gather_cmd, setucode_cmd, sync = 0; u32 wordcount = 0; u32 index, value = -1; - mutex_lock(&nvavp->pushbuffer_lock); + channel_info = nvavp_get_channel_info(nvavp, channel_id); + + control = channel_info->os_control; + pr_debug("nvavp_pushbuffer_update for channel_id (%d):\ + control->put (0x%x) control->get (0x%x)\n", + channel_id, (u32) &control->put, (u32) &control->get); + + mutex_lock(&channel_info->pushbuffer_lock); /* check for pushbuffer wrapping */ - if (nvavp->pushbuf_index >= nvavp->pushbuf_fence) - nvavp->pushbuf_index = 0; + if (channel_info->pushbuf_index >= channel_info->pushbuf_fence) + channel_info->pushbuf_index = 0; if (!ext_ucode_flag) { setucode_cmd = NVE26E_CH_OPCODE_INCR(NVE276_SET_MICROCODE_A, 3); - index = wordcount + nvavp->pushbuf_index; - writel(setucode_cmd, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(setucode_cmd, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(0, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(0, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(nvavp->ucode_info.phys, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(nvavp->ucode_info.phys, + (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(nvavp->ucode_info.size, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(nvavp->ucode_info.size, + (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); } @@ -474,39 +582,58 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, } /* write commands out */ - index = wordcount + nvavp->pushbuf_index; - writel(gather_cmd, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(gather_cmd, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(phys_addr, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(phys_addr, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); if (syncpt) { - index = wordcount + nvavp->pushbuf_index; - writel(sync, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(sync, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); } /* enable clocks to VDE/BSEV */ - nvavp_clk_ctrl(nvavp, 1); + if (IS_VIDEO_CHANNEL_ID(channel_id)) { + mutex_lock(&nvavp->open_lock); + if (!nvavp->pending) { + nvavp_clks_enable(nvavp); + nvavp->pending = true; + } + mutex_unlock(&nvavp->open_lock); + } /* update put pointer */ - nvavp->pushbuf_index = (nvavp->pushbuf_index + wordcount) & + channel_info->pushbuf_index = (channel_info->pushbuf_index + wordcount)& (NVAVP_PUSHBUFFER_SIZE - 1); - writel(nvavp->pushbuf_index, &control->put); + + writel(channel_info->pushbuf_index, &control->put); wmb(); /* wake up avp */ - writel(0xA0000001, NVAVP_OS_OUTBOX); + if (IS_VIDEO_CHANNEL_ID(channel_id)) { + pr_debug("Wake up Video Channel\n"); + writel(0xA0000001, NVAVP_OS_OUTBOX); + } + else { +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (IS_AUDIO_CHANNEL_ID(channel_id)) { + pr_debug("Wake up Audio Channel\n"); + writel(0xA0000002, NVAVP_OS_OUTBOX); + } +#endif + } /* Fill out fence struct */ if (syncpt) { syncpt->id = nvavp->syncpt_id; syncpt->value = value; } - mutex_unlock(&nvavp->pushbuffer_lock); + mutex_unlock(&channel_info->pushbuffer_lock); return 0; } @@ -531,14 +658,14 @@ static int nvavp_load_ucode(struct nvavp_info *nvavp) sprintf(fw_ucode_file, "nvavp_vid_ucode.bin"); ret = request_firmware(&nvavp_ucode_fw, fw_ucode_file, - nvavp->misc_dev.this_device); + nvavp->video_misc_dev.this_device); if (ret) { /* Try alternative version */ sprintf(fw_ucode_file, "nvavp_vid_ucode_alt.bin"); ret = request_firmware(&nvavp_ucode_fw, fw_ucode_file, - nvavp->misc_dev.this_device); + nvavp->video_misc_dev.this_device); if (ret) { dev_err(&nvavp->nvhost_dev->dev, @@ -636,7 +763,7 @@ static int nvavp_load_os(struct nvavp_info *nvavp, char *fw_os_file) if (!os_info->os_bin) { ret = request_firmware(&nvavp_os_fw, fw_os_file, - nvavp->misc_dev.this_device); + nvavp->video_misc_dev.this_device); if (ret) { dev_err(&nvavp->nvhost_dev->dev, "cannot read os firmware '%s'\n", fw_os_file); @@ -701,14 +828,28 @@ err_req_fw: return ret; } -static int nvavp_init(struct nvavp_info *nvavp) + +static int nvavp_os_init(struct nvavp_info *nvavp) { char fw_os_file[32]; int ret = 0; + int video_initialized, audio_initialized = 0; + + video_initialized = nvavp_get_video_init_status(nvavp); + +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + audio_initialized = nvavp_get_audio_init_status(nvavp); +#endif + pr_debug("video_initialized(%d) audio_initialized(%d)\n", + video_initialized, audio_initialized); - if (nvavp->initialized) + /* Video and Audio both are initialized */ + if (video_initialized || audio_initialized) return ret; + /* Video or Audio both are uninitialized */ + pr_debug("video_initialized == audio_initialized (%d)\n", + nvavp->video_initialized); #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ /* paddr is any address returned from nvmap_pin */ /* vaddr is AVP_KERNEL_VIRT_BASE */ @@ -752,7 +893,6 @@ static int nvavp_init(struct nvavp_info *nvavp) nvavp->os_info.reset_addr = nvavp->os_info.phys; nvavp->os_info.data = ioremap(nvavp->os_info.phys, SZ_1M); #endif - ret = nvavp_load_os(nvavp, fw_os_file); if (ret) { dev_err(&nvavp->nvhost_dev->dev, @@ -766,21 +906,45 @@ static int nvavp_init(struct nvavp_info *nvavp) "unable to init pushbuffer\n"); goto err_exit; } + tegra_init_legacy_irq_cop(); + enable_irq(nvavp->mbox_from_avp_pend_irq); +err_exit: + return ret; +} - ret = nvavp_load_ucode(nvavp); +static int nvavp_init(struct nvavp_info *nvavp, int channel_id) +{ + int ret = 0; + + ret = nvavp_os_init(nvavp); if (ret) { dev_err(&nvavp->nvhost_dev->dev, - "unable to load ucode\n"); - goto err_exit; + "unable to load os firmware and allocate buffers\n"); } - tegra_init_legacy_irq_cop(); + if (IS_VIDEO_CHANNEL_ID(channel_id) && + (!nvavp_get_video_init_status(nvavp)) ) { + pr_debug("nvavp_init : channel_ID (%d)\n", channel_id); + ret = nvavp_load_ucode(nvavp); + if (ret) { + dev_err(&nvavp->nvhost_dev->dev, + "unable to load ucode\n"); + goto err_exit; + } - nvavp_reset_vde(nvavp); - nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); - enable_irq(nvavp->mbox_from_avp_pend_irq); + nvavp_reset_vde(nvavp); + nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); - nvavp->initialized = 1; + nvavp_set_video_init_status(nvavp, 1); + } +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (IS_AUDIO_CHANNEL_ID(channel_id) && + (!nvavp_get_audio_init_status(nvavp))) { + pr_debug("nvavp_init : channel_ID (%d)\n", channel_id); + nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); + nvavp_set_audio_init_status(nvavp, 1); + } +#endif err_exit: return ret; @@ -788,22 +952,48 @@ err_exit: static void nvavp_uninit(struct nvavp_info *nvavp) { - if (!nvavp->initialized) + int video_initialized, audio_initialized = 0; + + video_initialized = nvavp_get_video_init_status(nvavp); + +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + audio_initialized = nvavp_get_audio_init_status(nvavp); +#endif + + pr_debug("nvavp_uninit video_initialized(%d) audio_initialized(%d)\n", + video_initialized, audio_initialized); + + /* Video and Audio both are uninitialized */ + if (!video_initialized && !audio_initialized) return; - disable_irq(nvavp->mbox_from_avp_pend_irq); + if (video_initialized) { + pr_debug("nvavp_uninit nvavp->video_initialized\n"); + cancel_work_sync(&nvavp->clock_disable_work); - cancel_work_sync(&nvavp->clock_disable_work); + nvavp_halt_vde(nvavp); - nvavp_pushbuffer_deinit(nvavp); + clk_disable(nvavp->sclk); + clk_disable(nvavp->emc_clk); - nvavp_halt_vde(nvavp); - nvavp_halt_avp(nvavp); + nvavp_set_video_init_status(nvavp, 0); + video_initialized = 0; + } - clk_disable(nvavp->sclk); - clk_disable(nvavp->emc_clk); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (audio_initialized) { + nvavp_set_audio_init_status(nvavp, 0); + audio_initialized = 0; + } +#endif - nvavp->initialized = 0; + /* Video and Audio both becomes uninitialized */ + if (video_initialized == audio_initialized) { + pr_debug("nvavp_uninit both channels unitialized\n"); + disable_irq(nvavp->mbox_from_avp_pend_irq); + nvavp_pushbuffer_deinit(nvavp); + nvavp_halt_avp(nvavp); + } } static int nvavp_set_clock_ioctl(struct file *filp, unsigned int cmd, @@ -994,7 +1184,8 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, ret = nvavp_pushbuffer_update(nvavp, (phys_addr + hdr.cmdbuf.offset), hdr.cmdbuf.words, &syncpt, - (hdr.flags & NVAVP_UCODE_EXT)); + (hdr.flags & NVAVP_UCODE_EXT), + clientctx->channel_id); if (copy_to_user((void __user *)user_hdr->syncpt, &syncpt, sizeof(struct nvavp_syncpt))) { @@ -1005,7 +1196,8 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, ret = nvavp_pushbuffer_update(nvavp, (phys_addr + hdr.cmdbuf.offset), hdr.cmdbuf.words, NULL, - (hdr.flags & NVAVP_UCODE_EXT)); + (hdr.flags & NVAVP_UCODE_EXT), + clientctx->channel_id); } err_reloc_info: @@ -1033,33 +1225,32 @@ static int nvavp_force_clock_stay_on_ioctl(struct file *filp, unsigned int cmd, struct nvavp_clock_stay_on_state_args clock; if (copy_from_user(&clock, (void __user *)arg, - sizeof(struct nvavp_clock_stay_on_state_args))) + sizeof(struct nvavp_clock_stay_on_state_args))) return -EFAULT; dev_dbg(&nvavp->nvhost_dev->dev, "%s: state=%d\n", __func__, clock.state); if (clock.state != NVAVP_CLOCK_STAY_ON_DISABLED && - clock.state != NVAVP_CLOCK_STAY_ON_ENABLED) { + clock.state != NVAVP_CLOCK_STAY_ON_ENABLED) { dev_err(&nvavp->nvhost_dev->dev, "%s: invalid argument=%d\n", __func__, clock.state); return -EINVAL; } - if (clientctx->clock_stay_on == clock.state) - return 0; - - clientctx->clock_stay_on = clock.state; - - if (clientctx->clock_stay_on == NVAVP_CLOCK_STAY_ON_ENABLED) - atomic_inc(&nvavp->clock_stay_on_refcount); - else if (clientctx->clock_stay_on == NVAVP_CLOCK_STAY_ON_DISABLED) - atomic_dec(&nvavp->clock_stay_on_refcount); - + mutex_lock(&nvavp->open_lock); + if (clock.state) { + if (clientctx->clk_reqs++ == 0) + nvavp_clks_enable(nvavp); + } else { + if (--clientctx->clk_reqs == 0) + nvavp_clks_disable(nvavp); + } + mutex_unlock(&nvavp->open_lock); return 0; } -static int tegra_nvavp_open(struct inode *inode, struct file *filp) +static int tegra_nvavp_open(struct inode *inode, struct file *filp, int channel_id) { struct miscdevice *miscdev = filp->private_data; struct nvavp_info *nvavp = dev_get_drvdata(miscdev->parent); @@ -1076,15 +1267,17 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp) mutex_lock(&nvavp->open_lock); - if (!nvavp->refcount) - ret = nvavp_init(nvavp); + pr_debug("tegra_nvavp_open channel_id (%d)\n", channel_id); + + clientctx->channel_id = channel_id; + + ret = nvavp_init(nvavp, channel_id); if (!ret) nvavp->refcount++; clientctx->nvmap = nvavp->nvmap; clientctx->nvavp = nvavp; - clientctx->clock_stay_on = NVAVP_CLOCK_STAY_ON_DISABLED; filp->private_data = clientctx; @@ -1093,6 +1286,20 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp) return ret; } +static int tegra_nvavp_video_open(struct inode *inode, struct file *filp) +{ + pr_debug("tegra_nvavp_video_open NVAVP_VIDEO_CHANNEL\n"); + return tegra_nvavp_open(inode, filp, NVAVP_VIDEO_CHANNEL); +} + +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +static int tegra_nvavp_audio_open(struct inode *inode, struct file *filp) +{ + pr_debug("tegra_nvavp_audio_open NVAVP_AUDIO_CHANNEL\n"); + return tegra_nvavp_open(inode, filp, NVAVP_AUDIO_CHANNEL); +} +#endif + static int tegra_nvavp_release(struct inode *inode, struct file *filp) { struct nvavp_clientctx *clientctx = filp->private_data; @@ -1112,8 +1319,10 @@ static int tegra_nvavp_release(struct inode *inode, struct file *filp) goto out; } - if (clientctx->clock_stay_on == NVAVP_CLOCK_STAY_ON_ENABLED) - atomic_dec(&nvavp->clock_stay_on_refcount); + /* if this client had any requests, drop our clk ref */ + if (clientctx->clk_reqs) + nvavp_clks_disable(nvavp); + if (nvavp->refcount > 0) nvavp->refcount--; if (!nvavp->refcount) @@ -1165,20 +1374,30 @@ static long tegra_nvavp_ioctl(struct file *filp, unsigned int cmd, return ret; } -static const struct file_operations tegra_nvavp_fops = { +static const struct file_operations tegra_video_nvavp_fops = { .owner = THIS_MODULE, - .open = tegra_nvavp_open, + .open = tegra_nvavp_video_open, .release = tegra_nvavp_release, .unlocked_ioctl = tegra_nvavp_ioctl, }; -static int tegra_nvavp_probe(struct nvhost_device *ndev) +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +static const struct file_operations tegra_audio_nvavp_fops = { + .owner = THIS_MODULE, + .open = tegra_nvavp_audio_open, + .release = tegra_nvavp_release, + .unlocked_ioctl = tegra_nvavp_ioctl, +}; +#endif + +static int tegra_nvavp_probe(struct nvhost_device *ndev, + struct nvhost_device_id *id_table) { struct nvavp_info *nvavp; int irq; unsigned int heap_mask; u32 iovmm_addr; - int ret = 0; + int ret = 0, channel_id; irq = nvhost_get_irq_byname(ndev, "mbox_from_nvavp_pending"); if (irq < 0) { @@ -1186,7 +1405,6 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev) return -EINVAL; } - nvavp = kzalloc(sizeof(struct nvavp_info), GFP_KERNEL); if (!nvavp) { dev_err(&ndev->dev, "cannot allocate avp_info\n"); @@ -1195,13 +1413,6 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev) memset(nvavp, 0, sizeof(*nvavp)); - nvavp->nvhost_syncpt = &nvhost_get_host(ndev)->syncpt; - if (!nvavp->nvhost_syncpt) { - dev_err(&ndev->dev, "cannot get syncpt handle\n"); - ret = -ENOENT; - goto err_get_syncpt; - } - nvavp->nvmap = nvmap_create_client(nvmap_dev, "nvavp_drv"); if (IS_ERR_OR_NULL(nvavp->nvmap)) { dev_err(&ndev->dev, "cannot create nvmap client\n"); @@ -1289,7 +1500,9 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev) nvavp->mbox_from_avp_pend_irq = irq; mutex_init(&nvavp->open_lock); - mutex_init(&nvavp->pushbuffer_lock); + + for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) + mutex_init(&nvavp->channel_info[channel_id].pushbuffer_lock); /* TODO DO NOT USE NVAVP DEVICE */ nvavp->cop_clk = clk_get(&ndev->dev, "cop"); @@ -1332,18 +1545,32 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev) INIT_WORK(&nvavp->clock_disable_work, clock_disable_handler); - nvavp->misc_dev.minor = MISC_DYNAMIC_MINOR; - nvavp->misc_dev.name = "tegra_avpchannel"; - nvavp->misc_dev.fops = &tegra_nvavp_fops; - nvavp->misc_dev.mode = S_IRWXUGO; - nvavp->misc_dev.parent = &ndev->dev; + nvavp->video_misc_dev.minor = MISC_DYNAMIC_MINOR; + nvavp->video_misc_dev.name = "tegra_avpchannel"; + nvavp->video_misc_dev.fops = &tegra_video_nvavp_fops; + nvavp->video_misc_dev.mode = S_IRWXUGO; + nvavp->video_misc_dev.parent = &ndev->dev; - ret = misc_register(&nvavp->misc_dev); + ret = misc_register(&nvavp->video_misc_dev); if (ret) { dev_err(&ndev->dev, "unable to register misc device!\n"); goto err_misc_reg; } +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + nvavp->audio_misc_dev.minor = MISC_DYNAMIC_MINOR; + nvavp->audio_misc_dev.name = "tegra_audio_avpchannel"; + nvavp->audio_misc_dev.fops = &tegra_audio_nvavp_fops; + nvavp->audio_misc_dev.mode = S_IRWXUGO; + nvavp->audio_misc_dev.parent = &ndev->dev; + + ret = misc_register(&nvavp->audio_misc_dev); + if (ret) { + dev_err(&ndev->dev, "unable to register misc device!\n"); + goto err_audio_misc_reg; + } +#endif + ret = request_irq(irq, nvavp_mbox_pending_isr, 0, TEGRA_NVAVP_NAME, nvavp); if (ret) { @@ -1358,7 +1585,11 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev) return 0; err_req_irq_pend: - misc_deregister(&nvavp->misc_dev); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + misc_deregister(&nvavp->audio_misc_dev); +err_audio_misc_reg: +#endif + misc_deregister(&nvavp->video_misc_dev); err_misc_reg: clk_put(nvavp->emc_clk); err_get_emc_clk: @@ -1404,8 +1635,11 @@ static int tegra_nvavp_remove(struct nvhost_device *ndev) nvavp_unload_ucode(nvavp); nvavp_unload_os(nvavp); - misc_deregister(&nvavp->misc_dev); + misc_deregister(&nvavp->video_misc_dev); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + misc_deregister(&nvavp->audio_misc_dev); +#endif clk_put(nvavp->bsev_clk); clk_put(nvavp->vde_clk); clk_put(nvavp->cop_clk); @@ -1428,7 +1662,7 @@ static int tegra_nvavp_suspend(struct nvhost_device *ndev, pm_message_t state) mutex_lock(&nvavp->open_lock); if (nvavp->refcount) { - if (nvavp_check_idle(nvavp)) + if (!nvavp->clk_enabled) nvavp_uninit(nvavp); else ret = -EBUSY; @@ -1446,7 +1680,7 @@ static int tegra_nvavp_resume(struct nvhost_device *ndev) mutex_lock(&nvavp->open_lock); if (nvavp->refcount) - nvavp_init(nvavp); + nvavp_init(nvavp, NVAVP_VIDEO_CHANNEL); mutex_unlock(&nvavp->open_lock); diff --git a/drivers/media/video/tegra/ov2710.c b/drivers/media/video/tegra/ov2710.c index 5e8eaa123124..293cb8932dfb 100644 --- a/drivers/media/video/tegra/ov2710.c +++ b/drivers/media/video/tegra/ov2710.c @@ -21,6 +21,8 @@ #include <linux/uaccess.h> #include <media/ov2710.h> +#define SIZEOF_I2C_TRANSBUF 32 + struct ov2710_reg { u16 addr; u16 val; @@ -30,6 +32,7 @@ struct ov2710_info { int mode; struct i2c_client *i2c_client; struct ov2710_platform_data *pdata; + u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; }; #define OV2710_TABLE_WAIT_MS 0 @@ -152,7 +155,7 @@ static struct ov2710_reg mode_1920x1080[] = { {0x3704, 0x44}, {0x3801, 0xd2}, - {0x3503, 0x17}, + {0x3503, 0x33}, {0x3500, 0x00}, {0x3501, 0x00}, {0x3502, 0x00}, @@ -283,7 +286,7 @@ static struct ov2710_reg mode_1280x720[] = { {0x3704, 0x40}, {0x3801, 0xbc}, - {0x3503, 0x17}, + {0x3503, 0x33}, {0x3500, 0x00}, {0x3501, 0x00}, {0x3502, 0x00}, @@ -400,14 +403,39 @@ static int ov2710_write_reg(struct i2c_client *client, u16 addr, u8 val) return err; } -static int ov2710_write_table(struct i2c_client *client, +static int ov2710_write_bulk_reg(struct i2c_client *client, u8 *data, int len) +{ + int err; + struct i2c_msg msg; + + if (!client->adapter) + return -ENODEV; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err == 1) + return 0; + + pr_err("ov2710: i2c bulk transfer failed at %x\n", + (int)data[0] << 8 | data[1]); + + return err; +} + +static int ov2710_write_table(struct ov2710_info *info, const struct ov2710_reg table[], const struct ov2710_reg override_list[], int num_override_regs) { int err; - const struct ov2710_reg *next; - int i; + const struct ov2710_reg *next, *n_next; + u8 *b_ptr = info->i2c_trans_buf; + unsigned int buf_filled = 0; + unsigned int i; u16 val; for (next = table; next->addr != OV2710_TABLE_END; next++) { @@ -416,9 +444,7 @@ static int ov2710_write_table(struct i2c_client *client, continue; } - val = next->val; - /* When an override list is passed in, replace the reg */ /* value to write if the reg is in the list */ if (override_list) { @@ -430,9 +456,28 @@ static int ov2710_write_table(struct i2c_client *client, } } - err = ov2710_write_reg(client, next->addr, val); + if (!buf_filled) { + b_ptr = info->i2c_trans_buf; + *b_ptr++ = next->addr >> 8; + *b_ptr++ = next->addr & 0xff; + buf_filled = 2; + } + *b_ptr++ = val; + buf_filled++; + + n_next = next + 1; + if (n_next->addr != OV2710_TABLE_END && + n_next->addr != OV2710_TABLE_WAIT_MS && + buf_filled < SIZEOF_I2C_TRANSBUF && + n_next->addr == next->addr + 1) { + continue; + } + + err = ov2710_write_bulk_reg(info->i2c_client, + info->i2c_trans_buf, buf_filled); if (err) return err; + buf_filled = 0; } return 0; } @@ -463,7 +508,7 @@ static int ov2710_set_mode(struct ov2710_info *info, struct ov2710_mode *mode) ov2710_get_coarse_time_regs(reg_list + 2, mode->coarse_time); ov2710_get_gain_reg(reg_list + 5, mode->gain); - err = ov2710_write_table(info->i2c_client, mode_table[sensor_mode], + err = ov2710_write_table(info, mode_table[sensor_mode], reg_list, 6); if (err) return err; @@ -474,51 +519,37 @@ static int ov2710_set_mode(struct ov2710_info *info, struct ov2710_mode *mode) static int ov2710_set_frame_length(struct ov2710_info *info, u32 frame_length) { - struct ov2710_reg reg_list[2]; - int i = 0; int ret; + struct ov2710_reg reg_list[2]; + u8 *b_ptr = info->i2c_trans_buf; ov2710_get_frame_length_regs(reg_list, frame_length); - for (i = 0; i < 2; i++) { - ret = ov2710_write_reg(info->i2c_client, reg_list[i].addr, - reg_list[i].val); - if (ret) - return ret; - } + *b_ptr++ = reg_list[0].addr >> 8; + *b_ptr++ = reg_list[0].addr & 0xff; + *b_ptr++ = reg_list[0].val & 0xff; + *b_ptr++ = reg_list[1].val & 0xff; + ret = ov2710_write_bulk_reg(info->i2c_client, info->i2c_trans_buf, 4); - return 0; + return ret; } static int ov2710_set_coarse_time(struct ov2710_info *info, u32 coarse_time) { int ret; - struct ov2710_reg reg_list[3]; - int i = 0; + u8 *b_ptr = info->i2c_trans_buf; ov2710_get_coarse_time_regs(reg_list, coarse_time); - ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x01); - if (ret) - return ret; - - for (i = 0; i < 3; i++) { - ret = ov2710_write_reg(info->i2c_client, reg_list[i].addr, - reg_list[i].val); - if (ret) - return ret; - } - - ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x11); - if (ret) - return ret; + *b_ptr++ = reg_list[0].addr >> 8; + *b_ptr++ = reg_list[0].addr & 0xff; + *b_ptr++ = reg_list[0].val & 0xff; + *b_ptr++ = reg_list[1].val & 0xff; + *b_ptr++ = reg_list[2].val & 0xff; + ret = ov2710_write_bulk_reg(info->i2c_client, info->i2c_trans_buf, 5); - ret = ov2710_write_reg(info->i2c_client, 0x3212, 0xa1); - if (ret) - return ret; - - return 0; + return ret; } static int ov2710_set_gain(struct ov2710_info *info, u16 gain) @@ -533,6 +564,48 @@ static int ov2710_set_gain(struct ov2710_info *info, u16 gain) return ret; } +static int ov2710_set_group_hold(struct ov2710_info *info, struct ov2710_ae *ae) +{ + int ret; + int count = 0; + bool groupHoldEnabled = false; + + if (ae->gain_enable) + count++; + if (ae->coarse_time_enable) + count++; + if (ae->frame_length_enable) + count++; + if (count >= 2) + groupHoldEnabled = true; + + if (groupHoldEnabled) { + ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x01); + if (ret) + return ret; + } + + if (ae->gain_enable) + ov2710_set_gain(info, ae->gain); + if (ae->coarse_time_enable) + ov2710_set_coarse_time(info, ae->coarse_time); + if (ae->frame_length_enable) + ov2710_set_frame_length(info, ae->frame_length); + + if (groupHoldEnabled) { + ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x11); + if (ret) + return ret; + + ret = ov2710_write_reg(info->i2c_client, 0x3212, 0xa1); + if (ret) + return ret; + } + + return 0; +} + + static int ov2710_get_status(struct ov2710_info *info, u8 *status) { int err; @@ -567,6 +640,17 @@ static long ov2710_ioctl(struct file *file, return ov2710_set_coarse_time(info, (u32)arg); case OV2710_IOCTL_SET_GAIN: return ov2710_set_gain(info, (u16)arg); + case OV2710_IOCTL_SET_GROUP_HOLD: + { + struct ov2710_ae ae; + if (copy_from_user(&ae, + (const void __user *)arg, + sizeof(struct ov2710_ae))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + return ov2710_set_group_hold(info, &ae); + } case OV2710_IOCTL_GET_STATUS: { u8 status; diff --git a/drivers/media/video/tegra/ov5640.c b/drivers/media/video/tegra/ov5640.c new file mode 100644 index 000000000000..26580b0105e3 --- /dev/null +++ b/drivers/media/video/tegra/ov5640.c @@ -0,0 +1,493 @@ +/* + * ov5640.c - ov5640 sensor driver + * + * Copyright (c) 2011 - 2012, NVIDIA, All Rights Reserved. + * + * Contributors: + * Abhinav Sinha <absinha@nvidia.com> + * + * Leverage soc380.c + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/** + * SetMode Sequence for 640x480. Phase 0. Sensor Dependent. + * This sequence should put sensor in streaming mode for 640x480 + * This is usually given by the FAE or the sensor vendor. + */ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <media/ov5640.h> + +#include "ov5640_tables.h" + +/* Focuser single step & full scale transition time truth table + * in the format of: + * index mode single step transition full scale transition + * 0 0 0 0 + * 1 1 50uS 51.2mS + * 2 1 100uS 102.3mS + * 3 1 200uS 204.6mS + * 4 1 400uS 409.2mS + * 5 1 800uS 818.4mS + * 6 1 1600uS 1637.0mS + * 7 1 3200uS 3274.0mS + * 8 0 0 0 + * 9 2 50uS 1.1mS + * A 2 100uS 2.2mS + * B 2 200uS 4.4mS + * C 2 400uS 8.8mS + * D 2 800uS 17.6mS + * E 2 1600uS 35.2mS + * F 2 3200uS 70.4mS + */ + +/* pick up the mode index setting and its settle time from the above table */ +#define OV5640_VCM_DACMODE 0x3602 +#define OV5640_TRANSITION_MODE 0x0B +#define SETTLETIME_MS 5 + +#define POS_LOW (0) +#define POS_HIGH (1023) +#define FPOS_COUNT 1024 +#define FOCAL_LENGTH (10.0f) +#define FNUMBER (2.8f) + +#define SIZEOF_I2C_TRANSBUF 64 + +struct ov5640_info { + int mode; + struct miscdevice miscdev_info; + struct i2c_client *i2c_client; + struct ov5640_platform_data *pdata; + struct ov5640_config focuser; + int af_fw_loaded; + struct kobject *kobj; + struct device *dev; + u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; +}; + +static int ov5640_read_reg(struct i2c_client *client, u16 addr, u8 *val) +{ + int err; + struct i2c_msg msg[2]; + unsigned char data[3]; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = data; + + /* high byte goes out first */ + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = data + 2; + + err = i2c_transfer(client->adapter, msg, 2); + + if (err != 2) + return -EINVAL; + + *val = data[2]; + + return 0; +} + +#ifdef KERNEL_WARNING +static int ov5640_write_reg(struct i2c_client *client, u8 addr, u8 value) +{ + int count; + struct i2c_msg msg[1]; + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + + data[0] = addr; + data[1] = (u8) (addr & 0xff); + data[2] = value; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 3; + msg[0].buf = data; + + count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (count == ARRAY_SIZE(msg)) + return 0; + dev_err(&client->dev, + "ov5840: i2c transfer failed, addr: %x, value: %02x\n", + addr, (u32)value); + return -EIO; +} +#endif + +static int ov5640_write_bulk_reg(struct i2c_client *client, u8 *data, int len) +{ + int err; + struct i2c_msg msg; + + if (!client->adapter) + return -ENODEV; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err == 1) + return 0; + + dev_err(&client->dev, "ov5640: i2c transfer failed at %x\n", + (int)data[0] << 8 | data[1]); + + return err; +} + +static int ov5640_write_table(struct ov5640_info *info, + struct ov5640_reg table[], + struct ov5640_reg override_list[], + int num_override_regs) +{ + int err; + struct ov5640_reg *next, *n_next; + u8 *b_ptr = info->i2c_trans_buf; + unsigned int buf_filled = 0; + int i; + u16 val; + + for (next = table; next->addr != OV5640_TABLE_END; next++) { + if (next->addr == OV5640_TABLE_WAIT_MS) { + msleep(next->val); + continue; + } + + val = next->val; + + /* When an override list is passed in, replace the reg */ + /* value to write if the reg is in the list */ + if (override_list) { + for (i = 0; i < num_override_regs; i++) { + if (next->addr == override_list[i].addr) { + val = override_list[i].val; + break; + } + } + } + + if (!buf_filled) { + b_ptr = info->i2c_trans_buf; + *b_ptr++ = next->addr >> 8; + *b_ptr++ = next->addr & 0xff; + buf_filled = 2; + } + *b_ptr++ = val; + buf_filled++; + + n_next = next + 1; + if (n_next->addr != OV5640_TABLE_END && + n_next->addr != OV5640_TABLE_WAIT_MS && + buf_filled < SIZEOF_I2C_TRANSBUF && + n_next->addr == next->addr + 1) { + continue; + } + + err = ov5640_write_bulk_reg(info->i2c_client, + info->i2c_trans_buf, buf_filled); + if (err) + return err; + + buf_filled = 0; + } + return 0; +} + +static int ov5640_set_mode(struct ov5640_info *info, struct ov5640_mode *mode) +{ + int sensor_mode; + int err; + + dev_info(info->dev, "%s: xres %u yres %u\n", + __func__, mode->xres, mode->yres); + if (!info->af_fw_loaded) { + err = ov5640_write_table(info, tbl_af_firmware, NULL, 0); + if (err) + return err; + info->af_fw_loaded = 1; + } + + if (mode->xres == 2592 && mode->yres == 1944) + sensor_mode = OV5640_MODE_2592x1944; + else if (mode->xres == 1920 && mode->yres == 1080) + sensor_mode = OV5640_MODE_1920x1080; + else if (mode->xres == 1296 && mode->yres == 964) + sensor_mode = OV5640_MODE_1296x972; + else { + dev_info(info->dev, "%s: invalid resolution: %d %d\n", + __func__, mode->xres, mode->yres); + return -EINVAL; + } + + err = ov5640_write_table(info, mode_table[sensor_mode], + NULL, 0); + if (err) + return err; + + info->mode = sensor_mode; + return 0; +} + +static int ov5640_set_af_mode(struct ov5640_info *info, u8 mode) +{ + dev_info(info->dev, "%s: mode %d\n", __func__, mode); + if (mode == OV5640_AF_INIFINITY) + return ov5640_write_table(info, tbl_release_focus, NULL, 0); + + if (mode == OV5640_AF_TRIGGER) + return ov5640_write_table(info, tbl_single_focus, NULL, 0); + + return -EINVAL; +} + +static int ov5640_get_af_status(struct ov5640_info *info, u8 *val) +{ + int err; + + err = ov5640_read_reg(info->i2c_client, 0x3023, val); + if (err) + return -EINVAL; + + dev_info(info->dev, "%s: value %02x\n", __func__, (u32)val); + return 0; +} + +static int ov5640_set_position(struct ov5640_info *info, u32 position) +{ + u8 data[4]; + + if (position < info->focuser.pos_low || + position > info->focuser.pos_high) + return -EINVAL; + + data[0] = (OV5640_VCM_DACMODE >> 8) & 0xff; + data[1] = OV5640_VCM_DACMODE & 0xff; + data[2] = ((position & 0xf) << 4) | OV5640_TRANSITION_MODE; + data[3] = (position * 0x3f0) >> 4; + return ov5640_write_bulk_reg(info->i2c_client, data, 4); +} + +static int ov5640_set_power(struct ov5640_info *info, u32 level) +{ + switch (level) { + case OV5640_POWER_LEVEL_OFF: + case OV5640_POWER_LEVEL_SUS: + if (info->pdata && info->pdata->power_off) + info->pdata->power_off(); + info->af_fw_loaded = 0; + info->mode = 0; + break; + case OV5640_POWER_LEVEL_ON: + if (info->pdata && info->pdata->power_on) + info->pdata->power_on(); + break; + default: + dev_err(info->dev, "unknown power level %d.\n", level); + return -EINVAL; + } + + return 0; +} + +static long ov5640_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ov5640_info *info = file->private_data; + + switch (cmd) { + case OV5640_IOCTL_SET_SENSOR_MODE: + { + struct ov5640_mode mode; + if (copy_from_user(&mode, + (const void __user *)arg, + sizeof(struct ov5640_mode))) { + return -EFAULT; + } + + return ov5640_set_mode(info, &mode); + } + case OV5640_IOCTL_GET_CONFIG: + { + if (copy_to_user((void __user *) arg, + &info->focuser, + sizeof(info->focuser))) { + dev_err(info->dev, "%s: 0x%x\n", __func__, __LINE__); + return -EFAULT; + } + + break; + } + case OV5640_IOCTL_GET_AF_STATUS: + { + int err; + u8 val; + + if (!info->af_fw_loaded) { + dev_err(info->dev, "OV5640 AF fw not loaded!\n"); + break; + } + + err = ov5640_get_af_status(info, &val); + if (err) + return err; + + if (copy_to_user((void __user *) arg, + &val, sizeof(val))) { + dev_err(info->dev, "%s: 0x%x\n", __func__, __LINE__); + return -EFAULT; + } + break; + } + case OV5640_IOCTL_SET_AF_MODE: + if (!info->af_fw_loaded) { + dev_err(info->dev, "OV5640 AF fw not loaded!\n"); + break; + } + return ov5640_set_af_mode(info, (u8)arg); + case OV5640_IOCTL_POWER_LEVEL: + return ov5640_set_power(info, (u32)arg); + case OV5640_IOCTL_SET_FPOSITION: + return ov5640_set_position(info, (u32)arg); + case OV5640_IOCTL_GET_SENSOR_STATUS: + { + u8 status = 0; + if (copy_to_user((void __user *)arg, &status, + 1)) { + dev_info(info->dev, "%s %d\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } + default: + return -EINVAL; + } + return 0; +} + +static int ov5640_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct ov5640_info *info; + + pr_info("%s\n", __func__); + if (!miscdev) { + pr_err("miscdev == NULL\n"); + return -1; + } + info = container_of(miscdev, struct ov5640_info, miscdev_info); + file->private_data = info; + + return 0; +} + +int ov5640_release(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + pr_info("%s\n", __func__); + return 0; +} + +static const struct file_operations ov5640_fileops = { + .owner = THIS_MODULE, + .open = ov5640_open, + .unlocked_ioctl = ov5640_ioctl, + .release = ov5640_release, +}; + +static struct miscdevice ov5640_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ov5640", + .fops = &ov5640_fileops, +}; + +static int ov5640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ov5640_info *info; + int err; + + dev_info(&client->dev, "ov5640: probing sensor.\n"); + + info = devm_kzalloc(&client->dev, + sizeof(struct ov5640_info), GFP_KERNEL); + if (!info) { + dev_err(&client->dev, "ov5640: Unable to allocate memory!\n"); + return -ENOMEM; + } + + memcpy(&(info->miscdev_info), + &ov5640_device, + sizeof(struct miscdevice)); + + err = misc_register(&(info->miscdev_info)); + if (err) { + dev_err(&client->dev, + "ov5640: Unable to register misc device!\n"); + devm_kfree(&client->dev, info); + return err; + } + + info->dev = &client->dev; + info->pdata = client->dev.platform_data; + info->i2c_client = client; + info->focuser.settle_time = SETTLETIME_MS; + info->focuser.focal_length = FOCAL_LENGTH; + info->focuser.fnumber = FNUMBER; + info->focuser.pos_low = POS_LOW; + info->focuser.pos_high = POS_HIGH; + + i2c_set_clientdata(client, info); + return 0; +} + +static int ov5640_remove(struct i2c_client *client) +{ + struct ov5640_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&ov5640_device); + return 0; +} + +static const struct i2c_device_id ov5640_id[] = { + { "ov5640", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, ov5640_id); + +static struct i2c_driver ov5640_i2c_driver = { + .driver = { + .name = "ov5640", + .owner = THIS_MODULE, + }, + .probe = ov5640_probe, + .remove = ov5640_remove, + .id_table = ov5640_id, +}; + +module_i2c_driver(ov5640_i2c_driver); diff --git a/drivers/media/video/tegra/ov5640_tables.h b/drivers/media/video/tegra/ov5640_tables.h new file mode 100644 index 000000000000..94cf1da31ec2 --- /dev/null +++ b/drivers/media/video/tegra/ov5640_tables.h @@ -0,0 +1,4582 @@ +/* + * ov5640_tables.h - table header for YUV camera sensor OV5640 driver. + * + * Copyright (C) 2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef OV5640_I2C_TABLES +#define OV5640_I2C_TABLES + +struct ov5640_reg { + u16 addr; + u16 val; +}; + +#define OV5640_TABLE_WAIT_MS 0 +#define OV5640_TABLE_END 1 + +static struct ov5640_reg mode_2592x1944[] = { + /* PLL Control MIPI bit rate/lane = 672MHz, 16-bit mode. + * Output size: 2608x1948 (0, 0) - (2623, 1951), + * Line Length = 2844, Frame Length = 1968 + */ + {0x3103, 0x11}, + {0x3008, 0x82}, + {OV5640_TABLE_WAIT_MS, 5}, + {0x3008, 0x42}, + {0x3103, 0x03}, + {0x3017, 0x00}, + {0x3018, 0x00}, + {0x3034, 0x18}, + {0x3035, 0x11}, + {0x3036, 0x54}, + {0x3037, 0x13}, + {0x3108, 0x01}, + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3621, 0xe0}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x3901, 0x0a}, + {0x3731, 0x12}, + {0x3600, 0x08}, + {0x3601, 0x33}, + {0x302d, 0x60}, + {0x3620, 0x52}, + {0x371b, 0x20}, + {0x471c, 0x50}, + {0x3a13, 0x43}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3634, 0x40}, + {0x3622, 0x01}, + {0x3c01, 0x34}, + {0x3c04, 0x28}, + {0x3c05, 0x98}, + {0x3c06, 0x00}, + {0x3c07, 0x07}, + {0x3c08, 0x00}, + {0x3c09, 0x1c}, + {0x3c0a, 0x9c}, + {0x3c0b, 0x40}, + {0x3820, 0x40}, + {0x3821, 0x06}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0x9f}, + {0x3808, 0x0a}, + {0x3809, 0x30}, + {0x380a, 0x07}, + {0x380b, 0x9c}, + {0x380c, 0x0b}, + {0x380d, 0x1c}, + {0x380e, 0x07}, + {0x380f, 0xb0}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3618, 0x04}, + {0x3612, 0x2b}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x370c, 0x00}, + {0x3a02, 0x07}, + {0x3a03, 0xb0}, + {0x3a08, 0x01}, + {0x3a09, 0x27}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0e, 0x06}, + {0x3a0d, 0x08}, + {0x3a14, 0x07}, + {0x3a15, 0xb0}, + {0x4001, 0x02}, + {0x4004, 0x06}, + {0x3000, 0x00}, + {0x3002, 0x1c}, + {0x3004, 0xff}, + {0x3006, 0xc3}, + {0x300e, 0x45}, + {0x302e, 0x08}, + {0x4300, 0x32}, + {0x4800, 0x24}, + {0x4837, 0x0a}, + {0x501f, 0x00}, + {0x440e, 0x00}, + {0x5000, 0xa7}, + {0x5001, 0x83}, + {0x5180, 0xff}, + {0x5181, 0xf2}, + {0x5182, 0x00}, + {0x5183, 0x14}, + {0x5184, 0x25}, + {0x5185, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x75}, + {0x518a, 0x54}, + {0x518b, 0xe0}, + {0x518c, 0xb2}, + {0x518d, 0x42}, + {0x518e, 0x3d}, + {0x518f, 0x56}, + {0x5190, 0x46}, + {0x5191, 0xf8}, + {0x5192, 0x04}, + {0x5193, 0x70}, + {0x5194, 0xf0}, + {0x5195, 0xf0}, + {0x5196, 0x03}, + {0x5197, 0x01}, + {0x5198, 0x04}, + {0x5199, 0x12}, + {0x519a, 0x04}, + {0x519b, 0x00}, + {0x519c, 0x06}, + {0x519d, 0x82}, + {0x519e, 0x38}, + {0x5381, 0x1e}, + {0x5382, 0x5b}, + {0x5383, 0x08}, + {0x5384, 0x0a}, + {0x5385, 0x7e}, + {0x5386, 0x88}, + {0x5387, 0x7c}, + {0x5388, 0x6c}, + {0x5389, 0x10}, + {0x538a, 0x01}, + {0x538b, 0x98}, + {0x5300, 0x08}, + {0x5301, 0x30}, + {0x5302, 0x10}, + {0x5303, 0x00}, + {0x5304, 0x08}, + {0x5305, 0x30}, + {0x5306, 0x08}, + {0x5307, 0x16}, + {0x5309, 0x08}, + {0x530a, 0x30}, + {0x530b, 0x04}, + {0x530c, 0x06}, + {0x5480, 0x01}, + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d}, + {0x5580, 0x02}, + {0x5583, 0x40}, + {0x5584, 0x10}, + {0x5589, 0x10}, + {0x558a, 0x00}, + {0x558b, 0xf8}, + {0x5800, 0x23}, + {0x5801, 0x14}, + {0x5802, 0x0f}, + {0x5803, 0x0f}, + {0x5804, 0x12}, + {0x5805, 0x26}, + {0x5806, 0x0c}, + {0x5807, 0x08}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x08}, + {0x580b, 0x0d}, + {0x580c, 0x08}, + {0x580d, 0x03}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x09}, + {0x5812, 0x07}, + {0x5813, 0x03}, + {0x5814, 0x00}, + {0x5815, 0x01}, + {0x5816, 0x03}, + {0x5817, 0x08}, + {0x5818, 0x0d}, + {0x5819, 0x08}, + {0x581a, 0x05}, + {0x581b, 0x06}, + {0x581c, 0x08}, + {0x581d, 0x0e}, + {0x581e, 0x29}, + {0x581f, 0x17}, + {0x5820, 0x11}, + {0x5821, 0x11}, + {0x5822, 0x15}, + {0x5823, 0x28}, + {0x5824, 0x46}, + {0x5825, 0x26}, + {0x5826, 0x08}, + {0x5827, 0x26}, + {0x5828, 0x64}, + {0x5829, 0x26}, + {0x582a, 0x24}, + {0x582b, 0x22}, + {0x582c, 0x24}, + {0x582d, 0x24}, + {0x582e, 0x06}, + {0x582f, 0x22}, + {0x5830, 0x40}, + {0x5831, 0x42}, + {0x5832, 0x24}, + {0x5833, 0x26}, + {0x5834, 0x24}, + {0x5835, 0x22}, + {0x5836, 0x22}, + {0x5837, 0x26}, + {0x5838, 0x44}, + {0x5839, 0x24}, + {0x583a, 0x26}, + {0x583b, 0x28}, + {0x583c, 0x42}, + {0x583d, 0xce}, + {0x5025, 0x00}, + {0x3a0f, 0x30}, + {0x3a10, 0x28}, + {0x3a1b, 0x30}, + {0x3a1e, 0x26}, + {0x3a11, 0x60}, + {0x3a1f, 0x14}, + {0x3008, 0x02}, + {OV5640_TABLE_END, 0x0000} +}; + +static struct ov5640_reg mode_1920x1080[] = { + /* PLL Control MIPI bit rate/lane = 672MHz, 16-bit mode. + * Output size: 1936x1096 (336, 426) - (2287, 1529), + * Line Length = 2500, Frame Length = 1120. + */ + {0x3103, 0x11}, + {0x3008, 0x82}, + {OV5640_TABLE_WAIT_MS, 5}, + {0x3008, 0x42}, + {0x3103, 0x03}, + {0x3017, 0x00}, + {0x3018, 0x00}, + {0x3034, 0x18}, + {0x3035, 0x11}, + {0x3036, 0x54}, + {0x3037, 0x13}, + {0x3108, 0x01}, + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3621, 0xe0}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x3901, 0x0a}, + {0x3731, 0x12}, + {0x3600, 0x08}, + {0x3601, 0x33}, + {0x302d, 0x60}, + {0x3620, 0x52}, + {0x371b, 0x20}, + {0x471c, 0x50}, + {0x3a13, 0x43}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3634, 0x40}, + {0x3622, 0x01}, + {0x3c01, 0x34}, + {0x3c04, 0x28}, + {0x3c05, 0x98}, + {0x3c06, 0x00}, + {0x3c07, 0x07}, + {0x3c08, 0x00}, + {0x3c09, 0x1c}, + {0x3c0a, 0x9c}, + {0x3c0b, 0x40}, + {0x3820, 0x40}, + {0x3821, 0x06}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3800, 0x01}, + {0x3801, 0x50}, + {0x3802, 0x01}, + {0x3803, 0xaa}, + {0x3804, 0x08}, + {0x3805, 0xef}, + {0x3806, 0x05}, + {0x3807, 0xf9}, + {0x3808, 0x07}, + {0x3809, 0x90}, + {0x380a, 0x04}, + {0x380b, 0x48}, + {0x380c, 0x09}, + {0x380d, 0xc4}, + {0x380e, 0x04}, + {0x380f, 0x60}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3618, 0x04}, + {0x3612, 0x2b}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x370c, 0x00}, + {0x3a02, 0x04}, + {0x3a03, 0x60}, + {0x3a08, 0x01}, + {0x3a09, 0x50}, + {0x3a0a, 0x01}, + {0x3a0b, 0x18}, + {0x3a0e, 0x03}, + {0x3a0d, 0x04}, + {0x3a14, 0x04}, + {0x3a15, 0x60}, + {0x4001, 0x02}, + {0x4004, 0x06}, + {0x3000, 0x00}, + {0x3002, 0x1c}, + {0x3004, 0xff}, + {0x3006, 0xc3}, + {0x300e, 0x45}, + {0x302e, 0x08}, + {0x4300, 0x32}, + {0x501f, 0x00}, + {0x4713, 0x02}, + {0x4407, 0x04}, + {0x440e, 0x00}, + {0x460b, 0x37}, + {0x460c, 0x20}, + {0x4800, 0x24}, + {0x4837, 0x0a}, + {0x3824, 0x04}, + {0x5000, 0xa7}, + {0x5001, 0x83}, + {0x5180, 0xff}, + {0x5181, 0xf2}, + {0x5182, 0x00}, + {0x5183, 0x14}, + {0x5184, 0x25}, + {0x5185, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x75}, + {0x518a, 0x54}, + {0x518b, 0xe0}, + {0x518c, 0xb2}, + {0x518d, 0x42}, + {0x518e, 0x3d}, + {0x518f, 0x56}, + {0x5190, 0x46}, + {0x5191, 0xf8}, + {0x5192, 0x04}, + {0x5193, 0x70}, + {0x5194, 0xf0}, + {0x5195, 0xf0}, + {0x5196, 0x03}, + {0x5197, 0x01}, + {0x5198, 0x04}, + {0x5199, 0x12}, + {0x519a, 0x04}, + {0x519b, 0x00}, + {0x519c, 0x06}, + {0x519d, 0x82}, + {0x519e, 0x38}, + {0x5381, 0x1e}, + {0x5382, 0x5b}, + {0x5383, 0x08}, + {0x5384, 0x0a}, + {0x5385, 0x7e}, + {0x5386, 0x88}, + {0x5387, 0x7c}, + {0x5388, 0x6c}, + {0x5389, 0x10}, + {0x538a, 0x01}, + {0x538b, 0x98}, + {0x5300, 0x08}, + {0x5301, 0x30}, + {0x5302, 0x10}, + {0x5303, 0x00}, + {0x5304, 0x08}, + {0x5305, 0x30}, + {0x5306, 0x08}, + {0x5307, 0x16}, + {0x5309, 0x08}, + {0x530a, 0x30}, + {0x530b, 0x04}, + {0x530c, 0x06}, + {0x5480, 0x01}, + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d}, + {0x5580, 0x02}, + {0x5583, 0x40}, + {0x5584, 0x10}, + {0x5589, 0x10}, + {0x558a, 0x00}, + {0x558b, 0xf8}, + {0x5800, 0x23}, + {0x5801, 0x14}, + {0x5802, 0x0f}, + {0x5803, 0x0f}, + {0x5804, 0x12}, + {0x5805, 0x26}, + {0x5806, 0x0c}, + {0x5807, 0x08}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x08}, + {0x580b, 0x0d}, + {0x580c, 0x08}, + {0x580d, 0x03}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x09}, + {0x5812, 0x07}, + {0x5813, 0x03}, + {0x5814, 0x00}, + {0x5815, 0x01}, + {0x5816, 0x03}, + {0x5817, 0x08}, + {0x5818, 0x0d}, + {0x5819, 0x08}, + {0x581a, 0x05}, + {0x581b, 0x06}, + {0x581c, 0x08}, + {0x581d, 0x0e}, + {0x581e, 0x29}, + {0x581f, 0x17}, + {0x5820, 0x11}, + {0x5821, 0x11}, + {0x5822, 0x15}, + {0x5823, 0x28}, + {0x5824, 0x46}, + {0x5825, 0x26}, + {0x5826, 0x08}, + {0x5827, 0x26}, + {0x5828, 0x64}, + {0x5829, 0x26}, + {0x582a, 0x24}, + {0x582b, 0x22}, + {0x582c, 0x24}, + {0x582d, 0x24}, + {0x582e, 0x06}, + {0x582f, 0x22}, + {0x5830, 0x40}, + {0x5831, 0x42}, + {0x5832, 0x24}, + {0x5833, 0x26}, + {0x5834, 0x24}, + {0x5835, 0x22}, + {0x5836, 0x22}, + {0x5837, 0x26}, + {0x5838, 0x44}, + {0x5839, 0x24}, + {0x583a, 0x26}, + {0x583b, 0x28}, + {0x583c, 0x42}, + {0x583d, 0xce}, + {0x5025, 0x00}, + {0x3a0f, 0x30}, + {0x3a10, 0x28}, + {0x3a1b, 0x30}, + {0x3a1e, 0x26}, + {0x3a11, 0x60}, + {0x3a1f, 0x14}, + {0x3008, 0x02}, + {OV5640_TABLE_END, 0x0000} +}; + +static struct ov5640_reg mode_1296x972[] = { + /* PLL Control MIPI bit rate/lane = 448MHz, 16-bit mode. + * Output size: 1304x972 (0, 0) - (2623, 1951), + * Line Length = 1886, Frame Length = 990. + */ + {0x3103, 0x11}, + {0x3008, 0x82}, + {OV5640_TABLE_WAIT_MS, 5}, + {0x3008, 0x42}, + {0x3103, 0x03}, + {0x3017, 0x00}, + {0x3018, 0x00}, + {0x3034, 0x18}, + {0x3035, 0x21}, + {0x3036, 0x70}, + {0x3037, 0x13}, + {0x3108, 0x01}, + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3621, 0xe0}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x3901, 0x0a}, + {0x3731, 0x12}, + {0x3600, 0x08}, + {0x3601, 0x33}, + {0x302d, 0x60}, + {0x3620, 0x52}, + {0x371b, 0x20}, + {0x471c, 0x50}, + {0x3a13, 0x43}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3634, 0x40}, + {0x3622, 0x01}, + {0x3c01, 0x34}, + {0x3c04, 0x28}, + {0x3c05, 0x98}, + {0x3c06, 0x00}, + {0x3c07, 0x07}, + {0x3c08, 0x00}, + {0x3c09, 0x1c}, + {0x3c0a, 0x9c}, + {0x3c0b, 0x40}, + {0x3820, 0x41}, + {0x3821, 0x07}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0x9f}, + {0x3808, 0x05}, + {0x3809, 0x18}, + {0x380a, 0x03}, + {0x380b, 0xcc}, + {0x380c, 0x07}, + {0x380d, 0x5e}, + {0x380e, 0x03}, + {0x380f, 0xde}, + {0x3810, 0x00}, + {0x3811, 0x06}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3618, 0x00}, + {0x3612, 0x29}, + {0x3708, 0x62}, + {0x3709, 0x52}, + {0x370c, 0x03}, + {0x3a02, 0x03}, + {0x3a03, 0xd8}, + {0x3a08, 0x01}, + {0x3a09, 0x27}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0e, 0x03}, + {0x3a0d, 0x04}, + {0x3a14, 0x03}, + {0x3a15, 0xd8}, + {0x4001, 0x02}, + {0x4004, 0x02}, + {0x3000, 0x00}, + {0x3002, 0x1c}, + {0x3004, 0xff}, + {0x3006, 0xc3}, + {0x300e, 0x45}, + {0x302e, 0x08}, + {0x4300, 0x32}, + {0x501f, 0x00}, + {0x4713, 0x02}, + {0x4407, 0x04}, + {0x440e, 0x00}, + {0x460b, 0x37}, + {0x460c, 0x20}, + {0x4800, 0x24}, + {0x4837, 0x10}, + {0x3824, 0x04}, + {0x5000, 0xa7}, + {0x5001, 0x83}, + {0x5180, 0xff}, + {0x5181, 0xf2}, + {0x5182, 0x00}, + {0x5183, 0x14}, + {0x5184, 0x25}, + {0x5185, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x75}, + {0x518a, 0x54}, + {0x518b, 0xe0}, + {0x518c, 0xb2}, + {0x518d, 0x42}, + {0x518e, 0x3d}, + {0x518f, 0x56}, + {0x5190, 0x46}, + {0x5191, 0xf8}, + {0x5192, 0x04}, + {0x5193, 0x70}, + {0x5194, 0xf0}, + {0x5195, 0xf0}, + {0x5196, 0x03}, + {0x5197, 0x01}, + {0x5198, 0x04}, + {0x5199, 0x12}, + {0x519a, 0x04}, + {0x519b, 0x00}, + {0x519c, 0x06}, + {0x519d, 0x82}, + {0x519e, 0x38}, + {0x5381, 0x1e}, + {0x5382, 0x5b}, + {0x5383, 0x08}, + {0x5384, 0x0a}, + {0x5385, 0x7e}, + {0x5386, 0x88}, + {0x5387, 0x7c}, + {0x5388, 0x6c}, + {0x5389, 0x10}, + {0x538a, 0x01}, + {0x538b, 0x98}, + {0x5300, 0x08}, + {0x5301, 0x30}, + {0x5302, 0x10}, + {0x5303, 0x00}, + {0x5304, 0x08}, + {0x5305, 0x30}, + {0x5306, 0x08}, + {0x5307, 0x16}, + {0x5309, 0x08}, + {0x530a, 0x30}, + {0x530b, 0x04}, + {0x530c, 0x06}, + {0x5480, 0x01}, + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d}, + {0x5580, 0x02}, + {0x5583, 0x40}, + {0x5584, 0x10}, + {0x5589, 0x10}, + {0x558a, 0x00}, + {0x558b, 0xf8}, + {0x5800, 0x23}, + {0x5801, 0x14}, + {0x5802, 0x0f}, + {0x5803, 0x0f}, + {0x5804, 0x12}, + {0x5805, 0x26}, + {0x5806, 0x0c}, + {0x5807, 0x08}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x08}, + {0x580b, 0x0d}, + {0x580c, 0x08}, + {0x580d, 0x03}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x09}, + {0x5812, 0x07}, + {0x5813, 0x03}, + {0x5814, 0x00}, + {0x5815, 0x01}, + {0x5816, 0x03}, + {0x5817, 0x08}, + {0x5818, 0x0d}, + {0x5819, 0x08}, + {0x581a, 0x05}, + {0x581b, 0x06}, + {0x581c, 0x08}, + {0x581d, 0x0e}, + {0x581e, 0x29}, + {0x581f, 0x17}, + {0x5820, 0x11}, + {0x5821, 0x11}, + {0x5822, 0x15}, + {0x5823, 0x28}, + {0x5824, 0x46}, + {0x5825, 0x26}, + {0x5826, 0x08}, + {0x5827, 0x26}, + {0x5828, 0x64}, + {0x5829, 0x26}, + {0x582a, 0x24}, + {0x582b, 0x22}, + {0x582c, 0x24}, + {0x582d, 0x24}, + {0x582e, 0x06}, + {0x582f, 0x22}, + {0x5830, 0x40}, + {0x5831, 0x42}, + {0x5832, 0x24}, + {0x5833, 0x26}, + {0x5834, 0x24}, + {0x5835, 0x22}, + {0x5836, 0x22}, + {0x5837, 0x26}, + {0x5838, 0x44}, + {0x5839, 0x24}, + {0x583a, 0x26}, + {0x583b, 0x28}, + {0x583c, 0x42}, + {0x583d, 0xce}, + {0x5025, 0x00}, + {0x3a0f, 0x30}, + {0x3a10, 0x28}, + {0x3a1b, 0x30}, + {0x3a1e, 0x26}, + {0x3a11, 0x60}, + {0x3a1f, 0x14}, + {0x3008, 0x02}, + {OV5640_TABLE_END, 0x0000} +}; + +enum { + OV5640_MODE_2592x1944 = 1, + OV5640_MODE_1920x1080, + OV5640_MODE_1296x972, +}; + +static struct ov5640_reg *mode_table[] = { + [OV5640_MODE_2592x1944] = mode_2592x1944, + [OV5640_MODE_1920x1080] = mode_1920x1080, + [OV5640_MODE_1296x972] = mode_1296x972, +}; + +static struct ov5640_reg tbl_af_firmware[] = { + {0x3000, 0x20}, + {0x8000, 0x02}, + {0x8001, 0x0b}, + {0x8002, 0x1f}, + {0x8003, 0x02}, + {0x8004, 0x07}, + {0x8005, 0x89}, + {0x8006, 0xc2}, + {0x8007, 0x01}, + {0x8008, 0x22}, + {0x8009, 0x22}, + {0x800a, 0x00}, + {0x800b, 0x02}, + {0x800c, 0x0b}, + {0x800d, 0x09}, + {0x800e, 0xe5}, + {0x800f, 0x40}, + {0x8010, 0x60}, + {0x8011, 0x03}, + {0x8012, 0x02}, + {0x8013, 0x00}, + {0x8014, 0x97}, + {0x8015, 0xf5}, + {0x8016, 0x3f}, + {0x8017, 0xd2}, + {0x8018, 0x34}, + {0x8019, 0x75}, + {0x801a, 0x29}, + {0x801b, 0xff}, + {0x801c, 0x75}, + {0x801d, 0x2a}, + {0x801e, 0x0e}, + {0x801f, 0x75}, + {0x8020, 0x2b}, + {0x8021, 0x55}, + {0x8022, 0x75}, + {0x8023, 0x2c}, + {0x8024, 0x01}, + {0x8025, 0x12}, + {0x8026, 0x0a}, + {0x8027, 0x0e}, + {0x8028, 0xe4}, + {0x8029, 0xff}, + {0x802a, 0xef}, + {0x802b, 0x25}, + {0x802c, 0xe0}, + {0x802d, 0x24}, + {0x802e, 0x41}, + {0x802f, 0xf8}, + {0x8030, 0xe4}, + {0x8031, 0xf6}, + {0x8032, 0x08}, + {0x8033, 0xf6}, + {0x8034, 0x0f}, + {0x8035, 0xbf}, + {0x8036, 0x34}, + {0x8037, 0xf2}, + {0x8038, 0x90}, + {0x8039, 0x0e}, + {0x803a, 0x88}, + {0x803b, 0xe4}, + {0x803c, 0x93}, + {0x803d, 0xff}, + {0x803e, 0xe5}, + {0x803f, 0x3e}, + {0x8040, 0xc3}, + {0x8041, 0x9f}, + {0x8042, 0x50}, + {0x8043, 0x04}, + {0x8044, 0x7f}, + {0x8045, 0x05}, + {0x8046, 0x80}, + {0x8047, 0x02}, + {0x8048, 0x7f}, + {0x8049, 0xfb}, + {0x804a, 0x78}, + {0x804b, 0xb0}, + {0x804c, 0xa6}, + {0x804d, 0x07}, + {0x804e, 0x12}, + {0x804f, 0x0a}, + {0x8050, 0x78}, + {0x8051, 0x40}, + {0x8052, 0x04}, + {0x8053, 0x7f}, + {0x8054, 0x03}, + {0x8055, 0x80}, + {0x8056, 0x02}, + {0x8057, 0x7f}, + {0x8058, 0x30}, + {0x8059, 0x78}, + {0x805a, 0xaf}, + {0x805b, 0xa6}, + {0x805c, 0x07}, + {0x805d, 0xe6}, + {0x805e, 0x18}, + {0x805f, 0xf6}, + {0x8060, 0x08}, + {0x8061, 0xe6}, + {0x8062, 0x78}, + {0x8063, 0xac}, + {0x8064, 0xf6}, + {0x8065, 0x78}, + {0x8066, 0xaf}, + {0x8067, 0xe6}, + {0x8068, 0x78}, + {0x8069, 0xad}, + {0x806a, 0xf6}, + {0x806b, 0x78}, + {0x806c, 0xb2}, + {0x806d, 0x76}, + {0x806e, 0x33}, + {0x806f, 0xe4}, + {0x8070, 0x08}, + {0x8071, 0xf6}, + {0x8072, 0x78}, + {0x8073, 0xab}, + {0x8074, 0x76}, + {0x8075, 0x01}, + {0x8076, 0x75}, + {0x8077, 0x3d}, + {0x8078, 0x02}, + {0x8079, 0x78}, + {0x807a, 0xa9}, + {0x807b, 0xf6}, + {0x807c, 0x08}, + {0x807d, 0xf6}, + {0x807e, 0x74}, + {0x807f, 0xff}, + {0x8080, 0x78}, + {0x8081, 0xb4}, + {0x8082, 0xf6}, + {0x8083, 0x08}, + {0x8084, 0xf6}, + {0x8085, 0x75}, + {0x8086, 0x40}, + {0x8087, 0x01}, + {0x8088, 0x78}, + {0x8089, 0xaf}, + {0x808a, 0xe6}, + {0x808b, 0x75}, + {0x808c, 0xf0}, + {0x808d, 0x05}, + {0x808e, 0xa4}, + {0x808f, 0xf5}, + {0x8090, 0x3e}, + {0x8091, 0x12}, + {0x8092, 0x08}, + {0x8093, 0x1d}, + {0x8094, 0xc2}, + {0x8095, 0x36}, + {0x8096, 0x22}, + {0x8097, 0x78}, + {0x8098, 0xab}, + {0x8099, 0xe6}, + {0x809a, 0xd3}, + {0x809b, 0x94}, + {0x809c, 0x00}, + {0x809d, 0x40}, + {0x809e, 0x02}, + {0x809f, 0x16}, + {0x80a0, 0x22}, + {0x80a1, 0xe5}, + {0x80a2, 0x40}, + {0x80a3, 0x64}, + {0x80a4, 0x05}, + {0x80a5, 0x70}, + {0x80a6, 0x28}, + {0x80a7, 0xf5}, + {0x80a8, 0x40}, + {0x80a9, 0xc2}, + {0x80aa, 0x01}, + {0x80ab, 0x78}, + {0x80ac, 0xac}, + {0x80ad, 0xe6}, + {0x80ae, 0x25}, + {0x80af, 0xe0}, + {0x80b0, 0x24}, + {0x80b1, 0x41}, + {0x80b2, 0xf8}, + {0x80b3, 0xe6}, + {0x80b4, 0xfe}, + {0x80b5, 0x08}, + {0x80b6, 0xe6}, + {0x80b7, 0xff}, + {0x80b8, 0x78}, + {0x80b9, 0x41}, + {0x80ba, 0xa6}, + {0x80bb, 0x06}, + {0x80bc, 0x08}, + {0x80bd, 0xa6}, + {0x80be, 0x07}, + {0x80bf, 0xa2}, + {0x80c0, 0x36}, + {0x80c1, 0xe4}, + {0x80c2, 0x33}, + {0x80c3, 0xf5}, + {0x80c4, 0x31}, + {0x80c5, 0x90}, + {0x80c6, 0x30}, + {0x80c7, 0x28}, + {0x80c8, 0xf0}, + {0x80c9, 0x75}, + {0x80ca, 0x3f}, + {0x80cb, 0x10}, + {0x80cc, 0xd2}, + {0x80cd, 0x34}, + {0x80ce, 0x22}, + {0x80cf, 0xe5}, + {0x80d0, 0x3e}, + {0x80d1, 0x75}, + {0x80d2, 0xf0}, + {0x80d3, 0x05}, + {0x80d4, 0x84}, + {0x80d5, 0x78}, + {0x80d6, 0xaf}, + {0x80d7, 0xf6}, + {0x80d8, 0x90}, + {0x80d9, 0x0e}, + {0x80da, 0x85}, + {0x80db, 0xe4}, + {0x80dc, 0x93}, + {0x80dd, 0xff}, + {0x80de, 0x25}, + {0x80df, 0xe0}, + {0x80e0, 0x24}, + {0x80e1, 0x0a}, + {0x80e2, 0xf8}, + {0x80e3, 0xe6}, + {0x80e4, 0xfc}, + {0x80e5, 0x08}, + {0x80e6, 0xe6}, + {0x80e7, 0xfd}, + {0x80e8, 0x78}, + {0x80e9, 0xaf}, + {0x80ea, 0xe6}, + {0x80eb, 0x25}, + {0x80ec, 0xe0}, + {0x80ed, 0x24}, + {0x80ee, 0x41}, + {0x80ef, 0xf8}, + {0x80f0, 0xa6}, + {0x80f1, 0x04}, + {0x80f2, 0x08}, + {0x80f3, 0xa6}, + {0x80f4, 0x05}, + {0x80f5, 0xef}, + {0x80f6, 0x12}, + {0x80f7, 0x0a}, + {0x80f8, 0x7f}, + {0x80f9, 0xd3}, + {0x80fa, 0x78}, + {0x80fb, 0xaa}, + {0x80fc, 0x96}, + {0x80fd, 0xee}, + {0x80fe, 0x18}, + {0x80ff, 0x96}, + {0x8100, 0x40}, + {0x8101, 0x0d}, + {0x8102, 0x78}, + {0x8103, 0xaf}, + {0x8104, 0xe6}, + {0x8105, 0x78}, + {0x8106, 0xac}, + {0x8107, 0xf6}, + {0x8108, 0x78}, + {0x8109, 0xa9}, + {0x810a, 0xa6}, + {0x810b, 0x06}, + {0x810c, 0x08}, + {0x810d, 0xa6}, + {0x810e, 0x07}, + {0x810f, 0x90}, + {0x8110, 0x0e}, + {0x8111, 0x85}, + {0x8112, 0xe4}, + {0x8113, 0x93}, + {0x8114, 0x12}, + {0x8115, 0x0a}, + {0x8116, 0x7f}, + {0x8117, 0xc3}, + {0x8118, 0x78}, + {0x8119, 0xb5}, + {0x811a, 0x96}, + {0x811b, 0xee}, + {0x811c, 0x18}, + {0x811d, 0x96}, + {0x811e, 0x50}, + {0x811f, 0x0d}, + {0x8120, 0x78}, + {0x8121, 0xaf}, + {0x8122, 0xe6}, + {0x8123, 0x78}, + {0x8124, 0xad}, + {0x8125, 0xf6}, + {0x8126, 0x78}, + {0x8127, 0xb4}, + {0x8128, 0xa6}, + {0x8129, 0x06}, + {0x812a, 0x08}, + {0x812b, 0xa6}, + {0x812c, 0x07}, + {0x812d, 0x78}, + {0x812e, 0xa9}, + {0x812f, 0xe6}, + {0x8130, 0xfe}, + {0x8131, 0x08}, + {0x8132, 0xe6}, + {0x8133, 0xc3}, + {0x8134, 0x78}, + {0x8135, 0xb5}, + {0x8136, 0x96}, + {0x8137, 0xff}, + {0x8138, 0xee}, + {0x8139, 0x18}, + {0x813a, 0x96}, + {0x813b, 0x78}, + {0x813c, 0xb6}, + {0x813d, 0xf6}, + {0x813e, 0x08}, + {0x813f, 0xa6}, + {0x8140, 0x07}, + {0x8141, 0x90}, + {0x8142, 0x0e}, + {0x8143, 0x8a}, + {0x8144, 0xe4}, + {0x8145, 0x18}, + {0x8146, 0x12}, + {0x8147, 0x0a}, + {0x8148, 0x5d}, + {0x8149, 0x40}, + {0x814a, 0x02}, + {0x814b, 0xd2}, + {0x814c, 0x36}, + {0x814d, 0x78}, + {0x814e, 0xaf}, + {0x814f, 0xe6}, + {0x8150, 0x08}, + {0x8151, 0x26}, + {0x8152, 0x08}, + {0x8153, 0xf6}, + {0x8154, 0xe5}, + {0x8155, 0x40}, + {0x8156, 0x64}, + {0x8157, 0x01}, + {0x8158, 0x70}, + {0x8159, 0x55}, + {0x815a, 0xe6}, + {0x815b, 0xc3}, + {0x815c, 0x78}, + {0x815d, 0xb3}, + {0x815e, 0x12}, + {0x815f, 0x0a}, + {0x8160, 0x53}, + {0x8161, 0x40}, + {0x8162, 0x10}, + {0x8163, 0x12}, + {0x8164, 0x0a}, + {0x8165, 0x4e}, + {0x8166, 0x50}, + {0x8167, 0x0b}, + {0x8168, 0x30}, + {0x8169, 0x36}, + {0x816a, 0x41}, + {0x816b, 0x78}, + {0x816c, 0xaf}, + {0x816d, 0xe6}, + {0x816e, 0x78}, + {0x816f, 0xac}, + {0x8170, 0x66}, + {0x8171, 0x60}, + {0x8172, 0x39}, + {0x8173, 0x12}, + {0x8174, 0x0a}, + {0x8175, 0x76}, + {0x8176, 0x40}, + {0x8177, 0x04}, + {0x8178, 0x7f}, + {0x8179, 0xfe}, + {0x817a, 0x80}, + {0x817b, 0x02}, + {0x817c, 0x7f}, + {0x817d, 0x02}, + {0x817e, 0x78}, + {0x817f, 0xb0}, + {0x8180, 0xa6}, + {0x8181, 0x07}, + {0x8182, 0x78}, + {0x8183, 0xac}, + {0x8184, 0xe6}, + {0x8185, 0x24}, + {0x8186, 0x03}, + {0x8187, 0x78}, + {0x8188, 0xb2}, + {0x8189, 0xf6}, + {0x818a, 0x78}, + {0x818b, 0xac}, + {0x818c, 0xe6}, + {0x818d, 0x24}, + {0x818e, 0xfd}, + {0x818f, 0x78}, + {0x8190, 0xb3}, + {0x8191, 0xf6}, + {0x8192, 0x12}, + {0x8193, 0x0a}, + {0x8194, 0x76}, + {0x8195, 0x40}, + {0x8196, 0x06}, + {0x8197, 0x78}, + {0x8198, 0xb3}, + {0x8199, 0xe6}, + {0x819a, 0xff}, + {0x819b, 0x80}, + {0x819c, 0x04}, + {0x819d, 0x78}, + {0x819e, 0xb2}, + {0x819f, 0xe6}, + {0x81a0, 0xff}, + {0x81a1, 0x78}, + {0x81a2, 0xb1}, + {0x81a3, 0xa6}, + {0x81a4, 0x07}, + {0x81a5, 0x75}, + {0x81a6, 0x40}, + {0x81a7, 0x02}, + {0x81a8, 0x78}, + {0x81a9, 0xab}, + {0x81aa, 0x76}, + {0x81ab, 0x01}, + {0x81ac, 0x02}, + {0x81ad, 0x02}, + {0x81ae, 0x6e}, + {0x81af, 0xe5}, + {0x81b0, 0x40}, + {0x81b1, 0x64}, + {0x81b2, 0x02}, + {0x81b3, 0x60}, + {0x81b4, 0x03}, + {0x81b5, 0x02}, + {0x81b6, 0x02}, + {0x81b7, 0x4e}, + {0x81b8, 0x78}, + {0x81b9, 0xb1}, + {0x81ba, 0xe6}, + {0x81bb, 0xff}, + {0x81bc, 0xc3}, + {0x81bd, 0x78}, + {0x81be, 0xb3}, + {0x81bf, 0x12}, + {0x81c0, 0x0a}, + {0x81c1, 0x54}, + {0x81c2, 0x40}, + {0x81c3, 0x08}, + {0x81c4, 0x12}, + {0x81c5, 0x0a}, + {0x81c6, 0x4e}, + {0x81c7, 0x50}, + {0x81c8, 0x03}, + {0x81c9, 0x02}, + {0x81ca, 0x02}, + {0x81cb, 0x4c}, + {0x81cc, 0x12}, + {0x81cd, 0x0a}, + {0x81ce, 0x76}, + {0x81cf, 0x40}, + {0x81d0, 0x04}, + {0x81d1, 0x7f}, + {0x81d2, 0xff}, + {0x81d3, 0x80}, + {0x81d4, 0x02}, + {0x81d5, 0x7f}, + {0x81d6, 0x01}, + {0x81d7, 0x78}, + {0x81d8, 0xb0}, + {0x81d9, 0xa6}, + {0x81da, 0x07}, + {0x81db, 0x78}, + {0x81dc, 0xac}, + {0x81dd, 0xe6}, + {0x81de, 0x04}, + {0x81df, 0x78}, + {0x81e0, 0xb2}, + {0x81e1, 0xf6}, + {0x81e2, 0x78}, + {0x81e3, 0xac}, + {0x81e4, 0xe6}, + {0x81e5, 0x14}, + {0x81e6, 0x78}, + {0x81e7, 0xb3}, + {0x81e8, 0xf6}, + {0x81e9, 0x18}, + {0x81ea, 0x12}, + {0x81eb, 0x0a}, + {0x81ec, 0x78}, + {0x81ed, 0x40}, + {0x81ee, 0x04}, + {0x81ef, 0xe6}, + {0x81f0, 0xff}, + {0x81f1, 0x80}, + {0x81f2, 0x02}, + {0x81f3, 0x7f}, + {0x81f4, 0x00}, + {0x81f5, 0x78}, + {0x81f6, 0xb2}, + {0x81f7, 0xa6}, + {0x81f8, 0x07}, + {0x81f9, 0xd3}, + {0x81fa, 0x08}, + {0x81fb, 0xe6}, + {0x81fc, 0x64}, + {0x81fd, 0x80}, + {0x81fe, 0x94}, + {0x81ff, 0x80}, + {0x8200, 0x40}, + {0x8201, 0x04}, + {0x8202, 0xe6}, + {0x8203, 0xff}, + {0x8204, 0x80}, + {0x8205, 0x02}, + {0x8206, 0x7f}, + {0x8207, 0x00}, + {0x8208, 0x78}, + {0x8209, 0xb3}, + {0x820a, 0xa6}, + {0x820b, 0x07}, + {0x820c, 0xc3}, + {0x820d, 0x18}, + {0x820e, 0xe6}, + {0x820f, 0x64}, + {0x8210, 0x80}, + {0x8211, 0x94}, + {0x8212, 0xb3}, + {0x8213, 0x50}, + {0x8214, 0x04}, + {0x8215, 0xe6}, + {0x8216, 0xff}, + {0x8217, 0x80}, + {0x8218, 0x02}, + {0x8219, 0x7f}, + {0x821a, 0x33}, + {0x821b, 0x78}, + {0x821c, 0xb2}, + {0x821d, 0xa6}, + {0x821e, 0x07}, + {0x821f, 0xc3}, + {0x8220, 0x08}, + {0x8221, 0xe6}, + {0x8222, 0x64}, + {0x8223, 0x80}, + {0x8224, 0x94}, + {0x8225, 0xb3}, + {0x8226, 0x50}, + {0x8227, 0x04}, + {0x8228, 0xe6}, + {0x8229, 0xff}, + {0x822a, 0x80}, + {0x822b, 0x02}, + {0x822c, 0x7f}, + {0x822d, 0x33}, + {0x822e, 0x78}, + {0x822f, 0xb3}, + {0x8230, 0xa6}, + {0x8231, 0x07}, + {0x8232, 0x12}, + {0x8233, 0x0a}, + {0x8234, 0x76}, + {0x8235, 0x40}, + {0x8236, 0x06}, + {0x8237, 0x78}, + {0x8238, 0xb3}, + {0x8239, 0xe6}, + {0x823a, 0xff}, + {0x823b, 0x80}, + {0x823c, 0x04}, + {0x823d, 0x78}, + {0x823e, 0xb2}, + {0x823f, 0xe6}, + {0x8240, 0xff}, + {0x8241, 0x78}, + {0x8242, 0xb1}, + {0x8243, 0xa6}, + {0x8244, 0x07}, + {0x8245, 0x75}, + {0x8246, 0x40}, + {0x8247, 0x03}, + {0x8248, 0x78}, + {0x8249, 0xab}, + {0x824a, 0x76}, + {0x824b, 0x01}, + {0x824c, 0x80}, + {0x824d, 0x20}, + {0x824e, 0xe5}, + {0x824f, 0x40}, + {0x8250, 0x64}, + {0x8251, 0x03}, + {0x8252, 0x70}, + {0x8253, 0x26}, + {0x8254, 0x78}, + {0x8255, 0xb1}, + {0x8256, 0xe6}, + {0x8257, 0xff}, + {0x8258, 0xc3}, + {0x8259, 0x78}, + {0x825a, 0xb3}, + {0x825b, 0x12}, + {0x825c, 0x0a}, + {0x825d, 0x54}, + {0x825e, 0x40}, + {0x825f, 0x05}, + {0x8260, 0x12}, + {0x8261, 0x0a}, + {0x8262, 0x4e}, + {0x8263, 0x40}, + {0x8264, 0x09}, + {0x8265, 0x78}, + {0x8266, 0xac}, + {0x8267, 0xe6}, + {0x8268, 0x78}, + {0x8269, 0xb1}, + {0x826a, 0xf6}, + {0x826b, 0x75}, + {0x826c, 0x40}, + {0x826d, 0x04}, + {0x826e, 0x78}, + {0x826f, 0xb1}, + {0x8270, 0xe6}, + {0x8271, 0x75}, + {0x8272, 0xf0}, + {0x8273, 0x05}, + {0x8274, 0xa4}, + {0x8275, 0xf5}, + {0x8276, 0x3e}, + {0x8277, 0x02}, + {0x8278, 0x08}, + {0x8279, 0x1d}, + {0x827a, 0xe5}, + {0x827b, 0x40}, + {0x827c, 0xb4}, + {0x827d, 0x04}, + {0x827e, 0x1f}, + {0x827f, 0x90}, + {0x8280, 0x0e}, + {0x8281, 0x89}, + {0x8282, 0xe4}, + {0x8283, 0x78}, + {0x8284, 0xb6}, + {0x8285, 0x12}, + {0x8286, 0x0a}, + {0x8287, 0x5d}, + {0x8288, 0x40}, + {0x8289, 0x02}, + {0x828a, 0xd2}, + {0x828b, 0x36}, + {0x828c, 0x75}, + {0x828d, 0x40}, + {0x828e, 0x05}, + {0x828f, 0x75}, + {0x8290, 0x29}, + {0x8291, 0xff}, + {0x8292, 0x75}, + {0x8293, 0x2a}, + {0x8294, 0x0e}, + {0x8295, 0x75}, + {0x8296, 0x2b}, + {0x8297, 0x59}, + {0x8298, 0x75}, + {0x8299, 0x2c}, + {0x829a, 0x01}, + {0x829b, 0x12}, + {0x829c, 0x0a}, + {0x829d, 0x0e}, + {0x829e, 0x22}, + {0x829f, 0xef}, + {0x82a0, 0x8d}, + {0x82a1, 0xf0}, + {0x82a2, 0xa4}, + {0x82a3, 0xa8}, + {0x82a4, 0xf0}, + {0x82a5, 0xcf}, + {0x82a6, 0x8c}, + {0x82a7, 0xf0}, + {0x82a8, 0xa4}, + {0x82a9, 0x28}, + {0x82aa, 0xce}, + {0x82ab, 0x8d}, + {0x82ac, 0xf0}, + {0x82ad, 0xa4}, + {0x82ae, 0x2e}, + {0x82af, 0xfe}, + {0x82b0, 0x22}, + {0x82b1, 0xbc}, + {0x82b2, 0x00}, + {0x82b3, 0x0b}, + {0x82b4, 0xbe}, + {0x82b5, 0x00}, + {0x82b6, 0x29}, + {0x82b7, 0xef}, + {0x82b8, 0x8d}, + {0x82b9, 0xf0}, + {0x82ba, 0x84}, + {0x82bb, 0xff}, + {0x82bc, 0xad}, + {0x82bd, 0xf0}, + {0x82be, 0x22}, + {0x82bf, 0xe4}, + {0x82c0, 0xcc}, + {0x82c1, 0xf8}, + {0x82c2, 0x75}, + {0x82c3, 0xf0}, + {0x82c4, 0x08}, + {0x82c5, 0xef}, + {0x82c6, 0x2f}, + {0x82c7, 0xff}, + {0x82c8, 0xee}, + {0x82c9, 0x33}, + {0x82ca, 0xfe}, + {0x82cb, 0xec}, + {0x82cc, 0x33}, + {0x82cd, 0xfc}, + {0x82ce, 0xee}, + {0x82cf, 0x9d}, + {0x82d0, 0xec}, + {0x82d1, 0x98}, + {0x82d2, 0x40}, + {0x82d3, 0x05}, + {0x82d4, 0xfc}, + {0x82d5, 0xee}, + {0x82d6, 0x9d}, + {0x82d7, 0xfe}, + {0x82d8, 0x0f}, + {0x82d9, 0xd5}, + {0x82da, 0xf0}, + {0x82db, 0xe9}, + {0x82dc, 0xe4}, + {0x82dd, 0xce}, + {0x82de, 0xfd}, + {0x82df, 0x22}, + {0x82e0, 0xed}, + {0x82e1, 0xf8}, + {0x82e2, 0xf5}, + {0x82e3, 0xf0}, + {0x82e4, 0xee}, + {0x82e5, 0x84}, + {0x82e6, 0x20}, + {0x82e7, 0xd2}, + {0x82e8, 0x1c}, + {0x82e9, 0xfe}, + {0x82ea, 0xad}, + {0x82eb, 0xf0}, + {0x82ec, 0x75}, + {0x82ed, 0xf0}, + {0x82ee, 0x08}, + {0x82ef, 0xef}, + {0x82f0, 0x2f}, + {0x82f1, 0xff}, + {0x82f2, 0xed}, + {0x82f3, 0x33}, + {0x82f4, 0xfd}, + {0x82f5, 0x40}, + {0x82f6, 0x07}, + {0x82f7, 0x98}, + {0x82f8, 0x50}, + {0x82f9, 0x06}, + {0x82fa, 0xd5}, + {0x82fb, 0xf0}, + {0x82fc, 0xf2}, + {0x82fd, 0x22}, + {0x82fe, 0xc3}, + {0x82ff, 0x98}, + {0x8300, 0xfd}, + {0x8301, 0x0f}, + {0x8302, 0xd5}, + {0x8303, 0xf0}, + {0x8304, 0xea}, + {0x8305, 0x22}, + {0x8306, 0xe8}, + {0x8307, 0x8f}, + {0x8308, 0xf0}, + {0x8309, 0xa4}, + {0x830a, 0xcc}, + {0x830b, 0x8b}, + {0x830c, 0xf0}, + {0x830d, 0xa4}, + {0x830e, 0x2c}, + {0x830f, 0xfc}, + {0x8310, 0xe9}, + {0x8311, 0x8e}, + {0x8312, 0xf0}, + {0x8313, 0xa4}, + {0x8314, 0x2c}, + {0x8315, 0xfc}, + {0x8316, 0x8a}, + {0x8317, 0xf0}, + {0x8318, 0xed}, + {0x8319, 0xa4}, + {0x831a, 0x2c}, + {0x831b, 0xfc}, + {0x831c, 0xea}, + {0x831d, 0x8e}, + {0x831e, 0xf0}, + {0x831f, 0xa4}, + {0x8320, 0xcd}, + {0x8321, 0xa8}, + {0x8322, 0xf0}, + {0x8323, 0x8b}, + {0x8324, 0xf0}, + {0x8325, 0xa4}, + {0x8326, 0x2d}, + {0x8327, 0xcc}, + {0x8328, 0x38}, + {0x8329, 0x25}, + {0x832a, 0xf0}, + {0x832b, 0xfd}, + {0x832c, 0xe9}, + {0x832d, 0x8f}, + {0x832e, 0xf0}, + {0x832f, 0xa4}, + {0x8330, 0x2c}, + {0x8331, 0xcd}, + {0x8332, 0x35}, + {0x8333, 0xf0}, + {0x8334, 0xfc}, + {0x8335, 0xeb}, + {0x8336, 0x8e}, + {0x8337, 0xf0}, + {0x8338, 0xa4}, + {0x8339, 0xfe}, + {0x833a, 0xa9}, + {0x833b, 0xf0}, + {0x833c, 0xeb}, + {0x833d, 0x8f}, + {0x833e, 0xf0}, + {0x833f, 0xa4}, + {0x8340, 0xcf}, + {0x8341, 0xc5}, + {0x8342, 0xf0}, + {0x8343, 0x2e}, + {0x8344, 0xcd}, + {0x8345, 0x39}, + {0x8346, 0xfe}, + {0x8347, 0xe4}, + {0x8348, 0x3c}, + {0x8349, 0xfc}, + {0x834a, 0xea}, + {0x834b, 0xa4}, + {0x834c, 0x2d}, + {0x834d, 0xce}, + {0x834e, 0x35}, + {0x834f, 0xf0}, + {0x8350, 0xfd}, + {0x8351, 0xe4}, + {0x8352, 0x3c}, + {0x8353, 0xfc}, + {0x8354, 0x22}, + {0x8355, 0x75}, + {0x8356, 0xf0}, + {0x8357, 0x08}, + {0x8358, 0x75}, + {0x8359, 0x82}, + {0x835a, 0x00}, + {0x835b, 0xef}, + {0x835c, 0x2f}, + {0x835d, 0xff}, + {0x835e, 0xee}, + {0x835f, 0x33}, + {0x8360, 0xfe}, + {0x8361, 0xcd}, + {0x8362, 0x33}, + {0x8363, 0xcd}, + {0x8364, 0xcc}, + {0x8365, 0x33}, + {0x8366, 0xcc}, + {0x8367, 0xc5}, + {0x8368, 0x82}, + {0x8369, 0x33}, + {0x836a, 0xc5}, + {0x836b, 0x82}, + {0x836c, 0x9b}, + {0x836d, 0xed}, + {0x836e, 0x9a}, + {0x836f, 0xec}, + {0x8370, 0x99}, + {0x8371, 0xe5}, + {0x8372, 0x82}, + {0x8373, 0x98}, + {0x8374, 0x40}, + {0x8375, 0x0c}, + {0x8376, 0xf5}, + {0x8377, 0x82}, + {0x8378, 0xee}, + {0x8379, 0x9b}, + {0x837a, 0xfe}, + {0x837b, 0xed}, + {0x837c, 0x9a}, + {0x837d, 0xfd}, + {0x837e, 0xec}, + {0x837f, 0x99}, + {0x8380, 0xfc}, + {0x8381, 0x0f}, + {0x8382, 0xd5}, + {0x8383, 0xf0}, + {0x8384, 0xd6}, + {0x8385, 0xe4}, + {0x8386, 0xce}, + {0x8387, 0xfb}, + {0x8388, 0xe4}, + {0x8389, 0xcd}, + {0x838a, 0xfa}, + {0x838b, 0xe4}, + {0x838c, 0xcc}, + {0x838d, 0xf9}, + {0x838e, 0xa8}, + {0x838f, 0x82}, + {0x8390, 0x22}, + {0x8391, 0xb8}, + {0x8392, 0x00}, + {0x8393, 0xc1}, + {0x8394, 0xb9}, + {0x8395, 0x00}, + {0x8396, 0x59}, + {0x8397, 0xba}, + {0x8398, 0x00}, + {0x8399, 0x2d}, + {0x839a, 0xec}, + {0x839b, 0x8b}, + {0x839c, 0xf0}, + {0x839d, 0x84}, + {0x839e, 0xcf}, + {0x839f, 0xce}, + {0x83a0, 0xcd}, + {0x83a1, 0xfc}, + {0x83a2, 0xe5}, + {0x83a3, 0xf0}, + {0x83a4, 0xcb}, + {0x83a5, 0xf9}, + {0x83a6, 0x78}, + {0x83a7, 0x18}, + {0x83a8, 0xef}, + {0x83a9, 0x2f}, + {0x83aa, 0xff}, + {0x83ab, 0xee}, + {0x83ac, 0x33}, + {0x83ad, 0xfe}, + {0x83ae, 0xed}, + {0x83af, 0x33}, + {0x83b0, 0xfd}, + {0x83b1, 0xec}, + {0x83b2, 0x33}, + {0x83b3, 0xfc}, + {0x83b4, 0xeb}, + {0x83b5, 0x33}, + {0x83b6, 0xfb}, + {0x83b7, 0x10}, + {0x83b8, 0xd7}, + {0x83b9, 0x03}, + {0x83ba, 0x99}, + {0x83bb, 0x40}, + {0x83bc, 0x04}, + {0x83bd, 0xeb}, + {0x83be, 0x99}, + {0x83bf, 0xfb}, + {0x83c0, 0x0f}, + {0x83c1, 0xd8}, + {0x83c2, 0xe5}, + {0x83c3, 0xe4}, + {0x83c4, 0xf9}, + {0x83c5, 0xfa}, + {0x83c6, 0x22}, + {0x83c7, 0x78}, + {0x83c8, 0x18}, + {0x83c9, 0xef}, + {0x83ca, 0x2f}, + {0x83cb, 0xff}, + {0x83cc, 0xee}, + {0x83cd, 0x33}, + {0x83ce, 0xfe}, + {0x83cf, 0xed}, + {0x83d0, 0x33}, + {0x83d1, 0xfd}, + {0x83d2, 0xec}, + {0x83d3, 0x33}, + {0x83d4, 0xfc}, + {0x83d5, 0xc9}, + {0x83d6, 0x33}, + {0x83d7, 0xc9}, + {0x83d8, 0x10}, + {0x83d9, 0xd7}, + {0x83da, 0x05}, + {0x83db, 0x9b}, + {0x83dc, 0xe9}, + {0x83dd, 0x9a}, + {0x83de, 0x40}, + + {0x83df, 0x07}, + {0x83e0, 0xec}, + {0x83e1, 0x9b}, + {0x83e2, 0xfc}, + {0x83e3, 0xe9}, + {0x83e4, 0x9a}, + {0x83e5, 0xf9}, + {0x83e6, 0x0f}, + {0x83e7, 0xd8}, + {0x83e8, 0xe0}, + {0x83e9, 0xe4}, + {0x83ea, 0xc9}, + {0x83eb, 0xfa}, + {0x83ec, 0xe4}, + {0x83ed, 0xcc}, + {0x83ee, 0xfb}, + {0x83ef, 0x22}, + {0x83f0, 0x75}, + {0x83f1, 0xf0}, + {0x83f2, 0x10}, + {0x83f3, 0xef}, + {0x83f4, 0x2f}, + {0x83f5, 0xff}, + {0x83f6, 0xee}, + {0x83f7, 0x33}, + {0x83f8, 0xfe}, + {0x83f9, 0xed}, + {0x83fa, 0x33}, + {0x83fb, 0xfd}, + {0x83fc, 0xcc}, + {0x83fd, 0x33}, + {0x83fe, 0xcc}, + {0x83ff, 0xc8}, + {0x8400, 0x33}, + {0x8401, 0xc8}, + {0x8402, 0x10}, + {0x8403, 0xd7}, + {0x8404, 0x07}, + {0x8405, 0x9b}, + {0x8406, 0xec}, + {0x8407, 0x9a}, + {0x8408, 0xe8}, + {0x8409, 0x99}, + {0x840a, 0x40}, + {0x840b, 0x0a}, + {0x840c, 0xed}, + {0x840d, 0x9b}, + {0x840e, 0xfd}, + {0x840f, 0xec}, + {0x8410, 0x9a}, + {0x8411, 0xfc}, + {0x8412, 0xe8}, + {0x8413, 0x99}, + {0x8414, 0xf8}, + {0x8415, 0x0f}, + {0x8416, 0xd5}, + {0x8417, 0xf0}, + {0x8418, 0xda}, + {0x8419, 0xe4}, + {0x841a, 0xcd}, + {0x841b, 0xfb}, + {0x841c, 0xe4}, + {0x841d, 0xcc}, + {0x841e, 0xfa}, + {0x841f, 0xe4}, + {0x8420, 0xc8}, + {0x8421, 0xf9}, + {0x8422, 0x22}, + {0x8423, 0xeb}, + {0x8424, 0x9f}, + {0x8425, 0xf5}, + {0x8426, 0xf0}, + {0x8427, 0xea}, + {0x8428, 0x9e}, + {0x8429, 0x42}, + {0x842a, 0xf0}, + {0x842b, 0xe9}, + {0x842c, 0x9d}, + {0x842d, 0x42}, + {0x842e, 0xf0}, + {0x842f, 0xe8}, + {0x8430, 0x9c}, + {0x8431, 0x45}, + {0x8432, 0xf0}, + {0x8433, 0x22}, + {0x8434, 0xe8}, + {0x8435, 0x60}, + {0x8436, 0x0f}, + {0x8437, 0xef}, + {0x8438, 0xc3}, + {0x8439, 0x33}, + {0x843a, 0xff}, + {0x843b, 0xee}, + {0x843c, 0x33}, + {0x843d, 0xfe}, + {0x843e, 0xed}, + {0x843f, 0x33}, + {0x8440, 0xfd}, + {0x8441, 0xec}, + {0x8442, 0x33}, + {0x8443, 0xfc}, + {0x8444, 0xd8}, + {0x8445, 0xf1}, + {0x8446, 0x22}, + {0x8447, 0xe4}, + {0x8448, 0x93}, + {0x8449, 0xfc}, + {0x844a, 0x74}, + {0x844b, 0x01}, + {0x844c, 0x93}, + {0x844d, 0xfd}, + {0x844e, 0x74}, + {0x844f, 0x02}, + {0x8450, 0x93}, + {0x8451, 0xfe}, + {0x8452, 0x74}, + {0x8453, 0x03}, + {0x8454, 0x93}, + {0x8455, 0xff}, + {0x8456, 0x22}, + {0x8457, 0xe6}, + {0x8458, 0xfb}, + {0x8459, 0x08}, + {0x845a, 0xe6}, + {0x845b, 0xf9}, + {0x845c, 0x08}, + {0x845d, 0xe6}, + {0x845e, 0xfa}, + {0x845f, 0x08}, + {0x8460, 0xe6}, + {0x8461, 0xcb}, + {0x8462, 0xf8}, + {0x8463, 0x22}, + {0x8464, 0xec}, + {0x8465, 0xf6}, + {0x8466, 0x08}, + {0x8467, 0xed}, + {0x8468, 0xf6}, + {0x8469, 0x08}, + {0x846a, 0xee}, + {0x846b, 0xf6}, + {0x846c, 0x08}, + {0x846d, 0xef}, + {0x846e, 0xf6}, + {0x846f, 0x22}, + {0x8470, 0xd0}, + {0x8471, 0x83}, + {0x8472, 0xd0}, + {0x8473, 0x82}, + {0x8474, 0xe4}, + {0x8475, 0x93}, + {0x8476, 0xf6}, + {0x8477, 0x08}, + {0x8478, 0x74}, + {0x8479, 0x01}, + {0x847a, 0x93}, + {0x847b, 0xf6}, + {0x847c, 0x08}, + {0x847d, 0x74}, + {0x847e, 0x02}, + {0x847f, 0x93}, + {0x8480, 0xf6}, + {0x8481, 0x08}, + {0x8482, 0x74}, + {0x8483, 0x03}, + {0x8484, 0x93}, + {0x8485, 0xf6}, + {0x8486, 0x74}, + {0x8487, 0x04}, + {0x8488, 0x73}, + {0x8489, 0xa4}, + {0x848a, 0x25}, + {0x848b, 0x82}, + {0x848c, 0xf5}, + {0x848d, 0x82}, + {0x848e, 0xe5}, + {0x848f, 0xf0}, + {0x8490, 0x35}, + {0x8491, 0x83}, + {0x8492, 0xf5}, + {0x8493, 0x83}, + {0x8494, 0x22}, + {0x8495, 0xd0}, + {0x8496, 0x83}, + {0x8497, 0xd0}, + {0x8498, 0x82}, + {0x8499, 0xf8}, + {0x849a, 0xe4}, + {0x849b, 0x93}, + {0x849c, 0x70}, + {0x849d, 0x12}, + {0x849e, 0x74}, + {0x849f, 0x01}, + {0x84a0, 0x93}, + {0x84a1, 0x70}, + {0x84a2, 0x0d}, + {0x84a3, 0xa3}, + {0x84a4, 0xa3}, + {0x84a5, 0x93}, + {0x84a6, 0xf8}, + {0x84a7, 0x74}, + {0x84a8, 0x01}, + {0x84a9, 0x93}, + {0x84aa, 0xf5}, + {0x84ab, 0x82}, + {0x84ac, 0x88}, + {0x84ad, 0x83}, + {0x84ae, 0xe4}, + {0x84af, 0x73}, + {0x84b0, 0x74}, + {0x84b1, 0x02}, + {0x84b2, 0x93}, + {0x84b3, 0x68}, + {0x84b4, 0x60}, + {0x84b5, 0xef}, + {0x84b6, 0xa3}, + {0x84b7, 0xa3}, + {0x84b8, 0xa3}, + {0x84b9, 0x80}, + {0x84ba, 0xdf}, + {0x84bb, 0x90}, + {0x84bc, 0x38}, + {0x84bd, 0x04}, + {0x84be, 0x78}, + {0x84bf, 0x45}, + {0x84c0, 0x12}, + {0x84c1, 0x09}, + {0x84c2, 0x1f}, + {0x84c3, 0x90}, + {0x84c4, 0x38}, + {0x84c5, 0x00}, + {0x84c6, 0xe0}, + {0x84c7, 0xfe}, + {0x84c8, 0xa3}, + {0x84c9, 0xe0}, + {0x84ca, 0xfd}, + {0x84cb, 0xed}, + {0x84cc, 0xff}, + {0x84cd, 0xc3}, + {0x84ce, 0x12}, + {0x84cf, 0x08}, + {0x84d0, 0xcb}, + {0x84d1, 0x90}, + {0x84d2, 0x38}, + {0x84d3, 0x10}, + {0x84d4, 0x12}, + {0x84d5, 0x08}, + {0x84d6, 0xbf}, + {0x84d7, 0x90}, + {0x84d8, 0x38}, + {0x84d9, 0x06}, + {0x84da, 0x78}, + {0x84db, 0x47}, + {0x84dc, 0x12}, + {0x84dd, 0x09}, + {0x84de, 0x1f}, + {0x84df, 0x90}, + {0x84e0, 0x38}, + {0x84e1, 0x02}, + {0x84e2, 0xe0}, + {0x84e3, 0xfe}, + {0x84e4, 0xa3}, + {0x84e5, 0xe0}, + {0x84e6, 0xfd}, + {0x84e7, 0xed}, + {0x84e8, 0xff}, + {0x84e9, 0xc3}, + {0x84ea, 0x12}, + {0x84eb, 0x08}, + {0x84ec, 0xcb}, + {0x84ed, 0x90}, + {0x84ee, 0x38}, + {0x84ef, 0x12}, + {0x84f0, 0x12}, + {0x84f1, 0x08}, + {0x84f2, 0xbf}, + {0x84f3, 0xa3}, + {0x84f4, 0xe0}, + {0x84f5, 0xb4}, + {0x84f6, 0x31}, + {0x84f7, 0x07}, + {0x84f8, 0x78}, + {0x84f9, 0x45}, + {0x84fa, 0x79}, + {0x84fb, 0x45}, + {0x84fc, 0x12}, + {0x84fd, 0x09}, + {0x84fe, 0x2a}, + {0x84ff, 0x90}, + {0x8500, 0x38}, + {0x8501, 0x14}, + {0x8502, 0xe0}, + {0x8503, 0xb4}, + {0x8504, 0x71}, + {0x8505, 0x15}, + {0x8506, 0x78}, + {0x8507, 0x45}, + {0x8508, 0xe6}, + {0x8509, 0xfe}, + {0x850a, 0x08}, + {0x850b, 0xe6}, + {0x850c, 0x78}, + {0x850d, 0x02}, + {0x850e, 0xce}, + {0x850f, 0xc3}, + {0x8510, 0x13}, + {0x8511, 0xce}, + {0x8512, 0x13}, + {0x8513, 0xd8}, + {0x8514, 0xf9}, + {0x8515, 0x79}, + {0x8516, 0x46}, + {0x8517, 0xf7}, + {0x8518, 0xee}, + {0x8519, 0x19}, + {0x851a, 0xf7}, + {0x851b, 0x90}, + {0x851c, 0x38}, + {0x851d, 0x15}, + {0x851e, 0xe0}, + {0x851f, 0xb4}, + {0x8520, 0x31}, + {0x8521, 0x07}, + {0x8522, 0x78}, + {0x8523, 0x47}, + {0x8524, 0x79}, + {0x8525, 0x47}, + {0x8526, 0x12}, + {0x8527, 0x09}, + {0x8528, 0x2a}, + {0x8529, 0x90}, + {0x852a, 0x38}, + {0x852b, 0x15}, + {0x852c, 0xe0}, + {0x852d, 0xb4}, + {0x852e, 0x71}, + {0x852f, 0x15}, + {0x8530, 0x78}, + {0x8531, 0x47}, + {0x8532, 0xe6}, + {0x8533, 0xfe}, + {0x8534, 0x08}, + {0x8535, 0xe6}, + {0x8536, 0x78}, + {0x8537, 0x02}, + {0x8538, 0xce}, + {0x8539, 0xc3}, + {0x853a, 0x13}, + {0x853b, 0xce}, + {0x853c, 0x13}, + {0x853d, 0xd8}, + {0x853e, 0xf9}, + {0x853f, 0x79}, + {0x8540, 0x48}, + {0x8541, 0xf7}, + {0x8542, 0xee}, + {0x8543, 0x19}, + {0x8544, 0xf7}, + {0x8545, 0x79}, + {0x8546, 0x45}, + {0x8547, 0x12}, + {0x8548, 0x08}, + {0x8549, 0xfb}, + {0x854a, 0x09}, + {0x854b, 0x12}, + {0x854c, 0x08}, + {0x854d, 0xfb}, + {0x854e, 0xaf}, + {0x854f, 0x3a}, + {0x8550, 0x12}, + {0x8551, 0x08}, + {0x8552, 0xb0}, + {0x8553, 0x7d}, + {0x8554, 0x50}, + {0x8555, 0x12}, + {0x8556, 0x02}, + {0x8557, 0xb1}, + {0x8558, 0x78}, + {0x8559, 0x4d}, + {0x855a, 0xa6}, + {0x855b, 0x06}, + {0x855c, 0x08}, + {0x855d, 0xa6}, + {0x855e, 0x07}, + {0x855f, 0xaf}, + {0x8560, 0x38}, + {0x8561, 0x12}, + {0x8562, 0x08}, + {0x8563, 0xb0}, + {0x8564, 0x7d}, + {0x8565, 0x50}, + {0x8566, 0x12}, + {0x8567, 0x02}, + {0x8568, 0xb1}, + {0x8569, 0x78}, + {0x856a, 0x49}, + {0x856b, 0xa6}, + {0x856c, 0x06}, + {0x856d, 0x08}, + {0x856e, 0xa6}, + {0x856f, 0x07}, + {0x8570, 0xaf}, + {0x8571, 0x3b}, + {0x8572, 0x78}, + {0x8573, 0x47}, + {0x8574, 0x12}, + {0x8575, 0x08}, + {0x8576, 0xb2}, + {0x8577, 0x7d}, + {0x8578, 0x3c}, + {0x8579, 0x12}, + {0x857a, 0x02}, + {0x857b, 0xb1}, + {0x857c, 0x78}, + {0x857d, 0x4f}, + {0x857e, 0xa6}, + {0x857f, 0x06}, + {0x8580, 0x08}, + {0x8581, 0xa6}, + {0x8582, 0x07}, + {0x8583, 0xaf}, + {0x8584, 0x39}, + {0x8585, 0x7e}, + {0x8586, 0x00}, + {0x8587, 0x78}, + {0x8588, 0x47}, + {0x8589, 0x12}, + {0x858a, 0x08}, + {0x858b, 0xb4}, + {0x858c, 0x7d}, + {0x858d, 0x3c}, + {0x858e, 0x12}, + {0x858f, 0x02}, + {0x8590, 0xb1}, + {0x8591, 0x78}, + {0x8592, 0x4b}, + {0x8593, 0xa6}, + {0x8594, 0x06}, + {0x8595, 0x08}, + {0x8596, 0xa6}, + {0x8597, 0x07}, + {0x8598, 0xc3}, + {0x8599, 0x78}, + {0x859a, 0x4e}, + {0x859b, 0xe6}, + {0x859c, 0x94}, + {0x859d, 0x08}, + {0x859e, 0x18}, + {0x859f, 0xe6}, + {0x85a0, 0x94}, + {0x85a1, 0x00}, + {0x85a2, 0x50}, + {0x85a3, 0x05}, + {0x85a4, 0x76}, + {0x85a5, 0x00}, + {0x85a6, 0x08}, + {0x85a7, 0x76}, + {0x85a8, 0x08}, + {0x85a9, 0xc3}, + {0x85aa, 0x78}, + {0x85ab, 0x50}, + {0x85ac, 0xe6}, + {0x85ad, 0x94}, + {0x85ae, 0x08}, + {0x85af, 0x18}, + {0x85b0, 0xe6}, + {0x85b1, 0x94}, + {0x85b2, 0x00}, + {0x85b3, 0x50}, + {0x85b4, 0x05}, + {0x85b5, 0x76}, + {0x85b6, 0x00}, + {0x85b7, 0x08}, + {0x85b8, 0x76}, + {0x85b9, 0x08}, + {0x85ba, 0x78}, + {0x85bb, 0x4d}, + {0x85bc, 0x12}, + {0x85bd, 0x08}, + {0x85be, 0xe8}, + {0x85bf, 0xff}, + {0x85c0, 0xc3}, + {0x85c1, 0x78}, + {0x85c2, 0x4a}, + {0x85c3, 0xe6}, + {0x85c4, 0x9f}, + {0x85c5, 0xff}, + {0x85c6, 0x18}, + {0x85c7, 0xe6}, + {0x85c8, 0x9e}, + {0x85c9, 0x78}, + {0x85ca, 0x51}, + {0x85cb, 0x12}, + {0x85cc, 0x08}, + {0x85cd, 0xdf}, + {0x85ce, 0xff}, + {0x85cf, 0xc3}, + {0x85d0, 0x78}, + {0x85d1, 0x4c}, + {0x85d2, 0xe6}, + {0x85d3, 0x9f}, + {0x85d4, 0xff}, + {0x85d5, 0x18}, + {0x85d6, 0xe6}, + {0x85d7, 0x9e}, + {0x85d8, 0xfe}, + {0x85d9, 0xe4}, + {0x85da, 0xfc}, + {0x85db, 0xfd}, + {0x85dc, 0x78}, + {0x85dd, 0x55}, + {0x85de, 0x12}, + {0x85df, 0x04}, + {0x85e0, 0x64}, + {0x85e1, 0x78}, + {0x85e2, 0x4d}, + {0x85e3, 0x12}, + {0x85e4, 0x08}, + {0x85e5, 0xe8}, + {0x85e6, 0x78}, + {0x85e7, 0x4a}, + {0x85e8, 0x26}, + {0x85e9, 0xff}, + {0x85ea, 0xee}, + {0x85eb, 0x18}, + {0x85ec, 0x36}, + {0x85ed, 0x78}, + {0x85ee, 0x59}, + {0x85ef, 0x12}, + {0x85f0, 0x08}, + {0x85f1, 0xdf}, + {0x85f2, 0x78}, + {0x85f3, 0x4c}, + {0x85f4, 0x26}, + {0x85f5, 0xff}, + {0x85f6, 0xee}, + {0x85f7, 0x18}, + {0x85f8, 0x36}, + {0x85f9, 0xfe}, + {0x85fa, 0xe4}, + {0x85fb, 0xfc}, + {0x85fc, 0xfd}, + {0x85fd, 0x78}, + {0x85fe, 0x5d}, + {0x85ff, 0x12}, + {0x8600, 0x04}, + {0x8601, 0x64}, + {0x8602, 0x78}, + {0x8603, 0x51}, + {0x8604, 0x12}, + {0x8605, 0x09}, + {0x8606, 0x13}, + {0x8607, 0x50}, + {0x8608, 0x09}, + {0x8609, 0x78}, + {0x860a, 0x51}, + {0x860b, 0x12}, + {0x860c, 0x04}, + {0x860d, 0x70}, + {0x860e, 0x00}, + {0x860f, 0x00}, + {0x8610, 0x00}, + {0x8611, 0x00}, + {0x8612, 0x78}, + {0x8613, 0x55}, + {0x8614, 0x12}, + {0x8615, 0x09}, + {0x8616, 0x13}, + {0x8617, 0x50}, + {0x8618, 0x09}, + {0x8619, 0x78}, + {0x861a, 0x55}, + {0x861b, 0x12}, + {0x861c, 0x04}, + {0x861d, 0x70}, + {0x861e, 0x00}, + {0x861f, 0x00}, + {0x8620, 0x00}, + {0x8621, 0x00}, + {0x8622, 0x12}, + {0x8623, 0x08}, + {0x8624, 0xf0}, + {0x8625, 0x78}, + {0x8626, 0x59}, + {0x8627, 0x12}, + {0x8628, 0x04}, + {0x8629, 0x57}, + {0x862a, 0xd3}, + {0x862b, 0x12}, + {0x862c, 0x04}, + {0x862d, 0x23}, + {0x862e, 0x40}, + {0x862f, 0x08}, + {0x8630, 0x12}, + {0x8631, 0x08}, + {0x8632, 0xf0}, + {0x8633, 0x78}, + {0x8634, 0x59}, + {0x8635, 0x12}, + {0x8636, 0x04}, + {0x8637, 0x64}, + {0x8638, 0x78}, + {0x8639, 0x47}, + {0x863a, 0x12}, + {0x863b, 0x08}, + {0x863c, 0xf2}, + {0x863d, 0x78}, + {0x863e, 0x5d}, + {0x863f, 0x12}, + {0x8640, 0x04}, + {0x8641, 0x57}, + {0x8642, 0xd3}, + {0x8643, 0x12}, + {0x8644, 0x04}, + {0x8645, 0x23}, + {0x8646, 0x40}, + {0x8647, 0x0a}, + {0x8648, 0x78}, + {0x8649, 0x47}, + {0x864a, 0x12}, + {0x864b, 0x08}, + {0x864c, 0xf2}, + {0x864d, 0x78}, + {0x864e, 0x5d}, + {0x864f, 0x12}, + {0x8650, 0x04}, + {0x8651, 0x64}, + {0x8652, 0xe4}, + {0x8653, 0xfd}, + {0x8654, 0x78}, + {0x8655, 0x54}, + {0x8656, 0x12}, + {0x8657, 0x09}, + {0x8658, 0x0b}, + {0x8659, 0x24}, + {0x865a, 0x01}, + {0x865b, 0x12}, + {0x865c, 0x08}, + {0x865d, 0xd3}, + {0x865e, 0x78}, + {0x865f, 0x58}, + {0x8660, 0x12}, + {0x8661, 0x09}, + {0x8662, 0x0b}, + {0x8663, 0x24}, + {0x8664, 0x02}, + {0x8665, 0x12}, + {0x8666, 0x08}, + {0x8667, 0xd3}, + {0x8668, 0x78}, + {0x8669, 0x5c}, + {0x866a, 0x12}, + {0x866b, 0x09}, + {0x866c, 0x0b}, + {0x866d, 0x24}, + {0x866e, 0x03}, + {0x866f, 0x12}, + {0x8670, 0x08}, + {0x8671, 0xd3}, + {0x8672, 0x78}, + {0x8673, 0x60}, + {0x8674, 0x12}, + {0x8675, 0x09}, + {0x8676, 0x0b}, + {0x8677, 0x24}, + {0x8678, 0x04}, + {0x8679, 0x12}, + {0x867a, 0x08}, + {0x867b, 0xd3}, + {0x867c, 0x0d}, + {0x867d, 0xbd}, + {0x867e, 0x05}, + {0x867f, 0xd4}, + {0x8680, 0xc2}, + {0x8681, 0x0e}, + {0x8682, 0xc2}, + {0x8683, 0x06}, + {0x8684, 0x22}, + {0x8685, 0x85}, + {0x8686, 0x08}, + {0x8687, 0x36}, + {0x8688, 0x90}, + {0x8689, 0x30}, + {0x868a, 0x24}, + {0x868b, 0xe0}, + {0x868c, 0xf5}, + {0x868d, 0x32}, + {0x868e, 0xa3}, + {0x868f, 0xe0}, + {0x8690, 0xf5}, + {0x8691, 0x33}, + {0x8692, 0xa3}, + {0x8693, 0xe0}, + {0x8694, 0xf5}, + {0x8695, 0x34}, + {0x8696, 0xa3}, + {0x8697, 0xe0}, + {0x8698, 0xf5}, + {0x8699, 0x35}, + {0x869a, 0xa3}, + {0x869b, 0xe0}, + {0x869c, 0xf5}, + {0x869d, 0x31}, + {0x869e, 0xd2}, + {0x869f, 0x33}, + {0x86a0, 0xe5}, + {0x86a1, 0x36}, + {0x86a2, 0x12}, + {0x86a3, 0x04}, + {0x86a4, 0x95}, + {0x86a5, 0x06}, + {0x86a6, 0xdf}, + {0x86a7, 0x03}, + {0x86a8, 0x06}, + {0x86a9, 0xe3}, + {0x86aa, 0x04}, + {0x86ab, 0x06}, + {0x86ac, 0xe9}, + {0x86ad, 0x07}, + {0x86ae, 0x06}, + {0x86af, 0xf1}, + {0x86b0, 0x08}, + {0x86b1, 0x07}, + {0x86b2, 0x14}, + {0x86b3, 0x18}, + {0x86b4, 0x07}, + {0x86b5, 0x2a}, + {0x86b6, 0x19}, + {0x86b7, 0x07}, + {0x86b8, 0x01}, + {0x86b9, 0x1a}, + {0x86ba, 0x07}, + {0x86bb, 0x0c}, + {0x86bc, 0x1b}, + {0x86bd, 0x07}, + {0x86be, 0x4e}, + {0x86bf, 0x80}, + {0x86c0, 0x07}, + {0x86c1, 0x51}, + {0x86c2, 0x81}, + {0x86c3, 0x07}, + {0x86c4, 0x6d}, + {0x86c5, 0x8f}, + {0x86c6, 0x07}, + {0x86c7, 0x5c}, + {0x86c8, 0x90}, + {0x86c9, 0x07}, + {0x86ca, 0x6d}, + {0x86cb, 0x91}, + {0x86cc, 0x07}, + {0x86cd, 0x6d}, + {0x86ce, 0x92}, + {0x86cf, 0x07}, + {0x86d0, 0x6d}, + {0x86d1, 0x93}, + {0x86d2, 0x07}, + {0x86d3, 0x6d}, + {0x86d4, 0x94}, + {0x86d5, 0x07}, + {0x86d6, 0x6d}, + {0x86d7, 0x98}, + {0x86d8, 0x07}, + {0x86d9, 0x6a}, + {0x86da, 0x9f}, + {0x86db, 0x00}, + {0x86dc, 0x00}, + {0x86dd, 0x07}, + {0x86de, 0x88}, + {0x86df, 0x12}, + {0x86e0, 0x0a}, + {0x86e1, 0xb8}, + {0x86e2, 0x22}, + {0x86e3, 0x12}, + {0x86e4, 0x0a}, + {0x86e5, 0xb8}, + {0x86e6, 0xd2}, + {0x86e7, 0x03}, + {0x86e8, 0x22}, + {0x86e9, 0xa2}, + {0x86ea, 0x36}, + {0x86eb, 0xe4}, + {0x86ec, 0x33}, + {0x86ed, 0xf5}, + {0x86ee, 0x31}, + {0x86ef, 0x80}, + {0x86f0, 0x7c}, + {0x86f1, 0xc2}, + {0x86f2, 0x01}, + {0x86f3, 0xc2}, + {0x86f4, 0x02}, + {0x86f5, 0xc2}, + {0x86f6, 0x03}, + {0x86f7, 0x12}, + {0x86f8, 0x09}, + {0x86f9, 0x34}, + {0x86fa, 0x75}, + {0x86fb, 0x3f}, + {0x86fc, 0x70}, + {0x86fd, 0xd2}, + {0x86fe, 0x34}, + {0x86ff, 0x80}, + {0x8700, 0x6c}, + {0x8701, 0x85}, + {0x8702, 0x35}, + {0x8703, 0x3d}, + {0x8704, 0x85}, + {0x8705, 0x31}, + {0x8706, 0x3e}, + {0x8707, 0x12}, + {0x8708, 0x08}, + {0x8709, 0x1d}, + {0x870a, 0x80}, + {0x870b, 0x61}, + {0x870c, 0x85}, + {0x870d, 0x3d}, + {0x870e, 0x35}, + {0x870f, 0x85}, + {0x8710, 0x3e}, + {0x8711, 0x31}, + {0x8712, 0x80}, + {0x8713, 0x59}, + {0x8714, 0xe4}, + {0x8715, 0xf5}, + {0x8716, 0x22}, + {0x8717, 0xf5}, + {0x8718, 0x23}, + {0x8719, 0x85}, + {0x871a, 0x35}, + {0x871b, 0x1e}, + {0x871c, 0x85}, + {0x871d, 0x34}, + {0x871e, 0x1d}, + {0x871f, 0x85}, + {0x8720, 0x33}, + {0x8721, 0x1c}, + {0x8722, 0x85}, + {0x8723, 0x32}, + {0x8724, 0x1b}, + {0x8725, 0x12}, + {0x8726, 0x0a}, + {0x8727, 0x8a}, + {0x8728, 0x80}, + {0x8729, 0x1f}, + {0x872a, 0x75}, + {0x872b, 0x22}, + {0x872c, 0x00}, + {0x872d, 0x75}, + {0x872e, 0x23}, + {0x872f, 0x01}, + {0x8730, 0x74}, + {0x8731, 0xff}, + {0x8732, 0xf5}, + {0x8733, 0x1a}, + {0x8734, 0xf5}, + {0x8735, 0x19}, + {0x8736, 0xf5}, + {0x8737, 0x18}, + {0x8738, 0xf5}, + {0x8739, 0x17}, + {0x873a, 0x12}, + {0x873b, 0x0a}, + {0x873c, 0x8a}, + {0x873d, 0x85}, + {0x873e, 0x1a}, + {0x873f, 0x35}, + {0x8740, 0x85}, + {0x8741, 0x19}, + {0x8742, 0x34}, + {0x8743, 0x85}, + {0x8744, 0x18}, + {0x8745, 0x33}, + {0x8746, 0x85}, + {0x8747, 0x17}, + {0x8748, 0x32}, + {0x8749, 0xe4}, + {0x874a, 0xf5}, + {0x874b, 0x31}, + {0x874c, 0x80}, + {0x874d, 0x1f}, + {0x874e, 0x02}, + {0x874f, 0x0a}, + {0x8750, 0xef}, + {0x8751, 0x85}, + {0x8752, 0x32}, + {0x8753, 0x38}, + {0x8754, 0x85}, + {0x8755, 0x33}, + {0x8756, 0x39}, + {0x8757, 0x12}, + {0x8758, 0x04}, + {0x8759, 0xbb}, + {0x875a, 0x80}, + {0x875b, 0x11}, + {0x875c, 0x85}, + {0x875d, 0x35}, + {0x875e, 0x3b}, + {0x875f, 0x85}, + {0x8760, 0x34}, + {0x8761, 0x3a}, + {0x8762, 0x85}, + {0x8763, 0x33}, + {0x8764, 0x39}, + {0x8765, 0x85}, + {0x8766, 0x32}, + {0x8767, 0x38}, + {0x8768, 0x80}, + {0x8769, 0x03}, + {0x876a, 0x02}, + {0x876b, 0x04}, + {0x876c, 0xbb}, + {0x876d, 0x90}, + {0x876e, 0x30}, + {0x876f, 0x24}, + {0x8770, 0xe5}, + {0x8771, 0x32}, + {0x8772, 0xf0}, + {0x8773, 0xa3}, + {0x8774, 0xe5}, + {0x8775, 0x33}, + {0x8776, 0xf0}, + {0x8777, 0xa3}, + {0x8778, 0xe5}, + {0x8779, 0x34}, + {0x877a, 0xf0}, + {0x877b, 0xa3}, + {0x877c, 0xe5}, + {0x877d, 0x35}, + {0x877e, 0xf0}, + {0x877f, 0xa3}, + {0x8780, 0xe5}, + {0x8781, 0x31}, + {0x8782, 0xf0}, + {0x8783, 0x90}, + {0x8784, 0x30}, + {0x8785, 0x23}, + {0x8786, 0xe4}, + {0x8787, 0xf0}, + {0x8788, 0x22}, + {0x8789, 0xc0}, + {0x878a, 0xe0}, + {0x878b, 0xc0}, + {0x878c, 0x83}, + {0x878d, 0xc0}, + {0x878e, 0x82}, + {0x878f, 0xc0}, + {0x8790, 0xd0}, + {0x8791, 0x90}, + {0x8792, 0x3f}, + {0x8793, 0x0c}, + {0x8794, 0xe0}, + {0x8795, 0xf5}, + {0x8796, 0x27}, + {0x8797, 0xe5}, + {0x8798, 0x27}, + {0x8799, 0x30}, + {0x879a, 0xe3}, + {0x879b, 0x42}, + {0x879c, 0x30}, + {0x879d, 0x35}, + {0x879e, 0x34}, + {0x879f, 0x90}, + {0x87a0, 0x60}, + {0x87a1, 0x19}, + {0x87a2, 0xe0}, + {0x87a3, 0xf5}, + {0x87a4, 0x0a}, + {0x87a5, 0xa3}, + {0x87a6, 0xe0}, + {0x87a7, 0xf5}, + {0x87a8, 0x0b}, + {0x87a9, 0x30}, + {0x87aa, 0x01}, + {0x87ab, 0x06}, + {0x87ac, 0x30}, + {0x87ad, 0x32}, + {0x87ae, 0x03}, + {0x87af, 0xd3}, + {0x87b0, 0x80}, + {0x87b1, 0x01}, + {0x87b2, 0xc3}, + {0x87b3, 0x92}, + {0x87b4, 0x09}, + {0x87b5, 0x30}, + {0x87b6, 0x02}, + {0x87b7, 0x06}, + {0x87b8, 0x30}, + {0x87b9, 0x32}, + {0x87ba, 0x03}, + {0x87bb, 0xd3}, + {0x87bc, 0x80}, + {0x87bd, 0x01}, + {0x87be, 0xc3}, + {0x87bf, 0x92}, + {0x87c0, 0x0a}, + {0x87c1, 0x30}, + {0x87c2, 0x32}, + {0x87c3, 0x0c}, + {0x87c4, 0x30}, + {0x87c5, 0x03}, + {0x87c6, 0x09}, + {0x87c7, 0x20}, + {0x87c8, 0x02}, + {0x87c9, 0x06}, + {0x87ca, 0x20}, + {0x87cb, 0x01}, + {0x87cc, 0x03}, + {0x87cd, 0xd3}, + {0x87ce, 0x80}, + {0x87cf, 0x01}, + {0x87d0, 0xc3}, + {0x87d1, 0x92}, + {0x87d2, 0x0b}, + {0x87d3, 0x90}, + {0x87d4, 0x30}, + {0x87d5, 0x01}, + {0x87d6, 0xe0}, + {0x87d7, 0x44}, + {0x87d8, 0x40}, + {0x87d9, 0xf0}, + {0x87da, 0xe0}, + {0x87db, 0x54}, + {0x87dc, 0xbf}, + {0x87dd, 0xf0}, + {0x87de, 0xe5}, + {0x87df, 0x27}, + {0x87e0, 0x30}, + {0x87e1, 0xe1}, + {0x87e2, 0x14}, + {0x87e3, 0x30}, + {0x87e4, 0x33}, + {0x87e5, 0x11}, + {0x87e6, 0x90}, + {0x87e7, 0x30}, + {0x87e8, 0x22}, + {0x87e9, 0xe0}, + {0x87ea, 0xf5}, + {0x87eb, 0x08}, + {0x87ec, 0xe4}, + {0x87ed, 0xf0}, + {0x87ee, 0x30}, + {0x87ef, 0x00}, + {0x87f0, 0x03}, + {0x87f1, 0xd3}, + {0x87f2, 0x80}, + {0x87f3, 0x01}, + {0x87f4, 0xc3}, + {0x87f5, 0x92}, + {0x87f6, 0x08}, + {0x87f7, 0xe5}, + {0x87f8, 0x27}, + {0x87f9, 0x30}, + {0x87fa, 0xe5}, + {0x87fb, 0x12}, + {0x87fc, 0x90}, + {0x87fd, 0x56}, + {0x87fe, 0x90}, + {0x87ff, 0xe0}, + {0x8800, 0xf5}, + {0x8801, 0x09}, + {0x8802, 0x30}, + {0x8803, 0x30}, + {0x8804, 0x09}, + {0x8805, 0x30}, + {0x8806, 0x05}, + {0x8807, 0x03}, + {0x8808, 0xd3}, + {0x8809, 0x80}, + {0x880a, 0x01}, + {0x880b, 0xc3}, + {0x880c, 0x92}, + {0x880d, 0x0d}, + {0x880e, 0x90}, + {0x880f, 0x3f}, + {0x8810, 0x0c}, + {0x8811, 0xe5}, + {0x8812, 0x27}, + {0x8813, 0xf0}, + {0x8814, 0xd0}, + {0x8815, 0xd0}, + {0x8816, 0xd0}, + {0x8817, 0x82}, + {0x8818, 0xd0}, + {0x8819, 0x83}, + {0x881a, 0xd0}, + {0x881b, 0xe0}, + {0x881c, 0x32}, + {0x881d, 0x90}, + {0x881e, 0x0e}, + {0x881f, 0x7d}, + {0x8820, 0xe4}, + {0x8821, 0x93}, + {0x8822, 0xfe}, + {0x8823, 0x74}, + {0x8824, 0x01}, + {0x8825, 0x93}, + {0x8826, 0xff}, + {0x8827, 0xc3}, + {0x8828, 0x90}, + {0x8829, 0x0e}, + {0x882a, 0x7b}, + {0x882b, 0x74}, + {0x882c, 0x01}, + {0x882d, 0x93}, + {0x882e, 0x9f}, + {0x882f, 0xff}, + {0x8830, 0xe4}, + {0x8831, 0x93}, + {0x8832, 0x9e}, + {0x8833, 0xfe}, + {0x8834, 0xe4}, + {0x8835, 0x8f}, + {0x8836, 0x30}, + {0x8837, 0x8e}, + {0x8838, 0x2f}, + {0x8839, 0xf5}, + {0x883a, 0x2e}, + {0x883b, 0xf5}, + {0x883c, 0x2d}, + {0x883d, 0xab}, + {0x883e, 0x30}, + {0x883f, 0xaa}, + {0x8840, 0x2f}, + {0x8841, 0xa9}, + {0x8842, 0x2e}, + {0x8843, 0xa8}, + {0x8844, 0x2d}, + {0x8845, 0xaf}, + {0x8846, 0x3e}, + {0x8847, 0xfc}, + {0x8848, 0xfd}, + {0x8849, 0xfe}, + {0x884a, 0x12}, + {0x884b, 0x03}, + {0x884c, 0x06}, + {0x884d, 0x12}, + {0x884e, 0x0a}, + {0x884f, 0xd4}, + {0x8850, 0xe4}, + {0x8851, 0x7b}, + {0x8852, 0xff}, + {0x8853, 0xfa}, + {0x8854, 0xf9}, + {0x8855, 0xf8}, + {0x8856, 0x12}, + {0x8857, 0x03}, + {0x8858, 0x91}, + {0x8859, 0x12}, + {0x885a, 0x0a}, + {0x885b, 0xd4}, + {0x885c, 0x90}, + {0x885d, 0x0e}, + {0x885e, 0x69}, + {0x885f, 0xe4}, + {0x8860, 0x12}, + {0x8861, 0x0a}, + {0x8862, 0xe9}, + {0x8863, 0x12}, + {0x8864, 0x0a}, + {0x8865, 0xd4}, + {0x8866, 0xe4}, + {0x8867, 0x85}, + {0x8868, 0x3d}, + {0x8869, 0x2c}, + {0x886a, 0xf5}, + {0x886b, 0x2b}, + {0x886c, 0xf5}, + {0x886d, 0x2a}, + {0x886e, 0xf5}, + {0x886f, 0x29}, + {0x8870, 0xaf}, + {0x8871, 0x2c}, + {0x8872, 0xae}, + {0x8873, 0x2b}, + {0x8874, 0xad}, + {0x8875, 0x2a}, + {0x8876, 0xac}, + {0x8877, 0x29}, + {0x8878, 0xa3}, + {0x8879, 0x12}, + {0x887a, 0x0a}, + {0x887b, 0xe9}, + {0x887c, 0x8f}, + {0x887d, 0x2c}, + {0x887e, 0x8e}, + {0x887f, 0x2b}, + {0x8880, 0x8d}, + {0x8881, 0x2a}, + {0x8882, 0x8c}, + {0x8883, 0x29}, + {0x8884, 0xe5}, + {0x8885, 0x30}, + {0x8886, 0x45}, + {0x8887, 0x2c}, + {0x8888, 0xf5}, + {0x8889, 0x30}, + {0x888a, 0xe5}, + {0x888b, 0x2f}, + {0x888c, 0x45}, + {0x888d, 0x2b}, + {0x888e, 0xf5}, + {0x888f, 0x2f}, + {0x8890, 0xe5}, + {0x8891, 0x2e}, + {0x8892, 0x45}, + {0x8893, 0x2a}, + {0x8894, 0xf5}, + {0x8895, 0x2e}, + {0x8896, 0xe5}, + {0x8897, 0x2d}, + {0x8898, 0x45}, + {0x8899, 0x29}, + {0x889a, 0xf5}, + {0x889b, 0x2d}, + {0x889c, 0xe4}, + {0x889d, 0xf5}, + {0x889e, 0x22}, + {0x889f, 0xf5}, + {0x88a0, 0x23}, + {0x88a1, 0x85}, + {0x88a2, 0x30}, + {0x88a3, 0x1e}, + {0x88a4, 0x85}, + {0x88a5, 0x2f}, + {0x88a6, 0x1d}, + {0x88a7, 0x85}, + {0x88a8, 0x2e}, + {0x88a9, 0x1c}, + {0x88aa, 0x85}, + {0x88ab, 0x2d}, + {0x88ac, 0x1b}, + {0x88ad, 0x02}, + {0x88ae, 0x0a}, + {0x88af, 0x8a}, + {0x88b0, 0x78}, + {0x88b1, 0x45}, + {0x88b2, 0x7e}, + {0x88b3, 0x00}, + {0x88b4, 0xe6}, + {0x88b5, 0xfc}, + {0x88b6, 0x08}, + {0x88b7, 0xe6}, + {0x88b8, 0xfd}, + {0x88b9, 0x12}, + {0x88ba, 0x02}, + {0x88bb, 0x9f}, + {0x88bc, 0x7c}, + {0x88bd, 0x00}, + {0x88be, 0x22}, + {0x88bf, 0xe0}, + {0x88c0, 0xa3}, + {0x88c1, 0xe0}, + {0x88c2, 0x75}, + {0x88c3, 0xf0}, + {0x88c4, 0x02}, + {0x88c5, 0xa4}, + {0x88c6, 0xff}, + {0x88c7, 0xae}, + {0x88c8, 0xf0}, + {0x88c9, 0xc3}, + {0x88ca, 0x08}, + {0x88cb, 0xe6}, + {0x88cc, 0x9f}, + {0x88cd, 0xf6}, + {0x88ce, 0x18}, + {0x88cf, 0xe6}, + {0x88d0, 0x9e}, + {0x88d1, 0xf6}, + {0x88d2, 0x22}, + {0x88d3, 0xff}, + {0x88d4, 0xe5}, + {0x88d5, 0xf0}, + {0x88d6, 0x34}, + {0x88d7, 0x60}, + {0x88d8, 0x8f}, + {0x88d9, 0x82}, + {0x88da, 0xf5}, + {0x88db, 0x83}, + {0x88dc, 0xec}, + {0x88dd, 0xf0}, + {0x88de, 0x22}, + {0x88df, 0xfe}, + {0x88e0, 0xe4}, + {0x88e1, 0xfc}, + {0x88e2, 0xfd}, + {0x88e3, 0x12}, + {0x88e4, 0x04}, + {0x88e5, 0x64}, + {0x88e6, 0x78}, + {0x88e7, 0x4f}, + {0x88e8, 0xe6}, + {0x88e9, 0xc3}, + {0x88ea, 0x13}, + {0x88eb, 0xfe}, + {0x88ec, 0x08}, + {0x88ed, 0xe6}, + {0x88ee, 0x13}, + {0x88ef, 0x22}, + {0x88f0, 0x78}, + {0x88f1, 0x45}, + {0x88f2, 0xe6}, + {0x88f3, 0xfe}, + {0x88f4, 0x08}, + {0x88f5, 0xe6}, + {0x88f6, 0xff}, + {0x88f7, 0xe4}, + {0x88f8, 0xfc}, + {0x88f9, 0xfd}, + {0x88fa, 0x22}, + {0x88fb, 0xe7}, + {0x88fc, 0xc4}, + {0x88fd, 0xf8}, + {0x88fe, 0x54}, + {0x88ff, 0xf0}, + {0x8900, 0xc8}, + {0x8901, 0x68}, + {0x8902, 0xf7}, + {0x8903, 0x09}, + {0x8904, 0xe7}, + {0x8905, 0xc4}, + {0x8906, 0x54}, + {0x8907, 0x0f}, + {0x8908, 0x48}, + {0x8909, 0xf7}, + {0x890a, 0x22}, + {0x890b, 0xe6}, + {0x890c, 0xfc}, + {0x890d, 0xed}, + {0x890e, 0x75}, + {0x890f, 0xf0}, + {0x8910, 0x04}, + {0x8911, 0xa4}, + {0x8912, 0x22}, + {0x8913, 0xe4}, + {0x8914, 0xff}, + {0x8915, 0xfe}, + {0x8916, 0xfd}, + {0x8917, 0xfc}, + {0x8918, 0x12}, + {0x8919, 0x04}, + {0x891a, 0x57}, + {0x891b, 0xc3}, + {0x891c, 0x02}, + {0x891d, 0x04}, + {0x891e, 0x23}, + {0x891f, 0xe0}, + {0x8920, 0xfe}, + {0x8921, 0xa3}, + {0x8922, 0xe0}, + {0x8923, 0xfd}, + {0x8924, 0xee}, + {0x8925, 0xf6}, + {0x8926, 0xed}, + {0x8927, 0x08}, + {0x8928, 0xf6}, + {0x8929, 0x22}, + {0x892a, 0xe6}, + {0x892b, 0xc3}, + {0x892c, 0x13}, + {0x892d, 0xf7}, + {0x892e, 0x08}, + {0x892f, 0xe6}, + {0x8930, 0x13}, + {0x8931, 0x09}, + {0x8932, 0xf7}, + {0x8933, 0x22}, + {0x8934, 0xe4}, + {0x8935, 0xf5}, + {0x8936, 0x3e}, + {0x8937, 0x90}, + {0x8938, 0x0e}, + {0x8939, 0x77}, + {0x893a, 0x93}, + {0x893b, 0xff}, + {0x893c, 0xe4}, + {0x893d, 0x8f}, + {0x893e, 0x2c}, + {0x893f, 0xf5}, + {0x8940, 0x2b}, + {0x8941, 0xf5}, + {0x8942, 0x2a}, + {0x8943, 0xf5}, + {0x8944, 0x29}, + {0x8945, 0xaf}, + {0x8946, 0x2c}, + {0x8947, 0xae}, + {0x8948, 0x2b}, + {0x8949, 0xad}, + {0x894a, 0x2a}, + {0x894b, 0xac}, + {0x894c, 0x29}, + {0x894d, 0x90}, + {0x894e, 0x0e}, + {0x894f, 0x6a}, + {0x8950, 0x12}, + {0x8951, 0x0a}, + {0x8952, 0xe9}, + {0x8953, 0x8f}, + {0x8954, 0x2c}, + {0x8955, 0x8e}, + {0x8956, 0x2b}, + {0x8957, 0x8d}, + {0x8958, 0x2a}, + {0x8959, 0x8c}, + {0x895a, 0x29}, + {0x895b, 0x90}, + {0x895c, 0x0e}, + {0x895d, 0x72}, + {0x895e, 0x12}, + {0x895f, 0x04}, + {0x8960, 0x47}, + {0x8961, 0xef}, + {0x8962, 0x45}, + {0x8963, 0x2c}, + {0x8964, 0xf5}, + {0x8965, 0x2c}, + {0x8966, 0xee}, + {0x8967, 0x45}, + {0x8968, 0x2b}, + {0x8969, 0xf5}, + {0x896a, 0x2b}, + {0x896b, 0xed}, + {0x896c, 0x45}, + {0x896d, 0x2a}, + {0x896e, 0xf5}, + {0x896f, 0x2a}, + {0x8970, 0xec}, + {0x8971, 0x45}, + {0x8972, 0x29}, + {0x8973, 0xf5}, + {0x8974, 0x29}, + {0x8975, 0xe4}, + {0x8976, 0xf5}, + {0x8977, 0x22}, + {0x8978, 0xf5}, + {0x8979, 0x23}, + {0x897a, 0x85}, + {0x897b, 0x2c}, + {0x897c, 0x1e}, + {0x897d, 0x85}, + {0x897e, 0x2b}, + {0x897f, 0x1d}, + {0x8980, 0x85}, + {0x8981, 0x2a}, + {0x8982, 0x1c}, + {0x8983, 0x85}, + {0x8984, 0x29}, + {0x8985, 0x1b}, + {0x8986, 0x12}, + {0x8987, 0x0a}, + {0x8988, 0x8a}, + {0x8989, 0xe4}, + {0x898a, 0xf5}, + {0x898b, 0x22}, + {0x898c, 0xf5}, + {0x898d, 0x23}, + {0x898e, 0x90}, + {0x898f, 0x0e}, + {0x8990, 0x72}, + {0x8991, 0x12}, + {0x8992, 0x0a}, + {0x8993, 0xdd}, + {0x8994, 0x12}, + {0x8995, 0x0a}, + {0x8996, 0x8a}, + {0x8997, 0xe4}, + {0x8998, 0xf5}, + {0x8999, 0x22}, + {0x899a, 0xf5}, + {0x899b, 0x23}, + {0x899c, 0x90}, + {0x899d, 0x0e}, + {0x899e, 0x6e}, + {0x899f, 0x12}, + {0x89a0, 0x0a}, + {0x89a1, 0xdd}, + {0x89a2, 0x02}, + {0x89a3, 0x0a}, + {0x89a4, 0x8a}, + {0x89a5, 0x75}, + {0x89a6, 0x89}, + {0x89a7, 0x03}, + {0x89a8, 0x75}, + {0x89a9, 0xa8}, + {0x89aa, 0x01}, + {0x89ab, 0x75}, + {0x89ac, 0xb8}, + {0x89ad, 0x04}, + {0x89ae, 0x75}, + {0x89af, 0x29}, + {0x89b0, 0xff}, + {0x89b1, 0x75}, + {0x89b2, 0x2a}, + {0x89b3, 0x0e}, + {0x89b4, 0x75}, + {0x89b5, 0x2b}, + {0x89b6, 0x15}, + {0x89b7, 0x75}, + {0x89b8, 0x2c}, + {0x89b9, 0x0d}, + {0x89ba, 0x12}, + {0x89bb, 0x0a}, + {0x89bc, 0x0e}, + {0x89bd, 0x12}, + {0x89be, 0x00}, + {0x89bf, 0x09}, + {0x89c0, 0x12}, + {0x89c1, 0x0a}, + {0x89c2, 0xef}, + {0x89c3, 0x12}, + {0x89c4, 0x00}, + {0x89c5, 0x06}, + {0x89c6, 0xd2}, + {0x89c7, 0x00}, + {0x89c8, 0xd2}, + {0x89c9, 0x33}, + {0x89ca, 0xd2}, + {0x89cb, 0xaf}, + {0x89cc, 0x75}, + {0x89cd, 0x29}, + {0x89ce, 0xff}, + {0x89cf, 0x75}, + {0x89d0, 0x2a}, + {0x89d1, 0x0e}, + {0x89d2, 0x75}, + {0x89d3, 0x2b}, + {0x89d4, 0x49}, + {0x89d5, 0x75}, + {0x89d6, 0x2c}, + {0x89d7, 0x03}, + {0x89d8, 0x12}, + {0x89d9, 0x0a}, + {0x89da, 0x0e}, + {0x89db, 0x30}, + {0x89dc, 0x08}, + {0x89dd, 0x09}, + {0x89de, 0xc2}, + {0x89df, 0x33}, + {0x89e0, 0x12}, + {0x89e1, 0x06}, + {0x89e2, 0x85}, + {0x89e3, 0xc2}, + {0x89e4, 0x08}, + {0x89e5, 0xd2}, + {0x89e6, 0x33}, + {0x89e7, 0x30}, + {0x89e8, 0x09}, + {0x89e9, 0x09}, + {0x89ea, 0xc2}, + {0x89eb, 0x35}, + {0x89ec, 0x12}, + {0x89ed, 0x00}, + {0x89ee, 0x0e}, + {0x89ef, 0xc2}, + {0x89f0, 0x09}, + {0x89f1, 0xd2}, + {0x89f2, 0x35}, + {0x89f3, 0x30}, + {0x89f4, 0x0e}, + {0x89f5, 0x03}, + {0x89f6, 0x12}, + {0x89f7, 0x04}, + {0x89f8, 0xbb}, + {0x89f9, 0x30}, + {0x89fa, 0x34}, + {0x89fb, 0xdf}, + {0x89fc, 0x90}, + {0x89fd, 0x30}, + {0x89fe, 0x29}, + {0x89ff, 0xe5}, + {0x8a00, 0x3f}, + {0x8a01, 0xf0}, + {0x8a02, 0xb4}, + {0x8a03, 0x10}, + {0x8a04, 0x05}, + {0x8a05, 0x90}, + {0x8a06, 0x30}, + {0x8a07, 0x23}, + {0x8a08, 0xe4}, + {0x8a09, 0xf0}, + {0x8a0a, 0xc2}, + {0x8a0b, 0x34}, + {0x8a0c, 0x80}, + {0x8a0d, 0xcd}, + {0x8a0e, 0xae}, + {0x8a0f, 0x2a}, + {0x8a10, 0xaf}, + {0x8a11, 0x2b}, + {0x8a12, 0xe4}, + {0x8a13, 0xfd}, + {0x8a14, 0xed}, + {0x8a15, 0xc3}, + {0x8a16, 0x95}, + {0x8a17, 0x2c}, + {0x8a18, 0x50}, + {0x8a19, 0x33}, + {0x8a1a, 0x12}, + {0x8a1b, 0x0b}, + {0x8a1c, 0x36}, + {0x8a1d, 0xe4}, + {0x8a1e, 0x93}, + {0x8a1f, 0xf5}, + {0x8a20, 0x2d}, + {0x8a21, 0x74}, + {0x8a22, 0x01}, + {0x8a23, 0x93}, + {0x8a24, 0xf5}, + {0x8a25, 0x2e}, + {0x8a26, 0x45}, + {0x8a27, 0x2d}, + {0x8a28, 0x60}, + {0x8a29, 0x23}, + {0x8a2a, 0x85}, + {0x8a2b, 0x2e}, + {0x8a2c, 0x82}, + {0x8a2d, 0x85}, + {0x8a2e, 0x2d}, + {0x8a2f, 0x83}, + {0x8a30, 0xe0}, + {0x8a31, 0xfc}, + {0x8a32, 0x12}, + {0x8a33, 0x0b}, + {0x8a34, 0x36}, + {0x8a35, 0x74}, + {0x8a36, 0x03}, + {0x8a37, 0x93}, + {0x8a38, 0x52}, + {0x8a39, 0x04}, + {0x8a3a, 0x12}, + {0x8a3b, 0x0b}, + {0x8a3c, 0x36}, + {0x8a3d, 0x74}, + {0x8a3e, 0x02}, + {0x8a3f, 0x93}, + {0x8a40, 0x42}, + {0x8a41, 0x04}, + {0x8a42, 0x85}, + {0x8a43, 0x2e}, + {0x8a44, 0x82}, + {0x8a45, 0x85}, + {0x8a46, 0x2d}, + {0x8a47, 0x83}, + {0x8a48, 0xec}, + {0x8a49, 0xf0}, + {0x8a4a, 0x0d}, + {0x8a4b, 0x80}, + {0x8a4c, 0xc7}, + {0x8a4d, 0x22}, + {0x8a4e, 0x78}, + {0x8a4f, 0xb1}, + {0x8a50, 0xe6}, + {0x8a51, 0xd3}, + {0x8a52, 0x08}, + {0x8a53, 0xff}, + {0x8a54, 0xe6}, + {0x8a55, 0x64}, + {0x8a56, 0x80}, + {0x8a57, 0xf8}, + {0x8a58, 0xef}, + {0x8a59, 0x64}, + {0x8a5a, 0x80}, + {0x8a5b, 0x98}, + {0x8a5c, 0x22}, + {0x8a5d, 0x93}, + {0x8a5e, 0xff}, + {0x8a5f, 0x7e}, + {0x8a60, 0x00}, + {0x8a61, 0xe6}, + {0x8a62, 0xfc}, + {0x8a63, 0x08}, + {0x8a64, 0xe6}, + {0x8a65, 0xfd}, + {0x8a66, 0x12}, + {0x8a67, 0x02}, + {0x8a68, 0x9f}, + {0x8a69, 0x78}, + {0x8a6a, 0xb4}, + {0x8a6b, 0xe6}, + {0x8a6c, 0xfc}, + {0x8a6d, 0x08}, + {0x8a6e, 0xe6}, + {0x8a6f, 0xfd}, + {0x8a70, 0xd3}, + {0x8a71, 0xef}, + {0x8a72, 0x9d}, + {0x8a73, 0xee}, + {0x8a74, 0x9c}, + {0x8a75, 0x22}, + {0x8a76, 0x78}, + {0x8a77, 0xb0}, + {0x8a78, 0xd3}, + {0x8a79, 0xe6}, + {0x8a7a, 0x64}, + {0x8a7b, 0x80}, + {0x8a7c, 0x94}, + {0x8a7d, 0x80}, + {0x8a7e, 0x22}, + {0x8a7f, 0x25}, + {0x8a80, 0xe0}, + {0x8a81, 0x24}, + {0x8a82, 0x0a}, + {0x8a83, 0xf8}, + {0x8a84, 0xe6}, + {0x8a85, 0xfe}, + {0x8a86, 0x08}, + {0x8a87, 0xe6}, + {0x8a88, 0xff}, + {0x8a89, 0x22}, + {0x8a8a, 0xa2}, + {0x8a8b, 0xaf}, + {0x8a8c, 0x92}, + {0x8a8d, 0x31}, + {0x8a8e, 0xc2}, + {0x8a8f, 0xaf}, + {0x8a90, 0xe5}, + {0x8a91, 0x23}, + {0x8a92, 0x45}, + {0x8a93, 0x22}, + {0x8a94, 0x90}, + {0x8a95, 0x0e}, + {0x8a96, 0x5d}, + {0x8a97, 0x60}, + {0x8a98, 0x0b}, + {0x8a99, 0x12}, + {0x8a9a, 0x0b}, + {0x8a9b, 0x2b}, + {0x8a9c, 0xe0}, + {0x8a9d, 0xf5}, + {0x8a9e, 0x19}, + {0x8a9f, 0xe0}, + {0x8aa0, 0xf5}, + {0x8aa1, 0x1a}, + {0x8aa2, 0x80}, + {0x8aa3, 0x0f}, + {0x8aa4, 0x12}, + {0x8aa5, 0x0b}, + {0x8aa6, 0x2b}, + {0x8aa7, 0xe5}, + {0x8aa8, 0x1d}, + {0x8aa9, 0xf0}, + {0x8aaa, 0x90}, + {0x8aab, 0x0e}, + {0x8aac, 0x5f}, + {0x8aad, 0x12}, + {0x8aae, 0x0b}, + {0x8aaf, 0x2b}, + {0x8ab0, 0xe5}, + {0x8ab1, 0x1e}, + {0x8ab2, 0xf0}, + {0x8ab3, 0xa2}, + {0x8ab4, 0x31}, + {0x8ab5, 0x92}, + {0x8ab6, 0xaf}, + {0x8ab7, 0x22}, + {0x8ab8, 0xd2}, + {0x8ab9, 0x01}, + {0x8aba, 0xc2}, + {0x8abb, 0x02}, + {0x8abc, 0xe4}, + {0x8abd, 0xf5}, + {0x8abe, 0x40}, + {0x8abf, 0xf5}, + {0x8ac0, 0x3f}, + {0x8ac1, 0xd2}, + {0x8ac2, 0x34}, + {0x8ac3, 0xd2}, + {0x8ac4, 0x32}, + {0x8ac5, 0xd2}, + {0x8ac6, 0x35}, + {0x8ac7, 0xd2}, + {0x8ac8, 0x01}, + {0x8ac9, 0xc2}, + {0x8aca, 0x02}, + {0x8acb, 0xf5}, + {0x8acc, 0x40}, + {0x8acd, 0xf5}, + {0x8ace, 0x3f}, + {0x8acf, 0xd2}, + {0x8ad0, 0x34}, + {0x8ad1, 0xd2}, + {0x8ad2, 0x32}, + {0x8ad3, 0x22}, + {0x8ad4, 0x8f}, + {0x8ad5, 0x30}, + {0x8ad6, 0x8e}, + {0x8ad7, 0x2f}, + {0x8ad8, 0x8d}, + {0x8ad9, 0x2e}, + {0x8ada, 0x8c}, + {0x8adb, 0x2d}, + {0x8adc, 0x22}, + {0x8add, 0x12}, + {0x8ade, 0x04}, + {0x8adf, 0x47}, + {0x8ae0, 0x8f}, + {0x8ae1, 0x1e}, + {0x8ae2, 0x8e}, + {0x8ae3, 0x1d}, + {0x8ae4, 0x8d}, + {0x8ae5, 0x1c}, + {0x8ae6, 0x8c}, + {0x8ae7, 0x1b}, + {0x8ae8, 0x22}, + {0x8ae9, 0x93}, + {0x8aea, 0xf9}, + {0x8aeb, 0xf8}, + {0x8aec, 0x02}, + {0x8aed, 0x04}, + {0x8aee, 0x34}, + {0x8aef, 0x90}, + {0x8af0, 0x0e}, + {0x8af1, 0x81}, + {0x8af2, 0x12}, + {0x8af3, 0x04}, + {0x8af4, 0x47}, + {0x8af5, 0x8f}, + {0x8af6, 0x3b}, + {0x8af7, 0x8e}, + {0x8af8, 0x3a}, + {0x8af9, 0x8d}, + {0x8afa, 0x39}, + {0x8afb, 0x8c}, + {0x8afc, 0x38}, + {0x8afd, 0xd2}, + {0x8afe, 0x06}, + {0x8aff, 0x30}, + {0x8b00, 0x06}, + {0x8b01, 0x03}, + {0x8b02, 0xd3}, + {0x8b03, 0x80}, + {0x8b04, 0x01}, + {0x8b05, 0xc3}, + {0x8b06, 0x92}, + {0x8b07, 0x0e}, + {0x8b08, 0x22}, + {0x8b09, 0xc0}, + {0x8b0a, 0xe0}, + {0x8b0b, 0xc0}, + {0x8b0c, 0x83}, + {0x8b0d, 0xc0}, + {0x8b0e, 0x82}, + {0x8b0f, 0x90}, + {0x8b10, 0x3f}, + {0x8b11, 0x0d}, + {0x8b12, 0xe0}, + {0x8b13, 0xf5}, + {0x8b14, 0x28}, + {0x8b15, 0xe5}, + {0x8b16, 0x28}, + {0x8b17, 0xf0}, + {0x8b18, 0xd0}, + {0x8b19, 0x82}, + {0x8b1a, 0xd0}, + {0x8b1b, 0x83}, + {0x8b1c, 0xd0}, + {0x8b1d, 0xe0}, + {0x8b1e, 0x32}, + {0x8b1f, 0x78}, + {0x8b20, 0x7f}, + {0x8b21, 0xe4}, + {0x8b22, 0xf6}, + {0x8b23, 0xd8}, + {0x8b24, 0xfd}, + {0x8b25, 0x75}, + {0x8b26, 0x81}, + {0x8b27, 0xc0}, + {0x8b28, 0x02}, + {0x8b29, 0x09}, + {0x8b2a, 0xa5}, + {0x8b2b, 0xe4}, + {0x8b2c, 0x93}, + {0x8b2d, 0xfe}, + {0x8b2e, 0x74}, + {0x8b2f, 0x01}, + {0x8b30, 0x93}, + {0x8b31, 0xf5}, + {0x8b32, 0x82}, + {0x8b33, 0x8e}, + {0x8b34, 0x83}, + {0x8b35, 0x22}, + {0x8b36, 0x8f}, + {0x8b37, 0x82}, + {0x8b38, 0x8e}, + {0x8b39, 0x83}, + {0x8b3a, 0x75}, + {0x8b3b, 0xf0}, + {0x8b3c, 0x04}, + {0x8b3d, 0xed}, + {0x8b3e, 0x02}, + {0x8b3f, 0x04}, + {0x8b40, 0x89}, + {0x8b41, 0x00}, + {0x8b42, 0x00}, + {0x8b43, 0x00}, + {0x8b44, 0x00}, + {0x8b45, 0x00}, + {0x8b46, 0x00}, + {0x8b47, 0x00}, + {0x8b48, 0x00}, + {0x8b49, 0x00}, + {0x8b4a, 0x00}, + {0x8b4b, 0x00}, + {0x8b4c, 0x00}, + {0x8b4d, 0x00}, + {0x8b4e, 0x00}, + {0x8b4f, 0x00}, + {0x8b50, 0x00}, + {0x8b51, 0x00}, + {0x8b52, 0x00}, + {0x8b53, 0x00}, + {0x8b54, 0x00}, + {0x8b55, 0x00}, + {0x8b56, 0x00}, + {0x8b57, 0x00}, + {0x8b58, 0x00}, + {0x8b59, 0x00}, + {0x8b5a, 0x00}, + {0x8b5b, 0x00}, + {0x8b5c, 0x00}, + {0x8b5d, 0x00}, + {0x8b5e, 0x00}, + {0x8b5f, 0x00}, + {0x8b60, 0x00}, + {0x8b61, 0x00}, + {0x8b62, 0x00}, + {0x8b63, 0x00}, + {0x8b64, 0x00}, + {0x8b65, 0x00}, + {0x8b66, 0x00}, + {0x8b67, 0x00}, + {0x8b68, 0x00}, + {0x8b69, 0x00}, + {0x8b6a, 0x00}, + {0x8b6b, 0x00}, + {0x8b6c, 0x00}, + {0x8b6d, 0x00}, + {0x8b6e, 0x00}, + {0x8b6f, 0x00}, + {0x8b70, 0x00}, + {0x8b71, 0x00}, + {0x8b72, 0x00}, + {0x8b73, 0x00}, + {0x8b74, 0x00}, + {0x8b75, 0x00}, + {0x8b76, 0x00}, + {0x8b77, 0x00}, + {0x8b78, 0x00}, + {0x8b79, 0x00}, + {0x8b7a, 0x00}, + {0x8b7b, 0x00}, + {0x8b7c, 0x00}, + {0x8b7d, 0x00}, + {0x8b7e, 0x00}, + {0x8b7f, 0x00}, + {0x8b80, 0x00}, + {0x8b81, 0x00}, + {0x8b82, 0x00}, + {0x8b83, 0x00}, + {0x8b84, 0x00}, + {0x8b85, 0x00}, + {0x8b86, 0x00}, + {0x8b87, 0x00}, + {0x8b88, 0x00}, + {0x8b89, 0x00}, + {0x8b8a, 0x00}, + {0x8b8b, 0x00}, + {0x8b8c, 0x00}, + {0x8b8d, 0x00}, + {0x8b8e, 0x00}, + {0x8b8f, 0x00}, + {0x8b90, 0x00}, + {0x8b91, 0x00}, + {0x8b92, 0x00}, + {0x8b93, 0x00}, + {0x8b94, 0x00}, + {0x8b95, 0x00}, + {0x8b96, 0x00}, + {0x8b97, 0x00}, + {0x8b98, 0x00}, + {0x8b99, 0x00}, + {0x8b9a, 0x00}, + {0x8b9b, 0x00}, + {0x8b9c, 0x00}, + {0x8b9d, 0x00}, + {0x8b9e, 0x00}, + {0x8b9f, 0x00}, + {0x8ba0, 0x00}, + {0x8ba1, 0x00}, + {0x8ba2, 0x00}, + {0x8ba3, 0x00}, + {0x8ba4, 0x00}, + {0x8ba5, 0x00}, + {0x8ba6, 0x00}, + {0x8ba7, 0x00}, + {0x8ba8, 0x00}, + {0x8ba9, 0x00}, + {0x8baa, 0x00}, + {0x8bab, 0x00}, + {0x8bac, 0x00}, + {0x8bad, 0x00}, + {0x8bae, 0x00}, + {0x8baf, 0x00}, + {0x8bb0, 0x00}, + {0x8bb1, 0x00}, + {0x8bb2, 0x00}, + {0x8bb3, 0x00}, + {0x8bb4, 0x00}, + {0x8bb5, 0x00}, + {0x8bb6, 0x00}, + {0x8bb7, 0x00}, + {0x8bb8, 0x00}, + {0x8bb9, 0x00}, + {0x8bba, 0x00}, + {0x8bbb, 0x00}, + {0x8bbc, 0x00}, + {0x8bbd, 0x00}, + {0x8bbe, 0x00}, + {0x8bbf, 0x00}, + {0x8bc0, 0x00}, + {0x8bc1, 0x00}, + {0x8bc2, 0x00}, + {0x8bc3, 0x00}, + {0x8bc4, 0x00}, + {0x8bc5, 0x00}, + {0x8bc6, 0x00}, + {0x8bc7, 0x00}, + {0x8bc8, 0x00}, + {0x8bc9, 0x00}, + {0x8bca, 0x00}, + {0x8bcb, 0x00}, + {0x8bcc, 0x00}, + {0x8bcd, 0x00}, + {0x8bce, 0x00}, + {0x8bcf, 0x00}, + {0x8bd0, 0x00}, + {0x8bd1, 0x00}, + {0x8bd2, 0x00}, + {0x8bd3, 0x00}, + {0x8bd4, 0x00}, + {0x8bd5, 0x00}, + {0x8bd6, 0x00}, + {0x8bd7, 0x00}, + {0x8bd8, 0x00}, + {0x8bd9, 0x00}, + {0x8bda, 0x00}, + {0x8bdb, 0x00}, + {0x8bdc, 0x00}, + {0x8bdd, 0x00}, + {0x8bde, 0x00}, + {0x8bdf, 0x00}, + {0x8be0, 0x00}, + {0x8be1, 0x00}, + {0x8be2, 0x00}, + {0x8be3, 0x00}, + {0x8be4, 0x00}, + {0x8be5, 0x00}, + {0x8be6, 0x00}, + {0x8be7, 0x00}, + {0x8be8, 0x00}, + {0x8be9, 0x00}, + {0x8bea, 0x00}, + {0x8beb, 0x00}, + {0x8bec, 0x00}, + {0x8bed, 0x00}, + {0x8bee, 0x00}, + {0x8bef, 0x00}, + {0x8bf0, 0x00}, + {0x8bf1, 0x00}, + {0x8bf2, 0x00}, + {0x8bf3, 0x00}, + {0x8bf4, 0x00}, + {0x8bf5, 0x00}, + {0x8bf6, 0x00}, + {0x8bf7, 0x00}, + {0x8bf8, 0x00}, + {0x8bf9, 0x00}, + {0x8bfa, 0x00}, + {0x8bfb, 0x00}, + {0x8bfc, 0x00}, + {0x8bfd, 0x00}, + {0x8bfe, 0x00}, + {0x8bff, 0x00}, + {0x8c00, 0x00}, + {0x8c01, 0x00}, + {0x8c02, 0x00}, + {0x8c03, 0x00}, + {0x8c04, 0x00}, + {0x8c05, 0x00}, + {0x8c06, 0x00}, + {0x8c07, 0x00}, + {0x8c08, 0x00}, + {0x8c09, 0x00}, + {0x8c0a, 0x00}, + {0x8c0b, 0x00}, + {0x8c0c, 0x00}, + {0x8c0d, 0x00}, + {0x8c0e, 0x00}, + {0x8c0f, 0x00}, + {0x8c10, 0x00}, + {0x8c11, 0x00}, + {0x8c12, 0x00}, + {0x8c13, 0x00}, + {0x8c14, 0x00}, + {0x8c15, 0x00}, + {0x8c16, 0x00}, + {0x8c17, 0x00}, + {0x8c18, 0x00}, + {0x8c19, 0x00}, + {0x8c1a, 0x00}, + {0x8c1b, 0x00}, + {0x8c1c, 0x00}, + {0x8c1d, 0x00}, + {0x8c1e, 0x00}, + {0x8c1f, 0x00}, + {0x8c20, 0x00}, + {0x8c21, 0x00}, + {0x8c22, 0x00}, + {0x8c23, 0x00}, + {0x8c24, 0x00}, + {0x8c25, 0x00}, + {0x8c26, 0x00}, + {0x8c27, 0x00}, + {0x8c28, 0x00}, + {0x8c29, 0x00}, + {0x8c2a, 0x00}, + {0x8c2b, 0x00}, + {0x8c2c, 0x00}, + {0x8c2d, 0x00}, + {0x8c2e, 0x00}, + {0x8c2f, 0x00}, + {0x8c30, 0x00}, + {0x8c31, 0x00}, + {0x8c32, 0x00}, + {0x8c33, 0x00}, + {0x8c34, 0x00}, + {0x8c35, 0x00}, + {0x8c36, 0x00}, + {0x8c37, 0x00}, + {0x8c38, 0x00}, + {0x8c39, 0x00}, + {0x8c3a, 0x00}, + {0x8c3b, 0x00}, + {0x8c3c, 0x00}, + {0x8c3d, 0x00}, + {0x8c3e, 0x00}, + {0x8c3f, 0x00}, + {0x8c40, 0x00}, + {0x8c41, 0x00}, + {0x8c42, 0x00}, + {0x8c43, 0x00}, + {0x8c44, 0x00}, + {0x8c45, 0x00}, + {0x8c46, 0x00}, + {0x8c47, 0x00}, + {0x8c48, 0x00}, + {0x8c49, 0x00}, + {0x8c4a, 0x00}, + {0x8c4b, 0x00}, + {0x8c4c, 0x00}, + {0x8c4d, 0x00}, + {0x8c4e, 0x00}, + {0x8c4f, 0x00}, + {0x8c50, 0x00}, + {0x8c51, 0x00}, + {0x8c52, 0x00}, + {0x8c53, 0x00}, + {0x8c54, 0x00}, + {0x8c55, 0x00}, + {0x8c56, 0x00}, + {0x8c57, 0x00}, + {0x8c58, 0x00}, + {0x8c59, 0x00}, + {0x8c5a, 0x00}, + {0x8c5b, 0x00}, + {0x8c5c, 0x00}, + {0x8c5d, 0x00}, + {0x8c5e, 0x00}, + {0x8c5f, 0x00}, + {0x8c60, 0x00}, + {0x8c61, 0x00}, + {0x8c62, 0x00}, + {0x8c63, 0x00}, + {0x8c64, 0x00}, + {0x8c65, 0x00}, + {0x8c66, 0x00}, + {0x8c67, 0x00}, + {0x8c68, 0x00}, + {0x8c69, 0x00}, + {0x8c6a, 0x00}, + {0x8c6b, 0x00}, + {0x8c6c, 0x00}, + {0x8c6d, 0x00}, + {0x8c6e, 0x00}, + {0x8c6f, 0x00}, + {0x8c70, 0x00}, + {0x8c71, 0x00}, + {0x8c72, 0x00}, + {0x8c73, 0x00}, + {0x8c74, 0x00}, + {0x8c75, 0x00}, + {0x8c76, 0x00}, + {0x8c77, 0x00}, + {0x8c78, 0x00}, + {0x8c79, 0x00}, + {0x8c7a, 0x00}, + {0x8c7b, 0x00}, + {0x8c7c, 0x00}, + {0x8c7d, 0x00}, + {0x8c7e, 0x00}, + {0x8c7f, 0x00}, + {0x8c80, 0x00}, + {0x8c81, 0x00}, + {0x8c82, 0x00}, + {0x8c83, 0x00}, + {0x8c84, 0x00}, + {0x8c85, 0x00}, + {0x8c86, 0x00}, + {0x8c87, 0x00}, + {0x8c88, 0x00}, + {0x8c89, 0x00}, + {0x8c8a, 0x00}, + {0x8c8b, 0x00}, + {0x8c8c, 0x00}, + {0x8c8d, 0x00}, + {0x8c8e, 0x00}, + {0x8c8f, 0x00}, + {0x8c90, 0x00}, + {0x8c91, 0x00}, + {0x8c92, 0x00}, + {0x8c93, 0x00}, + {0x8c94, 0x00}, + {0x8c95, 0x00}, + {0x8c96, 0x00}, + {0x8c97, 0x00}, + {0x8c98, 0x00}, + {0x8c99, 0x00}, + {0x8c9a, 0x00}, + {0x8c9b, 0x00}, + {0x8c9c, 0x00}, + {0x8c9d, 0x00}, + {0x8c9e, 0x00}, + {0x8c9f, 0x00}, + {0x8ca0, 0x00}, + {0x8ca1, 0x00}, + {0x8ca2, 0x00}, + {0x8ca3, 0x00}, + {0x8ca4, 0x00}, + {0x8ca5, 0x00}, + {0x8ca6, 0x00}, + {0x8ca7, 0x00}, + {0x8ca8, 0x00}, + {0x8ca9, 0x00}, + {0x8caa, 0x00}, + {0x8cab, 0x00}, + {0x8cac, 0x00}, + {0x8cad, 0x00}, + {0x8cae, 0x00}, + {0x8caf, 0x00}, + {0x8cb0, 0x00}, + {0x8cb1, 0x00}, + {0x8cb2, 0x00}, + {0x8cb3, 0x00}, + {0x8cb4, 0x00}, + {0x8cb5, 0x00}, + {0x8cb6, 0x00}, + {0x8cb7, 0x00}, + {0x8cb8, 0x00}, + {0x8cb9, 0x00}, + {0x8cba, 0x00}, + {0x8cbb, 0x00}, + {0x8cbc, 0x00}, + {0x8cbd, 0x00}, + {0x8cbe, 0x00}, + {0x8cbf, 0x00}, + {0x8cc0, 0x00}, + {0x8cc1, 0x00}, + {0x8cc2, 0x00}, + {0x8cc3, 0x00}, + {0x8cc4, 0x00}, + {0x8cc5, 0x00}, + {0x8cc6, 0x00}, + {0x8cc7, 0x00}, + {0x8cc8, 0x00}, + {0x8cc9, 0x00}, + {0x8cca, 0x00}, + {0x8ccb, 0x00}, + {0x8ccc, 0x00}, + {0x8ccd, 0x00}, + {0x8cce, 0x00}, + {0x8ccf, 0x00}, + {0x8cd0, 0x00}, + {0x8cd1, 0x00}, + {0x8cd2, 0x00}, + {0x8cd3, 0x00}, + {0x8cd4, 0x00}, + {0x8cd5, 0x00}, + {0x8cd6, 0x00}, + {0x8cd7, 0x00}, + {0x8cd8, 0x00}, + {0x8cd9, 0x00}, + {0x8cda, 0x00}, + {0x8cdb, 0x00}, + {0x8cdc, 0x00}, + {0x8cdd, 0x00}, + {0x8cde, 0x00}, + {0x8cdf, 0x00}, + {0x8ce0, 0x00}, + {0x8ce1, 0x00}, + {0x8ce2, 0x00}, + {0x8ce3, 0x00}, + {0x8ce4, 0x00}, + {0x8ce5, 0x00}, + {0x8ce6, 0x00}, + {0x8ce7, 0x00}, + {0x8ce8, 0x00}, + {0x8ce9, 0x00}, + {0x8cea, 0x00}, + {0x8ceb, 0x00}, + {0x8cec, 0x00}, + {0x8ced, 0x00}, + {0x8cee, 0x00}, + {0x8cef, 0x00}, + {0x8cf0, 0x00}, + {0x8cf1, 0x00}, + {0x8cf2, 0x00}, + {0x8cf3, 0x00}, + {0x8cf4, 0x00}, + {0x8cf5, 0x00}, + {0x8cf6, 0x00}, + {0x8cf7, 0x00}, + {0x8cf8, 0x00}, + {0x8cf9, 0x00}, + {0x8cfa, 0x00}, + {0x8cfb, 0x00}, + {0x8cfc, 0x00}, + {0x8cfd, 0x00}, + {0x8cfe, 0x00}, + {0x8cff, 0x00}, + {0x8d00, 0x00}, + {0x8d01, 0x00}, + {0x8d02, 0x00}, + {0x8d03, 0x00}, + {0x8d04, 0x00}, + {0x8d05, 0x00}, + {0x8d06, 0x00}, + {0x8d07, 0x00}, + {0x8d08, 0x00}, + {0x8d09, 0x00}, + {0x8d0a, 0x00}, + {0x8d0b, 0x00}, + {0x8d0c, 0x00}, + {0x8d0d, 0x00}, + {0x8d0e, 0x00}, + {0x8d0f, 0x00}, + {0x8d10, 0x00}, + {0x8d11, 0x00}, + {0x8d12, 0x00}, + {0x8d13, 0x00}, + {0x8d14, 0x00}, + {0x8d15, 0x00}, + {0x8d16, 0x00}, + {0x8d17, 0x00}, + {0x8d18, 0x00}, + {0x8d19, 0x00}, + {0x8d1a, 0x00}, + {0x8d1b, 0x00}, + {0x8d1c, 0x00}, + {0x8d1d, 0x00}, + {0x8d1e, 0x00}, + {0x8d1f, 0x00}, + {0x8d20, 0x00}, + {0x8d21, 0x00}, + {0x8d22, 0x00}, + {0x8d23, 0x00}, + {0x8d24, 0x00}, + {0x8d25, 0x00}, + {0x8d26, 0x00}, + {0x8d27, 0x00}, + {0x8d28, 0x00}, + {0x8d29, 0x00}, + {0x8d2a, 0x00}, + {0x8d2b, 0x00}, + {0x8d2c, 0x00}, + {0x8d2d, 0x00}, + {0x8d2e, 0x00}, + {0x8d2f, 0x00}, + {0x8d30, 0x00}, + {0x8d31, 0x00}, + {0x8d32, 0x00}, + {0x8d33, 0x00}, + {0x8d34, 0x00}, + {0x8d35, 0x00}, + {0x8d36, 0x00}, + {0x8d37, 0x00}, + {0x8d38, 0x00}, + {0x8d39, 0x00}, + {0x8d3a, 0x00}, + {0x8d3b, 0x00}, + {0x8d3c, 0x00}, + {0x8d3d, 0x00}, + {0x8d3e, 0x00}, + {0x8d3f, 0x00}, + {0x8d40, 0x00}, + {0x8d41, 0x00}, + {0x8d42, 0x00}, + {0x8d43, 0x00}, + {0x8d44, 0x00}, + {0x8d45, 0x00}, + {0x8d46, 0x00}, + {0x8d47, 0x00}, + {0x8d48, 0x00}, + {0x8d49, 0x00}, + {0x8d4a, 0x00}, + {0x8d4b, 0x00}, + {0x8d4c, 0x00}, + {0x8d4d, 0x00}, + {0x8d4e, 0x00}, + {0x8d4f, 0x00}, + {0x8d50, 0x00}, + {0x8d51, 0x00}, + {0x8d52, 0x00}, + {0x8d53, 0x00}, + {0x8d54, 0x00}, + {0x8d55, 0x00}, + {0x8d56, 0x00}, + {0x8d57, 0x00}, + {0x8d58, 0x00}, + {0x8d59, 0x00}, + {0x8d5a, 0x00}, + {0x8d5b, 0x00}, + {0x8d5c, 0x00}, + {0x8d5d, 0x00}, + {0x8d5e, 0x00}, + {0x8d5f, 0x00}, + {0x8d60, 0x00}, + {0x8d61, 0x00}, + {0x8d62, 0x00}, + {0x8d63, 0x00}, + {0x8d64, 0x00}, + {0x8d65, 0x00}, + {0x8d66, 0x00}, + {0x8d67, 0x00}, + {0x8d68, 0x00}, + {0x8d69, 0x00}, + {0x8d6a, 0x00}, + {0x8d6b, 0x00}, + {0x8d6c, 0x00}, + {0x8d6d, 0x00}, + {0x8d6e, 0x00}, + {0x8d6f, 0x00}, + {0x8d70, 0x00}, + {0x8d71, 0x00}, + {0x8d72, 0x00}, + {0x8d73, 0x00}, + {0x8d74, 0x00}, + {0x8d75, 0x00}, + {0x8d76, 0x00}, + {0x8d77, 0x00}, + {0x8d78, 0x00}, + {0x8d79, 0x00}, + {0x8d7a, 0x00}, + {0x8d7b, 0x00}, + {0x8d7c, 0x00}, + {0x8d7d, 0x00}, + {0x8d7e, 0x00}, + {0x8d7f, 0x00}, + {0x8d80, 0x00}, + {0x8d81, 0x00}, + {0x8d82, 0x00}, + {0x8d83, 0x00}, + {0x8d84, 0x00}, + {0x8d85, 0x00}, + {0x8d86, 0x00}, + {0x8d87, 0x00}, + {0x8d88, 0x00}, + {0x8d89, 0x00}, + {0x8d8a, 0x00}, + {0x8d8b, 0x00}, + {0x8d8c, 0x00}, + {0x8d8d, 0x00}, + {0x8d8e, 0x00}, + {0x8d8f, 0x00}, + {0x8d90, 0x00}, + {0x8d91, 0x00}, + {0x8d92, 0x00}, + {0x8d93, 0x00}, + {0x8d94, 0x00}, + {0x8d95, 0x00}, + {0x8d96, 0x00}, + {0x8d97, 0x00}, + {0x8d98, 0x00}, + {0x8d99, 0x00}, + {0x8d9a, 0x00}, + {0x8d9b, 0x00}, + {0x8d9c, 0x00}, + {0x8d9d, 0x00}, + {0x8d9e, 0x00}, + {0x8d9f, 0x00}, + {0x8da0, 0x00}, + {0x8da1, 0x00}, + {0x8da2, 0x00}, + {0x8da3, 0x00}, + {0x8da4, 0x00}, + {0x8da5, 0x00}, + {0x8da6, 0x00}, + {0x8da7, 0x00}, + {0x8da8, 0x00}, + {0x8da9, 0x00}, + {0x8daa, 0x00}, + {0x8dab, 0x00}, + {0x8dac, 0x00}, + {0x8dad, 0x00}, + {0x8dae, 0x00}, + {0x8daf, 0x00}, + {0x8db0, 0x00}, + {0x8db1, 0x00}, + {0x8db2, 0x00}, + {0x8db3, 0x00}, + {0x8db4, 0x00}, + {0x8db5, 0x00}, + {0x8db6, 0x00}, + {0x8db7, 0x00}, + {0x8db8, 0x00}, + {0x8db9, 0x00}, + {0x8dba, 0x00}, + {0x8dbb, 0x00}, + {0x8dbc, 0x00}, + {0x8dbd, 0x00}, + {0x8dbe, 0x00}, + {0x8dbf, 0x00}, + {0x8dc0, 0x00}, + {0x8dc1, 0x00}, + {0x8dc2, 0x00}, + {0x8dc3, 0x00}, + {0x8dc4, 0x00}, + {0x8dc5, 0x00}, + {0x8dc6, 0x00}, + {0x8dc7, 0x00}, + {0x8dc8, 0x00}, + {0x8dc9, 0x00}, + {0x8dca, 0x00}, + {0x8dcb, 0x00}, + {0x8dcc, 0x00}, + {0x8dcd, 0x00}, + {0x8dce, 0x00}, + {0x8dcf, 0x00}, + {0x8dd0, 0x00}, + {0x8dd1, 0x00}, + {0x8dd2, 0x00}, + {0x8dd3, 0x00}, + {0x8dd4, 0x00}, + {0x8dd5, 0x00}, + {0x8dd6, 0x00}, + {0x8dd7, 0x00}, + {0x8dd8, 0x00}, + {0x8dd9, 0x00}, + {0x8dda, 0x00}, + {0x8ddb, 0x00}, + {0x8ddc, 0x00}, + {0x8ddd, 0x00}, + {0x8dde, 0x00}, + {0x8ddf, 0x00}, + {0x8de0, 0x00}, + {0x8de1, 0x00}, + {0x8de2, 0x00}, + {0x8de3, 0x00}, + {0x8de4, 0x00}, + {0x8de5, 0x00}, + {0x8de6, 0x00}, + {0x8de7, 0x00}, + {0x8de8, 0x00}, + {0x8de9, 0x00}, + {0x8dea, 0x00}, + {0x8deb, 0x00}, + {0x8dec, 0x00}, + {0x8ded, 0x00}, + {0x8dee, 0x00}, + {0x8def, 0x00}, + {0x8df0, 0x00}, + {0x8df1, 0x00}, + {0x8df2, 0x00}, + {0x8df3, 0x00}, + {0x8df4, 0x00}, + {0x8df5, 0x00}, + {0x8df6, 0x00}, + {0x8df7, 0x00}, + {0x8df8, 0x00}, + {0x8df9, 0x00}, + {0x8dfa, 0x00}, + {0x8dfb, 0x00}, + {0x8dfc, 0x00}, + {0x8dfd, 0x00}, + {0x8dfe, 0x00}, + {0x8dff, 0x00}, + {0x8e00, 0x11}, + {0x8e01, 0x02}, + {0x8e02, 0x28}, + {0x8e03, 0x09}, + {0x8e04, 0x00}, + {0x8e05, 0x12}, + {0x8e06, 0x4f}, + {0x8e07, 0x56}, + {0x8e08, 0x54}, + {0x8e09, 0x20}, + {0x8e0a, 0x20}, + {0x8e0b, 0x20}, + {0x8e0c, 0x20}, + {0x8e0d, 0x20}, + {0x8e0e, 0x20}, + {0x8e0f, 0x01}, + {0x8e10, 0x10}, + {0x8e11, 0x00}, + {0x8e12, 0x56}, + {0x8e13, 0x40}, + {0x8e14, 0x1a}, + {0x8e15, 0x30}, + {0x8e16, 0x29}, + {0x8e17, 0x7e}, + {0x8e18, 0x00}, + {0x8e19, 0x30}, + {0x8e1a, 0x04}, + {0x8e1b, 0x20}, + {0x8e1c, 0xdf}, + {0x8e1d, 0x30}, + {0x8e1e, 0x05}, + {0x8e1f, 0x40}, + {0x8e20, 0xbf}, + {0x8e21, 0x50}, + {0x8e22, 0x25}, + {0x8e23, 0x04}, + {0x8e24, 0xfb}, + {0x8e25, 0x50}, + {0x8e26, 0x03}, + {0x8e27, 0x00}, + {0x8e28, 0xfd}, + {0x8e29, 0x50}, + {0x8e2a, 0x27}, + {0x8e2b, 0x01}, + {0x8e2c, 0xfe}, + {0x8e2d, 0x60}, + {0x8e2e, 0x00}, + {0x8e2f, 0x11}, + {0x8e30, 0x00}, + {0x8e31, 0x3f}, + {0x8e32, 0x05}, + {0x8e33, 0x30}, + {0x8e34, 0x00}, + {0x8e35, 0x3f}, + {0x8e36, 0x06}, + {0x8e37, 0x22}, + {0x8e38, 0x00}, + {0x8e39, 0x3f}, + {0x8e3a, 0x01}, + {0x8e3b, 0x29}, + {0x8e3c, 0x00}, + {0x8e3d, 0x3f}, + {0x8e3e, 0x02}, + {0x8e3f, 0x00}, + {0x8e40, 0x00}, + {0x8e41, 0x36}, + {0x8e42, 0x06}, + {0x8e43, 0x07}, + {0x8e44, 0x00}, + {0x8e45, 0x3f}, + {0x8e46, 0x0b}, + {0x8e47, 0x0f}, + {0x8e48, 0xf0}, + {0x8e49, 0x30}, + {0x8e4a, 0x01}, + {0x8e4b, 0x40}, + {0x8e4c, 0xbf}, + {0x8e4d, 0x30}, + {0x8e4e, 0x01}, + {0x8e4f, 0x00}, + {0x8e50, 0xbf}, + {0x8e51, 0x30}, + {0x8e52, 0x29}, + {0x8e53, 0x70}, + {0x8e54, 0x00}, + {0x8e55, 0x3a}, + {0x8e56, 0x00}, + {0x8e57, 0x01}, + {0x8e58, 0xfe}, + {0x8e59, 0x3a}, + {0x8e5a, 0x00}, + {0x8e5b, 0x00}, + {0x8e5c, 0xfe}, + {0x8e5d, 0x36}, + {0x8e5e, 0x03}, + {0x8e5f, 0x36}, + {0x8e60, 0x02}, + {0x8e61, 0x41}, + {0x8e62, 0x44}, + {0x8e63, 0x58}, + {0x8e64, 0x20}, + {0x8e65, 0x18}, + {0x8e66, 0x10}, + {0x8e67, 0x0a}, + {0x8e68, 0x04}, + {0x8e69, 0x04}, + {0x8e6a, 0x00}, + {0x8e6b, 0x03}, + {0x8e6c, 0xff}, + {0x8e6d, 0x64}, + {0x8e6e, 0x00}, + {0x8e6f, 0x00}, + {0x8e70, 0x80}, + {0x8e71, 0x00}, + {0x8e72, 0x00}, + {0x8e73, 0x00}, + {0x8e74, 0x00}, + {0x8e75, 0x00}, + {0x8e76, 0x00}, + {0x8e77, 0x02}, + {0x8e78, 0x04}, + {0x8e79, 0x06}, + {0x8e7a, 0x00}, + {0x8e7b, 0x03}, + {0x8e7c, 0x98}, + {0x8e7d, 0x00}, + {0x8e7e, 0xcc}, + {0x8e7f, 0x50}, + {0x8e80, 0x3c}, + {0x8e81, 0x28}, + {0x8e82, 0x1e}, + {0x8e83, 0x0c}, + {0x8e84, 0x0c}, + {0x8e85, 0x00}, + {0x8e86, 0x00}, + {0x8e87, 0x00}, + {0x8e88, 0x6e}, + {0x8e89, 0x05}, + {0x8e8a, 0x05}, + {0x8e8b, 0x00}, + {0x8e8c, 0xa5}, + {0x8e8d, 0x5a}, + {0x3022, 0x00}, + {0x3023, 0x00}, + {0x3024, 0x00}, + {0x3025, 0x00}, + {0x3026, 0x00}, + {0x3027, 0x00}, + {0x3028, 0x00}, + {0x3029, 0xFF}, + {0x3000, 0x00}, + {OV5640_TABLE_END, 0x0000} +}; + +static struct ov5640_reg tbl_single_focus[] = { + {0x3023, 0x01}, + {0x3022, 0x03}, + {OV5640_TABLE_END, 0x0000} +}; + +static struct ov5640_reg tbl_release_focus[] = { + {0x3023, 0x01}, + {0x3022, 0x08}, + {OV5640_TABLE_END, 0x0000} +}; + +#endif diff --git a/drivers/media/video/tegra/ov5650.c b/drivers/media/video/tegra/ov5650.c index a8436210561a..bac1d00c3bd0 100644 --- a/drivers/media/video/tegra/ov5650.c +++ b/drivers/media/video/tegra/ov5650.c @@ -39,6 +39,11 @@ struct ov5650_info { enum StereoCameraMode camera_mode; struct ov5650_sensor left; struct ov5650_sensor right; + struct ov5650_sensordata sensor_data; + struct mutex mutex_le; + struct mutex mutex_ri; + int power_refcnt_le; + int power_refcnt_ri; u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; }; @@ -118,7 +123,7 @@ static struct ov5650_reg mode_start[] = { {0x4000, 0x01}, {0x401c, 0x48}, {0x401d, 0x08}, - {0x5000, 0x00}, + {0x5000, 0x06}, {0x5001, 0x00}, {0x5002, 0x00}, {0x503d, 0x00}, @@ -291,7 +296,6 @@ static struct ov5650_reg mode_2080x1164[] = { {0x401d, 0x08}, {0x4001, 0x02}, - {0x5000, 0x00}, {0x5001, 0x00}, {0x5002, 0x00}, {0x503d, 0x00}, @@ -414,7 +418,6 @@ static struct ov5650_reg mode_1920x1080[] = { {0x401d, 0x08}, {0x4001, 0x02}, - {0x5000, 0x00}, {0x5001, 0x00}, {0x5002, 0x00}, {0x503d, 0x00}, @@ -664,7 +667,6 @@ static struct ov5650_reg mode_320x240[] = { {0x3810, 0x40}, {0x3836, 0x41}, {0x505f, 0x04}, - {0x5000, 0x06}, {0x5001, 0x00}, {0x5002, 0x02}, {0x503d, 0x00}, @@ -788,7 +790,7 @@ static int ov5650_read_reg(struct i2c_client *client, u16 addr, u8 *val) err = i2c_transfer(client->adapter, msg, 2); - if (err != 1) + if (err != 2) return -EINVAL; *val = data[2]; @@ -1245,31 +1247,71 @@ static int ov5650_test_pattern(struct ov5650_info *info, } static int set_power_helper(struct ov5650_platform_data *pdata, - int powerLevel) + int powerLevel, int *ref_cnt) { if (pdata) { - if (powerLevel && pdata->power_on) - pdata->power_on(); - else if (pdata->power_off) - pdata->power_off(); + if (powerLevel && pdata->power_on) { + if (*ref_cnt == 0) + pdata->power_on(); + *ref_cnt = *ref_cnt + 1; + } + else if (pdata->power_off) { + *ref_cnt = *ref_cnt - 1; + if (*ref_cnt <= 0) + pdata->power_off(); + } } return 0; } -static int ov5650_set_power(int powerLevel) +static int ov5650_set_power(struct ov5650_info *info, int powerLevel) { pr_info("%s: powerLevel=%d camera mode=%d\n", __func__, powerLevel, - stereo_ov5650_info->camera_mode); + info->camera_mode); - if (StereoCameraMode_Left & stereo_ov5650_info->camera_mode) - set_power_helper(stereo_ov5650_info->left.pdata, powerLevel); + if (StereoCameraMode_Left & info->camera_mode) { + mutex_lock(&info->mutex_le); + set_power_helper(info->left.pdata, powerLevel, + &info->power_refcnt_le); + mutex_unlock(&info->mutex_le); + } - if (StereoCameraMode_Right & stereo_ov5650_info->camera_mode) - set_power_helper(stereo_ov5650_info->right.pdata, powerLevel); + if (StereoCameraMode_Right & info->camera_mode) { + mutex_lock(&info->mutex_ri); + set_power_helper(info->right.pdata, powerLevel, + &info->power_refcnt_ri); + mutex_unlock(&info->mutex_ri); + } return 0; } +static int ov5650_get_sensor_id(struct ov5650_info *info) +{ + int ret = 0; + int i; + u8 bak; + + pr_info("%s\n", __func__); + if (info->sensor_data.fuse_id_size) + return 0; + + ov5650_set_power(info, 1); + + for (i = 0; i < 5; i++) { + ret |= ov5650_write_reg_helper(info, 0x3d00, i); + ret |= ov5650_read_reg_helper(info, 0x3d04, + &bak); + info->sensor_data.fuse_id[i] = bak; + } + + if (!ret) + info->sensor_data.fuse_id_size = i; + + ov5650_set_power(info, 0); + return ret; +} + static long ov5650_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1280,13 +1322,13 @@ static long ov5650_ioctl(struct file *file, case OV5650_IOCTL_SET_CAMERA_MODE: { if (info->camera_mode != arg) { - err = ov5650_set_power(0); + err = ov5650_set_power(info, 0); if (err) { pr_info("%s %d\n", __func__, __LINE__); return err; } info->camera_mode = arg; - err = ov5650_set_power(1); + err = ov5650_set_power(info, 1); if (err) return err; } @@ -1344,6 +1386,21 @@ static long ov5650_ioctl(struct file *file, } return ov5650_set_group_hold(info, &ae); } + case OV5650_IOCTL_GET_SENSORDATA: + { + err = ov5650_get_sensor_id(info); + if (err) { + pr_err("%s %d %d\n", __func__, __LINE__, err); + return err; + } + if (copy_to_user((void __user *)arg, + &info->sensor_data, + sizeof(struct ov5650_sensordata))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } default: return -EINVAL; } @@ -1354,13 +1411,15 @@ static int ov5650_open(struct inode *inode, struct file *file) { pr_info("%s\n", __func__); file->private_data = stereo_ov5650_info; - ov5650_set_power(1); + ov5650_set_power(stereo_ov5650_info, 1); return 0; } int ov5650_release(struct inode *inode, struct file *file) { - ov5650_set_power(0); + struct ov5650_info *info = file->private_data; + + ov5650_set_power(info, 0); file->private_data = NULL; return 0; } @@ -1426,6 +1485,8 @@ static int left_ov5650_probe(struct i2c_client *client, stereo_ov5650_info->left.pdata = client->dev.platform_data; stereo_ov5650_info->left.i2c_client = client; + mutex_init(&stereo_ov5650_info->mutex_le); + mutex_init(&stereo_ov5650_info->mutex_ri); return 0; } diff --git a/drivers/media/video/tegra/sh532u.c b/drivers/media/video/tegra/sh532u.c index 014b4bd30ca9..84692d992154 100644 --- a/drivers/media/video/tegra/sh532u.c +++ b/drivers/media/video/tegra/sh532u.c @@ -113,7 +113,9 @@ #define SH532U_TIMEOUT_MS 200 #define SH532U_POS_LOW_DEFAULT 0xA000 #define SH532U_POS_HIGH_DEFAULT 0x6000 - +#define SH532U_SLEW_RATE 1 +#define SH532U_POS_TRANSLATE 0 +#define SH532U_POS_SIGN_CHANGER (-1) static u8 sh532u_ids[] = { 0xF0, @@ -148,6 +150,7 @@ struct sh532u_info { unsigned i2c_addr_rom; struct nvc_focus_nvc nvc; struct nvc_focus_cap cap; + struct nv_focuser_config config; enum nvc_focus_sts sts; struct sh532u_pdata_info cfg; bool reset_flag; @@ -172,6 +175,8 @@ static struct nvc_focus_cap sh532u_default_cap = { .focus_macro = SH532U_FOCUS_MACRO, .focus_hyper = SH532U_FOCUS_HYPER, .focus_infinity = SH532U_FOCUS_INFINITY, + .slew_rate = SH532U_SLEW_RATE, + .position_translate = SH532U_POS_TRANSLATE, }; static struct nvc_focus_nvc sh532u_default_nvc = { @@ -532,7 +537,7 @@ static int sh532u_vreg_init(struct sh532u_info *info) else dev_info(&info->i2c_client->dev, "%s no regulator found for %s. " - "This board may not have an" + "This board may not have an " "independent %s regulator.\n", __func__, info->vreg[j].vreg_name, info->vreg[j].vreg_name); @@ -736,22 +741,31 @@ static void sh532u_sts_rd(struct sh532u_info *info) } } -static s16 sh532u_rel2abs(struct sh532u_info *info, u32 rel_position) +static s16 sh532u_rel2abs(struct sh532u_info *info, s32 rel_position) { s16 abs_pos; if (rel_position > info->cap.actuator_range) rel_position = info->cap.actuator_range; - rel_position = info->cap.actuator_range - rel_position; - if (rel_position) { - rel_position *= info->abs_range; - rel_position /= info->cap.actuator_range; + if (info->cap.position_translate) { + rel_position = info->cap.actuator_range - rel_position; + if (rel_position) { + rel_position *= info->abs_range; + rel_position /= info->cap.actuator_range; + } + abs_pos = (s16)(info->abs_base + rel_position); + } else { + abs_pos = rel_position * SH532U_POS_SIGN_CHANGER; } - abs_pos = (s16)(info->abs_base + rel_position); + if (abs_pos < info->cfg.limit_low) abs_pos = info->cfg.limit_low; if (abs_pos > info->cfg.limit_high) abs_pos = info->cfg.limit_high; + + dev_dbg(&info->i2c_client->dev, "%s: rel_position %d returning abs_pos %d\n", + __func__, rel_position, abs_pos); + return abs_pos; } @@ -763,12 +777,21 @@ static u32 sh532u_abs2rel(struct sh532u_info *info, s16 abs_position) abs_position = info->cfg.limit_high; if (abs_position < info->abs_base) abs_position = info->abs_base; - rel_pos = (u32)(abs_position - info->abs_base); - rel_pos *= info->cap.actuator_range; - rel_pos /= info->abs_range; - if (rel_pos > info->cap.actuator_range) - rel_pos = info->cap.actuator_range; - rel_pos = info->cap.actuator_range - rel_pos; + + if (info->cap.position_translate) { + rel_pos = (u32)(abs_position - info->abs_base); + rel_pos *= info->cap.actuator_range; + rel_pos /= info->abs_range; + + if (rel_pos > info->cap.actuator_range) + rel_pos = info->cap.actuator_range; + rel_pos = info->cap.actuator_range - rel_pos; + } else { + rel_pos = abs_position * SH532U_POS_SIGN_CHANGER; + } + dev_dbg(&info->i2c_client->dev, "%s: abs_position %d returning rel_pos %d", + __func__, abs_position, rel_pos); + return rel_pos; } @@ -782,7 +805,7 @@ static int sh532u_abs_pos_rd(struct sh532u_info *info, s16 *position) return err; } -static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position) +static int sh532u_rel_pos_rd(struct sh532u_info *info, s32 *position) { s16 abs_pos; long msec; @@ -811,9 +834,11 @@ static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position) pos = (int)info->pos_rel; } } - if (pos < 0) - pos = 0; - *position = (u32)pos; + if (info->cap.position_translate) { + if (pos < 0) + pos = 0; + } + *position = pos; return 0; } @@ -851,6 +876,8 @@ static void sh532u_calibration_caps(struct sh532u_info *info) rel_hi = info->cap.focus_infinity; info->abs_range = (u32)(info->cfg.pos_high - info->cfg.pos_low); loop_limit = (rel_lo > rel_hi) ? rel_lo : rel_hi; + dev_dbg(&info->i2c_client->dev, "%s: rel_lo %d rel_hi %d loop_limit %d\n", + __func__, rel_lo, rel_hi, loop_limit); for (i = 0; i <= loop_limit; i++) { rel_range = info->cap.actuator_range - (rel_lo + rel_hi); step = info->abs_range / rel_range; @@ -868,22 +895,36 @@ static void sh532u_calibration_caps(struct sh532u_info *info) abs_top <= info->cfg.limit_high) break; } + dev_dbg(&info->i2c_client->dev, "%s: info->abs_range %d abs_base %d abs_top %d\n", + __func__, info->abs_range, info->abs_base, abs_top); + + if (!info->cap.position_translate && info->abs_range) + info->cap.actuator_range = info->abs_range; + info->cap.focus_hyper = info->abs_range; info->abs_range = (u32)(abs_top - info->abs_base); /* calculate absolute hyperfocus position */ info->cap.focus_hyper *= info->cfg.focus_hyper_ratio; info->cap.focus_hyper /= info->cfg.focus_hyper_div; abs_top = (s16)(info->cfg.pos_high - info->cap.focus_hyper); + /* update actual relative positions */ info->cap.focus_hyper = sh532u_abs2rel(info, abs_top); + dev_dbg(&info->i2c_client->dev, "%s: focus_hyper abs %d rel %d\n", + __func__, abs_top, info->cap.focus_hyper); + info->cap.focus_infinity = sh532u_abs2rel(info, info->cfg.pos_high); + dev_dbg(&info->i2c_client->dev, "%s: focus_infinity abs %d rel %d\n", + __func__, info->cfg.pos_high, info->cap.focus_infinity); + info->cap.focus_macro = sh532u_abs2rel(info, info->cfg.pos_low); - dev_dbg(&info->i2c_client->dev, "%s focus_macro=%u\n", - __func__, info->cap.focus_macro); - dev_dbg(&info->i2c_client->dev, "%s focus_infinity=%u\n", - __func__, info->cap.focus_infinity); - dev_dbg(&info->i2c_client->dev, "%s focus_hyper=%u\n", - __func__, info->cap.focus_hyper); + dev_dbg(&info->i2c_client->dev, "%s: focus_macro abs %d rel %d\n", + __func__, info->cfg.pos_low, info->cap.focus_macro); + + dev_dbg(&info->i2c_client->dev, "%s: Version %d actuator_range %d " + "settle_time %d position_traslate %d\n", + __func__, info->cap.version, info->cap.actuator_range, + info->cap.settle_time, info->cap.position_translate); } static int sh532u_calibration(struct sh532u_info *info, bool use_defaults) @@ -892,8 +933,11 @@ static int sh532u_calibration(struct sh532u_info *info, bool use_defaults) int err; int ret = 0; - if (info->init_cal_flag) + if (info->init_cal_flag) { + dev_dbg(&info->i2c_client->dev, "%s: Already initialized" + "Returning\n", __func__); return 0; + } /* * Get Inf1, Mac1 @@ -965,8 +1009,9 @@ static int sh532u_calibration(struct sh532u_info *info, bool use_defaults) * 1 PASS PASS Continue to calculations */ /* err = DATA where FAIL = 1 */ - if (!info->cfg.pos_low || !info->cfg.pos_high || - !info->cfg.limit_low || !info->cfg.limit_high) + if (!info->cfg.pos_low || info->cfg.pos_high <= info->cfg.pos_low || + !info->cfg.limit_low || + info->cfg.limit_high <= info->cfg.limit_low) err = 1; else err = 0; @@ -998,6 +1043,7 @@ static int sh532u_calibration(struct sh532u_info *info, bool use_defaults) __func__, (int)info->cfg.limit_low); dev_dbg(&info->i2c_client->dev, "%s limit_high=%d\n", __func__, (int)info->cfg.limit_high); + sh532u_calibration_caps(info); info->init_cal_flag = 1; dev_dbg(&info->i2c_client->dev, "%s complete\n", __func__); @@ -1215,6 +1261,7 @@ static int sh532u_pos_abs_wr(struct sh532u_info *info, s16 tar_pos) STMLFF_OFF | STMVEN_ON)); } + dev_dbg(&info->i2c_client->dev, "%s: position %d\n", __func__, tar_pos); return err; } @@ -1289,30 +1336,101 @@ static int sh532u_hvca_pos_init(struct sh532u_info *info) return err; } -static int sh532u_pos_rel_wr(struct sh532u_info *info, u32 position) +static int sh532u_pos_rel_wr(struct sh532u_info *info, s32 position) { s16 abs_pos; - if (position > info->cap.actuator_range) { - dev_err(&info->i2c_client->dev, "%s invalid position %u\n", - __func__, position); - return -EINVAL; + if (info->cap.position_translate) { + if (position > info->cap.actuator_range) { + dev_err(&info->i2c_client->dev, "%s invalid position %d\n", + __func__, position); + return -EINVAL; + } } - abs_pos = sh532u_rel2abs(info, position); + info->pos_rel = position; info->pos_abs = abs_pos; info->pos_time_wr = jiffies; return sh532u_pos_abs_wr(info, abs_pos); } +static void sh532u_get_focuser_capabilities(struct sh532u_info *info) +{ + memset(&info->config, 0, sizeof(info->config)); + + info->config.focal_length = info->nvc.focal_length; + info->config.fnumber = info->nvc.fnumber; + info->config.max_aperture = info->nvc.fnumber; + info->config.range_ends_reversed = (SH532U_POS_SIGN_CHANGER == -1) + ? 1 : 0; + + info->config.settle_time = info->cap.settle_time; + + /* + * We do not use pos_working_low and pos_working_high + * in the kernel driver. + */ + info->config.pos_working_low = AF_POS_INVALID_VALUE; + info->config.pos_working_high = AF_POS_INVALID_VALUE; + + info->config.pos_actual_low = info->cfg.limit_high * + SH532U_POS_SIGN_CHANGER; + info->config.pos_actual_high = info->cfg.limit_low * + SH532U_POS_SIGN_CHANGER; + info->config.slew_rate = info->cap.slew_rate; + info->config.circle_of_confusion = -1; + + /* + * These need to be passed up once we have the EEPROM/OTP read + * routines in teh kernel. These need to be passed up much earlier on. + * Till we have these routines, we pass them up as part of the get call. + */ + info->config.num_focuser_sets = 1; + info->config.focuser_set[0].posture = 'S'; + info->config.focuser_set[0].macro = info->cap.focus_macro; + info->config.focuser_set[0].hyper = info->cap.focus_hyper; + info->config.focuser_set[0].inf = info->cap.focus_infinity; + info->config.focuser_set[0].hysteresis = 0; + info->config.focuser_set[0].settle_time = info->cap.settle_time; + info->config.focuser_set[0].num_dist_pairs = 0; + + dev_dbg(&info->i2c_client->dev, "%s: pos_actual_low %d pos_actual_high %d " + " settle_time %d\n", __func__, info->config.pos_actual_low, + info->config.pos_actual_high, info->cap.settle_time); + +} + + +static int sh532u_set_focuser_capabilities(struct sh532u_info *info, + struct nvc_param *params) +{ + if (copy_from_user(&info->config, (const void __user *)params->p_value, + params->sizeofvalue)) { + dev_err(&info->i2c_client->dev, "%s Error: copy_from_user bytes %d\n", + __func__, params->sizeofvalue); + return -EFAULT; + } + + /* info.config.focuser_set[0].posture, macro, hyper, infinity and + * hysterisis can remain there only. We need only settle_time & + * slew_rate for use here. + */ + info->cap.settle_time = info->config.focuser_set[0].settle_time; + info->config.slew_rate = info->config.slew_rate; + + dev_dbg(&info->i2c_client->dev, "%s: copy_from_user bytes %d\n", + __func__, params->sizeofvalue); + return 0; +} + static int sh532u_param_rd(struct sh532u_info *info, unsigned long arg) { struct nvc_param params; const void *data_ptr; u32 data_size = 0; - u32 position; + s32 position; int err; if (copy_from_user(¶ms, @@ -1366,16 +1484,14 @@ static int sh532u_param_rd(struct sh532u_info *info, unsigned long arg) sh532u_pm_dev_wr(info, NVC_PWR_STDBY); if (err) return -EIO; + dev_dbg(&info->i2c_client->dev, "%s: NVC_PARAM_CAPS: params.param %d " + "params.sizeofvalue %d\n", + __func__, params.param, params.sizeofvalue); - data_ptr = &info->cap; - /* there are different sizes depending on the version */ - /* send back just what's requested or our max size */ - if (params.sizeofvalue < sizeof(info->cap)) - data_size = params.sizeofvalue; - else - data_size = sizeof(info->cap); - dev_dbg(&info->i2c_client->dev, "%s CAPS\n", - __func__); + sh532u_get_focuser_capabilities(info); + + data_ptr = &info->config; + data_size = params.sizeofvalue; break; case NVC_PARAM_STS: @@ -1421,16 +1537,15 @@ static int sh532u_param_wr_s(struct sh532u_info *info, struct nvc_param *params, u32 u32val) { - struct nvc_focus_cap cap; u8 u8val; int err; u8val = (u8)u32val; switch (params->param) { case NVC_PARAM_LOCUS: - dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n", - __func__, u32val); - err = sh532u_pos_rel_wr(info, u32val); + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n", + __func__, (s32) u32val); + err = sh532u_pos_rel_wr(info, (s32) u32val); return err; case NVC_PARAM_RESET: @@ -1446,27 +1561,9 @@ static int sh532u_param_wr_s(struct sh532u_info *info, return err; case NVC_PARAM_CAPS: - dev_dbg(&info->i2c_client->dev, "%s CAPS\n", - __func__); - if (copy_from_user(&cap, (const void __user *)params->p_value, - sizeof(params->sizeofvalue))) { - dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", - __func__, __LINE__); - return -EFAULT; - } - - if (!cap.version) - return -EINVAL; - - if (cap.version >= NVC_FOCUS_CAP_VER1) - info->cap.actuator_range = cap.actuator_range; - if (cap.version >= NVC_FOCUS_CAP_VER2) { - info->cap.focus_macro = cap.focus_macro; - info->cap.focus_hyper = cap.focus_hyper; - info->cap.focus_infinity = cap.focus_infinity; - } - sh532u_calibration_caps(info); - return 0; + dev_dbg(&info->i2c_client->dev, "%s CAPS. Error. sh532u_param_wr " + "should be called instead\n", __func__); + return -EFAULT; default: dev_dbg(&info->i2c_client->dev, @@ -1575,6 +1672,14 @@ static int sh532u_param_wr(struct sh532u_info *info, unsigned long arg) return err; + case NVC_PARAM_CAPS: + if (sh532u_set_focuser_capabilities(info, ¶ms)) { + dev_err(&info->i2c_client->dev, "%s: Error: copy_from_user bytes %d\n", + __func__, params.sizeofvalue); + return -EFAULT; + } + return 0; + default: /* parameters dependent on sync mode */ switch (info->s_mode) { @@ -1773,6 +1878,7 @@ static int sh532u_open(struct inode *inode, struct file *file) file->private_data = info; dev_dbg(&info->i2c_client->dev, "%s\n", __func__); sh532u_pos_rel_wr(info, info->cap.focus_infinity); + sh532u_pm_wr_s(info, NVC_PWR_OFF); return 0; } @@ -1828,7 +1934,6 @@ static int sh532u_probe( char dname[16]; int err; - dev_dbg(&client->dev, "%s\n", __func__); info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) { dev_err(&client->dev, "%s: kzalloc error\n", __func__); diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c index 36ecde087bed..03eecf464c48 100644 --- a/drivers/media/video/tegra/tegra_camera.c +++ b/drivers/media/video/tegra/tegra_camera.c @@ -96,25 +96,18 @@ static int tegra_camera_disable_clk(struct tegra_camera_dev *dev) static int tegra_camera_enable_emc(struct tegra_camera_dev *dev) { - /* - * tegra_camera wasn't added as a user of emc_clk until 3x. - * set to 150 MHz, will likely need to be increased as we support - * sensors with higher framerates and resolutions. - */ + int ret = tegra_emc_disable_eack(); clk_enable(dev->emc_clk); - #ifdef CONFIG_ARCH_TEGRA_2x_SOC clk_set_rate(dev->emc_clk, 300000000); -#else - clk_set_rate(dev->emc_clk, 150000000); #endif - return 0; + return ret; } static int tegra_camera_disable_emc(struct tegra_camera_dev *dev) { clk_disable(dev->emc_clk); - return 0; + return tegra_emc_enable_eack(); } static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) @@ -130,7 +123,8 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) return -EINVAL; } - if (info->id != TEGRA_CAMERA_MODULE_VI) { + if (info->id != TEGRA_CAMERA_MODULE_VI && + info->id != TEGRA_CAMERA_MODULE_EMC) { dev_err(dev->dev, "%s: set rate only aplies to vi module %d\n", __func__, info->id); @@ -144,6 +138,14 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) case TEGRA_CAMERA_VI_SENSOR_CLK: clk = dev->vi_sensor_clk; break; + case TEGRA_CAMERA_EMC_CLK: + clk = dev->emc_clk; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + dev_dbg(dev->dev, "%s: emc_clk rate=%lu\n", + __func__, info->rate); + clk_set_rate(dev->emc_clk, info->rate); +#endif + goto set_rate_end; default: dev_err(dev->dev, "%s: invalid clk id for set rate %d\n", @@ -182,13 +184,17 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2); #ifdef CONFIG_ARCH_TEGRA_2x_SOC - u32 val; - void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE); - val = readl(apb_misc + 0x42c); - writel(val | 0x1, apb_misc + 0x42c); + { + u32 val; + void __iomem *apb_misc = + IO_ADDRESS(TEGRA_APB_MISC_BASE); + val = readl(apb_misc + 0x42c); + writel(val | 0x1, apb_misc + 0x42c); + } #endif } +set_rate_end: info->rate = clk_get_rate(clk); dev_dbg(dev->dev, "%s: get_rate=%lu", __func__, info->rate); diff --git a/drivers/media/video/tegra/tegra_dtv.c b/drivers/media/video/tegra/tegra_dtv.c index 8a53930aec7a..f0b9a0a33a33 100644 --- a/drivers/media/video/tegra/tegra_dtv.c +++ b/drivers/media/video/tegra/tegra_dtv.c @@ -921,6 +921,7 @@ static int tegra_dtv_probe(struct platform_device *pdev) ret = -EIO; goto fail_no_clk; } + dtv_ctx->clk = clk; ret = clk_enable(clk); if (ret < 0) { dev_err(&pdev->dev, "cannot enable clk for tegra_dtv.\n"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f89c3eedee36..f37d3ec02123 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -175,7 +175,6 @@ config MFD_TPS65910 bool "TPS65910 Power Management chip" depends on I2C=y && GPIOLIB select MFD_CORE - select GPIO_TPS65910 select REGMAP_I2C help if you say yes here you get support for the TPS65910 series of @@ -868,6 +867,16 @@ config MFD_RICOH583 additional drivers must be enabled in order to use the functionality of the device. +config MFD_PALMAS + bool "Support for the TI Palmas series chips" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y + help + If you say yes here you get support for the Palmas + series of PMIC chips from Texas Instruments. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 90f02cdc538a..6634515b64e4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -111,3 +111,5 @@ obj-$(CONFIG_MFD_MAX8907C) += max8907c-irq.o obj-$(CONFIG_MFD_MAX77663) += max77663-core.o obj-$(CONFIG_MFD_RICOH583) += ricoh583.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o +obj-$(CONFIG_MFD_PALMAS) += palmas.o +obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o diff --git a/drivers/mfd/max77663-core.c b/drivers/mfd/max77663-core.c index ff47cb123d0d..46728c331f83 100644 --- a/drivers/mfd/max77663-core.c +++ b/drivers/mfd/max77663-core.c @@ -118,6 +118,8 @@ #define ONOFF_SLP_LPM_MASK (1 << 5) +#define ONOFF_IRQ_EN0_RISING (1 << 3) + enum { CACHE_IRQ_LBT, CACHE_IRQ_SD, @@ -903,6 +905,8 @@ static void max77663_irq_sync_unlock(struct irq_data *data) irq_mask = irq_data->trigger_type; else irq_mask = GPIO_REFE_IRQ_EDGE_FALLING << shift; + } else { + irq_mask = GPIO_REFE_IRQ_NONE << shift; } ret = max77663_cache_write(chip->dev, GPIO_REG_ADDR(offset), @@ -1132,6 +1136,10 @@ static int max77663_irq_init(struct max77663_chip *chip) max77663_write(chip->dev, MAX77663_REG_LBT_IRQ_MASK, &chip->cache_irq_mask[CACHE_IRQ_LBT], 1, 0); + chip->cache_irq_mask[CACHE_IRQ_ONOFF] &= ~ONOFF_IRQ_EN0_RISING; + max77663_write(chip->dev, MAX77663_REG_ONOFF_IRQ_MASK, + &chip->cache_irq_mask[CACHE_IRQ_ONOFF], 1, 0); + return 0; } diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c new file mode 100644 index 000000000000..00c0aba7eba0 --- /dev/null +++ b/drivers/mfd/palmas.c @@ -0,0 +1,509 @@ +/* + * TI Palmas MFD Driver + * + * Copyright 2011-2012 Texas Instruments Inc. + * + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/regmap.h> +#include <linux/err.h> +#include <linux/mfd/core.h> +#include <linux/mfd/palmas.h> + +static const struct resource gpadc_resource[] = { + { + .name = "EOC_SW", + .start = PALMAS_GPADC_EOC_SW_IRQ, + .end = PALMAS_GPADC_EOC_SW_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static const struct resource usb_resource[] = { + { + .name = "ID", + .start = PALMAS_ID_OTG_IRQ, + .end = PALMAS_ID_OTG_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "ID_WAKEUP", + .start = PALMAS_ID_IRQ, + .end = PALMAS_ID_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS", + .start = PALMAS_VBUS_OTG_IRQ, + .end = PALMAS_VBUS_OTG_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_WAKEUP", + .start = PALMAS_VBUS_IRQ, + .end = PALMAS_VBUS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct resource rtc_resource[] = { + { + .name = "RTC_ALARM", + .start = PALMAS_RTC_ALARM_IRQ, + .end = PALMAS_RTC_ALARM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct resource pwron_resource[] = { + { + .name = "PWRON_BUTTON", + .start = PALMAS_PWRON_IRQ, + .end = PALMAS_PWRON_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +enum palmas_ids { + PALMAS_PMIC_ID, + PALMAS_GPIO_ID, + PALMAS_LEDS_ID, + PALMAS_WDT_ID, + PALMAS_RTC_ID, + PALMAS_PWRBUTTON_ID, + PALMAS_GPADC_ID, + PALMAS_RESOURCE_ID, + PALMAS_CLK_ID, + PALMAS_PWM_ID, + PALMAS_USB_ID, +}; + +static const struct mfd_cell palmas_children[] = { + { + .name = "palmas-pmic", + .id = PALMAS_PMIC_ID, + }, + { + .name = "palmas-gpio", + .id = PALMAS_GPIO_ID, + }, + { + .name = "palmas-leds", + .id = PALMAS_LEDS_ID, + }, + { + .name = "palmas-wdt", + .id = PALMAS_WDT_ID, + }, + { + .name = "palmas-rtc", + .num_resources = ARRAY_SIZE(rtc_resource), + .resources = rtc_resource, + .id = PALMAS_RTC_ID, + }, + { + .name = "palmas-pwrbutton", + .num_resources = ARRAY_SIZE(pwron_resource), + .resources = pwron_resource, + .id = PALMAS_PWRBUTTON_ID, + }, + { + .name = "palmas-gpadc", + .num_resources = ARRAY_SIZE(gpadc_resource), + .resources = gpadc_resource, + .id = PALMAS_GPADC_ID, + }, + { + .name = "palmas-resource", + .id = PALMAS_RESOURCE_ID, + }, + { + .name = "palmas-clk", + .id = PALMAS_CLK_ID, + }, + { + .name = "palmas-pwm", + .id = PALMAS_PWM_ID, + }, + { + .name = "palmas-usb", + .num_resources = ARRAY_SIZE(usb_resource), + .resources = usb_resource, + .id = PALMAS_USB_ID, + } +}; + +static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = { + { + .reg_bits = 8, + .val_bits = 8, + .max_register = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD3), + }, + { + .reg_bits = 8, + .val_bits = 8, + .max_register = PALMAS_BASE_TO_REG(PALMAS_GPADC_BASE, + PALMAS_GPADC_SMPS_VSEL_MONITORING), + }, + { + .reg_bits = 8, + .val_bits = 8, + .max_register = PALMAS_BASE_TO_REG(PALMAS_TRIM_GPADC_BASE, + PALMAS_GPADC_TRIM16), + }, +}; + +static const struct regmap_irq palmas_irqs[] = { + /* INT1 IRQs */ + [PALMAS_CHARG_DET_N_VBUS_OVV_IRQ] = { + .mask = PALMAS_INT1_STATUS_CHARG_DET_N_VBUS_OVV, + }, + [PALMAS_PWRON_IRQ] = { + .mask = PALMAS_INT1_STATUS_PWRON, + }, + [PALMAS_LONG_PRESS_KEY_IRQ] = { + .mask = PALMAS_INT1_STATUS_LONG_PRESS_KEY, + }, + [PALMAS_RPWRON_IRQ] = { + .mask = PALMAS_INT1_STATUS_RPWRON, + }, + [PALMAS_PWRDOWN_IRQ] = { + .mask = PALMAS_INT1_STATUS_PWRDOWN, + }, + [PALMAS_HOTDIE_IRQ] = { + .mask = PALMAS_INT1_STATUS_HOTDIE, + }, + [PALMAS_VSYS_MON_IRQ] = { + .mask = PALMAS_INT1_STATUS_VSYS_MON, + }, + [PALMAS_VBAT_MON_IRQ] = { + .mask = PALMAS_INT1_STATUS_VBAT_MON, + }, + /* INT2 IRQs*/ + [PALMAS_RTC_ALARM_IRQ] = { + .mask = PALMAS_INT2_STATUS_RTC_ALARM, + .reg_offset = 1, + }, + [PALMAS_RTC_TIMER_IRQ] = { + .mask = PALMAS_INT2_STATUS_RTC_TIMER, + .reg_offset = 1, + }, + [PALMAS_WDT_IRQ] = { + .mask = PALMAS_INT2_STATUS_WDT, + .reg_offset = 1, + }, + [PALMAS_BATREMOVAL_IRQ] = { + .mask = PALMAS_INT2_STATUS_BATREMOVAL, + .reg_offset = 1, + }, + [PALMAS_RESET_IN_IRQ] = { + .mask = PALMAS_INT2_STATUS_RESET_IN, + .reg_offset = 1, + }, + [PALMAS_FBI_BB_IRQ] = { + .mask = PALMAS_INT2_STATUS_FBI_BB, + .reg_offset = 1, + }, + [PALMAS_SHORT_IRQ] = { + .mask = PALMAS_INT2_STATUS_SHORT, + .reg_offset = 1, + }, + [PALMAS_VAC_ACOK_IRQ] = { + .mask = PALMAS_INT2_STATUS_VAC_ACOK, + .reg_offset = 1, + }, + /* INT3 IRQs */ + [PALMAS_GPADC_AUTO_0_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_AUTO_0, + .reg_offset = 2, + }, + [PALMAS_GPADC_AUTO_1_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_AUTO_1, + .reg_offset = 2, + }, + [PALMAS_GPADC_EOC_SW_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_EOC_SW, + .reg_offset = 2, + }, + [PALMAS_GPADC_EOC_RT_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_EOC_RT, + .reg_offset = 2, + }, + [PALMAS_ID_OTG_IRQ] = { + .mask = PALMAS_INT3_STATUS_ID_OTG, + .reg_offset = 2, + }, + [PALMAS_ID_IRQ] = { + .mask = PALMAS_INT3_STATUS_ID, + .reg_offset = 2, + }, + [PALMAS_VBUS_OTG_IRQ] = { + .mask = PALMAS_INT3_STATUS_VBUS_OTG, + .reg_offset = 2, + }, + [PALMAS_VBUS_IRQ] = { + .mask = PALMAS_INT3_STATUS_VBUS, + .reg_offset = 2, + }, + /* INT4 IRQs */ + [PALMAS_GPIO_0_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_0, + .reg_offset = 3, + }, + [PALMAS_GPIO_1_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_1, + .reg_offset = 3, + }, + [PALMAS_GPIO_2_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_2, + .reg_offset = 3, + }, + [PALMAS_GPIO_3_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_3, + .reg_offset = 3, + }, + [PALMAS_GPIO_4_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_4, + .reg_offset = 3, + }, + [PALMAS_GPIO_5_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_5, + .reg_offset = 3, + }, + [PALMAS_GPIO_6_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_6, + .reg_offset = 3, + }, + [PALMAS_GPIO_7_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_7, + .reg_offset = 3, + }, +}; + +static struct regmap_irq_chip palmas_irq_chip = { + .name = "palmas", + .irqs = palmas_irqs, + .num_irqs = ARRAY_SIZE(palmas_irqs), + + .num_regs = 4, + .irq_reg_stride = 5, + .status_base = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, + PALMAS_INT1_STATUS), + .mask_base = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, + PALMAS_INT1_MASK), +}; + +static int __devinit palmas_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct palmas *palmas; + struct palmas_platform_data *pdata; + int ret = 0, i; + unsigned int reg, addr; + int slave; + struct mfd_cell *children; + + pdata = dev_get_platdata(&i2c->dev); + if (!pdata) + return -EINVAL; + + palmas = devm_kzalloc(&i2c->dev, sizeof(struct palmas), GFP_KERNEL); + if (palmas == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, palmas); + palmas->dev = &i2c->dev; + palmas->id = id->driver_data; + palmas->irq = i2c->irq; + + for (i = 0; i < PALMAS_NUM_CLIENTS; i++) { + if (i == 0) + palmas->i2c_clients[i] = i2c; + else { + palmas->i2c_clients[i] = + i2c_new_dummy(i2c->adapter, + i2c->addr + i); + if (!palmas->i2c_clients[i]) { + dev_err(palmas->dev, + "can't attach client %d\n", i); + ret = -ENOMEM; + goto err; + } + } + palmas->regmap[i] = devm_regmap_init_i2c(palmas->i2c_clients[i], + &palmas_regmap_config[i]); + if (IS_ERR(palmas->regmap[i])) { + ret = PTR_ERR(palmas->regmap[i]); + dev_err(palmas->dev, + "Failed to allocate regmap %d, err: %d\n", + i, ret); + goto err; + } + } + + ret = regmap_add_irq_chip(palmas->regmap[1], palmas->irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, -1, &palmas_irq_chip, + &palmas->irq_data); + if (ret < 0) + goto err; + + slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE); + addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD1); + + if (pdata->mux_from_pdata) { + reg = pdata->pad1; + ret = regmap_write(palmas->regmap[slave], addr, reg); + if (ret) + goto err; + } else { + ret = regmap_read(palmas->regmap[slave], addr, ®); + if (ret) + goto err; + } + + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_0)) + palmas->gpio_muxed |= PALMAS_GPIO_0_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_1_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK) == + (2 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT)) + palmas->led_muxed |= PALMAS_LED1_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK) == + (3 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT)) + palmas->pwm_muxed |= PALMAS_PWM1_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_2_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK) == + (2 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT)) + palmas->led_muxed |= PALMAS_LED2_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK) == + (3 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT)) + palmas->pwm_muxed |= PALMAS_PWM2_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_3)) + palmas->gpio_muxed |= PALMAS_GPIO_3_MUXED; + + addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD2); + + if (pdata->mux_from_pdata) { + reg = pdata->pad2; + ret = regmap_write(palmas->regmap[slave], addr, reg); + if (ret) + goto err; + } else { + ret = regmap_read(palmas->regmap[slave], addr, ®); + if (ret) + goto err; + } + + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_4)) + palmas->gpio_muxed |= PALMAS_GPIO_4_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_5_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_5_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_6)) + palmas->gpio_muxed |= PALMAS_GPIO_6_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_7_MUXED; + + dev_info(palmas->dev, "Muxing GPIO %x, PWM %x, LED %x\n", + palmas->gpio_muxed, palmas->pwm_muxed, + palmas->led_muxed); + + reg = pdata->power_ctrl; + + slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE); + addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_POWER_CTRL); + + ret = regmap_write(palmas->regmap[slave], addr, reg); + if (ret) + goto err; + + children = kmemdup(palmas_children, sizeof(palmas_children), + GFP_KERNEL); + if (!children) { + ret = -ENOMEM; + goto err; + } + + ret = mfd_add_devices(palmas->dev, -1, + children, ARRAY_SIZE(palmas_children), + NULL, regmap_irq_chip_get_base(palmas->irq_data)); + kfree(children); + + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(palmas->dev); + kfree(palmas); + return ret; +} + +static int palmas_i2c_remove(struct i2c_client *i2c) +{ + struct palmas *palmas = i2c_get_clientdata(i2c); + + mfd_remove_devices(palmas->dev); + regmap_del_irq_chip(palmas->irq, palmas->irq_data); + + return 0; +} + +static const struct i2c_device_id palmas_i2c_id[] = { + { "palmas", }, + { "twl6035", }, + { "twl6037", }, + { "tps65913", }, +}; +MODULE_DEVICE_TABLE(i2c, palmas_i2c_id); + +static struct of_device_id __devinitdata of_palmas_match_tbl[] = { + { .compatible = "ti,palmas", }, + { /* end */ } +}; + +static struct i2c_driver palmas_i2c_driver = { + .driver = { + .name = "palmas", + .of_match_table = of_palmas_match_tbl, + .owner = THIS_MODULE, + }, + .probe = palmas_i2c_probe, + .remove = palmas_i2c_remove, + .id_table = palmas_i2c_id, +}; + +static int __init palmas_i2c_init(void) +{ + return i2c_add_driver(&palmas_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(palmas_i2c_init); + +static void __exit palmas_i2c_exit(void) +{ + i2c_del_driver(&palmas_i2c_driver); +} +module_exit(palmas_i2c_exit); + +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_DESCRIPTION("Palmas chip family multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index 99ef944c621d..cdc1df7fa0e9 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -75,49 +75,12 @@ static struct deepsleep_control_data deepsleep_data[] = { (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) static struct mfd_cell rc5t583_subdevs[] = { + {.name = "rc5t583-gpio",}, {.name = "rc5t583-regulator",}, {.name = "rc5t583-rtc", }, {.name = "rc5t583-key", } }; -int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_write(rc5t583->regmap, reg, val); -} - -int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - unsigned int ival; - int ret; - ret = regmap_read(rc5t583->regmap, reg, &ival); - if (!ret) - *val = (uint8_t)ival; - return ret; -} - -int rc5t583_set_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask); -} - -int rc5t583_clear_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0); -} - -int rc5t583_update(struct device *dev, unsigned int reg, - unsigned int val, unsigned int mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, mask, val); -} - static int __rc5t583_set_ext_pwrreq1_control(struct device *dev, int id, int ext_pwr, int slots) { @@ -197,6 +160,7 @@ int rc5t583_ext_power_req_config(struct device *dev, int ds_id, ds_id, ext_pwr_req); return 0; } +EXPORT_SYMBOL(rc5t583_ext_power_req_config); static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583, struct rc5t583_platform_data *pdata) @@ -304,7 +268,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, rc5t583->dev = &i2c->dev; i2c_set_clientdata(i2c, rc5t583); - rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + rc5t583->regmap = devm_regmap_init_i2c(i2c, &rc5t583_regmap_config); if (IS_ERR(rc5t583->regmap)) { ret = PTR_ERR(rc5t583->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); @@ -313,7 +277,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, ret = rc5t583_clear_ext_power_req(rc5t583, pdata); if (ret < 0) - goto err_irq_init; + return ret; if (i2c->irq) { ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); @@ -336,8 +300,6 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, err_add_devs: if (irq_init_success) rc5t583_irq_exit(rc5t583); -err_irq_init: - regmap_exit(rc5t583->regmap); return ret; } @@ -347,7 +309,6 @@ static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(rc5t583->dev); rc5t583_irq_exit(rc5t583); - regmap_exit(rc5t583->regmap); return 0; } diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index c9ed5c00a621..0f1ff7fbdc74 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -41,28 +41,28 @@ static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, static irqreturn_t tps65910_irq(int irq, void *irq_data) { struct tps65910 *tps65910 = irq_data; + unsigned int reg; u32 irq_sts; u32 irq_mask; - u8 reg; int i; - tps65910->read(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS, ®); irq_sts = reg; - tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®); irq_sts |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®); irq_sts |= reg << 16; } - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); irq_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); irq_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); irq_mask |= reg << 16; } @@ -82,13 +82,13 @@ static irqreturn_t tps65910_irq(int irq, void *irq_data) /* Write the STS register back to clear IRQs we handled */ reg = irq_sts & 0xFF; irq_sts >>= 8; - tps65910->write(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS, reg); reg = irq_sts & 0xFF; - tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = irq_sts >> 8; - tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg); } return IRQ_HANDLED; @@ -105,27 +105,27 @@ static void tps65910_irq_sync_unlock(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); u32 reg_mask; - u8 reg; + unsigned int reg; - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); reg_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); reg_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); reg_mask |= reg << 16; } if (tps65910->irq_mask != reg_mask) { reg = tps65910->irq_mask & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg); reg = tps65910->irq_mask >> 8 & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = tps65910->irq_mask >> 16; - tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg); } } mutex_unlock(&tps65910->irq_lock); diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 1c4f53efee74..18b30cf45e5b 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -19,13 +19,16 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/mfd/core.h> #include <linux/regmap.h> #include <linux/mfd/tps65910.h> +#include <linux/of_device.h> static struct mfd_cell tps65910s[] = { { + .name = "tps65910-gpio", + }, + { .name = "tps65910-pmic", }, { @@ -37,30 +40,6 @@ static struct mfd_cell tps65910s[] = { }; -static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg, - int bytes, void *dest) -{ - return regmap_bulk_read(tps65910->regmap, reg, dest, bytes); -} - -static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, - int bytes, void *src) -{ - return regmap_bulk_write(tps65910->regmap, reg, src, bytes); -} - -int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, mask); -} -EXPORT_SYMBOL_GPL(tps65910_set_bits); - -int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, 0); -} -EXPORT_SYMBOL_GPL(tps65910_clear_bits); - static bool is_volatile_reg(struct device *dev, unsigned int reg) { struct tps65910 *tps65910 = dev_get_drvdata(dev); @@ -81,84 +60,212 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg) return true; } -static const struct regmap_config rc5t583_regmap_config = { +static const struct regmap_config tps65910_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_reg = is_volatile_reg, - .max_register = TPS65910_MAX_REGISTER, - .num_reg_defaults_raw = TPS65910_MAX_REGISTER, + .max_register = TPS65910_MAX_REGISTER - 1, .cache_type = REGCACHE_RBTREE, }; -static int tps65910_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + struct device *dev = NULL; + int ret = 0; + + dev = tps65910->dev; + + if (!pmic_pdata->en_dev_slp) + return 0; + + /* enabling SLEEP device state */ + ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + if (ret < 0) { + dev_err(dev, "set dev_slp failed: %d\n", ret); + goto err_sleep_init; + } + + /* Return if there is no sleep keepon data. */ + if (!pmic_pdata->slp_keepon) + return 0; + + if (pmic_pdata->slp_keepon->therm_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set therm_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->clkout32k_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set clkout32k_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->i2chs_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set i2chs_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + return 0; + +disable_dev_slp: + tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + +err_sleep_init: + return ret; +} + +#ifdef CONFIG_OF +static struct of_device_id tps65910_of_match[] = { + { .compatible = "ti,tps65910", .data = (void *)TPS65910}, + { .compatible = "ti,tps65911", .data = (void *)TPS65911}, + { }, +}; +MODULE_DEVICE_TABLE(of, tps65910_of_match); + +static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + struct device_node *np = client->dev.of_node; + struct tps65910_board *board_info; + unsigned int prop; + const struct of_device_id *match; + unsigned int prop_array[TPS6591X_MAX_NUM_GPIO]; + int ret = 0; + int idx; + + match = of_match_device(tps65910_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + *chip_id = (int)match->data; + + board_info = devm_kzalloc(&client->dev, sizeof(*board_info), + GFP_KERNEL); + if (!board_info) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return NULL; + } + + ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop); + if (!ret) + board_info->vmbch_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH-Threshold not specified"); + + ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop); + if (!ret) + board_info->vmbch2_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH2-Threshold not specified"); + + ret = of_property_read_u32_array(np, "ti,en-gpio-sleep", + prop_array, TPS6591X_MAX_NUM_GPIO); + if (!ret) + for (idx = 0; idx < ARRAY_SIZE(prop_array); idx++) + board_info->en_gpio_sleep[idx] = (prop_array[idx] != 0); + else if (ret != -EINVAL) { + dev_err(&client->dev, + "error reading property ti,en-gpio-sleep: %d\n.", ret); + return NULL; + } + + + board_info->irq = client->irq; + board_info->irq_base = -1; + board_info->gpio_base = -1; + + return board_info; +} +#else +static inline +struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + return NULL; +} +#endif + +static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct tps65910 *tps65910; struct tps65910_board *pmic_plat_data; struct tps65910_platform_data *init_data; int ret = 0; + int chip_id = id->driver_data; pmic_plat_data = dev_get_platdata(&i2c->dev); + + if (!pmic_plat_data && i2c->dev.of_node) + pmic_plat_data = tps65910_parse_dt(i2c, &chip_id); + if (!pmic_plat_data) return -EINVAL; - init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL); + init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL); if (init_data == NULL) return -ENOMEM; - tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL); - if (tps65910 == NULL) { - kfree(init_data); + tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL); + if (tps65910 == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, tps65910); tps65910->dev = &i2c->dev; tps65910->i2c_client = i2c; - tps65910->id = id->driver_data; - tps65910->read = tps65910_i2c_read; - tps65910->write = tps65910_i2c_write; + tps65910->id = chip_id; mutex_init(&tps65910->io_mutex); - tps65910->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { ret = PTR_ERR(tps65910->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); - goto regmap_err; + return ret; } ret = mfd_add_devices(tps65910->dev, -1, tps65910s, ARRAY_SIZE(tps65910s), NULL, 0); - if (ret < 0) - goto err; + if (ret < 0) { + dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); + return ret; + } init_data->irq = pmic_plat_data->irq; init_data->irq_base = pmic_plat_data->irq_base; - tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base); - tps65910_irq_init(tps65910, init_data->irq, init_data); - kfree(init_data); - return ret; + tps65910_sleepinit(tps65910, pmic_plat_data); -err: - regmap_exit(tps65910->regmap); -regmap_err: - kfree(tps65910); - kfree(init_data); return ret; } -static int tps65910_i2c_remove(struct i2c_client *i2c) +static __devexit int tps65910_i2c_remove(struct i2c_client *i2c) { struct tps65910 *tps65910 = i2c_get_clientdata(i2c); tps65910_irq_exit(tps65910); mfd_remove_devices(tps65910->dev); - regmap_exit(tps65910->regmap); - kfree(tps65910); return 0; } @@ -175,9 +282,10 @@ static struct i2c_driver tps65910_i2c_driver = { .driver = { .name = "tps65910", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65910_of_match), }, .probe = tps65910_i2c_probe, - .remove = tps65910_i2c_remove, + .remove = __devexit_p(tps65910_i2c_remove), .id_table = tps65910_i2c_id, }; diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index e8ea75c424c6..f0f2ce1f6840 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -105,6 +105,9 @@ #define TPS80031_CFG_INPUT_PUPD3 0xF2 #define TPS80031_CFG_INPUT_PUPD4 0xF3 +#define TPS80031_BBSPOR_CFG 0xE6 +#define TPS80031_BBSPOR_CHG_EN 0x8 + struct tps80031_pupd_data { u8 reg; u8 pullup_bit; @@ -568,6 +571,17 @@ static void tps80031_pupd_init(struct tps80031 *tps80031, } } +static void tps80031_backup_battery_charger_control(struct tps80031 *tps80031, + int enable) +{ + if (enable) + tps80031_update(tps80031->dev, SLAVE_ID1, TPS80031_BBSPOR_CFG, + TPS80031_BBSPOR_CHG_EN, TPS80031_BBSPOR_CHG_EN); + else + tps80031_update(tps80031->dev, SLAVE_ID1, TPS80031_BBSPOR_CFG, + 0, TPS80031_BBSPOR_CHG_EN); +} + static void tps80031_init_ext_control(struct tps80031 *tps80031, struct tps80031_platform_data *pdata) { int ret; @@ -874,8 +888,22 @@ static irqreturn_t tps80031_irq(int irq, void *data) acks = (tmp[2] << 16) | (tmp[1] << 8) | tmp[0]; if (acks) { - ret = tps80031_writes(tps80031->dev, SLAVE_ID2, - TPS80031_INT_STS_A, 3, tmp); + /* + * Hardware behavior: hardware have the shadow register for + * interrupt status register which is updated if interrupt + * comes just after the interrupt status read. This shadow + * register gets written to main status register and cleared + * if any byte write happens in any of status register like + * STS_A, STS_B or STS_C. + * Hence here to clear the original interrupt status and + * updating the STS register with the shadow register, it is + * require to write only one byte in any of STS register. + * Having multiple register write can cause the STS register + * to clear without handling those interrupt and can cause + * interrupt miss. + */ + ret = tps80031_write(tps80031->dev, SLAVE_ID2, + TPS80031_INT_STS_A, 0); if (ret < 0) { dev_err(tps80031->dev, "failed to write " "interrupt status\n"); @@ -1273,6 +1301,8 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, tps80031_debuginit(tps80031); + tps80031_backup_battery_charger_control(tps80031, 1); + if (pdata->use_power_off && !pm_power_off) pm_power_off = tps80031_power_off; @@ -1288,13 +1318,17 @@ fail: #ifdef CONFIG_PM static int tps80031_i2c_suspend(struct i2c_client *client, pm_message_t state) { + struct tps80031 *tps80031 = i2c_get_clientdata(client); if (client->irq) disable_irq(client->irq); + tps80031_backup_battery_charger_control(tps80031, 0); return 0; } static int tps80031_i2c_resume(struct i2c_client *client) { + struct tps80031 *tps80031 = i2c_get_clientdata(client); + tps80031_backup_battery_charger_control(tps80031, 1); if (client->irq) enable_irq(client->irq); return 0; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7b7e948ff4f5..dcf345e23487 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -562,6 +562,12 @@ config MAX1749_VIBRATOR ---help--- Adds a timed output vibrator device node for MAX1749 vibrator motor +config THERM_EST + bool "Thermal estimator driver" + default n + ---help--- + Thermal driver which estimates temperature based of other sensors. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -571,5 +577,6 @@ source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/inv_mpu/Kconfig" source "drivers/misc/tegra-baseband/Kconfig" +source "drivers/misc/tegra-cec/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 5b3728ebcaee..d9172c99bf6d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -57,4 +57,6 @@ obj-$(CONFIG_BCM4329_RFKILL) += bcm4329_rfkill.o obj-$(CONFIG_INV_SENSORS) += inv_mpu/ obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o obj-$(CONFIG_TEGRA_BB_SUPPORT) += tegra-baseband/ +obj-$(CONFIG_TEGRA_CEC_SUPPORT) += tegra-cec/ obj-$(CONFIG_MAX1749_VIBRATOR) += max1749.o +obj-$(CONFIG_THERM_EST) += therm_est.o diff --git a/drivers/misc/bcm4329_rfkill.c b/drivers/misc/bcm4329_rfkill.c index a077326f2553..9dc33fd51e59 100644 --- a/drivers/misc/bcm4329_rfkill.c +++ b/drivers/misc/bcm4329_rfkill.c @@ -44,6 +44,13 @@ static struct bcm4329_rfkill_data *bcm4329_rfkill; static int bcm4329_bt_rfkill_set_power(void *data, bool blocked) { + /* + * check if BT gpio_shutdown line status and current request are same. + * If same, then return, else perform requested operation. + */ + if (gpio_get_value(bcm4329_rfkill->gpio_shutdown) && !blocked) + return 0; + if (blocked) { if (bcm4329_rfkill->gpio_shutdown) gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0); @@ -104,7 +111,9 @@ static int bcm4329_rfkill_probe(struct platform_device *pdev) ret = gpio_request(bcm4329_rfkill->gpio_reset, "bcm4329_nreset_gpio"); } else { - pr_warn("%s : can't find reset gpio.\n", __func__); + pr_warn("%s : can't find reset gpio. " + "reset gpio may not be defined for " + "this platform \n", __func__); bcm4329_rfkill->gpio_reset = 0; } @@ -116,7 +125,9 @@ static int bcm4329_rfkill_probe(struct platform_device *pdev) ret = gpio_request(bcm4329_rfkill->gpio_shutdown, "bcm4329_nshutdown_gpio"); } else { - pr_warn("%s : can't find shutdown gpio.\n", __func__); + pr_warn("%s : can't find shutdown gpio " + "shutdown gpio may not be defined for " + "this platform \n", __func__); bcm4329_rfkill->gpio_shutdown = 0; } diff --git a/drivers/misc/nct1008.c b/drivers/misc/nct1008.c index 56678a795497..a2714698d00d 100644 --- a/drivers/misc/nct1008.c +++ b/drivers/misc/nct1008.c @@ -3,7 +3,7 @@ * * Driver for NCT1008, temperature monitoring device from ON Semiconductors * - * Copyright (c) 2010-2011, NVIDIA Corporation. + * Copyright (c) 2010-2012, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,8 +32,6 @@ #include <linux/delay.h> #include <linux/regulator/consumer.h> -#define DRIVER_NAME "nct1008" - /* Register Addresses */ #define LOCAL_TEMP_RD 0x00 #define EXT_TEMP_RD_HI 0x01 @@ -94,7 +92,7 @@ static inline u8 temperature_to_value(bool extended, s8 temp) return extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : (u8)temp; } -static int nct1008_get_temp(struct device *dev, long *pTemp) +static int nct1008_get_temp(struct device *dev, long *etemp, long *itemp) { struct i2c_client *client = to_i2c_client(dev); struct nct1008_platform_data *pdata = client->dev.platform_data; @@ -106,30 +104,34 @@ static int nct1008_get_temp(struct device *dev, long *pTemp) u8 value; /* Read Local Temp */ - value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD); - if (value < 0) - goto error; - temp_local = value_to_temperature(pdata->ext_range, value); - temp_local_milli = CELSIUS_TO_MILLICELSIUS(temp_local); + if (itemp) { + value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD); + if (value < 0) + goto error; + temp_local = value_to_temperature(pdata->ext_range, value); + temp_local_milli = CELSIUS_TO_MILLICELSIUS(temp_local); + + *itemp = temp_local_milli; + } /* Read External Temp */ - value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO); - if (value < 0) - goto error; - temp_ext_lo = (value >> 6); + if (etemp) { + value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO); + if (value < 0) + goto error; + temp_ext_lo = (value >> 6); - value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI); - if (value < 0) - goto error; - temp_ext_hi = value_to_temperature(pdata->ext_range, value); + value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI); + if (value < 0) + goto error; + temp_ext_hi = value_to_temperature(pdata->ext_range, value); - temp_ext_milli = CELSIUS_TO_MILLICELSIUS(temp_ext_hi) + - temp_ext_lo * 250; + temp_ext_milli = CELSIUS_TO_MILLICELSIUS(temp_ext_hi) + + temp_ext_lo * 250; - /* Return max between Local and External Temp */ - *pTemp = max(temp_local_milli, temp_ext_milli); + *etemp = temp_ext_milli; + } - dev_dbg(dev, "\n %s: ret temp=%ldC ", __func__, *pTemp); return 0; error: dev_err(&client->dev, "\n error in file=: %s %s() line=%d: " @@ -225,7 +227,7 @@ static ssize_t nct1008_set_temp_overheat(struct device *dev, return -EINVAL; } /* check for system power down */ - err = nct1008_get_temp(dev, &currTemp); + err = nct1008_get_temp(dev, &currTemp, NULL); if (err) goto error; @@ -403,7 +405,7 @@ static void print_reg(const char *reg_name, struct seq_file *s, static int dbg_nct1008_show(struct seq_file *s, void *unused) { - seq_printf(s, "nct1008 Registers\n"); + seq_printf(s, "nct1008 nct72 Registers\n"); seq_printf(s, "------------------\n"); print_reg("Local Temp Value ", s, 0x00); print_reg("Ext Temp Value Hi ", s, 0x01); @@ -442,8 +444,12 @@ static int __init nct1008_debuginit(struct nct1008_data *nct) { int err = 0; struct dentry *d; - d = debugfs_create_file("nct1008", S_IRUGO, NULL, - (void *)nct, &debug_fops); + if (nct->chip == NCT72) + d = debugfs_create_file("nct72", S_IRUGO, NULL, + (void *)nct, &debug_fops); + else + d = debugfs_create_file("nct1008", S_IRUGO, NULL, + (void *)nct, &debug_fops); if ((!d) || IS_ERR(d)) { dev_err(&nct->client->dev, "Error: %s debugfs_create_file" " returned an error\n", __func__); @@ -563,12 +569,14 @@ static void nct1008_power_control(struct nct1008_data *data, bool is_enable) ret = regulator_disable(data->nct_reg); if (ret < 0) - dev_err(&data->client->dev, "Error in %s rail vdd_nct1008, " + dev_err(&data->client->dev, "Error in %s rail vdd_nct%s, " "error %d\n", (is_enable) ? "enabling" : "disabling", + (data->chip == NCT72) ? "72" : "1008", ret); else - dev_info(&data->client->dev, "success in %s rail vdd_nct1008\n", - (is_enable) ? "enabling" : "disabling"); + dev_info(&data->client->dev, "success in %s rail vdd_nct%s\n", + (is_enable) ? "enabling" : "disabling", + (data->chip == NCT72) ? "72" : "1008"); } static int __devinit nct1008_configure_sensor(struct nct1008_data* data) @@ -692,7 +700,8 @@ error: static int __devinit nct1008_configure_irq(struct nct1008_data *data) { - data->workqueue = create_singlethread_workqueue("nct1008"); + data->workqueue = create_singlethread_workqueue((data->chip == NCT72) \ + ? "nct72" : "nct1008"); INIT_WORK(&data->work, nct1008_work_func); @@ -701,12 +710,18 @@ static int __devinit nct1008_configure_irq(struct nct1008_data *data) else return request_irq(data->client->irq, nct1008_irq, IRQF_TRIGGER_LOW, - DRIVER_NAME, data); + (data->chip == NCT72) ? "nct72" : "nct1008", + data); } int nct1008_thermal_get_temp(struct nct1008_data *data, long *temp) { - return nct1008_get_temp(&data->client->dev, temp); + return nct1008_get_temp(&data->client->dev, temp, NULL); +} + +int nct1008_thermal_get_temps(struct nct1008_data *data, long *etemp, long *itemp) +{ + return nct1008_get_temp(&data->client->dev, etemp, itemp); } int nct1008_thermal_get_temp_low(struct nct1008_data *data, long *temp) @@ -816,6 +831,7 @@ static int __devinit nct1008_probe(struct i2c_client *client, return -ENOMEM; data->client = client; + data->chip = id->driver_data; memcpy(&data->plat_data, client->dev.platform_data, sizeof(struct nct1008_platform_data)); i2c_set_clientdata(client, data); @@ -911,14 +927,15 @@ static int nct1008_resume(struct i2c_client *client) #endif static const struct i2c_device_id nct1008_id[] = { - { DRIVER_NAME, 0 }, - { } + { "nct1008", NCT1008 }, + { "nct72", NCT72}, + {} }; MODULE_DEVICE_TABLE(i2c, nct1008_id); static struct i2c_driver nct1008_driver = { .driver = { - .name = DRIVER_NAME, + .name = "nct1008_nct72", }, .probe = nct1008_probe, .remove = __devexit_p(nct1008_remove), @@ -939,7 +956,7 @@ static void __exit nct1008_exit(void) i2c_del_driver(&nct1008_driver); } -MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008"); +MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008/NCT72"); MODULE_LICENSE("GPL"); module_init(nct1008_init); diff --git a/drivers/misc/tegra-baseband/Makefile b/drivers/misc/tegra-baseband/Makefile index a95d84dbf117..68ec4fe83ac1 100644 --- a/drivers/misc/tegra-baseband/Makefile +++ b/drivers/misc/tegra-baseband/Makefile @@ -2,5 +2,7 @@ # Makefile for tegra baseband support. # +subdir-ccflags-y := -Werror + obj-$(CONFIG_TEGRA_BB_POWER) += bb-power.o obj-$(CONFIG_TEGRA_BB_M7400) += bb-m7400.o diff --git a/drivers/misc/tegra-baseband/bb-m7400.c b/drivers/misc/tegra-baseband/bb-m7400.c index 5808a6e321cd..adabefdb100d 100644 --- a/drivers/misc/tegra-baseband/bb-m7400.c +++ b/drivers/misc/tegra-baseband/bb-m7400.c @@ -28,10 +28,12 @@ #include <linux/device.h> #include <linux/usb.h> #include <linux/wakelock.h> +#include <linux/platform_data/tegra_usb.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <mach/tegra-bb-power.h> #include <mach/usb_phy.h> + #include "bb-power.h" static struct tegra_bb_gpio_data m7400_gpios[] = { @@ -106,27 +108,26 @@ static void m7400_apdown_handshake(void) gpio_set_value(gpio_awr, 0); } -static int m7400_l2_suspend(void) +static void m7400_l2_suspend(void) { /* Gets called for two cases : a) Port suspend. b) Bus suspend. */ if (modem_status == BBSTATE_L2) - return 0; + return; /* Post bus suspend: Drive ARR low. */ gpio_set_value(gpio_arr, 0); modem_status = BBSTATE_L2; - return 0; } -static int m7400_l2_resume(void) +static void m7400_l2_resume(void) { /* Gets called for two cases : a) L2 resume. b) bus resume phase of L3 resume. */ if (modem_status == BBSTATE_L0) - return 0; + return; /* Pre bus resume: Drive ARR high. */ gpio_set_value(gpio_arr, 1); @@ -136,10 +137,9 @@ static int m7400_l2_resume(void) if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) { pr_info("%s: Error: timeout waiting for modem ack.\n", __func__); - return -1; + return; } modem_status = BBSTATE_L0; - return 0; } static void m7400_l3_suspend(void) @@ -193,20 +193,17 @@ static int m7400_power(int code) static void m7400_ehci_customize(struct platform_device *pdev) { - struct tegra_ehci_platform_data *ehci_pdata; - struct tegra_uhsic_config *hsic_config; + struct tegra_usb_platform_data *ehci_pdata; - ehci_pdata = (struct tegra_ehci_platform_data *) + ehci_pdata = (struct tegra_usb_platform_data *) pdev->dev.platform_data; - hsic_config = (struct tegra_uhsic_config *) - ehci_pdata->phy_config; /* Register PHY callbacks */ - hsic_config->postsuspend = m7400_l2_suspend; - hsic_config->preresume = m7400_l2_resume; + ehci_pdata->ops->post_suspend = m7400_l2_suspend; + ehci_pdata->ops->pre_resume = m7400_l2_resume; /* Override required settings */ - ehci_pdata->power_down_on_bus_suspend = 0; + ehci_pdata->u_data.host.power_off_on_suspend = false; } static int m7400_attrib_write(struct device *dev, int value) diff --git a/drivers/misc/tegra-cec/Kconfig b/drivers/misc/tegra-cec/Kconfig new file mode 100644 index 000000000000..11fa9bc9ce17 --- /dev/null +++ b/drivers/misc/tegra-cec/Kconfig @@ -0,0 +1,19 @@ +menuconfig TEGRA_CEC_SUPPORT + bool "Tegra CEC support" + depends on ARCH_TEGRA + ---help--- + Say Y here to get to see options for tegra CEC support. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if TEGRA_CEC_SUPPORT + +config TEGRA_CEC_T30 + bool "Enable T30 CEC support" + ---help--- + Adds HDMI-CEC driver for T30. + + Disabled by default. Choose Y here if you want to build the driver. + +endif # TEGRA_CEC_SUPPORT diff --git a/drivers/misc/tegra-cec/Makefile b/drivers/misc/tegra-cec/Makefile new file mode 100644 index 000000000000..ab380305c35f --- /dev/null +++ b/drivers/misc/tegra-cec/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for tegra cec support. +# + +obj-$(CONFIG_TEGRA_CEC_T30) += tegra_cec.o diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c new file mode 100644 index 000000000000..a876d9d60712 --- /dev/null +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -0,0 +1,383 @@ +/* + * drivers/misc/tegra-cec/tegra_cec.c + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/ktime.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/uaccess.h> + +#include <linux/platform_device.h> +#include <linux/miscdevice.h> + +#include <mach/clk.h> + +#include "tegra_cec.h" + + +int tegra_cec_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct tegra_cec *cec = container_of(miscdev, + struct tegra_cec, misc_dev); + dev_dbg(cec->dev, "%s\n", __func__); + file->private_data = cec; + + return 0; +} + +int tegra_cec_release(struct inode *inode, struct file *file) +{ + struct tegra_cec *cec = file->private_data; + + dev_dbg(cec->dev, "%s\n", __func__); + + return 0; +} + +ssize_t tegra_cec_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct tegra_cec *cec = file->private_data; + unsigned long write_buff; + + count = 4; + + if (copy_from_user(&write_buff, buffer, count)) + return -EFAULT; + + writel((TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY | + TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), + cec->cec_base + TEGRA_CEC_INT_MASK); + + wait_event_interruptible(cec->tx_waitq, cec->tx_wake == 1); + writel(write_buff, cec->cec_base + TEGRA_CEC_TX_REGISTER); + cec->tx_wake = 0; + + writel((TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), + cec->cec_base + TEGRA_CEC_INT_MASK); + + write_buff = 0x00; + return count; +} + +ssize_t tegra_cec_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct tegra_cec *cec = file->private_data; + unsigned short rx_buffer; + count = 2; + + if (cec->rx_wake == 0) + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + wait_event_interruptible(cec->rx_waitq, cec->rx_wake == 1); + + rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER); + + if (copy_to_user(buffer, &rx_buffer, count)) + return -EFAULT; + + rx_buffer = 0x0; + cec->rx_wake = 0; + return count; +} + +static irqreturn_t tegra_cec_irq_handler(int irq, void *data) +{ + struct device *dev = data; + struct tegra_cec *cec = dev_get_drvdata(dev); + unsigned long status; + + status = readl(cec->cec_base + TEGRA_CEC_INT_STAT); + + if (!status) + return IRQ_HANDLED; + + if ((status & TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN) || + (status & TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED) || + (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) || + (status & TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { + writel((TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | + TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | + TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + } else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { + writel((TEGRA_CEC_INT_STAT_RX_REGISTER_FULL), + cec->cec_base + TEGRA_CEC_INT_STAT); + cec->rx_wake = 1; + wake_up_interruptible(&cec->rx_waitq); + } else if ((status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) || + (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) || + (status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || + (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { + writel((TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY | + TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + } else if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { + cec->tx_wake = 1; + wake_up_interruptible(&cec->tx_waitq); + writel((TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY), + cec->cec_base + TEGRA_CEC_INT_STAT); + } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { + writel((TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + } + + return IRQ_HANDLED; +} + +static const struct file_operations tegra_cec_fops = { + .owner = THIS_MODULE, + .open = tegra_cec_open, + .release = tegra_cec_release, + .read = tegra_cec_read, + .write = tegra_cec_write, +}; + +static void tegra_cec_init(struct tegra_cec *cec) +{ + + writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); + writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); + msleep(1000); + + writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); + + writel((TEGRA_CEC_LOGICAL_ADDR<<TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK)& + (~TEGRA_CEC_HW_CONTROL_RX_SNOOP) & + (~TEGRA_CEC_HW_CONTROL_RX_NAK_MODE) & + (~TEGRA_CEC_HW_CONTROL_TX_NAK_MODE) & + (~TEGRA_CEC_HW_CONTROL_FAST_SIM_MODE) | + (TEGRA_CEC_HW_CONTROL_TX_RX_MODE), + cec->cec_base + TEGRA_CEC_HW_CONTROL); + + writel(0x00, cec->cec_base + TEGRA_CEC_INPUT_FILTER); + + writel((0x7a << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_LO_TIME_MASK) | + (0x6d << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_LO_TIME_MASK) | + (0x93 << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_DURATION_MASK) | + (0x86 << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_DURATION_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_0); + + writel((0x35 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_LO_TIME_MASK) | + (0x21 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_SAMPLE_TIME_MASK) | + (0x56 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_DURATION_MASK) | + (0x40 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MIN_DURATION_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_1); + + writel((0x50 << TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_2); + + writel((0x74 << TEGRA_CEC_TX_TIMING_0_TX_START_BIT_LO_TIME_MASK) | + (0x8d << TEGRA_CEC_TX_TIMING_0_TX_START_BIT_DURATION_MASK) | + (0x08 << TEGRA_CEC_TX_TIMING_0_TX_BUS_XITION_TIME_MASK) | + (0x71 << TEGRA_CEC_TX_TIMING_0_TX_BUS_ERROR_LO_TIME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_0); + + writel((0x2f << TEGRA_CEC_TX_TIMING_1_TX_LO_DATA_BIT_LO_TIME_MASK) | + (0x13 << TEGRA_CEC_TX_TIMING_1_TX_HI_DATA_BIT_LO_TIME_MASK) | + (0x4b << TEGRA_CEC_TX_TIMING_1_TX_DATA_BIT_DURATION_MASK) | + (0x21 << TEGRA_CEC_TX_TIMING_1_TX_ACK_NAK_BIT_SAMPLE_TIME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_1); + + writel((0x07 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK) | + (0x05 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_NEW_FRAME_MASK) | + (0x03 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_RETRY_FRAME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_2); + + writel((TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), + cec->cec_base + TEGRA_CEC_INT_MASK); +} + +static int __devinit tegra_cec_probe(struct platform_device *pdev) +{ + struct tegra_cec *cec; + struct resource *res; + int ret = 0; + + cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL); + + if (!cec) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(&pdev->dev, + "Unable to allocate resources for device.\n"); + ret = -EBUSY; + goto cec_error; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, + "Unable to request mem region for device.\n"); + ret = -EBUSY; + goto cec_error; + } + + cec->tegra_cec_irq = platform_get_irq(pdev, 0); + + if (cec->tegra_cec_irq <= 0) { + ret = -EBUSY; + goto cec_error; + } + + cec->cec_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + + if (!cec->cec_base) { + dev_err(&pdev->dev, "Unable to grab IOs for device.\n"); + ret = -EBUSY; + goto cec_error; + } + + cec->clk = clk_get(&pdev->dev, "cec"); + + if (IS_ERR_OR_NULL(cec->clk)) { + dev_err(&pdev->dev, "can't get clock for CEC\n"); + ret = -ENOENT; + goto clk_error; + } + + clk_enable(cec->clk); + + /* set context info. */ + cec->dev = &pdev->dev; + cec->rx_wake = 0; + cec->tx_wake = 0; + init_waitqueue_head(&cec->rx_waitq); + init_waitqueue_head(&cec->tx_waitq); + + platform_set_drvdata(pdev, cec); + /* clear out the hardware. */ + + tegra_cec_init(cec); + + device_init_wakeup(&pdev->dev, 1); + + cec->misc_dev.minor = MISC_DYNAMIC_MINOR; + cec->misc_dev.name = TEGRA_CEC_NAME; + cec->misc_dev.fops = &tegra_cec_fops; + cec->misc_dev.parent = &pdev->dev; + + if (misc_register(&cec->misc_dev)) { + printk(KERN_WARNING "Couldn't register device , %s.\n", TEGRA_CEC_NAME); + goto cec_error; + } + + ret = devm_request_irq(&pdev->dev, cec->tegra_cec_irq, + tegra_cec_irq_handler, IRQF_DISABLED, "cec_irq", &pdev->dev); + + if (ret) { + dev_err(&pdev->dev, + "Unable to request interrupt for device (err=%d).\n", ret); + goto cec_error; + } + + dev_notice(&pdev->dev, "probed\n"); + + return 0; + +cec_error: + clk_disable(cec->clk) + clk_put(cec->clk); +clk_error: + return ret; +} + +static int tegra_cec_remove(struct platform_device *pdev) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + clk_disable(cec->clk) + clk_put(cec->clk); + + misc_deregister(&cec->misc_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + clk_disable(cec->clk); + + return 0; +} + +static int tegra_cec_resume(struct platform_device *pdev) +{ + + struct tegra_cec *cec = platform_get_drvdata(pdev); + clk_enable(cec->clk); + tegra_cec_init(cec); + return 0; +} +#endif + +static struct platform_driver tegra_cec_driver = { + .driver = { + .name = TEGRA_CEC_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_cec_probe, + .remove = tegra_cec_remove, + +#ifdef CONFIG_PM + .suspend = tegra_cec_suspend, + .resume = tegra_cec_resume, +#endif +}; + +module_platform_driver(tegra_cec_driver); diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h new file mode 100644 index 000000000000..acc94dc61965 --- /dev/null +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -0,0 +1,134 @@ +/* + * drivers/misc/tegra-cec/tegra_cec.h + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/pm.h> + + +struct tegra_cec { + struct device *dev; + struct miscdevice misc_dev; + struct clk *clk; + void __iomem *cec_base; + int tegra_cec_irq; + wait_queue_head_t rx_waitq; + wait_queue_head_t tx_waitq; + unsigned int rx_wake; + unsigned int tx_wake; +}; +static int tegra_cec_remove(struct platform_device *pdev); + +/*CEC Timing registers*/ +#define TEGRA_CEC_SW_CONTROL 0X000 +#define TEGRA_CEC_HW_CONTROL 0X004 +#define TEGRA_CEC_INPUT_FILTER 0X008 +#define TEGRA_CEC_TX_REGISTER 0X010 +#define TEGRA_CEC_RX_REGISTER 0X014 +#define TEGRA_CEC_RX_TIMING_0 0X018 +#define TEGRA_CEC_RX_TIMING_1 0X01C +#define TEGRA_CEC_RX_TIMING_2 0X020 +#define TEGRA_CEC_TX_TIMING_0 0X024 +#define TEGRA_CEC_TX_TIMING_1 0X028 +#define TEGRA_CEC_TX_TIMING_2 0X02C +#define TEGRA_CEC_INT_STAT 0X030 +#define TEGRA_CEC_INT_MASK 0X034 +#define TEGRA_CEC_HW_DEBUG_RX 0X038 +#define TEGRA_CEC_HW_DEBUG_TX 0X03C + +#define TEGRA_CEC_LOGICAL_ADDR 0x10 + +#define TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK 0 +#define TEGRA_CEC_HW_CONTROL_RX_SNOOP (1<<15) +#define TEGRA_CEC_HW_CONTROL_RX_NAK_MODE (1<<16) +#define TEGRA_CEC_HW_CONTROL_TX_NAK_MODE (1<<24) +#define TEGRA_CEC_HW_CONTROL_FAST_SIM_MODE (1<<30) +#define TEGRA_CEC_HW_CONTROL_TX_RX_MODE (1<<31) + +#define TEGRA_CEC_INPUT_FILTER_MODE (1<<31) +#define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_MASK 0 + +#define TEGRA_CEC_TX_REGISTER_DATA_MASK 0 +#define TEGRA_CEC_TX_REGISTER_EOM_MASK 8 +#define TEGRA_CEC_TX_REGISTER_ADDRESS_MODE_MASK 12 +#define TEGRA_CEC_TX_REGISTER_GENERATE_START_BIT_MASK 16 +#define TEGRA_CEC_TX_REGISTER_RETRY_FRAME_MASK 17 + +#define TEGRA_CEC_RX_REGISTER_MASK 0 +#define TEGRA_CEC_RX_REGISTER_EOM (1<<8) +#define TEGRA_CEC_RX_REGISTER_ACK (1<<9) + +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_LO_TIME_MASK 0 +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_LO_TIME_MASK 8 +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_DURATION_MASK 16 +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_DURATION_MASK 24 + +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_LO_TIME_MASK 0 +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_SAMPLE_TIME_MASK 8 +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_DURATION_MASK 16 +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MIN_DURATION_MASK 24 + +#define TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK 0 + +#define TEGRA_CEC_TX_TIMING_0_TX_START_BIT_LO_TIME_MASK 0 +#define TEGRA_CEC_TX_TIMING_0_TX_START_BIT_DURATION_MASK 8 +#define TEGRA_CEC_TX_TIMING_0_TX_BUS_XITION_TIME_MASK 16 +#define TEGRA_CEC_TX_TIMING_0_TX_BUS_ERROR_LO_TIME_MASK 24 + +#define TEGRA_CEC_TX_TIMING_1_TX_LO_DATA_BIT_LO_TIME_MASK 0 +#define TEGRA_CEC_TX_TIMING_1_TX_HI_DATA_BIT_LO_TIME_MASK 8 +#define TEGRA_CEC_TX_TIMING_1_TX_DATA_BIT_DURATION_MASK 16 +#define TEGRA_CEC_TX_TIMING_1_TX_ACK_NAK_BIT_SAMPLE_TIME_MASK 24 + +#define TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK 0 +#define TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_NEW_FRAME_MASK 4 +#define TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_RETRY_FRAME_MASK 8 + +#define TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY (1<<0) +#define TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN (1<<1) +#define TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD (1<<2) +#define TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED (1<<3) +#define TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED (1<<4) +#define TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED (1<<5) +#define TEGRA_CEC_INT_STAT_RX_REGISTER_FULL (1<<8) +#define TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN (1<<9) +#define TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED (1<<10) +#define TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED (1<<11) +#define TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED (1<<12) +#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_H2L (1<<13) +#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_L2H (1<<14) + +#define TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY (1<<0) +#define TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN (1<<1) +#define TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD (1<<2) +#define TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED (1<<3) +#define TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED (1<<4) +#define TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED (1<<5) +#define TEGRA_CEC_INT_MASK_RX_REGISTER_FULL (1<<8) +#define TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN (1<<9) +#define TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED (1<<10) +#define TEGRA_CEC_INT_MASK_RX_BUS_ANOMALY_DETECTED (1<<11) +#define TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED (1<<12) +#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_H2L (1<<13) +#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_L2H (1<<14) + +#define TEGRA_CEC_HW_DEBUG_TX_DURATION_COUNT_MASK 0 +#define TEGRA_CEC_HW_DEBUG_TX_TXBIT_COUNT_MASK 17 +#define TEGRA_CEC_HW_DEBUG_TX_STATE_MASK 21 +#define TEGRA_CEC_HW_DEBUG_TX_FORCELOOUT (1<<25) +#define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER (1<<26) + +#define TEGRA_CEC_NAME "tegra_cec" diff --git a/drivers/misc/therm_est.c b/drivers/misc/therm_est.c new file mode 100644 index 000000000000..e67fc664c7af --- /dev/null +++ b/drivers/misc/therm_est.c @@ -0,0 +1,139 @@ +/* + * drivers/misc/therm_est.c + * + * Copyright (C) 2010-2012 NVIDIA Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/syscalls.h> +#include <linux/therm_est.h> + +int therm_est_get_temp(struct therm_estimator *est, long *temp) +{ + *temp = est->cur_temp; + return 0; +} + +int therm_est_set_limits(struct therm_estimator *est, + long lo_limit, + long hi_limit) +{ + est->therm_est_lo_limit = lo_limit; + est->therm_est_hi_limit = hi_limit; + return 0; +} + +int therm_est_set_alert(struct therm_estimator *est, + void (*cb)(void *), + void *cb_data) +{ + if ((!cb) || est->callback) + BUG(); + + est->callback = cb; + est->callback_data = cb_data; + + return 0; +} + +static void therm_est_work_func(struct work_struct *work) +{ + int i, j, index, sum = 0; + long temp; + struct delayed_work *dwork = container_of (work, + struct delayed_work, work); + struct therm_estimator *est = container_of( + dwork, + struct therm_estimator, + therm_est_work); + + for (i = 0; i < est->ndevs; i++) { + if (est->devs[i]->get_temp(est->devs[i]->dev_data, &temp)) + continue; + est->devs[i]->hist[(est->ntemp % HIST_LEN)] = temp; + } + + for (i = 0; i < est->ndevs; i++) { + for (j = 0; j < HIST_LEN; j++) { + index = (est->ntemp - j + HIST_LEN) % HIST_LEN; + sum += est->devs[i]->hist[index] * + est->devs[i]->coeffs[j]; + } + } + + est->cur_temp = sum / 100 + est->toffset; + + est->ntemp++; + + if (est->callback && ((est->cur_temp >= est->therm_est_hi_limit) || + (est->cur_temp <= est->therm_est_lo_limit))) + est->callback(est->callback_data); + + queue_delayed_work(est->workqueue, &est->therm_est_work, + msecs_to_jiffies(est->polling_period)); +} + +struct therm_estimator *therm_est_register( + struct therm_est_subdevice **devs, + int ndevs, + long toffset, + long polling_period) +{ + int i, j; + long temp; + struct therm_estimator *est; + struct therm_est_subdevice *dev; + + est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL); + if (IS_ERR_OR_NULL(est)) + return ERR_PTR(-ENOMEM); + + est->devs = devs; + est->ndevs = ndevs; + est->toffset = toffset; + est->polling_period = polling_period; + + /* initialize history */ + for (i = 0; i < ndevs; i++) { + dev = est->devs[i]; + + if (dev->get_temp(dev->dev_data, &temp)) { + kfree(est); + return ERR_PTR(-EINVAL); + } + + for (j = 0; j < HIST_LEN; j++) { + dev->hist[j] = temp; + } + } + + est->workqueue = alloc_workqueue("therm_est", + WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1); + INIT_DELAYED_WORK(&est->therm_est_work, therm_est_work_func); + + queue_delayed_work(est->workqueue, + &est->therm_est_work, + msecs_to_jiffies(est->polling_period)); + + return est; +} diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index ebb4afe6c702..3875c21e04fa 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -76,3 +76,13 @@ config MMC_TEST This driver is only of interest to those developing or testing a host driver. Most people should say N here. + +config MMC_BKOPS + bool "Enable background ops" + default n + help + Say Y here to enable background ops in driver. This will result + in issuing of MMC_SWITCH command to write byte 164 of EXT_CSD, + in order to trigger background ops in the MMC device's + firmware, whenever URGENT_BKOPS flag is found to be set in a + read/write command's response. diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 2bd93d7a5170..bd5427d1f9e3 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -59,8 +59,6 @@ MODULE_ALIAS("mmc:block"); #define INAND_CMD38_ARG_SECTRIM1 0x81 #define INAND_CMD38_ARG_SECTRIM2 0x88 -#define MMC_CMD_RETRIES 10 - static DEFINE_MUTEX(block_mutex); /* @@ -945,7 +943,6 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, if (!mmc_card_blockaddr(card)) brq->cmd.arg <<= 9; brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; - brq->cmd.retries = MMC_CMD_RETRIES; brq->data.blksz = 512; brq->stop.opcode = MMC_STOP_TRANSMISSION; brq->stop.arg = 0; diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 440b97d9e44b..86817085aec7 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -211,7 +211,7 @@ static struct mmc_test_parameter mmc_test_parameter[] = { static long mmc_test_set_testcase(struct mmc_test_card *test) { - return 0; + return mmc_test_parameter[0].value; } static long mmc_test_set_clock(struct mmc_test_card *test) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2a288e936a84..35f3df8810e0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -109,11 +109,6 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) cmd->retries--; cmd->error = 0; - if (mrq->data) { - mrq->data->error = 0; - if (mrq->stop) - mrq->stop->error = 0; - } host->ops->request(host, mrq); } else { led_trigger_event(host->led, LED_OFF); @@ -277,13 +272,44 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, { int err = 0; struct mmc_async_req *data = host->areq; + struct mmc_card *card = host->card; + struct timeval before_time, after_time; /* Prepare a new request */ if (areq) mmc_pre_req(host, areq->mrq, !host->areq); if (host->areq) { + if (card->ext_csd.refresh && + (host->areq->mrq->data->flags & MMC_DATA_WRITE)) + do_gettimeofday(&before_time); mmc_wait_for_req_done(host, host->areq->mrq); + if (card->ext_csd.refresh && + (host->areq->mrq->data->flags & MMC_DATA_WRITE)) { + do_gettimeofday(&after_time); + switch (after_time.tv_sec - before_time.tv_sec) { + case 0: + if (after_time.tv_usec - + before_time.tv_usec >= + MMC_SLOW_WRITE_TIME) { + card->ext_csd.last_tv_sec = + after_time.tv_sec; + card->ext_csd.last_bkops_tv_sec = + after_time.tv_sec; + } + break; + case 1: + if (after_time.tv_usec - + before_time.tv_usec < + MMC_SLOW_WRITE_TIME - 1000000) + break; + default: + card->ext_csd.last_tv_sec = + after_time.tv_sec; + card->ext_csd.last_bkops_tv_sec = + after_time.tv_sec; + } + } err = host->areq->err_check(host->card, host->areq); if (err) { mmc_post_req(host, host->areq->mrq, 0); @@ -336,6 +362,7 @@ int mmc_bkops_start(struct mmc_card *card, bool is_synchronous) { int err; unsigned long flags; + struct timeval before_time, after_time; BUG_ON(!card); @@ -343,10 +370,29 @@ int mmc_bkops_start(struct mmc_card *card, bool is_synchronous) return 1; mmc_claim_host(card->host); + if (card->ext_csd.refresh) + do_gettimeofday(&before_time); err = mmc_send_bk_ops_cmd(card, is_synchronous); if (err) pr_err("%s: abort bk ops (%d error)\n", mmc_hostname(card->host), err); + if (card->ext_csd.refresh) { + do_gettimeofday(&after_time); + switch (after_time.tv_sec - before_time.tv_sec) { + case 0: + if (after_time.tv_usec - before_time.tv_usec >= + MMC_SLOW_WRITE_TIME) + card->ext_csd.last_tv_sec = after_time.tv_sec; + break; + case 1: + if (after_time.tv_usec - before_time.tv_usec < + MMC_SLOW_WRITE_TIME - 1000000) + break; + default: + card->ext_csd.last_tv_sec = after_time.tv_sec; + } + card->ext_csd.last_bkops_tv_sec = after_time.tv_sec; + } /* * Incase of asynchronous backops, set card state @@ -365,6 +411,57 @@ int mmc_bkops_start(struct mmc_card *card, bool is_synchronous) } EXPORT_SYMBOL(mmc_bkops_start); +static void mmc_bkops_work(struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, bkops); + mmc_bkops_start(card, true); +} + +static void mmc_refresh_work(struct work_struct *work) +{ + struct mmc_card *card = container_of(work, struct mmc_card, refresh); + char buf[512]; + mmc_gen_cmd(card, buf, 0x44, 0x1, 0x0, 0x1); +} + +void mmc_refresh(unsigned long data) +{ + struct mmc_card *card = (struct mmc_card *) data; + struct timeval cur_time; + __kernel_time_t timeout, timeout1, timeout2; + + if ((!card) || (!card->ext_csd.refresh)) + return; + + INIT_WORK(&card->bkops, (work_func_t) mmc_bkops_work); + INIT_WORK(&card->refresh, (work_func_t) mmc_refresh_work); + + do_gettimeofday(&cur_time); + timeout1 = MMC_REFRESH_INTERVAL - (cur_time.tv_sec - + card->ext_csd.last_tv_sec); + if ((cur_time.tv_sec < card->ext_csd.last_tv_sec) || + (timeout1 <= 0)) { + queue_work(workqueue, &card->refresh); + card->ext_csd.last_tv_sec = cur_time.tv_sec; + card->ext_csd.last_bkops_tv_sec = cur_time.tv_sec; + timeout1 = MMC_REFRESH_INTERVAL; + } + + timeout2 = MMC_BKOPS_INTERVAL - (cur_time.tv_sec - + card->ext_csd.last_bkops_tv_sec); + if ((cur_time.tv_sec < card->ext_csd.last_bkops_tv_sec) || + (timeout2 <= 0)) { + mmc_card_set_need_bkops(card); + queue_work(workqueue, &card->bkops); + timeout2 = MMC_BKOPS_INTERVAL; + } + + timeout = timeout1 < timeout2 ? timeout1 : timeout2; + card->timer.expires = jiffies + timeout*HZ; + add_timer(&card->timer); +} +EXPORT_SYMBOL(mmc_refresh); + /** * mmc_interrupt_hpi - Issue for High priority Interrupt * @card: the MMC card associated with the HPI transfer diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 69fb2275845c..40c93b3dccd7 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -96,6 +96,7 @@ static int mmc_decode_cid(struct mmc_card *card) card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_rev = UNSTUFF_BITS(resp, 48, 8); card->cid.serial = UNSTUFF_BITS(resp, 16, 32); card->cid.month = UNSTUFF_BITS(resp, 12, 4); card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; @@ -425,6 +426,11 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) /* Check whether the eMMC card supports background ops */ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) card->ext_csd.bk_ops = 1; + + /* Check whether the eMMC card needs proactive refresh */ + if ((card->cid.manfid == 0x90) && ((card->cid.prod_rev == 0x73) + || (card->cid.prod_rev == 0x7b))) + card->ext_csd.refresh = 1; } if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) @@ -516,6 +522,7 @@ MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name); MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid); +MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prod_rev); MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial); MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", card->ext_csd.enhanced_area_offset); @@ -532,6 +539,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_manfid.attr, &dev_attr_name.attr, &dev_attr_oemid.attr, + &dev_attr_prv.attr, &dev_attr_serial.attr, &dev_attr_enhanced_area_offset.attr, &dev_attr_enhanced_area_size.attr, @@ -672,6 +680,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (err) goto free_card; + if (card->ext_csd.refresh) { + init_timer(&card->timer); + card->timer.data = (unsigned long) card; + card->timer.function = mmc_refresh; + card->timer.expires = MMC_BKOPS_INTERVAL < + MMC_REFRESH_INTERVAL ? MMC_BKOPS_INTERVAL : + MMC_REFRESH_INTERVAL; + card->timer.expires *= HZ; + card->timer.expires += jiffies; + add_timer(&card->timer); + } /* If doing byte addressing, check if required to do sector * addressing. Handle the case of <2GB cards needing sector * addressing. See section 8.1 JEDEC Standard JED84-A441; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 330b968393d6..c85c58aca3e2 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -588,7 +588,7 @@ int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous) cmd.opcode = MMC_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_BKOPS_EN << 16) | + (EXT_CSD_BKOPS_START << 16) | (1 << 8) | EXT_CSD_CMD_SET_NORMAL; if (is_synchronous) @@ -617,3 +617,66 @@ int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous) return 0; } + +int mmc_gen_cmd(struct mmc_card *card, void *buf, u8 index, u8 arg1, u8 arg2, u8 mode) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct mmc_command stop; + struct scatterlist sg; + void *data_buf; + + mmc_set_blocklen(card, 512); + + data_buf = kmalloc(512, GFP_KERNEL); + if (data_buf == NULL) + return -ENOMEM; + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + memset(&stop, 0, sizeof(struct mmc_command)); + + mrq.cmd = &cmd; + mrq.data = &data; + mrq.stop = &stop; + + cmd.opcode = MMC_GEN_CMD; + cmd.arg = (arg2 << 16) | + (arg1 << 8) | + (index << 1) | + mode; + + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 512; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + + sg_init_one(&sg, data_buf, 512); + + mmc_set_data_timeout(&data, card); + + mmc_claim_host(card->host); + mmc_wait_for_req(card->host, &mrq); + mmc_release_host(card->host); + + memcpy(buf, data_buf, 512); + kfree(data_buf); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + if (stop.error) + return stop.error; + + return 0; +} diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index d8f157dee147..a453531b46b6 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -29,6 +29,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous); +int mmc_gen_cmd(struct mmc_card *card, void *buf, u8 index, u8 arg1, u8 arg2, u8 mode); #endif diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index d6ed03d4f22a..609fd6391d1f 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -112,9 +112,13 @@ struct tegra_sdhci_host { unsigned int vddio_max_uv; /* max clk supported by the platform */ unsigned int max_clk_limit; + /* max ddr clk supported by the platform */ + unsigned int ddr_clk_limit; struct tegra_io_dpd *dpd; bool card_present; bool is_rail_enabled; + struct clk *emc_clk; + unsigned int emc_max_clk; }; static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) @@ -366,14 +370,30 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci, struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci); struct tegra_sdhci_host *tegra_host = pltfm_host->priv; unsigned int clk_rate; + unsigned int emc_clk; + /* + * In SDR50 mode, run the sdmmc controller at freq greater than + * 104MHz to ensure the core voltage is at 1.2V. If the core voltage + * is below 1.2V, CRC errors would occur during data transfers. + */ if (sdhci->mmc->card && - mmc_card_ddr_mode(sdhci->mmc->card)) { + (mmc_card_ddr_mode(sdhci->mmc->card) || + (sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50))) { /* * In ddr mode, tegra sdmmc controller clock frequency * should be double the card clock frequency. */ - clk_rate = clock * 2; + if (tegra_host->ddr_clk_limit) { + clk_rate = tegra_host->ddr_clk_limit * 2; + if (tegra_host->emc_clk) { + emc_clk = clk_get_rate(tegra_host->emc_clk); + if (emc_clk == tegra_host->emc_max_clk) + clk_rate = clock * 2; + } + } else { + clk_rate = clock * 2; + } } else { if (clock <= tegra_sdhost_min_freq) clk_rate = tegra_sdhost_min_freq; @@ -381,15 +401,6 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci, clk_rate = tegra_sdhost_std_freq; else clk_rate = clock; - - /* - * In SDR50 mode, run the sdmmc controller at 208MHz to ensure - * the core voltage is at 1.2V. If the core voltage is below 1.2V, CRC - * errors would occur during data transfers. - */ - if ((sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50) && - (clk_rate == tegra_sdhost_std_freq)) - clk_rate <<= 1; } if (tegra_host->max_clk_limit && @@ -534,11 +545,6 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci, u16 clk, ctrl; unsigned int val; - /* Switch OFF the card clock to prevent glitches on the clock line */ - clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL); - clk &= ~SDHCI_CLOCK_CARD_EN; - sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL); - ctrl = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2); if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) { ctrl |= SDHCI_CTRL_VDD_180; @@ -548,6 +554,17 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci, if (ctrl & SDHCI_CTRL_VDD_180) ctrl &= ~SDHCI_CTRL_VDD_180; } + + /* Check if the slot can support the required voltage */ + if (min_uV > tegra_host->vddio_max_uv) + return 0; + + /* Switch OFF the card clock to prevent glitches on the clock line */ + clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL); + + /* Set/clear the 1.8V signalling */ sdhci_writew(sdhci, ctrl, SDHCI_HOST_CONTROL2); /* Switch the I/O rail voltage */ @@ -1075,10 +1092,23 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) rc = clk_enable(clk); if (rc != 0) goto err_clk_put; + + if (!strcmp(dev_name(mmc_dev(host->mmc)), "sdhci-tegra.3")) { + tegra_host->emc_clk = clk_get(mmc_dev(host->mmc), "emc"); + if (IS_ERR(tegra_host->emc_clk)) { + dev_err(mmc_dev(host->mmc), "clk err\n"); + rc = PTR_ERR(tegra_host->emc_clk); + goto err_clk_put; + } + tegra_host->emc_max_clk = + clk_round_rate(tegra_host->emc_clk, ULONG_MAX); + } + pltfm_host->clk = clk; pltfm_host->priv = tegra_host; tegra_host->clk_enabled = true; tegra_host->max_clk_limit = plat->max_clk_limit; + tegra_host->ddr_clk_limit = plat->ddr_clk_limit; tegra_host->instance = pdev->id; tegra_host->dpd = tegra_io_dpd_get(mmc_dev(host->mmc)); @@ -1098,6 +1128,11 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_NONREMOVABLE; } host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; + +#ifdef CONFIG_MMC_BKOPS + host->mmc->caps |= MMC_CAP_BKOPS; +#endif + #ifdef CONFIG_MMC_EMBEDDED_SDIO /* Do not turn OFF embedded sdio cards as it support Wake on Wireless */ if (plat->mmc_data.embedded_sdio) @@ -1120,6 +1155,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) return 0; err_add_host: + clk_put(tegra_host->emc_clk); clk_disable(pltfm_host->clk); err_clk_put: clk_put(pltfm_host->clk); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 495586924d60..22814660bc34 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2330,20 +2330,34 @@ out: int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) { int ret = 0; + bool has_tuning_timer; struct mmc_host *mmc = host->mmc; sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ - if (host->version >= SDHCI_SPEC_300 && host->tuning_count && - host->tuning_mode == SDHCI_TUNING_MODE_1) { + has_tuning_timer = host->version >= SDHCI_SPEC_300 && + host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1; + if (has_tuning_timer) { host->flags &= ~SDHCI_NEEDS_RETUNING; mod_timer(&host->tuning_timer, jiffies + host->tuning_count * HZ); } - if (mmc->card) + if (mmc->card) { ret = mmc_suspend_host(host->mmc); + if (ret) { + if (has_tuning_timer) { + host->flags |= SDHCI_NEEDS_RETUNING; + mod_timer(&host->tuning_timer, jiffies + + host->tuning_count * HZ); + } + + sdhci_enable_card_detection(host); + + return ret; + } + } if (mmc->pm_flags & MMC_PM_KEEP_POWER) host->card_int_set = sdhci_readl(host, SDHCI_INT_ENABLE) & diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 34f83a4c9561..dc44c5c33e01 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -277,13 +277,8 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) DMA_TO_DEVICE); if (ret > 0) { host->dma_active = true; -<<<<<<< HEAD - desc = chan->device->device_prep_slave_sg(chan, sg, ret, - DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -======= desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers } if (desc) { diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index d298e7bd26d2..def9c54f73f5 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -157,13 +157,8 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE); if (ret > 0) -<<<<<<< HEAD - desc = chan->device->device_prep_slave_sg(chan, sg, ret, - DMA_TO_DEVICE, DMA_CTRL_ACK); -======= desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, DMA_CTRL_ACK); ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers if (desc) { cookie = dmaengine_submit(desc); diff --git a/drivers/mtd/maps/tegra_nor.c b/drivers/mtd/maps/tegra_nor.c index b455fd5e1c00..505a2591f884 100644 --- a/drivers/mtd/maps/tegra_nor.c +++ b/drivers/mtd/maps/tegra_nor.c @@ -217,6 +217,8 @@ static void tegra_flash_dma(struct map_info *map, bytes_remaining += (word32_count << 2); break; } + dma_sync_single_for_cpu(c->dev, c->dma_phys_buffer, + (current_transfer << 2), DMA_FROM_DEVICE); memcpy((char *)(copy_to), (char *)(c->dma_virt_buffer), (current_transfer << 2)); @@ -279,7 +281,7 @@ static int tegra_snor_controller_init(struct tegra_nor_info *info) info->timing0_default = chip_parm->timing_default.timing0; info->timing0_read = chip_parm->timing_read.timing0; info->timing1_default = chip_parm->timing_default.timing1; - info->timing1_read = chip_parm->timing_read.timing0; + info->timing1_read = chip_parm->timing_read.timing1; snor_tegra_writel(info, info->timing1_default, TEGRA_SNOR_TIMING1_REG); snor_tegra_writel(info, info->timing0_default, TEGRA_SNOR_TIMING0_REG); diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index aa826d90f00d..c31b1185f492 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -469,6 +469,15 @@ static const struct driver_info wwan_info = { .manage_power = cdc_manage_power, }; +static const struct driver_info rmnet_info = { + .description = "Mobile Broadband Network Device", + .flags = FLAG_RMNET, + .bind = usbnet_cdc_bind, + .unbind = usbnet_cdc_unbind, + .status = usbnet_cdc_status, + .manage_power = cdc_manage_power, +}; + /*-------------------------------------------------------------------------*/ #define HUAWEI_VENDOR_ID 0x12D1 @@ -575,17 +584,17 @@ static const struct usb_device_id products [] = { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, USB_DEVICE(0x1983, 0x0310), - .driver_info = (unsigned long)&wwan_info, + .driver_info = (unsigned long)&rmnet_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, USB_DEVICE(0x1983, 0x0321), - .driver_info = (unsigned long)&wwan_info, + .driver_info = (unsigned long)&rmnet_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, USB_DEVICE(0x1983, 0x0327), /* 5AE */ - .driver_info = (unsigned long)&wwan_info, + .driver_info = (unsigned long)&rmnet_info, }, /* Tango module */ @@ -593,7 +602,7 @@ static const struct usb_device_id products [] = { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, USB_DEVICE(0x0489,0xE03A), - .driver_info = (unsigned long)&wwan_info, + .driver_info = (unsigned long)&rmnet_info, }, /* * WHITELIST!!! diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 7b4687974987..370846911800 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1,7 +1,7 @@ /*************************************************************************** * * Copyright (C) 2007-2008 SMSC - * + * Copyright (C) 2012 NVIDIA Corporation. * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -63,6 +63,11 @@ static int turbo_mode = true; module_param(turbo_mode, bool, 0644); MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); +static u8 mac_addr[6] = {0}; +static bool smsc_mac_addr_set; +module_param_array_named(mac_addr, mac_addr, byte, NULL, 0); +MODULE_PARM_DESC(mac_addr, "SMSC command line MAC address"); + static int smsc95xx_read_reg(struct usbnet *dev, u32 index, u32 *data) { u32 *buf = kmalloc(4, GFP_KERNEL); @@ -612,6 +617,15 @@ static void smsc95xx_init_mac_address(struct usbnet *dev) } } + /* try reading mac address from command line */ + if (is_valid_ether_addr(mac_addr) && !smsc_mac_addr_set) { + memcpy(dev->net->dev_addr, mac_addr, sizeof(mac_addr)); + smsc_mac_addr_set = true; + netif_dbg(dev, ifup, dev->net, + "MAC address read from command line"); + return; + } + /* no eeprom, or eeprom values are invalid. generate random MAC */ random_ether_addr(dev->net->dev_addr); netif_dbg(dev, ifup, dev->net, "MAC address set to random_ether_addr\n"); diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig index 427cd9911569..ef9ce5a00538 100644 --- a/drivers/net/wireless/bcmdhd/Kconfig +++ b/drivers/net/wireless/bcmdhd/Kconfig @@ -24,11 +24,6 @@ config BCMDHD_NVRAM_DIR ---help--- Path to the calibration file. -choice - prompt "Select API" - depends on BCMDHD - default BCMDHD_NOAPI - config BCMDHD_WEXT bool "Enable WEXT support" select WIRELESS_EXT @@ -36,6 +31,27 @@ config BCMDHD_WEXT help Enables WEXT support +config DHD_USE_STATIC_BUF + bool "Enable memory preallocation" + depends on BCMDHD + default n + ---help--- + Use memory preallocated in platform + +config DHD_USE_SCHED_SCAN + bool "Use CFG80211 sched scan" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use CFG80211 sched scan + +config DHD_ENABLE_P2P + bool "Enable Wifi Direct" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use Enable Wifi Direct + config BCMDHD_CFG80211 bool "Enable CFG80211 support" depends on CFG80211 @@ -47,8 +63,6 @@ config BCMDHD_NOAPI help No wireless API is needed -endchoice - config BCMDHD_WIFI_CONTROL_FUNC bool "Use bcmdhd_wlan device" depends on BCMDHD diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 85e93f7a56c3..2851388cf194 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -8,6 +8,7 @@ DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ -DNEW_COMPAT_WIRELESS -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ -DKEEP_ALIVE -DPKT_FILTER_SUPPORT \ -DEMBEDDED_PLATFORM \ + -DSET_RANDOM_MAC_SOFTAP -DWL_CFG80211_STA_EVENT \ -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include ifeq ($(CONFIG_BCMDHD_WIFI_CONTROL_FUNC),y) @@ -37,21 +38,26 @@ endif DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ dhd_linux_sched.o bcmwifi.o dhd_sdio.o bcmevent.o dhd_bta.o hndpmu.o \ bcmsdh.o dhd_cdc.o bcmsdh_linux.o dhd_common.o linux_osl.o \ - bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o + bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o dhd_cfg80211.o obj-$(CONFIG_BCMDHD) += bcmdhd.o bcmdhd-objs += $(DHDOFILES) ifeq ($(CONFIG_BCMDHD_WEXT),y) bcmdhd-objs += wl_iw.o -DHDCFLAGS += -DSOFTAP +DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT endif -ifeq ($(CONFIG_BCMDHD_CFG80211),y) -bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o dhd_linux_mon.o +ifneq ($(CONFIG_CFG80211),) +bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o DHDCFLAGS += -DWL_CFG80211 endif - +ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) +DHDCFLAGS += -DWL_SCHED_SCAN +endif +ifneq ($(CONFIG_DHD_ENABLE_P2P),) +DHDCFLAGS += -DWL_ENABLE_P2P_IF +endif EXTRA_CFLAGS = $(DHDCFLAGS) ifeq ($(CONFIG_BCMDHD),m) EXTRA_LDFLAGS += --strip-debug diff --git a/drivers/net/wireless/bcmdhd/aiutils.c b/drivers/net/wireless/bcmdhd/aiutils.c index 059df8907928..5ca0993c9333 100644 --- a/drivers/net/wireless/bcmdhd/aiutils.c +++ b/drivers/net/wireless/bcmdhd/aiutils.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: aiutils.c,v 1.26.2.1 2010-03-09 18:41:21 Exp $ + * $Id: aiutils.c,v 1.26.2.1 2010-03-09 18:41:21 $ */ diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c index 24581ddd353c..6a25d9a5a57f 100644 --- a/drivers/net/wireless/bcmdhd/bcmevent.c +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: bcmevent.c,v 1.8.2.7 2011-02-01 06:23:39 Exp $ + * $Id: bcmevent.c,v 1.8.2.7 2011-02-01 06:23:39 $ */ #include <typedefs.h> @@ -29,7 +29,7 @@ #include <proto/bcmeth.h> #include <proto/bcmevent.h> -#if WLC_E_LAST != 85 +#if WLC_E_LAST != 87 #error "You need to add an entry to bcmevent_names[] for the new event" #endif @@ -117,8 +117,10 @@ const bcmevent_name_t bcmevent_names[] = { { WLC_E_PFN_SCAN_NONE, "PFN_SCAN_NONE" }, { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" }, #ifdef SOFTAP - { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" } + { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" }, #endif + { WLC_E_ASSOC_REQ_IE, "ASSOC_REQ_IE" }, + { WLC_E_ASSOC_RESP_IE, "ASSOC_RESP_IE" } }; diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c index 918c8e648f13..f67b13a03a1c 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh.c 275784 2011-08-04 22:41:49Z $ + * $Id: bcmsdh.c 300445 2011-12-03 05:37:20Z $ */ /** diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c index d467e4b5630e..237af4ae8417 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_linux.c,v 1.72.6.5 2010-12-23 01:13:15 Exp $ + * $Id: bcmsdh_linux.c 312788 2012-02-03 23:06:32Z $ */ /** @@ -148,17 +148,6 @@ static int __devexit bcmsdh_remove_bcmdhd(struct device *dev); #endif /* BCMLXSDMMC */ #ifndef BCMLXSDMMC -static struct device_driver bcmsdh_driver = { - .name = "pxa2xx-mci", - .bus = &platform_bus_type, - .probe = bcmsdh_probe_bcmdhd, - .remove = bcmsdh_remove_bcmdhd, - .suspend = NULL, - .resume = NULL, - }; -#endif /* BCMLXSDMMC */ - -#ifndef BCMLXSDMMC static #endif /* BCMLXSDMMC */ int bcmsdh_probe_bcmdhd(struct device *dev) @@ -277,6 +266,7 @@ int bcmsdh_remove_bcmdhd(struct device *dev) sdhc = sdhcinfo; drvinfo.detach(sdhc->ch); bcmsdh_detach(sdhc->osh, sdhc->sdh); + /* find the SDIO Host Controller state for this pdev and take it out from the list */ for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { if (sdhc->dev == (void *)dev) { @@ -293,7 +283,6 @@ int bcmsdh_remove_bcmdhd(struct device *dev) return 0; } - /* release SDIO Host Controller info */ osh = sdhc->osh; MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); @@ -534,13 +523,8 @@ bcmsdh_register(bcmsdh_driver_t *driver) drvinfo = *driver; #if defined(BCMPLATFORM_BUS) -#if defined(BCMLXSDMMC) SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n")); error = sdio_function_init(); -#else - SDLX_MSG(("Intel PXA270 SDIO Driver\n")); - error = driver_register(&bcmsdh_driver); -#endif /* defined(BCMLXSDMMC) */ return error; #endif /* defined(BCMPLATFORM_BUS) */ @@ -568,14 +552,12 @@ bcmsdh_unregister(void) if (bcmsdh_pci_driver.node.next) #endif -#if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) - driver_unregister(&bcmsdh_driver); -#endif #if defined(BCMLXSDMMC) sdio_function_cleanup(); #endif /* BCMLXSDMMC */ + #if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) - pci_unregister_driver(&bcmsdh_pci_driver); + pci_unregister_driver(&bcmsdh_pci_driver); #endif /* BCMPLATFORM_BUS */ } @@ -614,13 +596,6 @@ static irqreturn_t wlan_oob_irq(int irq, void *dev_id) return IRQ_HANDLED; } -void *bcmsdh_get_drvdata(void) -{ - if (!sdhcinfo) - return NULL; - return dev_get_drvdata(sdhcinfo->dev); -} - int bcmsdh_register_oob_intr(void * dhdp) { int error = 0; @@ -674,6 +649,16 @@ void bcmsdh_unregister_oob_intr(void) } } #endif /* defined(OOB_INTR_ONLY) */ + +#if defined(BCMLXSDMMC) +void *bcmsdh_get_drvdata(void) +{ + if (!sdhcinfo) + return NULL; + return dev_get_drvdata(sdhcinfo->dev); +} +#endif + /* Module parameters specific to each host-controller driver */ extern uint sd_msglevel; /* Debug message level */ @@ -697,6 +682,10 @@ module_param(sd_hiok, uint, 0); extern uint sd_f2_blocksize; module_param(sd_f2_blocksize, int, 0); +#ifdef BCMSDIOH_STD +extern int sd_uhsimode; +module_param(sd_uhsimode, int, 0); +#endif #ifdef BCMSDH_MODULE EXPORT_SYMBOL(bcmsdh_attach); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index 7499a1ec55fd..e67eeca1f99c 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc.c 282820 2011-09-09 15:40:35Z $ + * $Id: bcmsdh_sdmmc.c 314904 2012-02-14 21:36:04Z $ */ #include <typedefs.h> @@ -35,6 +35,7 @@ #include <sdiovar.h> /* ioctl/iovars */ #include <linux/mmc/core.h> +#include <linux/mmc/card.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_ids.h> @@ -148,6 +149,7 @@ sdioh_attach(osl_t *osh, void *bar0, uint irq) sd->sd_blockmode = TRUE; sd->use_client_ints = TRUE; sd->client_block_size[0] = 64; + sd->use_rxchain = FALSE; gInstance->sd = sd; @@ -459,6 +461,7 @@ sdioh_iovar_op(sdioh_info_t *si, const char *name, bcopy(params, &int_val, sizeof(int_val)); bool_val = (int_val != 0) ? TRUE : FALSE; + BCM_REFERENCE(bool_val); actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); switch (actionid) { @@ -522,7 +525,7 @@ sdioh_iovar_op(sdioh_info_t *si, const char *name, } case IOV_GVAL(IOV_RXCHAIN): - int_val = FALSE; + int_val = (int32)si->use_rxchain; bcopy(&int_val, arg, val_size); break; @@ -689,14 +692,9 @@ sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) uint8 data; if (enable) - data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; /* enable hw oob interrupt */ + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI; else - data = SDIO_SEPINT_ACT_HI; /* disable hw oob interrupt */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) - /* Needed for Android Linux Kernel 2.6.35 */ - data |= SDIO_SEPINT_ACT_HI; /* Active HIGH */ -#endif + data = SDIO_SEPINT_ACT_HI; status = sdioh_request_byte(sd, SDIOH_WRITE, 0, SDIOD_CCCR_BRCM_SEPINT, &data); return status; @@ -912,8 +910,12 @@ sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, bool fifo = (fix_inc == SDIOH_DATA_FIX); uint32 SGCount = 0; int err_ret = 0; - - void *pnext; + void *pnext, *pprev; + uint ttl_len, dma_len, lft_len, xfred_len, pkt_len; + uint blk_num; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; sd_trace(("%s: Enter\n", __FUNCTION__)); @@ -921,66 +923,148 @@ sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); - /* Claim host controller */ - sdio_claim_host(gInstance->func[func]); - for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { - uint pkt_len = PKTLEN(sd->osh, pnext); - pkt_len += 3; - pkt_len &= 0xFFFFFFFC; + ttl_len = xfred_len = 0; + /* at least 4 bytes alignment of skb buff is guaranteed */ + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) + ttl_len += PKTLEN(sd->osh, pnext); -#ifdef CONFIG_MMC_MSM7X00A - if ((pkt_len % 64) == 32) { - sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); - pkt_len += 32; - } -#endif /* CONFIG_MMC_MSM7X00A */ - /* Make sure the packet is aligned properly. If it isn't, then this - * is the fault of sdioh_request_buffer() which is supposed to give - * us something we can work with. - */ - ASSERT(((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) == 0); - - if ((write) && (!fifo)) { - err_ret = sdio_memcpy_toio(gInstance->func[func], addr, - ((uint8*)PKTDATA(sd->osh, pnext)), - pkt_len); - } else if (write) { - err_ret = sdio_memcpy_toio(gInstance->func[func], addr, - ((uint8*)PKTDATA(sd->osh, pnext)), - pkt_len); - } else if (fifo) { - err_ret = sdio_readsb(gInstance->func[func], - ((uint8*)PKTDATA(sd->osh, pnext)), - addr, - pkt_len); - } else { - err_ret = sdio_memcpy_fromio(gInstance->func[func], - ((uint8*)PKTDATA(sd->osh, pnext)), - addr, + if (!sd->use_rxchain || ttl_len <= sd->client_block_size[func]) { + blk_num = 0; + dma_len = 0; + } else { + blk_num = ttl_len / sd->client_block_size[func]; + dma_len = blk_num * sd->client_block_size[func]; + } + lft_len = ttl_len - dma_len; + + sd_trace(("%s: %s %dB to func%d:%08x, %d blks with DMA, %dB leftover\n", + __FUNCTION__, write ? "W" : "R", + ttl_len, func, addr, blk_num, lft_len)); + + if (0 != dma_len) { + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + /* Set up DMA descriptors */ + pprev = pkt; + for (pnext = pkt; + pnext && dma_len; + pnext = PKTNEXT(sd->osh, pnext)) { + pkt_len = PKTLEN(sd->osh, pnext); + + if (dma_len > pkt_len) + dma_len -= pkt_len; + else { + pkt_len = xfred_len = dma_len; + dma_len = 0; + pkt = pnext; + } + + sg_set_buf(&sd->sg_list[SGCount++], + (uint8*)PKTDATA(sd->osh, pnext), pkt_len); - } - if (err_ret) { - sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", - __FUNCTION__, - (write) ? "TX" : "RX", - pnext, SGCount, addr, pkt_len, err_ret)); - } else { - sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", - __FUNCTION__, - (write) ? "TX" : "RX", - pnext, SGCount, addr, pkt_len)); + if (SGCount >= SDIOH_SDMMC_MAX_SG_ENTRIES) { + sd_err(("%s: sg list entries exceed limit\n", + __FUNCTION__)); + return (SDIOH_API_RC_FAIL); + } } - if (!fifo) { - addr += pkt_len; - } - SGCount ++; + mmc_dat.sg = sd->sg_list; + mmc_dat.sg_len = SGCount; + mmc_dat.blksz = sd->client_block_size[func]; + mmc_dat.blocks = blk_num; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ + mmc_cmd.arg = write ? 1<<31 : 0; + mmc_cmd.arg |= (func & 0x7) << 28; + mmc_cmd.arg |= 1<<27; + mmc_cmd.arg |= fifo ? 0 : 1<<26; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; + mmc_cmd.arg |= blk_num & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + + sdio_claim_host(gInstance->func[func]); + mmc_set_data_timeout(&mmc_dat, gInstance->func[func]->card); + mmc_wait_for_req(gInstance->func[func]->card->host, &mmc_req); + sdio_release_host(gInstance->func[func]); + err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error; + if (0 != err_ret) { + sd_err(("%s:CMD53 %s failed with code %d\n", + __FUNCTION__, + write ? "write" : "read", + err_ret)); + sd_err(("%s:Disabling rxchain and fire it with PIO\n", + __FUNCTION__)); + sd->use_rxchain = FALSE; + pkt = pprev; + lft_len = ttl_len; + } else if (!fifo) { + addr = addr + ttl_len - lft_len - dma_len; + } } - /* Release host controller */ - sdio_release_host(gInstance->func[func]); + /* PIO mode */ + if (0 != lft_len) { + /* Claim host controller */ + sdio_claim_host(gInstance->func[func]); + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { + uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext) + + xfred_len; + pkt_len = PKTLEN(sd->osh, pnext); + if (0 != xfred_len) { + pkt_len -= xfred_len; + xfred_len = 0; + } + pkt_len = (pkt_len + 3) & 0xFFFFFFFC; +#ifdef CONFIG_MMC_MSM7X00A + if ((pkt_len % 64) == 32) { + sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); + pkt_len += 32; + } +#endif /* CONFIG_MMC_MSM7X00A */ + + if ((write) && (!fifo)) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (write) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (fifo) + err_ret = sdio_readsb( + gInstance->func[func], + buf, addr, pkt_len); + else + err_ret = sdio_memcpy_fromio( + gInstance->func[func], + buf, addr, pkt_len); + + if (err_ret) + sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len, err_ret)); + else + sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len)); + + if (!fifo) + addr += pkt_len; + SGCount ++; + } + sdio_release_host(gInstance->func[func]); + } sd_trace(("%s: Exit\n", __FUNCTION__)); return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); @@ -1013,11 +1097,11 @@ sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, u if (pkt == NULL) { sd_data(("%s: Creating new %s Packet, len=%d\n", __FUNCTION__, write ? "TX" : "RX", buflen_u)); -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) { #else if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) { -#endif /* DHD_USE_STATIC_BUF */ +#endif /* CONFIG_DHD_USE_STATIC_BUF */ sd_err(("%s: PKTGET failed: len %d\n", __FUNCTION__, buflen_u)); return SDIOH_API_RC_FAIL; @@ -1034,11 +1118,11 @@ sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, u if (!write) { bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u); } -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); #else PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); -#endif /* DHD_USE_STATIC_BUF */ +#endif /* CONFIG_DHD_USE_STATIC_BUF */ } else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) { /* Case 2: We have a packet, but it is unaligned. */ @@ -1047,11 +1131,11 @@ sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, u sd_data(("%s: Creating aligned %s Packet, len=%d\n", __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt))); -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { #else if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { -#endif /* DHD_USE_STATIC_BUF */ +#endif /* CONFIG_DHD_USE_STATIC_BUF */ sd_err(("%s: PKTGET failed: len %d\n", __FUNCTION__, PKTLEN(sd->osh, pkt))); return SDIOH_API_RC_FAIL; @@ -1072,11 +1156,11 @@ sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, u PKTDATA(sd->osh, pkt), PKTLEN(sd->osh, mypkt)); } -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); #else PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); -#endif /* DHD_USE_STATIC_BUF */ +#endif /* CONFIG_DHD_USE_STATIC_BUF */ } else { /* case 3: We have a packet and it is aligned. */ sd_data(("%s: Aligned %s Packet, direct DMA\n", __FUNCTION__, write ? "Tx" : "Rx")); @@ -1190,6 +1274,7 @@ static void IRQHandlerF2(struct sdio_func *func) sd = gInstance->sd; ASSERT(sd != NULL); + BCM_REFERENCE(sd); } #endif /* !defined(OOB_INTR_ONLY) */ @@ -1241,8 +1326,10 @@ sdioh_start(sdioh_info_t *si, int stage) 2.6.27. The implementation prior to that is buggy, and needs broadcom's patch for it */ - if ((ret = sdio_reset_comm(gInstance->func[0]->card))) + if ((ret = sdio_reset_comm(gInstance->func[0]->card))) { sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); + return ret; + } else { sd->num_funcs = 2; sd->sd_blockmode = TRUE; diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index 1a370862b334..656953939b71 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc_linux.c,v 1.8.6.2 2011-02-01 18:38:36 Exp $ + * $Id: bcmsdh_sdmmc_linux.c 312783 2012-02-03 22:53:56Z $ */ #include <typedefs.h> @@ -55,13 +55,22 @@ #if !defined(SDIO_DEVICE_ID_BROADCOM_4330) #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4334) +#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4324) +#define SDIO_DEVICE_ID_BROADCOM_4324 0x4324 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_43239) +#define SDIO_DEVICE_ID_BROADCOM_43239 43239 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */ #include <bcmsdh_sdmmc.h> #include <dhd_dbg.h> #ifdef WL_CFG80211 -extern void wl_cfg80211_set_sdio_func(void *func); +extern void wl_cfg80211_set_parent_dev(void *dev); #endif extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); @@ -118,7 +127,7 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, if (func->num == 2) { #ifdef WL_CFG80211 - wl_cfg80211_set_sdio_func(func); + wl_cfg80211_set_parent_dev(&func->dev); #endif sd_trace(("F2 found, calling bcmsdh_probe_bcmdhd...\n")); ret = bcmsdh_probe_bcmdhd(&func->dev); @@ -153,12 +162,15 @@ static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) static int bcmsdh_sdmmc_suspend(struct device *pdev) { struct sdio_func *func = dev_to_sdio_func(pdev); @@ -167,12 +179,30 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) if (func->num != 2) return 0; + + sd_trace(("%s Enter\n", __FUNCTION__)); + if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) return -EBUSY; + + + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__)); + return -EINVAL; + } + + /* keep power while host suspended */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); + return ret; + } + #if defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(0); -#endif - smp_mb(); +#endif /* defined(OOB_INTR_ONLY) */ sdio_flags = sdio_get_host_pm_caps(func); @@ -191,6 +221,7 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) } dhd_mmc_suspend = TRUE; + smp_mb(); out: return ret; @@ -198,15 +229,16 @@ out: static int bcmsdh_sdmmc_resume(struct device *pdev) { +#if defined(OOB_INTR_ONLY) struct sdio_func *func = dev_to_sdio_func(pdev); - - if (func->num != 2) - return 0; +#endif + sd_trace(("%s Enter\n", __FUNCTION__)); dhd_mmc_suspend = FALSE; #if defined(OOB_INTR_ONLY) - if (dhd_os_check_if_up(bcmsdh_get_drvdata())) + if ((func->num == 2) && dhd_os_check_if_up(bcmsdh_get_drvdata())) bcmsdh_oob_intr_set(1); -#endif +#endif /* (OOB_INTR_ONLY) */ + smp_mb(); return 0; } @@ -215,18 +247,18 @@ static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = { .suspend = bcmsdh_sdmmc_suspend, .resume = bcmsdh_sdmmc_resume, }; -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ static struct sdio_driver bcmsdh_sdmmc_driver = { .probe = bcmsdh_sdmmc_probe, .remove = bcmsdh_sdmmc_remove, .name = "bcmsdh_sdmmc", .id_table = bcmsdh_sdmmc_ids, -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) .drv = { .pm = &bcmsdh_sdmmc_pm_ops, }, -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ }; struct sdos_info { diff --git a/drivers/net/wireless/bcmdhd/bcmutils.c b/drivers/net/wireless/bcmdhd/bcmutils.c index fbdd7cd2d19b..6b578e653648 100644 --- a/drivers/net/wireless/bcmdhd/bcmutils.c +++ b/drivers/net/wireless/bcmdhd/bcmutils.c @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: bcmutils.c,v 1.277.2.18 2011-01-26 02:32:08 Exp $ + * $Id: bcmutils.c,v 1.277.2.18 2011-01-26 02:32:08 $ */ #include <typedefs.h> @@ -987,7 +987,6 @@ pktsetprio(void *pkt, bool update_vtag) return (rc | priority); } -#ifndef BCM_BOOTLOADER static char bcm_undeferrstr[32]; static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE; @@ -1009,7 +1008,6 @@ bcmerrorstr(int bcmerror) return bcmerrorstrtable[-bcmerror]; } -#endif /* !BCM_BOOTLOADER */ diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 1b7242d48280..5ff5c218ddcf 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd.h 290844 2011-10-20 08:54:39Z $ + * $Id: dhd.h 328934 2012-04-23 05:15:42Z $ */ /**************** @@ -76,13 +76,24 @@ enum dhd_bus_state { /* Firmware requested operation mode */ #define STA_MASK 0x0001 -#define HOSTAPD_MASK 0x0002 +#define HOSTAPD_MASK 0x0002 #define WFD_MASK 0x0004 -#define SOFTAP_FW_MASK 0x0008 +#define SOFTAP_FW_MASK 0x0008 +#define P2P_GO_ENABLED 0x0010 +#define P2P_GC_ENABLED 0x0020 +#define CONCURENT_MASK 0x00F0 + +#define MANUFACTRING_FW "WLTEST" /* max sequential rxcntl timeouts to set HANG event */ #define MAX_CNTL_TIMEOUT 2 +#define DHD_SCAN_ACTIVE_TIME 40 /* ms : Embedded default Active setting from DHD Driver */ +#define DHD_SCAN_PASSIVE_TIME 130 /* ms: Embedded default Passive setting from DHD Driver */ + +#define DHD_BEACON_TIMEOUT_NORMAL 4 +#define DHD_BEACON_TIMEOUT_HIGH 10 + enum dhd_bus_wake_state { WAKE_LOCK_OFF, WAKE_LOCK_PRIV, @@ -115,8 +126,7 @@ typedef enum { DHD_IF_DELETING } dhd_if_state_t; - -#if defined(DHD_USE_STATIC_BUF) +#if defined(CONFIG_DHD_USE_STATIC_BUF) uint8* dhd_os_prealloc(void *osh, int section, uint size); void dhd_os_prefree(void *osh, void *addr, uint size); @@ -128,7 +138,7 @@ void dhd_os_prefree(void *osh, void *addr, uint size); #define DHD_OS_PREALLOC(osh, section, size) MALLOC(osh, size) #define DHD_OS_PREFREE(osh, addr, size) MFREE(osh, addr, size) -#endif /* defined(DHD_USE_STATIC_BUF) */ +#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ /* Packet alignment for most efficient SDIO (can change based on platform) */ #ifndef DHD_SDALIGN @@ -202,9 +212,11 @@ typedef struct dhd_pub { char eventmask[WL_EVENTING_MASK_LEN]; int op_mode; /* STA, HostAPD, WFD, SoftAP */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) - struct wake_lock wakelock[WAKE_LOCK_MAX]; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ +/* Set this to 1 to use a seperate interface (p2p0) for p2p operations. + * For ICS MR1 releases it should be disable to be compatable with ICS MR1 Framework + * see target dhd-cdc-sdmmc-panda-cfg80211-icsmr1-gpl-debug in Makefile + */ + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 struct mutex wl_start_stop_lock; /* lock/unlock for Android start/stop */ struct mutex wl_softap_lock; /* lock/unlock for any SoftAP/STA settings */ @@ -286,7 +298,8 @@ void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); extern int dhd_os_wake_lock(dhd_pub_t *pub); extern int dhd_os_wake_unlock(dhd_pub_t *pub); extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub); -extern int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val); inline static void MUTEX_LOCK_SOFTAP_SET_INIT(dhd_pub_t * dhdp) { @@ -312,10 +325,10 @@ inline static void MUTEX_UNLOCK_SOFTAP_SET(dhd_pub_t * dhdp) #define DHD_OS_WAKE_LOCK(pub) dhd_os_wake_lock(pub) #define DHD_OS_WAKE_UNLOCK(pub) dhd_os_wake_unlock(pub) #define DHD_OS_WAKE_LOCK_TIMEOUT(pub) dhd_os_wake_lock_timeout(pub) -#define DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_timeout_enable(pub, val) - -#define DHD_PACKET_TIMEOUT 1 -#define DHD_EVENT_TIMEOUT 2 +#define DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_rx_timeout_enable(pub, val) +#define DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_ctrl_timeout_enable(pub, val) +#define DHD_PACKET_TIMEOUT_MS 1000 +#define DHD_EVENT_TIMEOUT_MS 1500 /* interface operations (register, remove) should be atomic, use this lock to prevent race * condition among wifi on/off and interface operation functions @@ -369,6 +382,12 @@ extern int dhd_net_attach(dhd_pub_t *dhdp, int idx); /* Indication from bus module regarding removal/absence of dongle */ extern void dhd_detach(dhd_pub_t *dhdp); +#if defined(WLP2P) && defined(WL_CFG80211) +/* To allow attach/detach calls corresponding to p2p0 interface */ +extern int dhd_attach_p2p(dhd_pub_t *); +extern int dhd_detach_p2p(dhd_pub_t *); +#endif /* WLP2P && WL_CFG80211 */ + extern void dhd_free(dhd_pub_t *dhdp); /* Indication from bus module to change flow-control state */ @@ -411,6 +430,9 @@ extern int dhd_custom_get_mac_address(unsigned char *buf); extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); +extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); + +#ifdef PNO_SUPPORT extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); extern int dhd_pno_clean(dhd_pub_t *dhd); extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, @@ -421,17 +443,20 @@ extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); extern int dhd_dev_get_pno_status(struct net_device *dev); -extern int dhd_get_dtim_skip(dhd_pub_t *dhd); -extern bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd); -extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +#endif /* PNO_SUPPORT */ #define DHD_UNICAST_FILTER_NUM 0 #define DHD_BROADCAST_FILTER_NUM 1 #define DHD_MULTICAST4_FILTER_NUM 2 #define DHD_MULTICAST6_FILTER_NUM 3 +#define DHD_MDNS_FILTER_NUM 4 +extern int dhd_os_set_packet_filter(dhd_pub_t *dhdp, int val); extern int net_os_set_packet_filter(struct net_device *dev, int val); extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); +extern bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd); + #ifdef DHD_DEBUG extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size); #endif /* DHD_DEBUG */ @@ -453,7 +478,7 @@ extern int dhd_timeout_expired(dhd_timeout_t *tmo); extern int dhd_ifname2idx(struct dhd_info *dhd, char *name); extern int dhd_net2idx(struct dhd_info *dhd, struct net_device *net); -extern struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx); +extern struct net_device * dhd_idx2net(void *pub, int ifidx); extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, wl_event_msg_t *, void **data_ptr); extern void wl_event_to_host_order(wl_event_msg_t * evt); @@ -465,6 +490,7 @@ extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uin extern struct dhd_cmn *dhd_common_init(uint16 devid, osl_t *osh); extern void dhd_common_deinit(dhd_pub_t *dhd_pub, dhd_cmn_t *sa_cmn); +extern int dhd_do_driver_init(struct net_device *net); extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle, char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx); extern void dhd_del_if(struct dhd_info *dhd, int ifidx); @@ -488,7 +514,8 @@ extern uint dhd_bus_status(dhd_pub_t *dhdp); extern int dhd_bus_start(dhd_pub_t *dhdp); extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size); extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); -extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf); +extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); +extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); #if defined(KEEP_ALIVE) extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); @@ -701,12 +728,6 @@ typedef struct dhd_pkttag { #define DHD_PKTTAG_DSTN(tag) ((dhd_pkttag_t*)(tag))->dstn_ether typedef int (*f_commitpkt_t)(void* ctx, void* p); -int dhd_wlfc_enable(dhd_pub_t *dhd); -int dhd_wlfc_interface_event(struct dhd_info *, uint8 action, uint8 ifid, uint8 iftype, uint8* ea); -int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); -int dhd_wlfc_event(struct dhd_info *dhd); -int dhd_os_wlfc_block(dhd_pub_t *pub); -int dhd_os_wlfc_unblock(dhd_pub_t *pub); #ifdef PROP_TXSTATUS_DEBUG #define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do { (entry)->closed_ct++; } while (0) diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c index 3a4de96c0028..f16d81c9e198 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cdc.c +++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_cdc.c,v 1.51.6.31 2011-02-09 14:31:43 Exp $ + * $Id: dhd_cdc.c 324280 2012-03-28 19:01:17Z $ * * BDC is like CDC, except it includes a header for data packets to convey * packet priority over the bus, and flags (e.g. to indicate checksum status @@ -78,6 +78,8 @@ typedef struct dhd_prot { unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; } dhd_prot_t; +extern int dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf); + static int dhdcdc_msg(dhd_pub_t *dhd) { @@ -2174,6 +2176,7 @@ dhd_wlfc_init(dhd_pub_t *dhd) WLFC_FLAGS_CREDIT_STATUS_SIGNALS | WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE : 0; + dhd->wlfc_state = NULL; /* try to enable/disable signaling by sending "tlv" iovar. if that fails, @@ -2460,10 +2463,10 @@ dhd_prot_attach(dhd_pub_t *dhd) return 0; fail: -#ifndef DHD_USE_STATIC_BUF +#ifndef CONFIG_DHD_USE_STATIC_BUF if (cdc != NULL) MFREE(dhd->osh, cdc, sizeof(dhd_prot_t)); -#endif +#endif /* CONFIG_DHD_USE_STATIC_BUF */ return BCME_NOMEM; } @@ -2474,9 +2477,9 @@ dhd_prot_detach(dhd_pub_t *dhd) #ifdef PROP_TXSTATUS dhd_wlfc_deinit(dhd); #endif -#ifndef DHD_USE_STATIC_BUF +#ifndef CONFIG_DHD_USE_STATIC_BUF MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); -#endif +#endif /* CONFIG_DHD_USE_STATIC_BUF */ dhd->prot = NULL; } @@ -2512,9 +2515,10 @@ dhd_prot_init(dhd_pub_t *dhd) ret = dhd_wlfc_init(dhd); #endif -#if !defined(WL_CFG80211) +#if defined(WL_CFG80211) + if (dhd_download_fw_on_driverload) +#endif /* defined(WL_CFG80211) */ ret = dhd_preinit_ioctls(dhd); -#endif /* WL_CFG80211 */ /* Always assumes wl for now */ dhd->iswl = TRUE; diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c new file mode 100644 index 000000000000..bc1be94802b4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -0,0 +1,655 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include <net/rtnetlink.h> + +#include <bcmutils.h> +#include <wldev_common.h> +#include <wl_cfg80211.h> +#include <dhd_cfg80211.h> +extern struct wl_priv *wlcfg_drv_priv; +static int dhd_dongle_up = FALSE; + +#include <dngl_stats.h> +#include <dhd.h> +#include <dhdioctl.h> +#include <wlioctl.h> +#include <dhd_cfg80211.h> + +static s32 wl_dongle_up(struct net_device *ndev, u32 up); + +/** + * Function implementations + */ + +s32 dhd_cfg80211_init(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_deinit(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_get_opmode(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + return dhd->op_mode; +} + +s32 dhd_cfg80211_down(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int bcn_timeout = DHD_BEACON_TIMEOUT_HIGH; + char iovbuf[30]; + + dhd->op_mode |= val; + WL_ERR(("Set : op_mode=%d\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* IF P2P is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, false); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_os_set_packet_filter(dhd, 0); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + + return 0; +} + +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int bcn_timeout = DHD_BEACON_TIMEOUT_NORMAL; + char iovbuf[30]; + + dhd->op_mode &= ~CONCURENT_MASK; + WL_ERR(("Clean : op_mode=%d\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* IF P2P is disabled, enable arpoe back for STA mode. */ + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, true); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_os_set_packet_filter(dhd, 1); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + return 0; +} + +static s32 wl_dongle_up(struct net_device *ndev, u32 up) +{ + s32 err = 0; + + err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + } + return err; +} + +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock) +{ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + struct net_device *ndev; + s32 err = 0; + + WL_TRACE(("In\n")); + if (dhd_dongle_up) { + WL_ERR(("Dongle is already up\n")); + return err; + } + + ndev = wl_to_prmry_ndev(wl); + + if (need_lock) + rtnl_lock(); + + err = wl_dongle_up(ndev, 0); + if (unlikely(err)) { + WL_ERR(("wl_dongle_up failed\n")); + goto default_conf_out; + } + dhd_dongle_up = true; + +default_conf_out: + if (need_lock) + rtnl_unlock(); + return err; + +} + + +/* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */ +#define COEX_DHCP + +#if defined(COEX_DHCP) + +/* use New SCO/eSCO smart YG suppression */ +#define BT_DHCP_eSCO_FIX +/* this flag boost wifi pkt priority to max, caution: -not fair to sco */ +#define BT_DHCP_USE_FLAGS +/* T1 start SCO/ESCo priority suppression */ +#define BT_DHCP_OPPR_WIN_TIME 2500 +/* T2 turn off SCO/SCO supperesion is (timeout) */ +#define BT_DHCP_FLAG_FORCE_TIME 5500 + +enum wl_cfg80211_btcoex_status { + BT_DHCP_IDLE, + BT_DHCP_START, + BT_DHCP_OPPR_WIN, + BT_DHCP_FLAG_FORCE_TIMEOUT +}; + +/* + * get named driver variable to uint register value and return error indication + * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) + */ +static int +dev_wlc_intvar_get_reg(struct net_device *dev, char *name, + uint reg, int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + bcm_mkiovar(name, (char *)(®), sizeof(reg), + (char *)(&var), sizeof(var.buf)); + error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); + + *retval = dtoh32(var.val); + return (error); +} + +static int +dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf_local[1024]; +#else + static char ioctlbuf_local[1024]; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + + bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local)); + + return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true)); +} +/* +get named driver variable to uint register value and return error indication +calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) +*/ +static int +dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) +{ + char reg_addr[8]; + + memset(reg_addr, 0, sizeof(reg_addr)); + memcpy((char *)®_addr[0], (char *)addr, 4); + memcpy((char *)®_addr[4], (char *)val, 4); + + return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); +} + +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = FALSE; + int sco_id_cnt = 0; + int param27; + int i; + + for (i = 0; i < 12; i++) { + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); + + WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { /* count both sco & esco */ + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); + res = TRUE; + break; + } + + msleep(5); + } + + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) +/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = FALSE; + + char buf_reg50va_dhcp_on[8] = + { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = + { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = + { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = + { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = + { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + /* this should reduce eSCO agressive retransmit + * w/o breaking it + */ + + /* 1st save current */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + saved_status = TRUE; + WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + } else { + WL_ERR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = FALSE; + return -1; + } + + WL_TRACE(("override with [50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = TRUE; + } else if (saved_status) { + /* restore previously saved bt params */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_TRACE(("restore bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = FALSE; + } else { + WL_ERR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif /* BT_DHCP_eSCO_FIX */ + +static void +wl_cfg80211_bt_setflag(struct net_device *dev, bool set) +{ +#if defined(BT_DHCP_USE_FLAGS) + char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + + +#if defined(BT_DHCP_eSCO_FIX) + /* set = 1, save & turn on 0 - off & restore prev settings */ + set_btc_esco_params(dev, set); +#endif + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); + if (set == TRUE) + /* Forcing bt_flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_dhcp_on[0], + sizeof(buf_flag7_dhcp_on)); + else + /* Restoring default bt flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], + sizeof(buf_flag7_default)); +#endif +} + +static void wl_cfg80211_bt_timerfunc(ulong data) +{ + struct btcoex_info *bt_local = (struct btcoex_info *)data; + WL_TRACE(("%s\n", __FUNCTION__)); + bt_local->timer_on = 0; + schedule_work(&bt_local->work); +} + +static void wl_cfg80211_bt_handler(struct work_struct *work) +{ + struct btcoex_info *btcx_inf; + + btcx_inf = container_of(work, struct btcoex_info, work); + + if (btcx_inf->timer_on) { + btcx_inf->timer_on = 0; + del_timer_sync(&btcx_inf->timer); + } + + switch (btcx_inf->bt_state) { + case BT_DHCP_START: + /* DHCP started + * provide OPPORTUNITY window to get DHCP address + */ + WL_TRACE(("%s bt_dhcp stm: started \n", + __FUNCTION__)); + btcx_inf->bt_state = BT_DHCP_OPPR_WIN; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_OPPR_WIN: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T1 expiration\n", + __FUNCTION__)); + goto btc_coex_idle; + } + + /* DHCP is not over yet, start lowering BT priority + * enforce btc_params + flags if necessary + */ + WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, + BT_DHCP_OPPR_WIN_TIME)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); + btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_FLAG_FORCE_TIMEOUT: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T2 expiration\n", + __FUNCTION__)); + } else { + /* Noo dhcp during T1+T2, restore BT priority */ + WL_TRACE(("%s DHCP wait interval T2:%d" + "msec expired\n", __FUNCTION__, + BT_DHCP_FLAG_FORCE_TIME)); + } + + /* Restoring default bt priority */ + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); +btc_coex_idle: + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + + default: + WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, + btcx_inf->bt_state)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + } + + net_os_wake_unlock(btcx_inf->dev); +} + +int wl_cfg80211_btcoex_init(struct wl_priv *wl) +{ + struct btcoex_info *btco_inf = NULL; + + btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); + if (!btco_inf) + return -ENOMEM; + + btco_inf->bt_state = BT_DHCP_IDLE; + btco_inf->ts_dhcp_start = 0; + btco_inf->ts_dhcp_ok = 0; + /* Set up timer for BT */ + btco_inf->timer_ms = 10; + init_timer(&btco_inf->timer); + btco_inf->timer.data = (ulong)btco_inf; + btco_inf->timer.function = wl_cfg80211_bt_timerfunc; + + btco_inf->dev = wl->wdev->netdev; + + INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + + wl->btcoex_info = btco_inf; + return 0; +} + +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +{ + if (!wl->btcoex_info) + return; + + if (!wl->btcoex_info->timer_on) { + wl->btcoex_info->timer_on = 0; + del_timer_sync(&wl->btcoex_info->timer); + } + + cancel_work_sync(&wl->btcoex_info->work); + + kfree(wl->btcoex_info); + wl->btcoex_info = NULL; +} + +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) +{ + + struct wl_priv *wl = wlcfg_drv_priv; + char powermode_val = 0; + char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; + char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; + char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg66; + static uint32 saved_reg41; + static uint32 saved_reg68; + static bool saved_status = FALSE; + +#ifdef COEX_DHCP + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; + struct btcoex_info *btco_inf = wl->btcoex_info; +#endif /* COEX_DHCP */ + + /* Figure out powermode 1 or o command */ + strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + + WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + + /* Retrieve and saved orig regs value */ + if ((saved_status == FALSE) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + + /* Disable PM mode during dhpc session */ + + /* Disable PM mode during dhpc session */ +#ifdef COEX_DHCP + /* Start BT timer only for SCO connection */ + if (btcoex_is_sco_active(dev)) { + /* btc_params 66 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg66va_dhcp_on[0], + sizeof(buf_reg66va_dhcp_on)); + /* btc_params 41 0x33 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg41va_dhcp_on[0], + sizeof(buf_reg41va_dhcp_on)); + /* btc_params 68 0x190 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg68va_dhcp_on[0], + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + btco_inf->bt_state = BT_DHCP_START; + btco_inf->timer_on = 1; + mod_timer(&btco_inf->timer, btco_inf->timer.expires); + WL_TRACE(("%s enable BT DHCP Timer\n", + __FUNCTION__)); + } +#endif /* COEX_DHCP */ + } + else if (saved_status == TRUE) { + WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); + } + } + else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { + + + /* Restoring PM mode */ + +#ifdef COEX_DHCP + /* Stop any bt timer because DHCP session is done */ + WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); + if (btco_inf->timer_on) { + btco_inf->timer_on = 0; + del_timer_sync(&btco_inf->timer); + + if (btco_inf->bt_state != BT_DHCP_IDLE) { + /* need to restore original btc flags & extra btc params */ + WL_TRACE(("%s bt->bt_state:%d\n", + __FUNCTION__, btco_inf->bt_state)); + /* wake up btcoex thread to restore btlags+params */ + schedule_work(&btco_inf->work); + } + } + + /* Restoring btc_flag paramter anyway */ + if (saved_status == TRUE) + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); +#endif /* COEX_DHCP */ + + /* Restore original values */ + if (saved_status == TRUE) { + regaddr = 66; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg66); + regaddr = 41; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg41); + regaddr = 68; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg68); + + WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + } + saved_status = FALSE; + + } + else { + WL_ERR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + snprintf(command, 3, "OK"); + + return (strlen("OK")); +} +#endif diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.h b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h new file mode 100644 index 000000000000..ced46dbdb96c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h @@ -0,0 +1,45 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + + +#ifndef __DHD_CFG80211__ +#define __DHD_CFG80211__ + +#include <wl_cfg80211.h> +#include <wl_cfgp2p.h> + +s32 dhd_cfg80211_init(struct wl_priv *wl); +s32 dhd_cfg80211_deinit(struct wl_priv *wl); +s32 dhd_cfg80211_get_opmode(struct wl_priv *wl); +s32 dhd_cfg80211_down(struct wl_priv *wl); +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val); +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl); +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock); + +int wl_cfg80211_btcoex_init(struct wl_priv *wl); +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); + +#endif /* __DHD_CFG80211__ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 372ec80c866a..d9810ace1cb4 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c 290546 2011-10-19 01:55:21Z $ + * $Id: dhd_common.c 329682 2012-04-26 09:20:38Z $ */ #include <typedefs.h> #include <osl.h> @@ -134,7 +134,7 @@ enum { }; const bcm_iovar_t dhd_iovars[] = { - {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, + {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, #ifdef DHD_DEBUG {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, #endif /* DHD_DEBUG */ @@ -305,7 +305,7 @@ dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int le dhd_os_proto_block(dhd_pub); ret = dhd_prot_ioctl(dhd_pub, ifindex, ioc, buf, len); - if (!ret) + if (ret) dhd_os_check_hang(dhd_pub, ifindex, ret); dhd_os_proto_unblock(dhd_pub); @@ -341,6 +341,11 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch case IOV_SVAL(IOV_MSGLEVEL): dhd_msg_level = int_val; +#ifdef WL_CFG80211 + /* Enable DHD and WL logs in oneshot */ + if (dhd_msg_level & DHD_WL_VAL) + wl_cfg80211_enable_trace(dhd_msg_level); +#endif break; case IOV_GVAL(IOV_BCMERRORSTR): bcm_strncpy_s((char *)arg, len, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN); @@ -862,6 +867,8 @@ wl_show_host_event(wl_event_msg_t *event, void *event_data) break; case WLC_E_SCAN_COMPLETE: + case WLC_E_ASSOC_REQ_IE: + case WLC_E_ASSOC_RESP_IE: case WLC_E_PMKID_CACHE: DHD_EVENT(("MACEVENT: %s\n", event_name)); break; @@ -994,6 +1001,9 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, datalen = ntoh32_ua((void *)&event->datalen); evlen = datalen + sizeof(bcm_event_t); + DHD_TRACE(("RX: event_type:%d flags:%d status:%d reason:%d \n", + type, flags, status, reason)); + switch (type) { #ifdef PROP_TXSTATUS case WLC_E_FIFO_CREDIT_MAP: @@ -1494,7 +1504,7 @@ dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) return -1; iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); - retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, TRUE, 0); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, FALSE, 0); if (retcode) { DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", @@ -1721,11 +1731,12 @@ fail: /* * returns = TRUE if associated, FALSE if not associated + * third paramter retval can return error from error */ -bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf) +bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval) { char bssid[6], zbuf[6]; - int ret = -1; + int ret; bzero(bssid, 6); bzero(zbuf, 6); @@ -1733,6 +1744,9 @@ bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf) ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BSSID, (char *)&bssid, ETHER_ADDR_LEN, FALSE, 0); DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret)); + if (retval) + *retval = ret; + if (ret == BCME_NOTASSOCIATED) { DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret)); } @@ -1769,7 +1783,7 @@ dhd_get_dtim_skip(dhd_pub_t *dhd) bcn_li_dtim = dhd->dtim_skip; /* Check if associated */ - if (dhd_is_associated(dhd, NULL) == FALSE) { + if (dhd_is_associated(dhd, NULL, NULL) == FALSE) { DHD_TRACE(("%s NOT assoc ret %d\n", __FUNCTION__, ret)); goto exit; } @@ -1812,10 +1826,24 @@ exit: bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd) { #ifdef WL_CFG80211 +#ifndef WL_ENABLE_P2P_IF + /* To be back compatble with ICS MR1 release where p2p interface + * disable but wlan0 used for p2p + */ if (((dhd->op_mode & HOSTAPD_MASK) == HOSTAPD_MASK) || - ((dhd->op_mode & WFD_MASK) == WFD_MASK)) + ((dhd->op_mode & WFD_MASK) == WFD_MASK)) { return TRUE; + } else +#else + /* concurent mode with p2p interface for wfd and wlan0 for sta */ + if (((dhd->op_mode & P2P_GO_ENABLED) == P2P_GO_ENABLED) || + ((dhd->op_mode & P2P_GC_ENABLED) == P2P_GC_ENABLED)) { + DHD_ERROR(("%s P2P enabled for mode=%d\n", __FUNCTION__, dhd->op_mode)); + return TRUE; + } + else +#endif /* WL_ENABLE_P2P_IF */ #endif /* WL_CFG80211 */ return FALSE; } @@ -1867,10 +1895,12 @@ dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) memset(iovbuf, 0, sizeof(iovbuf)); - if ((pfn_enabled) && (dhd_is_associated(dhd, NULL) == TRUE)) { +#ifndef WL_SCHED_SCAN + if ((pfn_enabled) && (dhd_is_associated(dhd, NULL, NULL) == TRUE)) { DHD_ERROR(("%s pno is NOT enable : called in assoc mode , ignore\n", __FUNCTION__)); return ret; } +#endif /* !WL_SCHED_SCAN */ /* Enable/disable PNO */ if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { @@ -1907,6 +1937,7 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, if ((!dhd) && (!ssids_local)) { DHD_ERROR(("%s error exit\n", __FUNCTION__)); err = -1; + return err; } if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) @@ -2056,6 +2087,7 @@ int dhd_keep_alive_onoff(dhd_pub_t *dhd) return res; } #endif /* defined(KEEP_ALIVE) */ + /* Android ComboSCAN support */ /* @@ -2160,14 +2192,14 @@ wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left) { - char* str = *list_str; + char* str; int idx = 0; if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) { DHD_ERROR(("%s error paramters\n", __FUNCTION__)); return -1; } - + str = *list_str; while (*bytes_left > 0) { if (str[0] != CSCAN_TLV_TYPE_SSID_IE) { diff --git a/drivers/net/wireless/bcmdhd/dhd_dbg.h b/drivers/net/wireless/bcmdhd/dhd_dbg.h index a195cbe88e5b..01be6a1f056f 100644 --- a/drivers/net/wireless/bcmdhd/dhd_dbg.h +++ b/drivers/net/wireless/bcmdhd/dhd_dbg.h @@ -29,8 +29,8 @@ #if defined(DHD_DEBUG) -#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \ - printf args;} while (0) +#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \ + printf args;} while (0) #define DHD_TRACE(args) do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0) #define DHD_INFO(args) do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0) #define DHD_DATA(args) do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 80a75699e270..6ba7df1cac32 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c 291449 2011-10-22 12:16:26Z $ + * $Id: dhd_linux.c 329682 2012-04-26 09:20:38Z $ */ #include <typedefs.h> @@ -50,6 +50,7 @@ #include <epivers.h> #include <bcmutils.h> #include <bcmendian.h> +#include <bcmdevs.h> #include <proto/ethernet.h> #include <dngl_stats.h> @@ -110,6 +111,7 @@ extern bool ap_fw_loaded; #include <wl_android.h> #ifdef ARP_OFFLOAD_SUPPORT +void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add); static int dhd_device_event(struct notifier_block *this, unsigned long event, void *ptr); @@ -128,6 +130,9 @@ DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); #if defined(OOB_INTR_ONLY) extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); #endif /* defined(OOB_INTR_ONLY) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work); +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) MODULE_LICENSE("GPL v2"); #endif /* LinuxVer */ @@ -154,11 +159,10 @@ print_tainted() extern wl_iw_extra_params_t g_wl_iw_params; #endif /* defined(CONFIG_BCMDHD_WEXT) */ -#if defined(CONFIG_HAS_EARLYSUSPEND) +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) #include <linux/earlysuspend.h> -extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); -extern int dhd_get_dtim_skip(dhd_pub_t *dhd); #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); #ifdef PKT_FILTER_SUPPORT extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); @@ -247,11 +251,15 @@ typedef struct dhd_info { bool dhd_tasklet_create; #endif /* DHDTHREAD */ tsk_ctl_t thr_sysioc_ctl; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + struct work_struct work_hang; +#endif /* Wakelocks */ #if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) struct wake_lock wl_wifi; /* Wifi wakelock */ struct wake_lock wl_rxwake; /* Wifi rx wakelock */ + struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) @@ -259,10 +267,12 @@ typedef struct dhd_info { * calls and wifi_on or wifi_off */ struct mutex dhd_net_if_mutex; + struct mutex dhd_suspend_mutex; #endif spinlock_t wakelock_spinlock; int wakelock_counter; - int wakelock_timeout_enable; + int wakelock_rx_timeout_enable; + int wakelock_ctrl_timeout_enable; /* Thread to issue ioctl for multicast */ bool set_macaddress; @@ -271,9 +281,13 @@ typedef struct dhd_info { atomic_t pend_8021x_cnt; dhd_attach_states_t dhd_state; -#ifdef CONFIG_HAS_EARLYSUSPEND +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) struct early_suspend early_suspend; #endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef ARP_OFFLOAD_SUPPORT + u32 pend_ipaddr; +#endif /* ARP_OFFLOAD_SUPPORT */ } dhd_info_t; /* Definitions to provide path to the firmware and nvram @@ -282,6 +296,8 @@ typedef struct dhd_info { char firmware_path[MOD_PARAM_PATHLEN]; char nvram_path[MOD_PARAM_PATHLEN]; +int op_mode = 0; +module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); extern int net_os_send_hang_message(struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -353,25 +369,6 @@ uint dhd_radio_up = 1; char iface_name[IFNAMSIZ] = {'\0'}; module_param_string(iface_name, iface_name, IFNAMSIZ, 0); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) -#define DAEMONIZE(a) daemonize(a); \ - allow_signal(SIGKILL); \ - allow_signal(SIGTERM); -#else /* Linux 2.4 (w/o preemption patch) */ -#define RAISE_RX_SOFTIRQ() \ - cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) -#define DAEMONIZE(a) daemonize(); \ - do { if (a) \ - strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ - } while (0); -#endif /* LINUX_VERSION_CODE */ - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) -#define BLOCKABLE() (!in_atomic()) -#else -#define BLOCKABLE() (!in_interrupt()) -#endif - /* The following are specific to the SDIO dongle */ /* IOCTL response timeout */ @@ -437,6 +434,11 @@ static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR ; static void dhd_net_if_lock_local(dhd_info_t *dhd); static void dhd_net_if_unlock_local(dhd_info_t *dhd); +static void dhd_suspend_lock(dhd_pub_t *dhdp); +static void dhd_suspend_unlock(dhd_pub_t *dhdp); +#if !defined(AP) && defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static u32 dhd_concurrent_fw(dhd_pub_t *dhd); +#endif #ifdef WLMEDIA_HTSF void htsf_update(dhd_info_t *dhd, void *data); @@ -479,7 +481,7 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long actio { int ret = NOTIFY_DONE; -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) switch (action) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: @@ -499,7 +501,7 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long actio static struct notifier_block dhd_sleep_pm_notifier = { .notifier_call = dhd_sleep_pm_callback, - .priority = 0 + .priority = 10 }; extern int register_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb); @@ -511,7 +513,8 @@ static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) DHD_TRACE(("%s: %d\n", __FUNCTION__, value)); /* 1 - Enable packet filter, only allow unicast packet to send up */ /* 0 - Disable packet filter */ - if (dhd_pkt_filter_enable) { + if (dhd_pkt_filter_enable && (!value || + (dhd_check_ap_wfd_mode_set(dhd) == FALSE))) { int i; for (i = 0; i < dhd->pktfilter_count; i++) { @@ -523,7 +526,6 @@ static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) #endif } -#if defined(CONFIG_HAS_EARLYSUSPEND) static int dhd_set_suspend(int value, dhd_pub_t *dhd) { int power_mode = PM_MAX; @@ -535,70 +537,76 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", __FUNCTION__, value, dhd->in_suspend)); + dhd_suspend_lock(dhd); if (dhd && dhd->up) { if (value && dhd->in_suspend) { - /* Kernel suspended */ - DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__)); - - dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, - sizeof(power_mode), TRUE, 0); + /* Kernel suspended */ + DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__)); - /* Enable packet filter, only allow unicast packet to send up */ - dhd_set_packet_filter(1, dhd); + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); - /* If DTIM skip is set up as default, force it to wake - * each third DTIM for better power savings. Note that - * one side effect is a chance to miss BC/MC packet. - */ - bcn_li_dtim = dhd_get_dtim_skip(dhd); - bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, - 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* Enable packet filter, only allow unicast packet to send up */ + dhd_set_packet_filter(1, dhd); - /* Disable firmware roaming during suspend */ - bcm_mkiovar("roam_off", (char *)&roamvar, 4, - iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - } else { + /* If DTIM skip is set up as default, force it to wake + * each third DTIM for better power savings. Note that + * one side effect is a chance to miss BC/MC packet. + */ + bcn_li_dtim = dhd_get_dtim_skip(dhd); + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + /* Disable firmware roaming during suspend */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, + iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } else { - /* Kernel resumed */ - DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); + /* Kernel resumed */ + DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); - power_mode = PM_FAST; - dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, - sizeof(power_mode), TRUE, 0); + power_mode = PM_FAST; + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); - /* disable pkt filter */ - dhd_set_packet_filter(0, dhd); + /* disable pkt filter */ + dhd_set_packet_filter(0, dhd); - /* restore pre-suspend setting for dtim_skip */ - bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip, - 4, iovbuf, sizeof(iovbuf)); + /* restore pre-suspend setting for dtim_skip */ + bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip, + 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - roamvar = dhd_roam_disable; - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, - sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - } + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + roamvar = dhd_roam_disable; + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, + sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } } - + dhd_suspend_unlock(dhd); return 0; } -static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val) +static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) { dhd_pub_t *dhdp = &dhd->pub; + int ret = 0; DHD_OS_WAKE_LOCK(dhdp); /* Set flag when early suspend was called */ dhdp->in_suspend = val; - if ((!dhdp->suspend_disable_flag) && (dhd_check_ap_wfd_mode_set(dhdp) == FALSE)) - dhd_set_suspend(val, dhdp); + if ((force || !dhdp->suspend_disable_flag) && + (dhd_check_ap_wfd_mode_set(dhdp) == FALSE)) { + ret = dhd_set_suspend(val, dhdp); + } DHD_OS_WAKE_UNLOCK(dhdp); + return ret; } +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) static void dhd_early_suspend(struct early_suspend *h) { struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); @@ -606,7 +614,7 @@ static void dhd_early_suspend(struct early_suspend *h) DHD_TRACE(("%s: enter\n", __FUNCTION__)); if (dhd) - dhd_suspend_resume_helper(dhd, 1); + dhd_suspend_resume_helper(dhd, 1, 0); } static void dhd_late_resume(struct early_suspend *h) @@ -616,7 +624,7 @@ static void dhd_late_resume(struct early_suspend *h) DHD_TRACE(("%s: enter\n", __FUNCTION__)); if (dhd) - dhd_suspend_resume_helper(dhd, 0); + dhd_suspend_resume_helper(dhd, 0, 0); } #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ @@ -664,16 +672,12 @@ dhd_timeout_expired(dhd_timeout_t *tmo) } else { wait_queue_head_t delay_wait; DECLARE_WAITQUEUE(wait, current); - int pending; init_waitqueue_head(&delay_wait); add_wait_queue(&delay_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); - pending = signal_pending(current); remove_wait_queue(&delay_wait, &wait); set_current_state(TASK_RUNNING); - if (pending) - return 1; /* Interrupted */ } return 0; @@ -694,8 +698,9 @@ dhd_net2idx(dhd_info_t *dhd, struct net_device *net) return DHD_BAD_IF; } -struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx) +struct net_device * dhd_idx2net(void *pub, int ifidx) { + struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; struct dhd_info *dhd_info; if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) @@ -923,6 +928,7 @@ _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); } else { memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); + memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); } return ret; @@ -942,8 +948,9 @@ dhd_op_if(dhd_if_t *ifp) unsigned long flags; #endif + if (!ifp || !ifp->info || !ifp->idx) + return; ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ - dhd = ifp->info; DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); @@ -978,7 +985,7 @@ dhd_op_if(dhd_if_t *ifp) #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, - dhd_net_attach)) { + (void*)dhd_net_attach)) { ifp->state = DHD_IF_NONE; return; } @@ -1014,12 +1021,18 @@ dhd_op_if(dhd_if_t *ifp) DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_notify_ifdel(ifp->net); + wl_cfg80211_ifdel_ops(ifp->net); } #endif netif_stop_queue(ifp->net); unregister_netdev(ifp->net); - ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ + ret = DHD_DEL_IF; + +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_notify_ifdel(); + } +#endif } break; case DHD_IF_DELETING: @@ -1034,6 +1047,7 @@ dhd_op_if(dhd_if_t *ifp) ifp->set_multicast = FALSE; if (ifp->net) { free_netdev(ifp->net); + ifp->net = NULL; } dhd->iflist[ifp->idx] = NULL; #ifdef SOFTAP @@ -1135,7 +1149,7 @@ dhd_set_mac_address(struct net_device *dev, void *addr) if (ifidx == DHD_BAD_IF) return -1; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN); dhd->set_macaddress = TRUE; up(&dhd->thr_sysioc_ctl.sema); @@ -1153,7 +1167,7 @@ dhd_set_multicast_list(struct net_device *dev) if (ifidx == DHD_BAD_IF) return; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); dhd->iflist[ifidx]->set_multicast = TRUE; up(&dhd->thr_sysioc_ctl.sema); } @@ -1164,6 +1178,7 @@ dhd_os_wlfc_block(dhd_pub_t *pub) { dhd_info_t *di = (dhd_info_t *)(pub->info); ASSERT(di != NULL); + spin_lock_bh(&di->wlfc_spinlock); return 1; } @@ -1197,7 +1212,7 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) } /* Update multicast statistic */ - if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) { + if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); eh = (struct ether_header *)pktdata; @@ -1205,6 +1220,9 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) dhdp->tx_multicast++; if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) atomic_inc(&dhd->pend_8021x_cnt); + } else { + PKTFREE(dhd->pub.osh, pktbuf, TRUE); + return BCME_ERROR; } /* Look into the packet and update the packet priority */ @@ -1280,11 +1298,13 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); netif_stop_queue(net); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) /* Send Event when bus down detected during data session */ if (dhd->pub.busstate == DHD_BUS_DOWN) { DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); net_os_send_hang_message(net); } +#endif DHD_OS_WAKE_UNLOCK(&dhd->pub); return -ENODEV; } @@ -1398,7 +1418,8 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) int i; dhd_if_t *ifp; wl_event_msg_t event; - int tout = DHD_PACKET_TIMEOUT; + int tout_rx = 0; + int tout_ctrl = 0; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -1415,7 +1436,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) PKTFREE(dhdp->osh, pktbuf, TRUE); continue; } - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) /* Dropping packets before registering net device to avoid kernel panic */ if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) { @@ -1424,6 +1445,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) PKTFREE(dhdp->osh, pktbuf, TRUE); continue; } +#endif pnext = PKTNEXT(dhdp->osh, pktbuf); PKTSETNEXT(wl->sh.osh, pktbuf, NULL); @@ -1448,6 +1470,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) */ ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++; PKTFREE(dhdp->osh, pktbuf, TRUE); + DHD_TRACE(("RX: wlfc header \n")); continue; } #endif @@ -1499,10 +1522,15 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) &data); wl_event_to_host_order(&event); + if (!tout_ctrl) + tout_ctrl = DHD_PACKET_TIMEOUT_MS; if (event.event_type == WLC_E_BTA_HCI_EVENT) { dhd_bta_doevt(dhdp, data, event.datalen); + } else if (event.event_type == WLC_E_PFN_NET_FOUND) { + tout_ctrl *= 2; } - tout = DHD_EVENT_TIMEOUT; + } else { + tout_rx = DHD_PACKET_TIMEOUT_MS; } ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); @@ -1535,7 +1563,8 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ } } - DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(dhdp, tout); + DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); } void @@ -1589,8 +1618,10 @@ dhd_get_stats(struct net_device *net) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ifidx = dhd_net2idx(dhd, net); - if (ifidx == DHD_BAD_IF) + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); return NULL; + } ifp = dhd->iflist[ifidx]; ASSERT(dhd && ifp); @@ -2001,6 +2032,7 @@ dhd_ethtool(dhd_info_t *dhd, void *uaddr) static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) if (!dhdp) return FALSE; if ((error == -ETIMEDOUT) || ((dhdp->busstate == DHD_BUS_DOWN) && @@ -2010,6 +2042,7 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) net_os_send_hang_message(net); return TRUE; } +#endif return FALSE; } @@ -2030,7 +2063,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) /* send to dongle only if we are not waiting for reload already */ if (dhd->pub.hang_was_sent) { DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); - DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); DHD_OS_WAKE_UNLOCK(&dhd->pub); return OSL_ERROR(BCME_DONGLE_DOWN); } @@ -2039,6 +2072,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); DHD_OS_WAKE_UNLOCK(&dhd->pub); return -1; } @@ -2236,6 +2270,7 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) #endif for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); if (dhd->iflist[i]) { DHD_TRACE(("Deleting IF: %d \n", i)); if ((dhd->iflist[i]->state != DHD_IF_DEL) && @@ -2245,6 +2280,7 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) dhd_op_if(dhd->iflist[i]); } } + dhd_net_if_unlock_local(dhd); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -2259,10 +2295,10 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) static int dhd_stop(struct net_device *net) { - int ifidx; + int ifidx = 0; dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); DHD_OS_WAKE_LOCK(&dhd->pub); - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net)); if (dhd->pub.up == 0) { goto exit; } @@ -2270,7 +2306,7 @@ dhd_stop(struct net_device *net) #ifdef WL_CFG80211 if (ifidx == 0) { - wl_cfg80211_down(); + wl_cfg80211_down(NULL); /* * For CFG80211: Clean up all the left over virtual interfaces @@ -2293,15 +2329,15 @@ dhd_stop(struct net_device *net) /* Stop the protocol module */ dhd_prot_stop(&dhd->pub); + OLD_MOD_DEC_USE_COUNT; +exit: #if defined(WL_CFG80211) if (ifidx == 0 && !dhd_download_fw_on_driverload) wl_android_wifi_off(net); #endif - dhd->pub.hang_was_sent = 0; dhd->pub.rxcnt_timeout = 0; dhd->pub.txcnt_timeout = 0; - OLD_MOD_DEC_USE_COUNT; -exit: + DHD_OS_WAKE_UNLOCK(&dhd->pub); return 0; } @@ -2326,13 +2362,19 @@ dhd_open(struct net_device *net) firmware_path[0] = '\0'; } + dhd->pub.hang_was_sent = 0; #if !defined(WL_CFG80211) /* * Force start if ifconfig_up gets called before START command * We keep WEXT's wl_control_wl_start to provide backward compatibility * This should be removed in the future */ - wl_control_wl_start(net); + ret = wl_control_wl_start(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } #endif ifidx = dhd_net2idx(dhd, net); @@ -2354,12 +2396,17 @@ dhd_open(struct net_device *net) atomic_set(&dhd->pend_8021x_cnt, 0); #if defined(WL_CFG80211) DHD_ERROR(("\n%s\n", dhd_version)); - if (!dhd_download_fw_on_driverload) - wl_android_wifi_on(net); + if (!dhd_download_fw_on_driverload) { + ret = wl_android_wifi_on(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } + } #endif /* defined(WL_CFG80211) */ if (dhd->pub.busstate != DHD_BUS_DATA) { - int ret; /* try to bring up bus */ if ((ret = dhd_bus_start(&dhd->pub)) != 0) { @@ -2382,7 +2429,7 @@ dhd_open(struct net_device *net) #endif /* TOE */ #if defined(WL_CFG80211) - if (unlikely(wl_cfg80211_up())) { + if (unlikely(wl_cfg80211_up(NULL))) { DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); ret = -1; goto exit; @@ -2400,10 +2447,39 @@ dhd_open(struct net_device *net) OLD_MOD_INC_USE_COUNT; exit: + if (ret) + dhd_stop(net); + DHD_OS_WAKE_UNLOCK(&dhd->pub); return ret; } +int dhd_do_driver_init(struct net_device *net) +{ + dhd_info_t *dhd = NULL; + + if (!net) { + DHD_ERROR(("Primary Interface not initialized \n")); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + + /* If driver is already initialized, do nothing + */ + if (dhd->pub.busstate == DHD_BUS_DATA) { + DHD_TRACE(("Driver already Inititalized. Nothing to do")); + return 0; + } + + if (dhd_open(net) < 0) { + DHD_ERROR(("Driver Init Failed \n")); + return -1; + } + + return 0; +} + osl_t * dhd_osl_attach(void *pdev, uint bustype) { @@ -2457,7 +2533,7 @@ dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, ifp->state = DHD_IF_ADD; ifp->idx = ifidx; ifp->bssidx = bssidx; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); up(&dhd->thr_sysioc_ctl.sema); } else ifp->net = (struct net_device *)handle; @@ -2481,10 +2557,30 @@ dhd_del_if(dhd_info_t *dhd, int ifidx) ifp->state = DHD_IF_DEL; ifp->idx = ifidx; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); up(&dhd->thr_sysioc_ctl.sema); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +static struct net_device_ops dhd_ops_pri = { + .ndo_open = dhd_open, + .ndo_stop = dhd_stop, + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, + .ndo_set_multicast_list = dhd_set_multicast_list, +}; + +static struct net_device_ops dhd_ops_virt = { + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, + .ndo_set_multicast_list = dhd_set_multicast_list, +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ + dhd_pub_t * dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) { @@ -2577,13 +2673,16 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wakelock_spinlock); dhd->wakelock_counter = 0; - dhd->wakelock_timeout_enable = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; #ifdef CONFIG_HAS_WAKELOCK wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); + wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) mutex_init(&dhd->dhd_net_if_mutex); + mutex_init(&dhd->dhd_suspend_mutex); #endif dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; @@ -2659,7 +2758,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) dhd->thr_sysioc_ctl.thr_pid = -1; } dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + INIT_WORK(&dhd->work_hang, dhd_hang_process); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ /* * Save the dhd_info into the priv */ @@ -2669,7 +2770,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) register_pm_notifier(&dhd_sleep_pm_notifier); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ -#ifdef CONFIG_HAS_EARLYSUSPEND +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; dhd->early_suspend.suspend = dhd_early_suspend; dhd->early_suspend.resume = dhd_late_resume; @@ -2678,6 +2779,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) #endif #ifdef ARP_OFFLOAD_SUPPORT + dhd->pend_ipaddr = 0; register_inetaddr_notifier(&dhd_notifier); #endif /* ARP_OFFLOAD_SUPPORT */ @@ -2711,7 +2813,8 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_TRACE(("Enter %s:\n", __FUNCTION__)); #ifdef DHDTHREAD - dhd_os_sdlock(dhdp); + if (dhd->threads_only) + dhd_os_sdlock(dhdp); #endif /* DHDTHREAD */ /* try to download image and nvram to the dongle */ @@ -2724,14 +2827,16 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", __FUNCTION__, fw_path, nv_path)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -1; } } if (dhd->pub.busstate != DHD_BUS_LOAD) { #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -ENETDOWN; } @@ -2745,7 +2850,8 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return ret; } @@ -2761,7 +2867,8 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -ENODEV; } @@ -2778,13 +2885,15 @@ dhd_bus_start(dhd_pub_t *dhdp) del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -ENODEV; } #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ #ifdef READ_MACADDR @@ -2799,45 +2908,89 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_write_macaddr(dhd->pub.mac.octet); #endif +#ifdef ARP_OFFLOAD_SUPPORT + if (dhd->pend_ipaddr) { +#ifdef AOE_IP_ALIAS_SUPPORT + aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE); +#endif /* AOE_IP_ALIAS_SUPPORT */ + dhd->pend_ipaddr = 0; + } +#endif /* ARP_OFFLOAD_SUPPORT */ + return 0; } +#if !defined(AP) && defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +/* For Android ICS MR2 release, the concurrent mode is enabled by default and the firmware + * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA + * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware + * would still be named as fw_bcmdhd_apsta. + */ +static u32 +dhd_concurrent_fw(dhd_pub_t *dhd) +{ + int ret = 0; + char buf[WLC_IOCTL_SMLEN]; + + if ((!op_mode) && (strstr(fw_path, "_p2p") == NULL) && + (strstr(fw_path, "_apsta") == NULL)) { + /* Given path is for the STA firmware. Check whether P2P support is present in + * the firmware. If so, set mode as P2P (concurrent support). + */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_TRACE(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); + } else if (buf[0] == 1) { + DHD_TRACE(("%s: P2P is supported\n", __FUNCTION__)); + return 1; + } + } + return 0; +} +#endif + int dhd_preinit_ioctls(dhd_pub_t *dhd) { int ret = 0; char eventmask[WL_EVENTING_MASK_LEN]; char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ - +#if !defined(WL_CFG80211) uint up = 0; +#endif uint power_mode = PM_FAST; uint32 dongle_align = DHD_SDALIGN; uint32 glom = 0; - uint bcn_timeout = 4; + uint bcn_timeout = DHD_BEACON_TIMEOUT_NORMAL; + uint retry_max = 3; #if defined(ARP_OFFLOAD_SUPPORT) int arpoe = 1; #endif - int scan_assoc_time = 40; +#if defined(KEEP_ALIVE) + int res; +#endif /* defined(KEEP_ALIVE) */ + int scan_assoc_time = DHD_SCAN_ACTIVE_TIME; int scan_unassoc_time = 40; - int scan_passive_time = 130; + int scan_passive_time = DHD_SCAN_PASSIVE_TIME; char buf[WLC_IOCTL_SMLEN]; char *ptr; uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ + uint16 chipID; #if defined(SOFTAP) uint dtim = 1; #endif #if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ #endif - #if defined(AP) || defined(WLP2P) uint32 apsta = 1; /* Enable APSTA mode */ #endif /* defined(AP) || defined(WLP2P) */ #ifdef GET_CUSTOM_MAC_ENABLE struct ether_addr ea_addr; #endif /* GET_CUSTOM_MAC_ENABLE */ - DHD_TRACE(("Enter %s\n", __FUNCTION__)); dhd->op_mode = 0; #ifdef GET_CUSTOM_MAC_ENABLE @@ -2847,9 +3000,10 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); if (ret < 0) { - DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + DHD_ERROR(("%s: can't set custom MAC address , error=%d\n", __FUNCTION__, ret)); return BCME_NOTUP; } + memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); } else { #endif /* GET_CUSTOM_MAC_ENABLE */ /* Get the default device MAC address directly from firmware */ @@ -2867,7 +3021,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* GET_CUSTOM_MAC_ENABLE */ #ifdef SET_RANDOM_MAC_SOFTAP - if (strstr(fw_path, "_apsta") != NULL) { + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || (op_mode == HOSTAPD_MASK)) { uint rand_mac; srandom32((uint)jiffies); @@ -2889,26 +3043,53 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* SET_RANDOM_MAC_SOFTAP */ DHD_TRACE(("Firmware = %s\n", fw_path)); -#if !defined(AP) && defined(WLP2P) +#if !defined(AP) && defined(WLP2P) /* Check if firmware with WFD support used */ - if (strstr(fw_path, "_p2p") != NULL) { + if ((!op_mode && strstr(fw_path, "_p2p") != NULL) +#if defined(WL_ENABLE_P2P_IF) + || (op_mode == 0x04) ||(dhd_concurrent_fw(dhd)) +#endif + ) { bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { DHD_ERROR(("%s APSTA for WFD failed ret= %d\n", __FUNCTION__, ret)); } else { dhd->op_mode |= WFD_MASK; -#if defined(ARP_OFFLOAD_SUPPORT) - arpoe = 0; -#endif /* (ARP_OFFLOAD_SUPPORT) */ - dhd_pkt_filter_enable = FALSE; } } #endif #if !defined(AP) && defined(WL_CFG80211) /* Check if firmware with HostAPD support used */ - if (strstr(fw_path, "_apsta") != NULL) { + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || (op_mode == HOSTAPD_MASK)) { + /* Disable A-band for HostAPD */ + uint band = WLC_BAND_2G; + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_BAND, (char *)&band, sizeof(band), + TRUE, 0)) < 0) { + DHD_ERROR(("%s:set band failed error (%d)\n", __FUNCTION__, ret)); + } + + /* Turn off wme if we are having only g ONLY firmware */ + bcm_mkiovar("nmode", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s:get nmode failed error (%d)\n", __FUNCTION__, ret)); + } + else { + DHD_TRACE(("%s:get nmode returned %d\n", __FUNCTION__,buf[0])); + } + if (buf[0] == 0) { + int wme = 0; + bcm_mkiovar("wme", (char *)&wme, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s set wme for HostAPD failed %d\n", __FUNCTION__, ret)); + } + else { + DHD_TRACE(("%s set wme succeeded for g ONLY firmware\n", __FUNCTION__)); + } + } /* Turn off MPC in AP mode */ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, @@ -2957,9 +3138,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - /* disable glom option per default */ - bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* disable glom option for some chips */ + chipID = (uint16)dhd_bus_chip_id(dhd); + if ((chipID == BCM4330_CHIP_ID) || (chipID == BCM4329_CHIP_ID)) { + DHD_INFO(("%s disable glom for chipID=0x%X\n", __FUNCTION__, chipID)); + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } /* Setup timeout if Beacons are lost and roam is off to report link down */ bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); @@ -2967,6 +3152,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) /* Setup assoc_retry_max count to reconnect target AP in dongle */ bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + #if defined(AP) && !defined(WLP2P) /* Turn off MPC in AP mode */ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); @@ -2982,17 +3168,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif #if defined(KEEP_ALIVE) - { /* Set Keep Alive : be sure to use FW with -keepalive */ - int res; - #if defined(SOFTAP) if (ap_fw_loaded == FALSE) #endif if ((res = dhd_keep_alive_onoff(dhd)) < 0) DHD_ERROR(("%s set keeplive failed %d\n", __FUNCTION__, res)); - } #endif /* defined(KEEP_ALIVE) */ /* Read event_msgs mask */ @@ -3019,6 +3201,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_LINK); setbit(eventmask, WLC_E_NDIS_LINK); setbit(eventmask, WLC_E_MIC_ERROR); + setbit(eventmask, WLC_E_ASSOC_REQ_IE); + setbit(eventmask, WLC_E_ASSOC_RESP_IE); setbit(eventmask, WLC_E_PMKID_CACHE); setbit(eventmask, WLC_E_JOIN_START); setbit(eventmask, WLC_E_SCAN_COMPLETE); @@ -3072,12 +3256,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef PKT_FILTER_SUPPORT /* Setup defintions for pktfilter , enable in suspend */ - dhd->pktfilter_count = 4; + dhd->pktfilter_count = 5; /* Setup filter to allow only unicast */ dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; dhd->pktfilter[1] = NULL; dhd->pktfilter[2] = NULL; dhd->pktfilter[3] = NULL; + dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; #if defined(SOFTAP) if (ap_fw_loaded) { int i; @@ -3089,12 +3274,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* defined(SOFTAP) */ #endif /* PKT_FILTER_SUPPORT */ +#if !defined(WL_CFG80211) /* Force STA UP */ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) { DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret)); goto done; } - +#endif /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); ptr = buf; @@ -3105,8 +3291,15 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcmstrtok(&ptr, "\n", 0); /* Print fw version info */ DHD_ERROR(("Firmware version = %s\n", buf)); + DHD_BLOG(buf, strlen(buf) + 1); DHD_BLOG(dhd_version, strlen(dhd_version) + 1); + + /* Check and adjust IOCTL response timeout for Manufactring firmware */ + if (strstr(buf, MANUFACTRING_FW) != NULL) { + dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT * 10); + DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n", __FUNCTION__)); + } } done: @@ -3138,26 +3331,6 @@ dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, in return ret; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) -static struct net_device_ops dhd_ops_pri = { - .ndo_open = dhd_open, - .ndo_stop = dhd_stop, - .ndo_get_stats = dhd_get_stats, - .ndo_do_ioctl = dhd_ioctl_entry, - .ndo_start_xmit = dhd_start_xmit, - .ndo_set_mac_address = dhd_set_mac_address, - .ndo_set_multicast_list = dhd_set_multicast_list, -}; - -static struct net_device_ops dhd_ops_virt = { - .ndo_get_stats = dhd_get_stats, - .ndo_do_ioctl = dhd_ioctl_entry, - .ndo_start_xmit = dhd_start_xmit, - .ndo_set_mac_address = dhd_set_mac_address, - .ndo_set_multicast_list = dhd_set_multicast_list, -}; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ - int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) { struct dhd_info *dhd = dhdp->info; @@ -3261,9 +3434,13 @@ static int dhd_device_event(struct notifier_block *this, DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); - /* firmware not downloaded, do nothing */ - if (dhd->pub.busstate == DHD_BUS_DOWN) { - DHD_ERROR(("%s: bus is down, exit\n", __FUNCTION__)); + if (dhd->pub.busstate != DHD_BUS_DATA) { + DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); + if (dhd->pend_ipaddr) { + DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", + __FUNCTION__, dhd->pend_ipaddr)); + } + dhd->pend_ipaddr = ifa->ifa_address; break; } @@ -3281,7 +3458,7 @@ static int dhd_device_event(struct notifier_block *this, case NETDEV_DOWN: DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); - + dhd->pend_ipaddr = 0; #ifdef AOE_IP_ALIAS_SUPPORT if (!(ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a)) { DHD_ARPOE(("%s: primary interface is down, AOE clr all\n", @@ -3355,7 +3532,8 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) * portable hotspot. This will not work in simultaneous AP/STA mode, * nor with P2P. Need to set the Donlge's MAC address, and then use that. */ - if (ifidx > 0) { + if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, + ETHER_ADDR_LEN)) { DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", __func__, net->name)); temp_addr[0] |= 0x02; @@ -3468,13 +3646,17 @@ void dhd_detach(dhd_pub_t *dhdp) unregister_inetaddr_notifier(&dhd_notifier); #endif /* ARP_OFFLOAD_SUPPORT */ -#if defined(CONFIG_HAS_EARLYSUSPEND) +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { if (dhd->early_suspend.suspend) unregister_early_suspend(&dhd->early_suspend); } #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + cancel_work_sync(&dhd->work_hang); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + #if defined(CONFIG_BCMDHD_WEXT) if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { /* Detatch and unlink in the iw */ @@ -3482,7 +3664,7 @@ void dhd_detach(dhd_pub_t *dhdp) } #endif /* defined(CONFIG_BCMDHD_WEXT) */ - if (&dhd->thr_sysioc_ctl.thr_pid >= 0) { + if (dhd->thr_sysioc_ctl.thr_pid >= 0) { PROC_STOP(&dhd->thr_sysioc_ctl); } @@ -3492,13 +3674,15 @@ void dhd_detach(dhd_pub_t *dhdp) dhd_if_t *ifp; /* Cleanup virtual interfaces */ - for (i = 1; i < DHD_MAX_IFS; i++) + for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); if (dhd->iflist[i]) { dhd->iflist[i]->state = DHD_IF_DEL; dhd->iflist[i]->idx = i; dhd_op_if(dhd->iflist[i]); } - + dhd_net_if_unlock_local(dhd); + } /* delete primary interface 0 */ ifp = dhd->iflist[0]; ASSERT(ifp); @@ -3549,7 +3733,7 @@ void dhd_detach(dhd_pub_t *dhdp) #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_detach(); + wl_cfg80211_detach(NULL); dhd_monitor_uninit(); } #endif @@ -3562,6 +3746,7 @@ void dhd_detach(dhd_pub_t *dhdp) #ifdef CONFIG_HAS_WAKELOCK wake_lock_destroy(&dhd->wl_wifi); wake_lock_destroy(&dhd->wl_rxwake); + wake_lock_destroy(&dhd->wl_ctrlwake); #endif } } @@ -3596,7 +3781,6 @@ dhd_module_cleanup(void) dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); } - static int __init dhd_module_init(void) { @@ -3643,26 +3827,26 @@ dhd_module_init(void) } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - /* - * Wait till MMC sdio_register_driver callback called and made driver attach. - * It's needed to make sync up exit from dhd insmod and - * Kernel MMC sdio device callback registration - */ + /* + * Wait till MMC sdio_register_driver callback called and made driver attach. + * It's needed to make sync up exit from dhd insmod and + * Kernel MMC sdio device callback registration + */ if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) { - error = -EINVAL; + error = -ENODEV; DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__)); goto fail_2; - } + } #endif #if defined(WL_CFG80211) - error = wl_android_post_init(); -#endif + wl_android_post_init(); +#endif /* defined(WL_CFG80211) */ return error; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) fail_2: dhd_bus_unregister(); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +#endif fail_1: #if defined(CONFIG_WIFI_CONTROL_FUNC) wl_android_wifictrl_func_del(); @@ -3674,7 +3858,11 @@ fail_1: return error; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) late_initcall(dhd_module_init); +#else +module_init(dhd_module_init); +#endif module_exit(dhd_module_cleanup); /* @@ -3722,7 +3910,6 @@ int dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) { dhd_info_t * dhd = (dhd_info_t *)(pub->info); - DECLARE_WAITQUEUE(wait, current); int timeout = dhd_ioctl_timeout_msec; /* Convert timeout in millsecond to jiffies */ @@ -3732,26 +3919,7 @@ dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) timeout = timeout * HZ / 1000; #endif - /* Wait until control frame is available */ - add_wait_queue(&dhd->ioctl_resp_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - /* Memory barrier to support multi-processing - * As the variable "condition", which points to dhd->rxlen (dhd_bus_rxctl[dhd_sdio.c]) - * Can be changed by another processor. - */ - smp_mb(); - while (!(*condition) && (!signal_pending(current) && timeout)) { - timeout = schedule_timeout(timeout); - smp_mb(); - } - - if (signal_pending(current)) - *pending = TRUE; - - set_current_state(TASK_RUNNING); - remove_wait_queue(&dhd->ioctl_resp_wait, &wait); - + timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); return timeout; } @@ -3761,7 +3929,7 @@ dhd_os_ioctl_resp_wake(dhd_pub_t *pub) dhd_info_t *dhd = (dhd_info_t *)(pub->info); if (waitqueue_active(&dhd->ioctl_resp_wait)) { - wake_up_interruptible(&dhd->ioctl_resp_wait); + wake_up(&dhd->ioctl_resp_wait); } return 0; @@ -3917,7 +4085,7 @@ dhd_os_sdtxunlock(dhd_pub_t *pub) dhd_os_sdunlock(pub); } -#if defined(DHD_USE_STATIC_BUF) +#if defined(CONFIG_DHD_USE_STATIC_BUF) uint8* dhd_os_prealloc(void *osh, int section, uint size) { return (uint8*)wl_android_prealloc(section, size); @@ -3926,7 +4094,7 @@ uint8* dhd_os_prealloc(void *osh, int section, uint size) void dhd_os_prefree(void *osh, void *addr, uint size) { } -#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ +#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ #if defined(CONFIG_BCMDHD_WEXT) struct iw_statistics * @@ -3975,7 +4143,13 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, #endif /* defined(CONFIG_BCMDHD_WEXT) */ #ifdef WL_CFG80211 - + if ((ntoh32(event->event_type) == WLC_E_IF) && + (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD)) + /* If ADD_IF has been called directly by wl utility then we + * should not report this. In case if ADD_IF was called from + * CFG stack, then too this event need not be reported back + */ + return (BCME_OK); if ((wl_cfg80211_is_progress_ifchange() || wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) { /* @@ -4104,8 +4278,13 @@ void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int timeout = msecs_to_jiffies(2000); +#else + int timeout = 2 * HZ; +#endif dhd_os_sdunlock(dhd); - wait_event_interruptible_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), HZ * 2); + wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); dhd_os_sdlock(dhd); #endif return; @@ -4116,7 +4295,7 @@ void dhd_wait_event_wakeup(dhd_pub_t *dhd) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; if (waitqueue_active(&dhdinfo->ctrl_wait)) - wake_up_interruptible(&dhdinfo->ctrl_wait); + wake_up(&dhdinfo->ctrl_wait); #endif return; } @@ -4149,16 +4328,18 @@ int net_os_set_suspend_disable(struct net_device *dev, int val) return ret; } -int net_os_set_suspend(struct net_device *dev, int val) +int net_os_set_suspend(struct net_device *dev, int val, int force) { int ret = 0; -#if defined(CONFIG_HAS_EARLYSUSPEND) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd) { +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ret = dhd_set_suspend(val, &dhd->pub); +#else + ret = dhd_suspend_resume_helper(dhd, val, force); +#endif } -#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ return ret; } @@ -4178,7 +4359,8 @@ int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) char *filterp = NULL; int ret = 0; - if (!dhd || (num == DHD_UNICAST_FILTER_NUM)) + if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || + (num == DHD_MDNS_FILTER_NUM)) return ret; if (num >= dhd->pub.pktfilter_count) return -EINVAL; @@ -4201,9 +4383,8 @@ int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) return ret; } -int net_os_set_packet_filter(struct net_device *dev, int val) +int dhd_os_set_packet_filter(dhd_pub_t *dhdp, int val) { - dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; /* Packet filtering is set only if we still in early-suspend and @@ -4211,22 +4392,29 @@ int net_os_set_packet_filter(struct net_device *dev, int val) * We can always turn it OFF in case of early-suspend, but we turn it * back ON only if suspend_disable_flag was not set */ - if (dhd && dhd->pub.up) { - if (dhd->pub.in_suspend) { - if (!val || (val && !dhd->pub.suspend_disable_flag)) - dhd_set_packet_filter(val, &dhd->pub); + if (dhdp && dhdp->up) { + if (dhdp->in_suspend) { + if (!val || (val && !dhdp->suspend_disable_flag)) + dhd_set_packet_filter(val, dhdp); } } return ret; + } +int net_os_set_packet_filter(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); -void + return dhd_os_set_packet_filter(&dhd->pub, val); +} + +int dhd_dev_init_ioctl(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - dhd_preinit_ioctls(&dhd->pub); + return dhd_preinit_ioctls(&dhd->pub); } #ifdef PNO_SUPPORT @@ -4271,6 +4459,28 @@ dhd_dev_get_pno_status(struct net_device *dev) #endif /* PNO_SUPPORT */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work) +{ + dhd_info_t *dhd; + struct net_device *dev; + + dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); + dev = dhd->iflist[0]->net; + + if (dev) { + rtnl_lock(); + dev_close(dev); + rtnl_unlock(); +#if defined(WL_WIRELESS_EXT) + wl_iw_send_priv_event(dev, "HANG"); +#endif +#if defined(WL_CFG80211) + wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif + } +} + int net_os_send_hang_message(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4279,26 +4489,21 @@ int net_os_send_hang_message(struct net_device *dev) if (dhd) { if (!dhd->pub.hang_was_sent) { dhd->pub.hang_was_sent = 1; -#if defined(CONFIG_BCMDHD_WEXT) - ret = wl_iw_send_priv_event(dev, "HANG"); -#endif -#if defined(WL_CFG80211) - ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); -#endif + schedule_work(&dhd->work_hang); } } return ret; } +#endif void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd && dhd->pub.up) - memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); + memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); } - void dhd_net_if_lock(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4327,6 +4532,24 @@ static void dhd_net_if_unlock_local(dhd_info_t *dhd) #endif } +static void dhd_suspend_lock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_lock(&dhd->dhd_suspend_mutex); +#endif +} + +static void dhd_suspend_unlock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_unlock(&dhd->dhd_suspend_mutex); +#endif +} + unsigned long dhd_os_spin_lock(dhd_pub_t *pub) { dhd_info_t *dhd = (dhd_info_t *)(pub->info); @@ -4419,13 +4642,18 @@ int dhd_os_wake_lock_timeout(dhd_pub_t *pub) if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); - ret = dhd->wakelock_timeout_enable; + ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? + dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; #ifdef CONFIG_HAS_WAKELOCK - if (dhd->wakelock_timeout_enable) + if (dhd->wakelock_rx_timeout_enable) wake_lock_timeout(&dhd->wl_rxwake, - dhd->wakelock_timeout_enable * HZ); + msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); + if (dhd->wakelock_ctrl_timeout_enable) + wake_lock_timeout(&dhd->wl_ctrlwake, + msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); #endif - dhd->wakelock_timeout_enable = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); } return ret; @@ -4441,27 +4669,51 @@ int net_os_wake_lock_timeout(struct net_device *dev) return ret; } -int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub, int val) +int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) { dhd_info_t *dhd = (dhd_info_t *)(pub->info); unsigned long flags; if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); - if (val > dhd->wakelock_timeout_enable) - dhd->wakelock_timeout_enable = val; + if (val > dhd->wakelock_rx_timeout_enable) + dhd->wakelock_rx_timeout_enable = val; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); } return 0; } -int net_os_wake_lock_timeout_enable(struct net_device *dev, int val) +int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (val > dhd->wakelock_ctrl_timeout_enable) + dhd->wakelock_ctrl_timeout_enable = val; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + +int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; if (dhd) - ret = dhd_os_wake_lock_timeout_enable(&dhd->pub, val); + ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); + return ret; +} + +int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); return ret; } @@ -4532,15 +4784,6 @@ int dhd_os_check_wakelock(void *dhdp) return 0; } -int dhd_os_check_if_up(void *dhdp) -{ - dhd_pub_t *pub = (dhd_pub_t *)dhdp; - - if (!pub) - return 0; - return pub->up; -} - int net_os_wake_unlock(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4551,6 +4794,15 @@ int net_os_wake_unlock(struct net_device *dev) return ret; } +int dhd_os_check_if_up(void *dhdp) +{ + dhd_pub_t *pub = (dhd_pub_t *)dhdp; + + if (!pub) + return 0; + return pub->up; +} + int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) { int ifidx; @@ -4590,8 +4842,8 @@ extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t uint8 iftype, uint8* ea); extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits); -int dhd_wlfc_interface_event(struct dhd_info *dhd, uint8 action, uint8 ifid, uint8 iftype, - uint8* ea) +int dhd_wlfc_interface_event(struct dhd_info *dhd, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) { if (dhd->pub.wlfc_state == NULL) return BCME_OK; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c index dd9c71f75be6..d4dca2607212 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c @@ -2,13 +2,13 @@ * Broadcom Dongle Host Driver (DHD), Linux monitor network interface * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -16,12 +16,12 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux_mon.c,v 1.131.2.55 2011-02-09 05:31:56 Exp $ + * $Id: dhd_linux_mon.c 297563 2011-11-20 15:38:29Z $ */ #include <linux/string.h> @@ -96,16 +96,30 @@ static const struct net_device_ops dhd_mon_if_ops = { static struct net_device* lookup_real_netdev(char *name) { int i; + int len = 0; int last_name_len = 0; struct net_device *ndev; struct net_device *ndev_found = NULL; - /* We want to find interface "p2p-eth0-0" for monitor interface "mon.p2p-eth0-0", so - * we skip "eth0" even if "mon.p2p-eth0-0" contains "eth0" + /* We need to find interface "p2p-p2p-0" corresponding to monitor interface "mon-p2p-0", + * Once mon iface name reaches IFNAMSIZ, it is reset to p2p0-0 and corresponding mon + * iface would be mon-p2p0-0. */ for (i = 0; i < DHD_MAX_IFS; i++) { ndev = dhd_idx2net(g_monitor.dhd_pub, i); - if (ndev && strstr(name, ndev->name)) { + + /* Skip "p2p" and look for "-p2p0-x" in monitor interface name. If it + * it matches, then this netdev is the corresponding real_netdev. + */ + if (ndev && strstr(ndev->name, "p2p-p2p0")) { + len = strlen("p2p"); + } else { + /* if p2p- is not present, then the IFNAMSIZ have reached and name + * would have got reset. In this casse,look for p2p0-x in mon-p2p0-x + */ + len = 0; + } + if (ndev && strstr(name, (ndev->name + len))) { if (strlen(ndev->name) > last_name_len) { ndev_found = ndev; last_name_len = strlen(ndev->name); @@ -225,9 +239,10 @@ static void dhd_mon_if_set_multicast_list(struct net_device *ndev) mon_if = ndev_to_monif(ndev); if (mon_if == NULL || mon_if->real_ndev == NULL) { MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); } - - MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); } static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) @@ -238,9 +253,10 @@ static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) mon_if = ndev_to_monif(ndev); if (mon_if == NULL || mon_if->real_ndev == NULL) { MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); } - - MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); return ret; } diff --git a/drivers/net/wireless/bcmdhd/dhd_proto.h b/drivers/net/wireless/bcmdhd/dhd_proto.h index e0a54ad02692..bb1d7365ea94 100644 --- a/drivers/net/wireless/bcmdhd/dhd_proto.h +++ b/drivers/net/wireless/bcmdhd/dhd_proto.h @@ -34,7 +34,7 @@ #include <wlioctl.h> #ifndef IOCTL_RESP_TIMEOUT -#define IOCTL_RESP_TIMEOUT 20000 /* In milli second */ +#define IOCTL_RESP_TIMEOUT 2000 /* In milli second */ #endif /* diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 57aee5705454..2cac95f2def4 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_sdio.c 288105 2011-10-06 01:58:02Z $ + * $Id: dhd_sdio.c 326662 2012-04-10 06:38:08Z $ */ #include <typedefs.h> @@ -382,7 +382,7 @@ static bool dhd_readahead; /* To check if there's window offered */ #define DATAOK(bus) \ - (((uint8)(bus->tx_max - bus->tx_seq) > 2) && \ + (((uint8)(bus->tx_max - bus->tx_seq) > 1) && \ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) /* To check if there's window offered for ctrl frame */ @@ -801,7 +801,8 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) #ifdef DHD_DEBUG if (dhd_console_ms == 0) #endif /* DHD_DEBUG */ - dhd_os_wd_timer(bus->dhd, 0); + if (bus->poll == 0) + dhd_os_wd_timer(bus->dhd, 0); break; } #ifdef DHD_DEBUG @@ -851,8 +852,10 @@ dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); /* Isolate the bus */ - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, - SBSDIO_DEVCTL_PADS_ISO, NULL); + if (bus->sih->chip != BCM4329_CHIP_ID && bus->sih->chip != BCM4319_CHIP_ID) { + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, + SBSDIO_DEVCTL_PADS_ISO, NULL); + } /* Change state */ bus->sleeping = TRUE; @@ -1366,9 +1369,7 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) /* Send from dpc */ bus->ctrl_frame_buf = frame; bus->ctrl_frame_len = len; - dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); - if (bus->ctrl_frame_stat == FALSE) { DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ret = 0; @@ -1453,7 +1454,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) { int timeleft; uint rxlen = 0; - bool pending; + bool pending = FALSE; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -1480,8 +1481,9 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) dhd_os_sdunlock(bus->dhd); #endif /* DHD_DEBUG */ } else if (pending == TRUE) { - DHD_CTL(("%s: canceled\n", __FUNCTION__)); - return -ERESTARTSYS; + /* signal pending */ + DHD_ERROR(("%s: signal pending\n", __FUNCTION__)); + return -EINTR; } else { DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); #ifdef DHD_DEBUG @@ -1520,7 +1522,7 @@ enum { #ifdef DHD_DEBUG IOV_CHECKDIED, IOV_SERIALCONS, -#endif +#endif /* DHD_DEBUG */ IOV_DOWNLOAD, IOV_SOCRAM_STATE, IOV_FORCEEVEN, @@ -2785,6 +2787,9 @@ dhdsdio_download_state(dhd_bus_t *bus, bool enter) uint retries; int bcmerror = 0; + if (!bus->sih) + return BCME_ERROR; + /* To enter download state, disable ARM and reset SOCRAM. * To exit download state, simply reset ARM (default is RAM boot). */ @@ -3058,6 +3063,13 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) bus->rxskip = FALSE; bus->tx_seq = bus->rx_seq = 0; + /* Set to a safe default. It gets updated when we + * receive a packet from the fw but when we reset, + * we need a safe default to be able to send the + * initial mac address. + */ + bus->tx_max = 4; + if (enforce_mutex) dhd_os_sdunlock(bus->dhd); } @@ -3112,9 +3124,9 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000); ready = 0; - while (ready != enable && !dhd_timeout_expired(&tmo)) - ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); - + do { + ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); + } while (ready != enable && !dhd_timeout_expired(&tmo)); DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n", __FUNCTION__, enable, ready, tmo.elapsed)); @@ -3565,7 +3577,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq; + txmax = bus->tx_max; } bus->tx_max = txmax; @@ -3766,6 +3778,14 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN; rxseq++, rxleft--) { +#ifdef DHDTHREAD + /* tx more to improve rx performance */ + if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) { + dhdsdio_sendfromq(bus, dhd_txbound); + } +#endif /* DHDTHREAD */ + /* Handle glomming separately */ if (bus->glom || bus->glomd) { uint8 cnt; @@ -3986,7 +4006,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq; + txmax = bus->tx_max; } bus->tx_max = txmax; @@ -4143,7 +4163,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq; + txmax = bus->tx_max; } bus->tx_max = txmax; @@ -4576,8 +4596,32 @@ clkwait: bcmsdh_intr_enable(sdh); } +#if defined(OOB_INTR_ONLY) && !defined(HW_OOB) + /* In case of SW-OOB(using edge trigger), + * Check interrupt status in the dongle again after enable irq on the host. + * and rechedule dpc if interrupt is pended in the dongle. + * There is a chance to miss OOB interrupt while irq is disabled on the host. + * No need to do this with HW-OOB(level trigger) + */ + R_SDREG(newstatus, ®s->intstatus, retries); + if (bcmsdh_regfail(bus->sdh)) + newstatus = 0; + if (newstatus & bus->hostintmask) { + bus->ipend = TRUE; + resched = TRUE; + } +#endif /* defined(OOB_INTR_ONLY) && !defined(HW_OOB) */ + if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int ret, i; + uint8* frame_seq = bus->ctrl_frame_buf + SDPCM_FRAMETAG_LEN; + + if (*frame_seq != bus->tx_seq) { + DHD_INFO(("%s IOCTL frame seq lag detected!" + " frm_seq:%d != bus->tx_seq:%d, corrected\n", + __FUNCTION__, *frame_seq, bus->tx_seq)); + *frame_seq = bus->tx_seq; + } ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, @@ -5183,16 +5227,17 @@ dhdsdio_chipmatch(uint16 chipid) return TRUE; if (chipid == BCM4319_CHIP_ID) return TRUE; - if (chipid == BCM4336_CHIP_ID) - return TRUE; if (chipid == BCM4330_CHIP_ID) return TRUE; + if (chipid == BCM43239_CHIP_ID) + return TRUE; + if (chipid == BCM4336_CHIP_ID) + return TRUE; if (chipid == BCM43237_CHIP_ID) return TRUE; if (chipid == BCM43362_CHIP_ID) return TRUE; - if (chipid == BCM43239_CHIP_ID) - return TRUE; + return FALSE; } @@ -5369,6 +5414,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, if (ret == BCME_NOTUP) goto fail; } + /* Ok, have the per-port tell the stack we're open for business */ if (dhd_net_attach(bus->dhd, 0) != 0) { DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__)); @@ -5545,8 +5591,10 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, return TRUE; fail: - if (bus->sih != NULL) + if (bus->sih != NULL) { si_detach(bus->sih); + bus->sih = NULL; + } return FALSE; } @@ -5736,7 +5784,7 @@ dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh) return; if (bus->rxbuf) { -#ifndef DHD_USE_STATIC_BUF +#ifndef CONFIG_DHD_USE_STATIC_BUF MFREE(osh, bus->rxbuf, bus->rxblen); #endif bus->rxctl = bus->rxbuf = NULL; @@ -5744,7 +5792,7 @@ dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh) } if (bus->databuf) { -#ifndef DHD_USE_STATIC_BUF +#ifndef CONFIG_DHD_USE_STATIC_BUF MFREE(osh, bus->databuf, MAX_DATA_BUF); #endif bus->databuf = NULL; @@ -5779,6 +5827,7 @@ dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool r dhdsdio_clkctl(bus, CLK_NONE, FALSE); } si_detach(bus->sih); + bus->sih = NULL; if (bus->vars && bus->varsz) MFREE(osh, bus->vars, bus->varsz); bus->vars = NULL; @@ -6155,6 +6204,7 @@ dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf uint dhd_bus_chip(struct dhd_bus *bus) { + ASSERT(bus); ASSERT(bus->sih != NULL); return bus->sih->chip; } @@ -6162,6 +6212,7 @@ dhd_bus_chip(struct dhd_bus *bus) void * dhd_bus_pub(struct dhd_bus *bus) { + ASSERT(bus); return bus->dhd; } @@ -6208,7 +6259,6 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) bus->dhd->dongle_reset = TRUE; bus->dhd->up = FALSE; dhd_os_sdunlock(dhdp); - DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); /* App can now remove power from device */ } else @@ -6279,6 +6329,14 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) return bcmerror; } +/* Get Chip ID version */ +uint dhd_bus_chip_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chip; +} + int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size) { diff --git a/drivers/net/wireless/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/bcmdhd/dhd_wlfc.h index 59d018b64c6f..c4d251806d78 100644 --- a/drivers/net/wireless/bcmdhd/dhd_wlfc.h +++ b/drivers/net/wireless/bcmdhd/dhd_wlfc.h @@ -18,7 +18,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. -* $Id: dhd_wlfc.h,v 1.1.8.1 2010-09-09 22:41:08 Exp $ +* $Id: dhd_wlfc.h 286994 2011-09-29 21:27:44Z $ * */ #ifndef __wlfc_host_driver_definitions_h__ @@ -201,6 +201,7 @@ typedef struct athost_wl_stat_counters { #define WLFC_FCMODE_IMPLIED_CREDIT 1 #define WLFC_FCMODE_EXPLICIT_CREDIT 2 +/* How long to defer borrowing in milliseconds */ #define WLFC_BORROW_DEFER_PERIOD_MS 100 /* Mask to represent available ACs (note: BC/MC is ignored */ @@ -261,6 +262,15 @@ typedef struct athost_wl_status_info { /* Timestamp to compute how long to defer borrowing for */ uint32 borrow_defer_timestamp; + } athost_wl_status_info_t; +int dhd_wlfc_enable(dhd_pub_t *dhd); +int dhd_wlfc_interface_event(struct dhd_info *, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea); +int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); +int dhd_wlfc_event(struct dhd_info *dhd); +int dhd_os_wlfc_block(dhd_pub_t *pub); +int dhd_os_wlfc_unblock(dhd_pub_t *pub); + #endif /* __wlfc_host_driver_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/hndpmu.c b/drivers/net/wireless/bcmdhd/hndpmu.c index b9586e40d0c2..0e493343c806 100644 --- a/drivers/net/wireless/bcmdhd/hndpmu.c +++ b/drivers/net/wireless/bcmdhd/hndpmu.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndpmu.c,v 1.228.2.56 2011-02-11 22:49:07 Exp $ + * $Id: hndpmu.c,v 1.228.2.56 2011-02-11 22:49:07 $ */ #include <typedefs.h> @@ -93,26 +93,8 @@ static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v8[] = { {0, 0x1} }; /* SDIO Drive Strength to sel value table for PMU Rev 11 (1.2v) */ -static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v2[] = { - {16, 0x3}, - {13, 0x2}, - {11, 0x1}, - {8, 0x0}, - {6, 0x7}, - {4, 0x6}, - {2, 0x5}, - {0, 0x4} }; /* SDIO Drive Strength to sel value table for PMU Rev 11 (2.5v) */ -static const sdiod_drive_str_t sdiod_drive_strength_tab4_2v5[] = { - {80, 0x5}, - {65, 0x4}, - {55, 0x7}, - {40, 0x6}, - {30, 0x1}, - {20, 0x0}, - {10, 0x3}, - {0, 0x2} }; /* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { @@ -125,14 +107,6 @@ static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { {0, 0x0} }; /* SDIO Drive Strength to sel value table for PMU Rev 13 (3.3v) */ -static const sdiod_drive_str_t sdiod_drive_strength_tab5_3v3[] = { - {12, 0x7}, - {10, 0x6}, - {8, 0x5}, - {6, 0x4}, - {4, 0x2}, - {2, 0x1}, - {0, 0x0} }; #define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) diff --git a/drivers/net/wireless/bcmdhd/include/Makefile b/drivers/net/wireless/bcmdhd/include/Makefile index c07266fd6fdc..67c4906f5889 100644 --- a/drivers/net/wireless/bcmdhd/include/Makefile +++ b/drivers/net/wireless/bcmdhd/include/Makefile @@ -10,7 +10,7 @@ # # Copyright 2005, Broadcom, Inc. # -# $Id: Makefile 241702 2011-02-19 00:41:03Z automrgr $ +# $Id: Makefile 241702 2011-02-19 00:41:03Z $ # SRCBASE := .. diff --git a/drivers/net/wireless/bcmdhd/include/aidmp.h b/drivers/net/wireless/bcmdhd/include/aidmp.h index 375df443a29a..b993a033abc2 100644 --- a/drivers/net/wireless/bcmdhd/include/aidmp.h +++ b/drivers/net/wireless/bcmdhd/include/aidmp.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: aidmp.h,v 13.4.14.1 2010-03-09 18:40:06 Exp $ + * $Id: aidmp.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmcdc.h b/drivers/net/wireless/bcmdhd/include/bcmcdc.h index ce45c50d9641..77a20f87b7ea 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmcdc.h +++ b/drivers/net/wireless/bcmdhd/include/bcmcdc.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmcdc.h,v 13.25.10.3 2010-12-22 23:47:26 Exp $ + * $Id: bcmcdc.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _bcmcdc_h_ diff --git a/drivers/net/wireless/bcmdhd/include/bcmdefs.h b/drivers/net/wireless/bcmdhd/include/bcmdefs.h index da1fd5e4eac4..17cc0e955f62 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmdefs.h +++ b/drivers/net/wireless/bcmdhd/include/bcmdefs.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmdefs.h,v 13.68.2.8 2011-01-08 04:04:19 Exp $ + * $Id: bcmdefs.h 279282 2011-08-23 22:44:02Z $ */ @@ -30,12 +30,26 @@ + +#define BCM_REFERENCE(data) ((void)(data)) + + + #define bcmreclaimed 0 #define _data _data #define _fn _fn +#define BCMPREATTACHDATA(_data) _data +#define BCMPREATTACHFN(_fn) _fn #define _data _data #define _fn _fn #define _fn _fn +#define BCMNMIATTACHFN(_fn) _fn +#define BCMNMIATTACHDATA(_data) _data +#define BCMOVERLAY0DATA(_sym) _sym +#define BCMOVERLAY0FN(_fn) _fn +#define BCMOVERLAY1DATA(_sym) _sym +#define BCMOVERLAY1FN(_fn) _fn +#define BCMOVERLAYERRFN(_fn) _fn #define CONST const #define BCMFASTPATH @@ -43,9 +57,30 @@ #define _data _data +#define BCMROMDAT_NAME(_data) _data #define _fn _fn #define _fn _fn #define STATIC static +#define BCMROMDAT_ARYSIZ(data) ARRAYSIZE(data) +#define BCMROMDAT_SIZEOF(data) sizeof(data) +#define BCMROMDAT_APATCH(data) +#define BCMROMDAT_SPATCH(data) + + + +#define OVERLAY_INLINE +#define OSTATIC static +#define BCMOVERLAYDATA(_ovly, _sym) _sym +#define BCMOVERLAYFN(_ovly, _fn) _fn +#define BCMOVERLAYERRFN(_fn) _fn +#define BCMROMOVERLAYDATA(_ovly, _data) _data +#define BCMROMOVERLAYFN(_ovly, _fn) _fn +#define BCMATTACHOVERLAYDATA(_ovly, _sym) _sym +#define BCMATTACHOVERLAYFN(_ovly, _fn) _fn +#define BCMINITOVERLAYDATA(_ovly, _sym) _sym +#define BCMINITOVERLAYFN(_ovly, _fn) _fn +#define BCMUNINITOVERLAYFN(_ovly, _fn) _fn + #define SI_BUS 0 diff --git a/drivers/net/wireless/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/bcmdhd/include/bcmdevs.h index 4f707c0c6920..287f1c65fc9a 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmdevs.h +++ b/drivers/net/wireless/bcmdhd/include/bcmdevs.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmdevs.h,v 13.285.2.39 2011-02-04 05:03:16 Exp $ + * $Id: bcmdevs.h 295140 2011-11-09 17:22:01Z $ */ @@ -31,7 +31,16 @@ #define VENDOR_EPIGRAM 0xfeda #define VENDOR_BROADCOM 0x14e4 +#define VENDOR_3COM 0x10b7 +#define VENDOR_NETGEAR 0x1385 +#define VENDOR_DIAMOND 0x1092 +#define VENDOR_INTEL 0x8086 +#define VENDOR_DELL 0x1028 +#define VENDOR_HP 0x103c +#define VENDOR_HP_COMPAQ 0x0e11 +#define VENDOR_APPLE 0x106b #define VENDOR_SI_IMAGE 0x1095 +#define VENDOR_BUFFALO 0x1154 #define VENDOR_TI 0x104c #define VENDOR_RICOH 0x1180 #define VENDOR_JMICRON 0x197b @@ -54,9 +63,38 @@ #define BCM_DNGL_BL_PID_43239 0xbd1b #define BCM_DNGL_BDC_PID 0x0bdc #define BCM_DNGL_JTAG_PID 0x4a44 +#define BCM_DNGL_BL_PID_4324 0xbd1c + + +#define BCM_HWUSB_PID_43239 43239 + + +#define BCM4210_DEVICE_ID 0x1072 +#define BCM4230_DEVICE_ID 0x1086 +#define BCM4401_ENET_ID 0x170c +#define BCM3352_DEVICE_ID 0x3352 +#define BCM3360_DEVICE_ID 0x3360 +#define BCM4211_DEVICE_ID 0x4211 +#define BCM4231_DEVICE_ID 0x4231 +#define BCM4303_D11B_ID 0x4303 +#define BCM4311_D11G_ID 0x4311 +#define BCM4311_D11DUAL_ID 0x4312 +#define BCM4311_D11A_ID 0x4313 +#define BCM4328_D11DUAL_ID 0x4314 +#define BCM4328_D11G_ID 0x4315 +#define BCM4328_D11A_ID 0x4316 +#define BCM4318_D11G_ID 0x4318 +#define BCM4318_D11DUAL_ID 0x4319 +#define BCM4318_D11A_ID 0x431a #define BCM4325_D11DUAL_ID 0x431b #define BCM4325_D11G_ID 0x431c #define BCM4325_D11A_ID 0x431d +#define BCM4306_D11G_ID 0x4320 +#define BCM4306_D11A_ID 0x4321 +#define BCM4306_UART_ID 0x4322 +#define BCM4306_V90_ID 0x4323 +#define BCM4306_D11DUAL_ID 0x4324 +#define BCM4306_D11G_ID2 0x4325 #define BCM4321_D11N_ID 0x4328 #define BCM4321_D11N2G_ID 0x4329 #define BCM4321_D11N5G_ID 0x432a @@ -98,17 +136,58 @@ #define BCM43237_D11N5G_ID 0x4356 #define BCM43227_D11N2G_ID 0x4358 #define BCM43228_D11N_ID 0x4359 -#define BCM43228_D11N5G_ID 0x435a +#define BCM43228_D11N5G_ID 0x435a #define BCM43362_D11N_ID 0x4363 #define BCM43239_D11N_ID 0x4370 +#define BCM4324_D11N_ID 0x4374 +#define BCM43217_D11N2G_ID 0x43a9 +#define BCM43131_D11N2G_ID 0x43aa +#define BCM4314_D11N2G_ID 0x4364 +#define BCM43142_D11N2G_ID 0x4365 +#define BCMGPRS_UART_ID 0x4333 +#define BCMGPRS2_UART_ID 0x4344 +#define FPGA_JTAGM_ID 0x43f0 +#define BCM_JTAGM_ID 0x43f1 #define SDIOH_FPGA_ID 0x43f2 +#define BCM_SDIOH_ID 0x43f3 +#define SDIOD_FPGA_ID 0x43f4 #define SPIH_FPGA_ID 0x43f5 +#define BCM_SPIH_ID 0x43f6 +#define MIMO_FPGA_ID 0x43f8 +#define BCM_JTAGM2_ID 0x43f9 +#define SDHCI_FPGA_ID 0x43fa +#define BCM4402_ENET_ID 0x4402 +#define BCM4402_V90_ID 0x4403 +#define BCM4410_DEVICE_ID 0x4410 +#define BCM4412_DEVICE_ID 0x4412 +#define BCM4430_DEVICE_ID 0x4430 +#define BCM4432_DEVICE_ID 0x4432 +#define BCM4704_ENET_ID 0x4706 #define BCM4710_DEVICE_ID 0x4710 +#define BCM47XX_AUDIO_ID 0x4711 +#define BCM47XX_V90_ID 0x4712 +#define BCM47XX_ENET_ID 0x4713 +#define BCM47XX_EXT_ID 0x4714 +#define BCM47XX_GMAC_ID 0x4715 +#define BCM47XX_USBH_ID 0x4716 +#define BCM47XX_USBD_ID 0x4717 +#define BCM47XX_IPSEC_ID 0x4718 +#define BCM47XX_ROBO_ID 0x4719 +#define BCM47XX_USB20H_ID 0x471a +#define BCM47XX_USB20D_ID 0x471b +#define BCM47XX_ATA100_ID 0x471d +#define BCM47XX_SATAXOR_ID 0x471e +#define BCM47XX_GIGETH_ID 0x471f +#define BCM4712_MIPS_ID 0x4720 +#define BCM4716_DEVICE_ID 0x4722 +#define BCM47XX_SMBUS_EMU_ID 0x47fe +#define BCM47XX_XOR_EMU_ID 0x47ff +#define EPI41210_DEVICE_ID 0xa0fa +#define EPI41230_DEVICE_ID 0xa10e +#define JINVANI_SDIOH_ID 0x4743 #define BCM27XX_SDIOH_ID 0x2702 -#define PCIXX21_FLASHMEDIA0_ID 0x8033 -#define PCIXX21_SDIOH0_ID 0x8034 #define PCIXX21_FLASHMEDIA_ID 0x803b #define PCIXX21_SDIOH_ID 0x803c #define R5C822_SDIOH_ID 0x0822 @@ -121,11 +200,13 @@ #define BCM43112_CHIP_ID 43112 #define BCM4312_CHIP_ID 0x4312 #define BCM4313_CHIP_ID 0x4313 +#define BCM43131_CHIP_ID 43131 #define BCM4315_CHIP_ID 0x4315 #define BCM4318_CHIP_ID 0x4318 #define BCM4319_CHIP_ID 0x4319 #define BCM4320_CHIP_ID 0x4320 #define BCM4321_CHIP_ID 0x4321 +#define BCM43217_CHIP_ID 43217 #define BCM4322_CHIP_ID 0x4322 #define BCM43221_CHIP_ID 43221 #define BCM43222_CHIP_ID 43222 @@ -152,15 +233,28 @@ #define BCM4336_CHIP_ID 0x4336 #define BCM43362_CHIP_ID 43362 #define BCM4330_CHIP_ID 0x4330 +#define BCM6362_CHIP_ID 0x6362 +#define BCM4314_CHIP_ID 0x4314 +#define BCM43142_CHIP_ID 43142 +#define BCM4324_CHIP_ID 0x4324 + +#define BCM4342_CHIP_ID 4342 #define BCM4402_CHIP_ID 0x4402 #define BCM4704_CHIP_ID 0x4704 #define BCM4710_CHIP_ID 0x4710 #define BCM4712_CHIP_ID 0x4712 +#define BCM4716_CHIP_ID 0x4716 +#define BCM47162_CHIP_ID 47162 +#define BCM4748_CHIP_ID 0x4748 +#define BCM4749_CHIP_ID 0x4749 #define BCM4785_CHIP_ID 0x4785 #define BCM5350_CHIP_ID 0x5350 #define BCM5352_CHIP_ID 0x5352 #define BCM5354_CHIP_ID 0x5354 #define BCM5365_CHIP_ID 0x5365 +#define BCM5356_CHIP_ID 0x5356 +#define BCM5357_CHIP_ID 0x5357 +#define BCM53572_CHIP_ID 53572 #define BCM4303_PKG_ID 2 @@ -175,8 +269,478 @@ #define BCM4329_289PIN_PKG_ID 0 #define BCM4329_182PIN_PKG_ID 1 #define BCM5354E_PKG_ID 1 +#define BCM4716_PKG_ID 8 +#define BCM4717_PKG_ID 9 +#define BCM4718_PKG_ID 10 +#define BCM5356_PKG_NONMODE 1 +#define BCM5358U_PKG_ID 8 +#define BCM5358_PKG_ID 9 +#define BCM47186_PKG_ID 10 +#define BCM5357_PKG_ID 11 +#define BCM5356U_PKG_ID 12 +#define BCM53572_PKG_ID 8 +#define BCM47188_PKG_ID 9 +#define BCM4331TT_PKG_ID 8 +#define BCM4331TN_PKG_ID 9 +#define BCM4331TNA0_PKG_ID 0xb + + #define HDLSIM5350_PKG_ID 1 #define HDLSIM_PKG_ID 14 #define HWSIM_PKG_ID 15 +#define BCM43224_FAB_CSM 0x8 +#define BCM43224_FAB_SMIC 0xa +#define BCM4336_WLBGA_PKG_ID 0x8 +#define BCM4330_WLBGA_PKG_ID 0x0 +#define BCM4314PCIE_ARM_PKG_ID (8 | 0) +#define BCM4314SDIO_PKG_ID (8 | 1) +#define BCM4314PCIE_PKG_ID (8 | 2) +#define BCM4314SDIO_ARM_PKG_ID (8 | 3) +#define BCM4314SDIO_FPBGA_PKG_ID (8 | 4) +#define BCM4314DEV_PKG_ID (8 | 6) + +#define PCIXX21_FLASHMEDIA0_ID 0x8033 +#define PCIXX21_SDIOH0_ID 0x8034 + + +#define BFL_BTC2WIRE 0x00000001 +#define BFL_BTCOEX 0x00000001 +#define BFL_PACTRL 0x00000002 +#define BFL_AIRLINEMODE 0x00000004 +#define BFL_ADCDIV 0x00000008 +#define BFL_ENETROBO 0x00000010 +#define BFL_NOPLLDOWN 0x00000020 +#define BFL_CCKHIPWR 0x00000040 +#define BFL_ENETADM 0x00000080 +#define BFL_ENETVLAN 0x00000100 +#ifdef WLAFTERBURNER +#define BFL_AFTERBURNER 0x00000200 +#endif +#define BFL_NOPCI 0x00000400 +#define BFL_FEM 0x00000800 +#define BFL_EXTLNA 0x00001000 +#define BFL_HGPA 0x00002000 +#define BFL_BTC2WIRE_ALTGPIO 0x00004000 +#define BFL_ALTIQ 0x00008000 +#define BFL_NOPA 0x00010000 +#define BFL_RSSIINV 0x00020000 +#define BFL_PAREF 0x00040000 +#define BFL_3TSWITCH 0x00080000 +#define BFL_PHASESHIFT 0x00100000 +#define BFL_BUCKBOOST 0x00200000 +#define BFL_FEM_BT 0x00400000 +#define BFL_NOCBUCK 0x00800000 +#define BFL_CCKFAVOREVM 0x01000000 +#define BFL_PALDO 0x02000000 +#define BFL_LNLDO2_2P5 0x04000000 +#define BFL_FASTPWR 0x08000000 +#define BFL_UCPWRCTL_MININDX 0x08000000 +#define BFL_EXTLNA_5GHz 0x10000000 +#define BFL_TRSW_1by2 0x20000000 +#define BFL_LO_TRSW_R_5GHz 0x40000000 +#define BFL_ELNA_GAINDEF 0x80000000 +#define BFL_EXTLNA_TX 0x20000000 + + +#define BFL2_RXBB_INT_REG_DIS 0x00000001 +#define BFL2_APLL_WAR 0x00000002 +#define BFL2_TXPWRCTRL_EN 0x00000004 +#define BFL2_2X4_DIV 0x00000008 +#define BFL2_5G_PWRGAIN 0x00000010 +#define BFL2_PCIEWAR_OVR 0x00000020 +#define BFL2_CAESERS_BRD 0x00000040 +#define BFL2_BTC3WIRE 0x00000080 +#define BFL2_BTCLEGACY 0x00000080 +#define BFL2_SKWRKFEM_BRD 0x00000100 +#define BFL2_SPUR_WAR 0x00000200 +#define BFL2_GPLL_WAR 0x00000400 +#define BFL2_TRISTATE_LED 0x00000800 +#define BFL2_SINGLEANT_CCK 0x00001000 +#define BFL2_2G_SPUR_WAR 0x00002000 +#define BFL2_BPHY_ALL_TXCORES 0x00004000 +#define BFL2_FCC_BANDEDGE_WAR 0x00008000 +#define BFL2_GPLL_WAR2 0x00010000 +#define BFL2_IPALVLSHIFT_3P3 0x00020000 +#define BFL2_INTERNDET_TXIQCAL 0x00040000 +#define BFL2_XTALBUFOUTEN 0x00080000 +#define BFL2_ANAPACTRL_2G 0x00100000 +#define BFL2_ANAPACTRL_5G 0x00200000 +#define BFL2_ELNACTRL_TRSW_2G 0x00400000 +#define BFL2_BT_SHARE_ANT0 0x00800000 +#define BFL2_TEMPSENSE_HIGHER 0x01000000 +#define BFL2_BTC3WIREONLY 0x02000000 +#define BFL2_PWR_NOMINAL 0x04000000 +#define BFL2_EXTLNA_TX 0x08000000 + +#define BFL2_4313_RADIOREG 0x10000000 + + + +#define BOARD_GPIO_BTC3W_IN 0x850 +#define BOARD_GPIO_BTC3W_OUT 0x020 +#define BOARD_GPIO_BTCMOD_IN 0x010 +#define BOARD_GPIO_BTCMOD_OUT 0x020 +#define BOARD_GPIO_BTC_IN 0x080 +#define BOARD_GPIO_BTC_OUT 0x100 +#define BOARD_GPIO_PACTRL 0x200 +#define BOARD_GPIO_12 0x1000 +#define BOARD_GPIO_13 0x2000 +#define BOARD_GPIO_BTC4_IN 0x0800 +#define BOARD_GPIO_BTC4_BT 0x2000 +#define BOARD_GPIO_BTC4_STAT 0x4000 +#define BOARD_GPIO_BTC4_WLAN 0x8000 +#define BOARD_GPIO_1_WLAN_PWR 0x2 +#define BOARD_GPIO_4_WLAN_PWR 0x10 + +#define GPIO_BTC4W_OUT_4312 0x010 +#define GPIO_BTC4W_OUT_43224 0x020 +#define GPIO_BTC4W_OUT_43224_SHARED 0x0e0 +#define GPIO_BTC4W_OUT_43225 0x0e0 +#define GPIO_BTC4W_OUT_43421 0x020 +#define GPIO_BTC4W_OUT_4313 0x060 + +#define PCI_CFG_GPIO_SCS 0x10 +#define PCI_CFG_GPIO_HWRAD 0x20 +#define PCI_CFG_GPIO_XTAL 0x40 +#define PCI_CFG_GPIO_PLL 0x80 + + +#define PLL_DELAY 150 +#define FREF_DELAY 200 +#define MIN_SLOW_CLK 32 +#define XTAL_ON_DELAY 1000 + + +#define BU4710_BOARD 0x0400 +#define VSIM4710_BOARD 0x0401 +#define QT4710_BOARD 0x0402 + +#define BU4309_BOARD 0x040a +#define BCM94309CB_BOARD 0x040b +#define BCM94309MP_BOARD 0x040c +#define BCM4309AP_BOARD 0x040d + +#define BCM94302MP_BOARD 0x040e + +#define BU4306_BOARD 0x0416 +#define BCM94306CB_BOARD 0x0417 +#define BCM94306MP_BOARD 0x0418 + +#define BCM94710D_BOARD 0x041a +#define BCM94710R1_BOARD 0x041b +#define BCM94710R4_BOARD 0x041c +#define BCM94710AP_BOARD 0x041d + +#define BU2050_BOARD 0x041f + +#define BCM94306P50_BOARD 0x0420 + +#define BCM94309G_BOARD 0x0421 + +#define BU4704_BOARD 0x0423 +#define BU4702_BOARD 0x0424 + +#define BCM94306PC_BOARD 0x0425 + +#define MPSG4306_BOARD 0x0427 + +#define BCM94702MN_BOARD 0x0428 + + +#define BCM94702CPCI_BOARD 0x0429 + + +#define BCM95380RR_BOARD 0x042a + + +#define BCM94306CBSG_BOARD 0x042b + + +#define PCSG94306_BOARD 0x042d + + +#define BU4704SD_BOARD 0x042e + + +#define BCM94704AGR_BOARD 0x042f + + +#define BCM94308MP_BOARD 0x0430 + + +#define BCM94306GPRS_BOARD 0x0432 + + +#define BU5365_FPGA_BOARD 0x0433 + +#define BU4712_BOARD 0x0444 +#define BU4712SD_BOARD 0x045d +#define BU4712L_BOARD 0x045f + + +#define BCM94712AP_BOARD 0x0445 +#define BCM94712P_BOARD 0x0446 + + +#define BU4318_BOARD 0x0447 +#define CB4318_BOARD 0x0448 +#define MPG4318_BOARD 0x0449 +#define MP4318_BOARD 0x044a +#define SD4318_BOARD 0x044b + + +#define BCM94313BU_BOARD 0x050f +#define BCM94313HM_BOARD 0x0510 +#define BCM94313EPA_BOARD 0x0511 +#define BCM94313HMG_BOARD 0x051C + + +#define BCM96338_BOARD 0x6338 +#define BCM96348_BOARD 0x6348 +#define BCM96358_BOARD 0x6358 +#define BCM96368_BOARD 0x6368 + + +#define BCM94306P_BOARD 0x044c + + +#define BCM94303MP_BOARD 0x044e + + +#define BCM94306MPSGH_BOARD 0x044f + + +#define BCM94306MPM 0x0450 +#define BCM94306MPL 0x0453 + + +#define BCM94712AGR_BOARD 0x0451 + + +#define PC4303_BOARD 0x0454 + + +#define BCM95350K_BOARD 0x0455 + + +#define BCM95350R_BOARD 0x0456 + + +#define BCM94306MPLNA_BOARD 0x0457 + + +#define BU4320_BOARD 0x0458 +#define BU4320S_BOARD 0x0459 +#define BCM94320PH_BOARD 0x045a + + +#define BCM94306MPH_BOARD 0x045b + + +#define BCM94306PCIV_BOARD 0x045c + +#define BU4712SD_BOARD 0x045d + +#define BCM94320PFLSH_BOARD 0x045e + +#define BU4712L_BOARD 0x045f +#define BCM94712LGR_BOARD 0x0460 +#define BCM94320R_BOARD 0x0461 + +#define BU5352_BOARD 0x0462 + +#define BCM94318MPGH_BOARD 0x0463 + +#define BU4311_BOARD 0x0464 +#define BCM94311MC_BOARD 0x0465 +#define BCM94311MCAG_BOARD 0x0466 + +#define BCM95352GR_BOARD 0x0467 + + +#define BCM95351AGR_BOARD 0x0470 + + +#define BCM94704MPCB_BOARD 0x0472 + + +#define BU4785_BOARD 0x0478 + + +#define BU4321_BOARD 0x046b +#define BU4321E_BOARD 0x047c +#define MP4321_BOARD 0x046c +#define CB2_4321_BOARD 0x046d +#define CB2_4321_AG_BOARD 0x0066 +#define MC4321_BOARD 0x046e + + +#define BU4328_BOARD 0x0481 +#define BCM4328SDG_BOARD 0x0482 +#define BCM4328SDAG_BOARD 0x0483 +#define BCM4328UG_BOARD 0x0484 +#define BCM4328UAG_BOARD 0x0485 +#define BCM4328PC_BOARD 0x0486 +#define BCM4328CF_BOARD 0x0487 + + +#define BCM94325DEVBU_BOARD 0x0490 +#define BCM94325BGABU_BOARD 0x0491 + +#define BCM94325SDGWB_BOARD 0x0492 + +#define BCM94325SDGMDL_BOARD 0x04aa +#define BCM94325SDGMDL2_BOARD 0x04c6 +#define BCM94325SDGMDL3_BOARD 0x04c9 + +#define BCM94325SDABGWBA_BOARD 0x04e1 + + +#define BCM94322MC_SSID 0x04a4 +#define BCM94322USB_SSID 0x04a8 +#define BCM94322HM_SSID 0x04b0 +#define BCM94322USB2D_SSID 0x04bf + + +#define BCM4312MCGSG_BOARD 0x04b5 + + +#define BCM94315DEVBU_SSID 0x04c2 +#define BCM94315USBGP_SSID 0x04c7 +#define BCM94315BGABU_SSID 0x04ca +#define BCM94315USBGP41_SSID 0x04cb + + +#define BCM94319DEVBU_SSID 0X04e5 +#define BCM94319USB_SSID 0X04e6 +#define BCM94319SD_SSID 0X04e7 + + +#define BCM94716NR2_SSID 0x04cd + + +#define BCM94319DEVBU_SSID 0X04e5 +#define BCM94319USBNP4L_SSID 0X04e6 +#define BCM94319WLUSBN4L_SSID 0X04e7 +#define BCM94319SDG_SSID 0X04ea +#define BCM94319LCUSBSDN4L_SSID 0X04eb +#define BCM94319USBB_SSID 0x04ee +#define BCM94319LCSDN4L_SSID 0X0507 +#define BCM94319LSUSBN4L_SSID 0X0508 +#define BCM94319SDNA4L_SSID 0X0517 +#define BCM94319SDELNA4L_SSID 0X0518 +#define BCM94319SDELNA6L_SSID 0X0539 +#define BCM94319ARCADYAN_SSID 0X0546 +#define BCM94319WINDSOR_SSID 0x0561 +#define BCM94319MLAP_SSID 0x0562 +#define BCM94319SDNA_SSID 0x058b +#define BCM94319BHEMU3_SSID 0x0563 +#define BCM94319SDHMB_SSID 0x058c +#define BCM94319SDBREF_SSID 0x05a1 +#define BCM94319USBSDB_SSID 0x05a2 + + + +#define BCM94329AGB_SSID 0X04b9 +#define BCM94329TDKMDL1_SSID 0X04ba +#define BCM94329TDKMDL11_SSID 0X04fc +#define BCM94329OLYMPICN18_SSID 0X04fd +#define BCM94329OLYMPICN90_SSID 0X04fe +#define BCM94329OLYMPICN90U_SSID 0X050c +#define BCM94329OLYMPICN90M_SSID 0X050b +#define BCM94329AGBF_SSID 0X04ff +#define BCM94329OLYMPICX17_SSID 0X0504 +#define BCM94329OLYMPICX17M_SSID 0X050a +#define BCM94329OLYMPICX17U_SSID 0X0509 +#define BCM94329OLYMPICUNO_SSID 0X0564 +#define BCM94329MOTOROLA_SSID 0X0565 +#define BCM94329OLYMPICLOCO_SSID 0X0568 + +#define BCM94336SD_WLBGABU_SSID 0x0511 +#define BCM94336SD_WLBGAREF_SSID 0x0519 +#define BCM94336SDGP_SSID 0x0538 +#define BCM94336SDG_SSID 0x0519 +#define BCM94336SDGN_SSID 0x0538 +#define BCM94336SDGFC_SSID 0x056B + + +#define BCM94330SDG_SSID 0x0528 +#define BCM94330SD_FCBGABU_SSID 0x052e +#define BCM94330SD_WLBGABU_SSID 0x052f +#define BCM94330SD_FCBGA_SSID 0x0530 +#define BCM94330FCSDAGB_SSID 0x0532 +#define BCM94330OLYMPICAMG_SSID 0x0549 +#define BCM94330OLYMPICAMGEPA_SSID 0x054F +#define BCM94330OLYMPICUNO3_SSID 0x0551 +#define BCM94330WLSDAGB_SSID 0x0547 +#define BCM94330CSPSDAGBB_SSID 0x054A + + +#define BCM943224X21 0x056e +#define BCM943224X21_FCC 0x00d1 + + +#define BCM943228BU8_SSID 0x0540 +#define BCM943228BU9_SSID 0x0541 +#define BCM943228BU_SSID 0x0542 +#define BCM943227HM4L_SSID 0x0543 +#define BCM943227HMB_SSID 0x0544 +#define BCM943228HM4L_SSID 0x0545 +#define BCM943228SD_SSID 0x0573 + + +#define BCM943239MOD_SSID 0x05ac +#define BCM943239REF_SSID 0x05aa + + +#define BCM94331X19 0x00D6 +#define BCM94331PCIEBT3Ax_SSID 0x00E4 +#define BCM94331X12_2G_SSID 0x00EC +#define BCM94331X12_5G_SSID 0x00ED +#define BCM94331X29B 0x00EF +#define BCM94331BU_SSID 0x0523 +#define BCM94331S9BU_SSID 0x0524 +#define BCM94331MC_SSID 0x0525 +#define BCM94331MCI_SSID 0x0526 +#define BCM94331PCIEBT4_SSID 0x0527 +#define BCM94331HM_SSID 0x0574 +#define BCM94331PCIEDUAL_SSID 0x059B +#define BCM94331MCH5_SSID 0x05A9 +#define BCM94331PCIEDUALV2_SSID 0x05B7 +#define BCM94331CS_SSID 0x05C6 +#define BCM94331CSAX_SSID 0x00EF + + +#define BCM953572BU_SSID 0x058D +#define BCM953572NR2_SSID 0x058E +#define BCM947188NR2_SSID 0x058F +#define BCM953572SDRNR2_SSID 0x0590 + + +#define BCM943236OLYMPICSULLEY_SSID 0x594 +#define BCM943236PREPROTOBLU2O3_SSID 0x5b9 +#define BCM943236USBELNA_SSID 0x5f8 + + +#define GPIO_NUMPINS 32 + + +#define RDL_RAM_BASE_4319 0x60000000 +#define RDL_RAM_BASE_4329 0x60000000 +#define RDL_RAM_SIZE_4319 0x48000 +#define RDL_RAM_SIZE_4329 0x48000 +#define RDL_RAM_SIZE_43236 0x70000 +#define RDL_RAM_BASE_43236 0x60000000 +#define RDL_RAM_SIZE_4328 0x60000 +#define RDL_RAM_BASE_4328 0x80000000 +#define RDL_RAM_SIZE_4322 0x60000 +#define RDL_RAM_BASE_4322 0x60000000 + + +#define MUXENAB_UART 0x00000001 +#define MUXENAB_GPIO 0x00000002 +#define MUXENAB_ERCX 0x00000004 +#define MUXENAB_JTAG 0x00000008 +#define MUXENAB_HOST_WAKE 0x00000010 #endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmendian.h b/drivers/net/wireless/bcmdhd/include/bcmendian.h index 04b07ecb8043..f3356a724b44 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmendian.h +++ b/drivers/net/wireless/bcmdhd/include/bcmendian.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmendian.h,v 1.36 2009-11-09 05:29:43 Exp $ + * $Id: bcmendian.h 277737 2011-08-16 17:54:59Z $ * * This file by default provides proper behavior on little-endian architectures. * On big-endian architectures, IL_BIGENDIAN should be defined. diff --git a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h index fd148c591d88..51e0427e7f60 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmpcispi.h,v 13.15.112.1 2010-11-15 18:22:12 Exp $ + * $Id: bcmpcispi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _BCM_PCI_SPI_H #define _BCM_PCI_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/bcmperf.h b/drivers/net/wireless/bcmdhd/include/bcmperf.h index a3985cf29375..a503edbd6226 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmperf.h +++ b/drivers/net/wireless/bcmdhd/include/bcmperf.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmperf.h,v 13.5 2007-09-14 22:00:59 Exp $ + * $Id: bcmperf.h 277737 2011-08-16 17:54:59Z $ */ /* essai */ #ifndef _BCMPERF_H_ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h index 5fda5e9b5df4..21a58b473e91 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdbus.h,v 13.17.86.2 2010-12-23 01:13:20 Exp $ + * $Id: bcmsdbus.h 300017 2011-12-01 20:30:27Z $ */ #ifndef _sdio_api_h_ @@ -117,4 +117,12 @@ void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); +extern SDIOH_API_RC sdioh_sleep(sdioh_info_t *si, bool enab); + +/* GPIO support */ +extern SDIOH_API_RC sdioh_gpio_init(sdioh_info_t *sd); +extern bool sdioh_gpioin(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab); + #endif /* _sdio_api_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh.h b/drivers/net/wireless/bcmdhd/include/bcmsdh.h index 4e3affde6b06..72ee65da8874 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdh.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh.h @@ -23,7 +23,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh.h,v 13.46.52.3 2010-10-19 00:41:44 Exp $ + * $Id: bcmsdh.h 300017 2011-12-01 20:30:27Z $ */ #ifndef _bcmsdh_h_ @@ -208,4 +208,12 @@ extern uint32 bcmsdh_cur_sbwad(void *sdh); extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev); +extern int bcmsdh_sleep(void *sdh, bool enab); + +/* GPIO support */ +extern int bcmsdh_gpio_init(void *sd); +extern bool bcmsdh_gpioin(void *sd, uint32 gpio); +extern int bcmsdh_gpioouten(void *sd, uint32 gpio); +extern int bcmsdh_gpioout(void *sd, uint32 gpio, bool enab); + #endif /* _bcmsdh_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h index d188c4ec7d5a..db8ea596304c 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc.h,v 13.5.88.1 2010-12-23 01:13:20 Exp $ + * $Id: bcmsdh_sdmmc.h 314048 2012-02-09 20:31:56Z $ */ #ifndef __BCMSDH_SDMMC_H__ @@ -82,9 +82,10 @@ struct sdioh_info { uint8 num_funcs; /* Supported funcs on client */ uint32 com_cis_ptr; uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; - uint max_dma_len; - uint max_dma_descriptors; /* DMA Descriptors supported by this controller. */ -// SDDMA_DESCRIPTOR SGList[32]; /* Scatter/Gather DMA List */ + +#define SDIOH_SDMMC_MAX_SG_ENTRIES 32 + struct scatterlist sg_list[SDIOH_SDMMC_MAX_SG_ENTRIES]; + bool use_rxchain; }; /************************************************************ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h index ee29b5c08a5c..1b9d39fee8fc 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdpcm.h,v 13.4.90.2 2010-05-12 04:14:25 Exp $ + * $Id: bcmsdpcm.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _bcmsdpcm_h_ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h index 0bff355f8ffd..a62bee42b2ba 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdspi.h,v 13.11.86.1 2010-11-15 18:14:56 Exp $ + * $Id: bcmsdspi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _BCM_SD_SPI_H #define _BCM_SD_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h index 0f4c0267dbc8..c7382540b84f 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdstd.h,v 13.21.2.6 2010-11-15 18:14:01 Exp $ + * $Id: bcmsdstd.h 324819 2012-03-30 12:15:19Z $ */ #ifndef _BCM_SD_STD_H #define _BCM_SD_STD_H @@ -78,6 +78,7 @@ extern void sdstd_osfree(sdioh_info_t *sd); #define SDIOH_CMD7_EXP_STATUS 0x00001E00 #define RETRIES_LARGE 100000 +#define sdstd_os_yield(sd) do {} while (0) #define RETRIES_SMALL 100 @@ -148,8 +149,8 @@ struct sdioh_info { bool got_hcint; /* local interrupt flag */ uint16 last_intrstatus; /* to cache intrstatus */ int host_UHSISupported; /* whether UHSI is supported for HC. */ - int card_UHSI_voltage_Supported; /* whether UHSI is supported for - * Card in terms of Voltage [1.8 or 3.3]. + int card_UHSI_voltage_Supported; /* whether UHSI is supported for + * Card in terms of Voltage [1.8 or 3.3]. */ int global_UHSI_Supp; /* type of UHSI support in both host and card. * HOST_SDR_UNSUPP: capabilities not supported/matched @@ -171,21 +172,6 @@ struct sdioh_info { #define USE_DMA(sd) ((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE)) -/* SDIO Host Control Register DMA Mode Definitions */ -#define SDIOH_SDMA_MODE 0 -#define SDIOH_ADMA1_MODE 1 -#define SDIOH_ADMA2_MODE 2 -#define SDIOH_ADMA2_64_MODE 3 - -#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ -#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ -#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ -#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ -#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ -#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ -#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ -#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ - /* States for Tuning and corr data */ #define TUNING_IDLE 0 #define TUNING_START 1 @@ -194,17 +180,9 @@ struct sdioh_info { #define DATA_TRANSFER_IDLE 0 #define DATA_TRANSFER_ONGOING 1 +#define CHECK_TUNING_PRE_DATA 1 +#define CHECK_TUNING_POST_DATA 2 -/* ADMA2 Descriptor Table Entry for 32-bit Address */ -typedef struct adma2_dscr_32b { - uint32 len_attr; - uint32 phys_addr; -} adma2_dscr_32b_t; - -/* ADMA1 Descriptor Table Entry */ -typedef struct adma1_dscr { - uint32 phys_addr_attr; -} adma1_dscr_t; /************************************************************ * Internal interfaces: per-port references into bcmsdstd.c @@ -252,9 +230,12 @@ extern int sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield, extern void sdstd_3_enable_retuning_int(sdioh_info_t *sd); extern void sdstd_3_disable_retuning_int(sdioh_info_t *sd); extern bool sdstd_3_is_retuning_int_set(sdioh_info_t *sd); +extern void sdstd_3_check_and_do_tuning(sdioh_info_t *sd, int tuning_param); extern bool sdstd_3_check_and_set_retuning(sdioh_info_t *sd); extern int sdstd_3_get_tune_state(sdioh_info_t *sd); +extern int sdstd_3_get_data_state(sdioh_info_t *sd); extern void sdstd_3_set_tune_state(sdioh_info_t *sd, int state); +extern void sdstd_3_set_data_state(sdioh_info_t *sd, int state); extern uint8 sdstd_3_get_tuning_exp(sdioh_info_t *sd); extern uint32 sdstd_3_get_uhsi_clkmode(sdioh_info_t *sd); extern int sdstd_3_clk_tuning(sdioh_info_t *sd, uint32 sd3ClkMode); diff --git a/drivers/net/wireless/bcmdhd/include/bcmspi.h b/drivers/net/wireless/bcmdhd/include/bcmspi.h index 0eb2a30c9a84..34a02d00c6bd 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmspi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmspi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmspi.h,v 13.5.112.1 2010-11-15 18:13:09 Exp $ + * $Id: bcmspi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _BCM_SPI_H #define _BCM_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/bcmutils.h b/drivers/net/wireless/bcmdhd/include/bcmutils.h index 530036f0ba77..6849c26da837 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmutils.h +++ b/drivers/net/wireless/bcmdhd/include/bcmutils.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmutils.h,v 13.236.2.16 2011-01-26 00:45:06 Exp $ + * $Id: bcmutils.h 294991 2011-11-09 00:17:28Z $ */ @@ -221,7 +221,9 @@ extern uint16 pktpool_avail(pktpool_t *pktp); extern int pktpool_avail_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); extern int pktpool_empty_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); extern int pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen); +extern int pktpool_setmaxlen_strict(osl_t *osh, pktpool_t *pktp, uint16 maxlen); extern void pktpool_emptycb_disable(pktpool_t *pktp, bool disable); +extern bool pktpool_emptycb_disabled(pktpool_t *pktp); #define POOLPTR(pp) ((pktpool_t *)(pp)) #define pktpool_len(pp) (POOLPTR(pp)->len - 1) @@ -330,6 +332,8 @@ extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf); extern void bcm_mdelay(uint ms); +#define NVRAM_RECLAIM_CHECK(name) + extern char *getvar(char *vars, const char *name); extern int getintvar(char *vars, const char *name); extern int getintvararray(char *vars, const char *name, int index); @@ -534,9 +538,17 @@ extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len); & ~((boundary) - 1)) #define ISPOWEROF2(x) ((((x)-1)&(x)) == 0) #define VALID_MASK(mask) !((mask) & ((mask) + 1)) + #ifndef OFFSETOF +#ifdef __ARMCC_VERSION + +#include <stddef.h> +#define OFFSETOF(type, member) offsetof(type, member) +#else #define OFFSETOF(type, member) ((uint)(uintptr)&((type *)0)->member) #endif +#endif + #ifndef ARRAYSIZE #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi.h b/drivers/net/wireless/bcmdhd/include/bcmwifi.h index 45f3c0312dcc..e5207e9c4086 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmwifi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmwifi.h @@ -23,7 +23,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmwifi.h,v 1.29.6.3 2010-08-03 17:47:04 Exp $ + * $Id: bcmwifi.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h index 9661dac2603f..175ff8545a0c 100644 --- a/drivers/net/wireless/bcmdhd/include/dhdioctl.h +++ b/drivers/net/wireless/bcmdhd/include/dhdioctl.h @@ -25,7 +25,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhdioctl.h,v 13.11.10.1 2010-12-22 23:47:26 Exp $ + * $Id: dhdioctl.h 323572 2012-03-26 06:28:14Z $ */ #ifndef _dhdioctl_h_ @@ -87,6 +87,8 @@ enum { #define DHD_BTA_VAL 0x1000 #define DHD_ISCAN_VAL 0x2000 #define DHD_ARPOE_VAL 0x4000 +#define DHD_REORDER_VAL 0x8000 +#define DHD_WL_VAL 0x10000 #ifdef SDTEST /* For pktgen iovar */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index ae1f975bdb64..3bff73e2a6bc 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -19,11 +19,10 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: epivers.h.in,v 13.32.4.1 2010-09-17 00:39:18 $ + * $Id: epivers.h.in 277737 2011-08-16 17:54:59Z $ * */ - #ifndef _epivers_h_ #define _epivers_h_ @@ -31,19 +30,19 @@ #define EPI_MINOR_VERSION 90 -#define EPI_RC_NUMBER 125 +#define EPI_RC_NUMBER 195 -#define EPI_INCREMENTAL_NUMBER 94 +#define EPI_INCREMENTAL_NUMBER 61 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 125, 94 +#define EPI_VERSION 5, 90, 195, 61 -#define EPI_VERSION_NUM 0x055a7d5e +#define EPI_VERSION_NUM 0x055ac33d -#define EPI_VERSION_DEV 5.90.125 +#define EPI_VERSION_DEV 5.90.195 -#define EPI_VERSION_STR "5.90.125.94" +#define EPI_VERSION_STR "5.90.195.61" #endif diff --git a/drivers/net/wireless/bcmdhd/include/hndpmu.h b/drivers/net/wireless/bcmdhd/include/hndpmu.h index 51c51b9734a2..69a834c6b7eb 100644 --- a/drivers/net/wireless/bcmdhd/include/hndpmu.h +++ b/drivers/net/wireless/bcmdhd/include/hndpmu.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndpmu.h,v 13.35.8.5 2011-02-11 00:56:32 Exp $ + * $Id: hndpmu.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _hndpmu_h_ diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h index 8b9615c35f35..7d862c4deb21 100644 --- a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h +++ b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndrte_armtrap.h,v 13.4.14.1 2011-02-05 00:04:30 Exp $ + * $Id: hndrte_armtrap.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _hndrte_armtrap_h diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h index b9ede53af701..859ddc8953a8 100644 --- a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h +++ b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndrte_cons.h,v 13.4.10.4 2011-02-05 00:08:20 Exp $ + * $Id: hndrte_cons.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _HNDRTE_CONS_H diff --git a/drivers/net/wireless/bcmdhd/include/hndsoc.h b/drivers/net/wireless/bcmdhd/include/hndsoc.h index 4e26121c3881..34f927c6af80 100644 --- a/drivers/net/wireless/bcmdhd/include/hndsoc.h +++ b/drivers/net/wireless/bcmdhd/include/hndsoc.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndsoc.h,v 13.11 2009-12-03 23:52:31 Exp $ + * $Id: hndsoc.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _HNDSOC_H diff --git a/drivers/net/wireless/bcmdhd/include/htsf.h b/drivers/net/wireless/bcmdhd/include/htsf.h index 379fbbe37dbc..d875edb816c9 100644 --- a/drivers/net/wireless/bcmdhd/include/htsf.h +++ b/drivers/net/wireless/bcmdhd/include/htsf.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: htsf.h,v 1.1.2.4 2011-01-21 08:27:03 Exp $ + * $Id: htsf.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _HTSF_H_ #define _HTSF_H_ diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h index 1ec136eb70dc..830d351d882b 100644 --- a/drivers/net/wireless/bcmdhd/include/linux_osl.h +++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: linux_osl.h,v 13.158.6.3 2010-12-22 23:47:26 Exp $ + * $Id: linux_osl.h 301794 2011-12-08 20:41:35Z $ */ @@ -268,7 +268,7 @@ extern int osl_error(int bcmerror); #define PKTLIST_DUMP(osh, buf) #define PKTDBG_TRACE(osh, pkt, bit) #define PKTFREE(osh, skb, send) osl_pktfree((osh), (skb), (send)) -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF #define PKTGET_STATIC(osh, len, send) osl_pktget_static((osh), (len)) #define PKTFREE_STATIC(osh, skb, send) osl_pktfree_static((osh), (skb), (send)) #endif diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h index 96844db2f059..54d88ee923b2 100644 --- a/drivers/net/wireless/bcmdhd/include/linuxver.h +++ b/drivers/net/wireless/bcmdhd/include/linuxver.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: linuxver.h,v 13.53.2.2 2010-12-22 23:47:26 Exp $ + * $Id: linuxver.h 312264 2012-02-02 00:49:43Z $ */ @@ -482,7 +482,11 @@ typedef struct { #define DBG_THR(x) #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define SMP_RD_BARRIER_DEPENDS(x) smp_read_barrier_depends(x) +#else #define SMP_RD_BARRIER_DEPENDS(x) smp_rmb(x) +#endif #define PROC_START(thread_func, owner, tsk_ctl, flags) \ @@ -507,7 +511,24 @@ typedef struct { (tsk_ctl)->thr_pid = -1; \ } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define DAEMONIZE(a) daemonize(a); \ + allow_signal(SIGKILL); \ + allow_signal(SIGTERM); +#else /* Linux 2.4 (w/o preemption patch) */ +#define RAISE_RX_SOFTIRQ() \ + cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) +#define DAEMONIZE(a) daemonize(); \ + do { if (a) \ + strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ + } while (0); +#endif /* LINUX_VERSION_CODE */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define BLOCKABLE() (!in_atomic()) +#else +#define BLOCKABLE() (!in_interrupt()) +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) #define KILL_PROC(nr, sig) \ diff --git a/drivers/net/wireless/bcmdhd/include/miniopt.h b/drivers/net/wireless/bcmdhd/include/miniopt.h index f468420f5346..77eace6252d7 100644 --- a/drivers/net/wireless/bcmdhd/include/miniopt.h +++ b/drivers/net/wireless/bcmdhd/include/miniopt.h @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: miniopt.h,v 1.3 2009-01-15 00:06:54 Exp $ + * $Id: miniopt.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/msgtrace.h b/drivers/net/wireless/bcmdhd/include/msgtrace.h index 721d42100f2a..088f1e845a43 100644 --- a/drivers/net/wireless/bcmdhd/include/msgtrace.h +++ b/drivers/net/wireless/bcmdhd/include/msgtrace.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: msgtrace.h,v 1.4 2009-04-10 04:15:32 Exp $ + * $Id: msgtrace.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _MSGTRACE_H diff --git a/drivers/net/wireless/bcmdhd/include/osl.h b/drivers/net/wireless/bcmdhd/include/osl.h index 80248ee7604e..b8cc2569f506 100644 --- a/drivers/net/wireless/bcmdhd/include/osl.h +++ b/drivers/net/wireless/bcmdhd/include/osl.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: osl.h,v 13.44.96.1 2010-05-20 11:09:18 Exp $ + * $Id: osl.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_end.h b/drivers/net/wireless/bcmdhd/include/packed_section_end.h index 5d4a87678071..71f8b2e13b3b 100644 --- a/drivers/net/wireless/bcmdhd/include/packed_section_end.h +++ b/drivers/net/wireless/bcmdhd/include/packed_section_end.h @@ -34,7 +34,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: packed_section_end.h,v 1.4 2008-12-09 23:43:22 Exp $ + * $Id: packed_section_end.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_start.h b/drivers/net/wireless/bcmdhd/include/packed_section_start.h index da2fed68afac..afc2ba32fd93 100644 --- a/drivers/net/wireless/bcmdhd/include/packed_section_start.h +++ b/drivers/net/wireless/bcmdhd/include/packed_section_start.h @@ -34,7 +34,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: packed_section_start.h,v 1.4.124.1 2010-09-17 00:47:03 Exp $ + * $Id: packed_section_start.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/pcicfg.h b/drivers/net/wireless/bcmdhd/include/pcicfg.h index fae063a72f18..66199431fb92 100644 --- a/drivers/net/wireless/bcmdhd/include/pcicfg.h +++ b/drivers/net/wireless/bcmdhd/include/pcicfg.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: pcicfg.h,v 1.50 2009-12-07 21:56:06 Exp $ + * $Id: pcicfg.h 277737 2011-08-16 17:54:59Z $ */ @@ -39,6 +39,19 @@ #define PCI_INT_MASK 0x94 #define PCIE_EXTCFG_OFFSET 0x100 +#define PCI_SPROM_CONTROL 0x88 +#define PCI_BAR1_CONTROL 0x8c +#define PCI_TO_SB_MB 0x98 +#define PCI_BACKPLANE_ADDR 0xa0 +#define PCI_BACKPLANE_DATA 0xa4 +#define PCI_CLK_CTL_ST 0xa8 +#define PCI_BAR0_WIN2 0xac +#define PCI_GPIO_IN 0xb0 +#define PCI_GPIO_OUT 0xb4 +#define PCI_GPIO_OUTEN 0xb8 + +#define PCI_BAR0_SHADOW_OFFSET (2 * 1024) +#define PCI_BAR0_SPROM_OFFSET (4 * 1024) #define PCI_BAR0_PCIREGS_OFFSET (6 * 1024) #define PCI_BAR0_PCISBR_OFFSET (4 * 1024) @@ -49,4 +62,17 @@ #define PCI_16KB0_CCREGS_OFFSET (12 * 1024) #define PCI_16KBB0_WINSZ (16 * 1024) + +#define PCI_16KB0_WIN2_OFFSET (4 * 1024) + + + +#define SPROM_SZ_MSK 0x02 +#define SPROM_LOCKED 0x08 +#define SPROM_BLANK 0x04 +#define SPROM_WRITEEN 0x10 +#define SPROM_BOOTROM_WE 0x20 +#define SPROM_BACKPLANE_EN 0x40 +#define SPROM_OTPIN_USE 0x80 + #endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11.h b/drivers/net/wireless/bcmdhd/include/proto/802.11.h index 2342cb383147..fd69aac41309 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.11.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11.h @@ -21,7 +21,7 @@ * * Fundamental types and constants relating to 802.11 * - * $Id: 802.11.h,v 9.260.2.6 2010-12-15 21:41:14 Exp $ + * $Id: 802.11.h 304058 2011-12-21 00:39:12Z $ */ @@ -429,10 +429,26 @@ typedef struct dot11_obss_chanlist dot11_obss_chanlist_t; BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie { uint8 id; uint8 len; - uint8 cap; + uint8 cap[1]; } BWL_POST_PACKED_STRUCT; typedef struct dot11_extcap_ie dot11_extcap_ie_t; #define DOT11_EXTCAP_LEN 1 +#define DOT11_EXTCAP_LEN_TDLS 5 + +BWL_PRE_PACKED_STRUCT struct dot11_extcap { + uint8 extcap[DOT11_EXTCAP_LEN_TDLS]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extcap dot11_extcap_t; + + +#define TDLS_CAP_TDLS 37 +#define TDLS_CAP_PU_BUFFER_STA 28 +#define TDLS_CAP_PEER_PSM 20 +#define TDLS_CAP_CH_SW 30 +#define TDLS_CAP_PROH 38 +#define TDLS_CAP_CH_SW_PROH 39 + +#define TDLS_CAP_MAX_BIT 39 @@ -545,6 +561,9 @@ typedef struct dot11_ibss_dfs dot11_ibss_dfs_t; #define WME_SUBTYPE_IE 0 #define WME_SUBTYPE_PARAM_IE 1 #define WME_SUBTYPE_TSPEC 2 +#define WME_VERSION_LEN 1 +#define WME_PARAMETER_IE_LEN 24 + #define AC_BE 0 @@ -709,6 +728,15 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MGMT_NOTIFICATION_LEN 4 +BWL_PRE_PACKED_STRUCT struct ti_ie { + uint8 ti_type; + uint32 ti_val; +} BWL_POST_PACKED_STRUCT; +typedef struct ti_ie ti_ie_t; +#define TI_TYPE_REASSOC_DEADLINE 1 +#define TI_TYPE_KEY_LIFETIME 2 + + #define WME_ADDTS_REQUEST 0 #define WME_ADDTS_RESPONSE 1 #define WME_DELTS_REQUEST 2 @@ -724,8 +752,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_OPEN_SYSTEM 0 #define DOT11_SHARED_KEY 1 -#define DOT11_OPEN_SHARED 2 -#define DOT11_FAST_BSS 3 +#define DOT11_FAST_BSS 2 #define DOT11_CHALLENGE_LEN 128 @@ -926,9 +953,18 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_RC_MAX 23 +#define DOT11_RC_TDLS_PEER_UNREACH 25 +#define DOT11_RC_TDLS_DOWN_UNSPECIFIED 26 + #define DOT11_SC_SUCCESS 0 #define DOT11_SC_FAILURE 1 +#define DOT11_SC_TDLS_WAKEUP_SCH_ALT 2 + +#define DOT11_SC_TDLS_WAKEUP_SCH_REJ 3 +#define DOT11_SC_TDLS_SEC_DISABLED 5 +#define DOT11_SC_LIFETIME_REJ 6 +#define DOT11_SC_NOT_SAME_BSS 7 #define DOT11_SC_CAP_MISMATCH 10 #define DOT11_SC_REASSOC_FAIL 11 #define DOT11_SC_ASSOC_FAIL 12 @@ -947,13 +983,22 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED 25 #define DOT11_SC_ASSOC_ERPBCC_REQUIRED 26 #define DOT11_SC_ASSOC_DSSOFDM_REQUIRED 27 - -#define DOT11_SC_DECLINED 37 -#define DOT11_SC_INVALID_PARAMS 38 -#define DOT11_SC_INVALID_AKMP 43 -#define DOT11_SC_INVALID_MDID 54 -#define DOT11_SC_INVALID_FTIE 55 - +#define DOT11_SC_ASSOC_R0KH_UNREACHABLE 28 +#define DOT11_SC_ASSOC_TRY_LATER 30 +#define DOT11_SC_ASSOC_MFP_VIOLATION 31 + +#define DOT11_SC_DECLINED 37 +#define DOT11_SC_INVALID_PARAMS 38 +#define DOT11_SC_INVALID_PAIRWISE_CIPHER 42 +#define DOT11_SC_INVALID_AKMP 43 +#define DOT11_SC_INVALID_RSNIE_CAP 45 +#define DOT11_SC_INVALID_PMKID 53 +#define DOT11_SC_INVALID_MDID 54 +#define DOT11_SC_INVALID_FTIE 55 + +#define DOT11_SC_UNEXP_MSG 70 +#define DOT11_SC_INVALID_SNONCE 71 +#define DOT11_SC_INVALID_RSNIE 72 #define DOT11_MNG_DS_PARAM_LEN 1 #define DOT11_MNG_IBSS_PARAM_LEN 2 @@ -1008,6 +1053,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MNG_MDIE_ID 54 #define DOT11_MNG_FTIE_ID 55 #define DOT11_MNG_FT_TI_ID 56 +#define DOT11_MNG_RDE_ID 57 #define DOT11_MNG_REGCLASS_ID 59 #define DOT11_MNG_EXT_CSA_ID 60 #define DOT11_MNG_HT_ADD 61 @@ -1018,7 +1064,13 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MNG_HT_BSS_COEXINFO_ID 72 #define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73 #define DOT11_MNG_HT_OBSS_ID 74 -#define DOT11_MNG_EXT_CAP 127 +#define DOT11_MNG_CHANNEL_USAGE 97 +#define DOT11_MNG_LINK_IDENTIFIER_ID 101 +#define DOT11_MNG_WAKEUP_SCHEDULE_ID 102 +#define DOT11_MNG_CHANNEL_SWITCH_TIMING_ID 104 +#define DOT11_MNG_PTI_CONTROL_ID 105 +#define DOT11_MNG_PU_BUFFER_STATUS_ID 106 +#define DOT11_MNG_EXT_CAP_ID 127 #define DOT11_MNG_WPA_ID 221 #define DOT11_MNG_PROPR_ID 221 @@ -1070,8 +1122,14 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_ACTION_CAT_RRM 5 #define DOT11_ACTION_CAT_FBT 6 #define DOT11_ACTION_CAT_HT 7 +#if defined(MFP) || defined(WLFBT) || defined(WLWNM) +#define DOT11_ACTION_CAT_SA_QUERY 8 +#define DOT11_ACTION_CAT_PDPA 9 #define DOT11_ACTION_CAT_BSSMGMT 10 #define DOT11_ACTION_NOTIFICATION 17 +#define DOT11_ACTION_CAT_VSP 126 +#endif +#define DOT11_ACTION_NOTIFICATION 17 #define DOT11_ACTION_CAT_VS 127 @@ -1107,6 +1165,121 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_ADDBA_POLICY_DELAYED 0 #define DOT11_ADDBA_POLICY_IMMEDIATE 1 + +#define DOT11_FT_ACTION_FT_RESERVED 0 +#define DOT11_FT_ACTION_FT_REQ 1 +#define DOT11_FT_ACTION_FT_RES 2 +#define DOT11_FT_ACTION_FT_CON 3 +#define DOT11_FT_ACTION_FT_ACK 4 + + + +#define DOT11_WNM_ACTION_EVENT_REQ 0 +#define DOT11_WNM_ACTION_EVENT_REP 1 +#define DOT11_WNM_ACTION_DIAG_REQ 2 +#define DOT11_WNM_ACTION_DIAG_REP 3 +#define DOT11_WNM_ACTION_LOC_CFG_REQ 4 +#define DOT11_WNM_ACTION_LOC_RFG_RESP 5 +#define DOT11_WNM_ACTION_BSS_TRANS_QURY 6 +#define DOT11_WNM_ACTION_BSS_TRANS_REQ 7 +#define DOT11_WNM_ACTION_BSS_TRANS_RESP 8 +#define DOT11_WNM_ACTION_FMS_REQ 9 +#define DOT11_WNM_ACTION_FMS_RESP 10 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REQ 11 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REP 12 +#define DOT11_WNM_ACTION_TFS_REQ 13 +#define DOT11_WNM_ACTION_TFS_RESP 14 +#define DOT11_WNM_ACTION_TFS_NOTIFY 15 +#define DOT11_WNM_ACTION_WNM_SLEEP_REQ 16 +#define DOT11_WNM_ACTION_WNM_SLEEP_RESP 17 +#define DOT11_WNM_ACTION_TIM_BCAST_REQ 18 +#define DOT11_WNM_ACTION_TIM_BCAST_RESP 19 +#define DOT11_WNM_ACTION_QOS_TRFC_CAP_UPD 20 +#define DOT11_WNM_ACTION_CHAN_USAGE_REQ 21 +#define DOT11_WNM_ACTION_CHAN_USAGE_RESP 22 +#define DOT11_WNM_ACTION_DMS_REQ 23 +#define DOT11_WNM_ACTION_DMS_RESP 24 +#define DOT11_WNM_ACTION_TMNG_MEASUR_REQ 25 +#define DOT11_WNM_ACTION_NOTFCTN_REQ 26 +#define DOT11_WNM_ACTION_NOTFCTN_RES 27 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_query { + uint8 category; + uint8 action; + uint8 token; + uint8 reason; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_query dot11_bss_trans_query_t; +#define DOT11_BSS_TRANS_QUERY_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_req { + uint8 category; + uint8 action; + uint8 token; + uint8 reqmode; + uint16 disassoc_tmr; + uint8 validity_intrvl; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_req dot11_bss_trans_req_t; +#define DOT11_BSS_TRANS_REQ_LEN 7 + +#define DOT11_BSS_TERM_DUR_LEN 12 + + + +#define DOT11_BSS_TRNS_REQMODE_PREF_LIST_INCL 0x01 +#define DOT11_BSS_TRNS_REQMODE_ABRIDGED 0x02 +#define DOT11_BSS_TRNS_REQMODE_DISASSOC_IMMINENT 0x04 +#define DOT11_BSS_TRNS_REQMODE_BSS_TERM_INCL 0x08 +#define DOT11_BSS_TRNS_REQMODE_ESS_DISASSOC_IMNT 0x10 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_res { + uint8 category; + uint8 action; + uint8 token; + uint8 status; + uint8 term_delay; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_res dot11_bss_trans_res_t; +#define DOT11_BSS_TRANS_RES_LEN 5 + + +#define DOT11_BSS_TRNS_RES_STATUS_ACCEPT 0 +#define DOT11_BSS_TRNS_RES_STATUS_REJECT 1 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_BCN 2 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_CAP 3 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_UNDESIRED 4 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_DELAY_REQ 5 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_BSS_LIST_PROVIDED 6 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_NO_SUITABLE_BSS 7 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_LEAVING_ESS 8 + + + +#define DOT11_NBR_RPRT_BSSID_INFO_REACHABILTY 0x0003 +#define DOT11_NBR_RPRT_BSSID_INFO_SEC 0x0004 +#define DOT11_NBR_RPRT_BSSID_INFO_KEY_SCOPE 0x0008 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP 0x03f0 + +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_SPEC_MGMT 0x0010 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_QOS 0x0020 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_APSD 0x0040 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_RDIO_MSMT 0x0080 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_DEL_BA 0x0100 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_IMM_BA 0x0200 + + +#define DOT11_NBR_RPRT_SUBELEM_BSS_CANDDT_PREF_ID 3 BWL_PRE_PACKED_STRUCT struct dot11_addba_req { uint8 category; uint8 action; @@ -1145,6 +1318,48 @@ typedef struct dot11_delba dot11_delba_t; #define DOT11_DELBA_LEN 6 +#define SA_QUERY_REQUEST 0 +#define SA_QUERY_RESPONSE 1 + + + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_req { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_req dot11_ft_req_t; +#define DOT11_FT_REQ_FIXED_LEN 14 + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_res { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint16 status; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_res dot11_ft_res_t; +#define DOT11_FT_RES_FIXED_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_rde_ie { + uint8 id; + uint8 length; + uint8 rde_id; + uint8 rd_count; + uint16 status; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rde_ie dot11_rde_ie_t; + + +#define DOT11_MNG_RDE_IE_LEN sizeof(dot11_rde_ie_t) + + @@ -1166,6 +1381,28 @@ typedef struct dot11_rrm_cap_ie dot11_rrm_cap_ie_t; #define DOT11_RRM_CAP_AP_CHANREP 16 + +#define DOT11_EXT_CAP_LEN 4 +BWL_PRE_PACKED_STRUCT struct dot11_ext_cap_ie { + uint8 cap[DOT11_EXT_CAP_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ext_cap_ie dot11_ext_cap_ie_t; + + +#define DOT11_EXT_CAP_BSS_TRANSITION_MGMT 19 + + +#define DOT11_OP_CLASS_NONE 255 + +BWL_PRE_PACKED_STRUCT struct do11_ap_chrep { + uint8 id; + uint8 len; + uint8 reg; + uint8 chanlist[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct do11_ap_chrep dot11_ap_chrep_t; + + #define DOT11_RM_ACTION_RM_REQ 0 #define DOT11_RM_ACTION_RM_REP 1 #define DOT11_RM_ACTION_LM_REQ 2 @@ -1255,7 +1492,7 @@ typedef struct dot11_rmrep_bcn dot11_rmrep_bcn_t; #define DOT11_RMREQ_BCN_REPINFO_ID 1 #define DOT11_RMREQ_BCN_REPDET_ID 2 #define DOT11_RMREQ_BCN_REQUEST_ID 10 -#define DOT11_RMREQ_BCN_APCHREP_ID 51 +#define DOT11_RMREQ_BCN_APCHREP_ID DOT11_MNG_AP_CHREP_ID #define DOT11_RMREQ_BCN_REPDET_FIXED 0 @@ -1272,6 +1509,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_rmrep_nbr { uint8 reg; uint8 channel; uint8 phytype; + uchar sub_elements[1]; } BWL_POST_PACKED_STRUCT; typedef struct dot11_rmrep_nbr dot11_rmrep_nbr_t; #define DOT11_RMREP_NBR_LEN 13 @@ -1660,6 +1898,9 @@ typedef struct dot11_obss_ie dot11_obss_ie_t; #define RSN_AKM_PSK 2 #define RSN_AKM_FBT_1X 3 #define RSN_AKM_FBT_PSK 4 +#define RSN_AKM_MFP_1X 5 +#define RSN_AKM_MFP_PSK 6 +#define RSN_AKM_TPK 7 #define DOT11_MAX_DEFAULT_KEYS 4 @@ -1724,6 +1965,66 @@ BWL_PRE_PACKED_STRUCT struct dot11_gtk_ie { } BWL_POST_PACKED_STRUCT; typedef struct dot11_gtk_ie dot11_gtk_ie_t; +#define BSSID_INVALID "\x00\x00\x00\x00\x00\x00" +#define BSSID_BROADCAST "\xFF\xFF\xFF\xFF\xFF\xFF" + + + +BWL_PRE_PACKED_STRUCT struct link_id_ie { + uint8 id; + uint8 len; + struct ether_addr bssid; + struct ether_addr tdls_init_mac; + struct ether_addr tdls_resp_mac; +} BWL_POST_PACKED_STRUCT; +typedef struct link_id_ie link_id_ie_t; +#define TDLS_LINK_ID_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct wakeup_sch_ie { + uint8 id; + uint8 len; + uint32 offset; + uint32 interval; + uint32 awake_win_slots; + uint32 max_wake_win; + uint16 idle_cnt; +} BWL_POST_PACKED_STRUCT; +typedef struct wakeup_sch_ie wakeup_sch_ie_t; +#define TDLS_WAKEUP_SCH_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct channel_switch_timing_ie { + uint8 id; + uint8 len; + uint16 switch_time; + uint16 switch_timeout; +} BWL_POST_PACKED_STRUCT; +typedef struct channel_switch_timing_ie channel_switch_timing_ie_t; +#define TDLS_CHANNEL_SWITCH_TIMING_IE_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct pti_control_ie { + uint8 id; + uint8 len; + uint8 tid; + uint16 seq_control; +} BWL_POST_PACKED_STRUCT; +typedef struct pti_control_ie pti_control_ie_t; +#define TDLS_PTI_CONTROL_IE_LEN 3 + + +BWL_PRE_PACKED_STRUCT struct pu_buffer_status_ie { + uint8 id; + uint8 len; + uint8 status; +} BWL_POST_PACKED_STRUCT; +typedef struct pu_buffer_status_ie pu_buffer_status_ie_t; +#define TDLS_PU_BUFFER_STATUS_IE_LEN 1 +#define TDLS_PU_BUFFER_STATUS_AC_BK 1 +#define TDLS_PU_BUFFER_STATUS_AC_BE 2 +#define TDLS_PU_BUFFER_STATUS_AC_VI 4 +#define TDLS_PU_BUFFER_STATUS_AC_VO 8 #include <packed_section_end.h> diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h index 4ccfab02056b..cbdd05e624bc 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: 802.11_bta.h,v 9.2 2008-10-28 23:27:13 Exp $ + * $Id: 802.11_bta.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _802_11_BTA_H_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h index ce8ad083f286..0e070a475b64 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: 802.11e.h,v 1.6 2008-12-01 22:55:11 Exp $ + * $Id: 802.11e.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _802_11e_H_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h index cf206250246f..c7e07bd5e7c3 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h @@ -21,7 +21,7 @@ * * Fundamental types and constants relating to 802.1D * - * $Id: 802.1d.h,v 9.3 2007-04-10 21:33:06 Exp $ + * $Id: 802.1d.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h index 46fa4c9f89e8..0f75d3c8f1d6 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmeth.h,v 9.12 2009-12-29 19:57:18 Exp $ + * $Id: bcmeth.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h index 30ec848c40ae..e8c2387dd10b 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h @@ -23,7 +23,7 @@ * * Dependencies: proto/bcmeth.h * - * $Id: bcmevent.h,v 9.64.2.9 2011-02-01 06:24:21 Exp $ + * $Id: bcmevent.h 288077 2011-10-06 00:08:47Z $ * */ @@ -182,7 +182,10 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { #define WLC_E_PFN_SCAN_NONE 82 #define WLC_E_PFN_SCAN_ALLGONE 83 #define WLC_E_GTK_PLUMBED 84 -#define WLC_E_LAST 85 +#define WLC_E_ASSOC_REQ_IE 85 +#define WLC_E_ASSOC_RESP_IE 86 +#define WLC_E_LAST 87 + typedef struct { @@ -226,6 +229,8 @@ extern const int bcmevent_names_size; #define WLC_E_REASON_TSPEC_REJECTED 7 #define WLC_E_REASON_BETTER_AP 8 +#define WLC_E_REASON_REQUESTED_ROAM 11 + #define WLC_E_PRUNE_ENCR_MISMATCH 1 #define WLC_E_PRUNE_BCAST_BSSID 2 diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h index 8a8f3146d56b..55eff247c492 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h @@ -21,7 +21,7 @@ * * Fundamental constants relating to IP Protocol * - * $Id: bcmip.h,v 9.19 2009-11-10 20:08:33 Exp $ + * $Id: bcmip.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h index 89c118179159..91ab4fe538f2 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bt_amp_hci.h,v 9.14.8.2 2010-09-10 18:37:47 Exp $ + * $Id: bt_amp_hci.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _bt_amp_hci_h diff --git a/drivers/net/wireless/bcmdhd/include/proto/eapol.h b/drivers/net/wireless/bcmdhd/include/proto/eapol.h index 5781d1312e35..92634c1221a6 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/eapol.h +++ b/drivers/net/wireless/bcmdhd/include/proto/eapol.h @@ -7,7 +7,7 @@ * * Copyright (C) 2002 Broadcom Corporation * - * $Id: eapol.h,v 9.23.86.1 2010-09-02 18:09:39 Exp $ + * $Id: eapol.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _eapol_h_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h index 6a6dd14c1bbb..20865dc5a231 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h +++ b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: ethernet.h,v 9.56 2009-10-15 22:54:58 Exp $ + * $Id: ethernet.h 285437 2011-09-21 22:16:56Z $ */ @@ -69,6 +69,7 @@ #define ETHER_TYPE_802_1X 0x888e #define ETHER_TYPE_802_1X_PREAUTH 0x88c7 #define ETHER_TYPE_WAI 0x88b4 +#define ETHER_TYPE_89_0D 0x890d diff --git a/drivers/net/wireless/bcmdhd/include/proto/p2p.h b/drivers/net/wireless/bcmdhd/include/proto/p2p.h index 4a0c9d1ddc37..d2bf3f20688c 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/p2p.h +++ b/drivers/net/wireless/bcmdhd/include/proto/p2p.h @@ -21,7 +21,7 @@ * * Fundamental types and constants relating to WFA P2P (aka WiFi Direct) * - * $Id: p2p.h,v 9.17.2.4 2010-12-15 21:41:21 Exp $ + * $Id: p2p.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _P2P_H_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h index 7fe4fbce310e..7353ff0d7c73 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h +++ b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdspi.h,v 9.2.120.1 2010-11-15 17:56:25 Exp $ + * $Id: sdspi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _SD_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/proto/vlan.h b/drivers/net/wireless/bcmdhd/include/proto/vlan.h index 07fa7e499c23..27f005537604 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/vlan.h +++ b/drivers/net/wireless/bcmdhd/include/proto/vlan.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: vlan.h,v 9.7 2009-03-13 01:11:50 Exp $ + * $Id: vlan.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/wpa.h b/drivers/net/wireless/bcmdhd/include/proto/wpa.h index 1ff06dc79423..7361cbf20b06 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/wpa.h +++ b/drivers/net/wireless/bcmdhd/include/proto/wpa.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wpa.h,v 1.19 2009-07-13 08:29:58 Exp $ + * $Id: wpa.h 285437 2011-09-21 22:16:56Z $ */ @@ -114,6 +114,8 @@ typedef BWL_PRE_PACKED_STRUCT struct #define WPA_CIPHER_AES_OCB 3 #define WPA_CIPHER_AES_CCM 4 #define WPA_CIPHER_WEP_104 5 +#define WPA_CIPHER_BIP 6 +#define WPA_CIPHER_TPK 7 #define IS_WPA_CIPHER(cipher) ((cipher) == WPA_CIPHER_NONE || \ @@ -121,7 +123,9 @@ typedef BWL_PRE_PACKED_STRUCT struct (cipher) == WPA_CIPHER_WEP_104 || \ (cipher) == WPA_CIPHER_TKIP || \ (cipher) == WPA_CIPHER_AES_OCB || \ - (cipher) == WPA_CIPHER_AES_CCM) + (cipher) == WPA_CIPHER_AES_CCM || \ + (cipher) == WPA_CIPHER_TPK) + #define WPA_TKIP_CM_DETECT 60 @@ -149,7 +153,11 @@ typedef BWL_PRE_PACKED_STRUCT struct #define WPA_CAP_REPLAY_CNTR_MASK RSN_CAP_PTK_REPLAY_CNTR_MASK +#define WPA_CAP_PEER_KEY_ENABLE (0x1 << 1) + + #define WPA_CAP_LEN RSN_CAP_LEN +#define WPA_PMKID_CNT_LEN 2 #define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h index cbd37490f1cb..8f757509b95d 100644 --- a/drivers/net/wireless/bcmdhd/include/sbchipc.h +++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h @@ -5,7 +5,7 @@ * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, * GPIO interface, extbus, and support for serial and parallel flashes. * - * $Id: sbchipc.h,v 13.169.2.14 2011-02-10 23:43:55 Exp $ + * $Id: sbchipc.h 311371 2012-01-28 05:47:25Z $ * * Copyright (C) 1999-2011, Broadcom Corporation * @@ -41,6 +41,50 @@ #define PAD _XSTR(__LINE__) #endif +typedef struct eci_prerev35 { + uint32 eci_output; + uint32 eci_control; + uint32 eci_inputlo; + uint32 eci_inputmi; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolaritymi; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskmi; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventmi; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskmi; + uint32 eci_eventmaskhi; + uint32 PAD[3]; +} eci_prerev35_t; + +typedef struct eci_rev35 { + uint32 eci_outputlo; + uint32 eci_outputhi; + uint32 eci_controllo; + uint32 eci_controlhi; + uint32 eci_inputlo; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskhi; + uint32 eci_auxtx; + uint32 eci_auxrx; + uint32 eci_datatag; + uint32 eci_uartescvalue; + uint32 eci_autobaudctr; + uint32 eci_uartfifolevel; +} eci_rev35_t; + typedef volatile struct { uint32 chipid; uint32 capabilities; @@ -153,10 +197,26 @@ typedef volatile struct { uint32 prog_waitcount; uint32 flash_config; uint32 flash_waitcount; - uint32 PAD[4]; - uint32 PAD[40]; + uint32 SECI_config; + uint32 SECI_status; + uint32 SECI_statusmask; + uint32 SECI_rxnibchanged; + + uint32 PAD[20]; + uint32 sromcontrol; + uint32 sromaddress; + uint32 sromdata; + uint32 PAD[9]; + uint32 seci_uart_data; + uint32 seci_uart_bauddiv; + uint32 seci_uart_fcr; + uint32 seci_uart_lcr; + uint32 seci_uart_mcr; + uint32 seci_uart_lsr; + uint32 seci_uart_msr; + uint32 seci_uart_baudadj; uint32 clk_ctl_st; uint32 hw_war; @@ -1332,9 +1392,27 @@ typedef volatile struct { #define CST43237_BOOT_FROM_INVALID 3 +#define RES43239_CBUCK_LPOM 0 +#define RES43239_CBUCK_BURST 1 +#define RES43239_CBUCK_LP_PWM 2 +#define RES43239_CBUCK_PWM 3 +#define RES43239_CLDO_PU 4 +#define RES43239_DIS_INT_RESET_PD 5 +#define RES43239_ILP_REQUEST 6 +#define RES43239_LNLDO_PU 7 +#define RES43239_LDO3P3_PU 8 #define RES43239_OTP_PU 9 +#define RES43239_XTAL_PU 10 +#define RES43239_ALP_AVAIL 11 +#define RES43239_RADIO_PU 12 #define RES43239_MACPHY_CLKAVAIL 23 #define RES43239_HT_AVAIL 24 +#define RES43239_XOLDO_PU 25 +#define RES43239_WL_XTAL_CTL_SEL 26 +#define RES43239_SR_CLK_STABLE 27 +#define RES43239_SR_SAVE_RESTORE 28 +#define RES43239_SR_PHY_PIC 29 +#define RES43239_SR_PHY_PWR_SW 30 #define CST43239_SPROM_MASK 0x00000002 @@ -1342,7 +1420,7 @@ typedef volatile struct { #define CST43239_RES_INIT_MODE_SHIFT 7 #define CST43239_RES_INIT_MODE_MASK 0x000001f0 #define CST43239_CHIPMODE_SDIOD(cs) ((cs) & (1 << 15)) -#define CST43239_CHIPMODE_USB20D(cs) ((cs) & !(1 << 15)) +#define CST43239_CHIPMODE_USB20D(cs) (~(cs) & (1 << 15)) #define CST43239_CHIPMODE_SDIO(cs) (((cs) & (1 << 0)) == 0) #define CST43239_CHIPMODE_GSPI(cs) (((cs) & (1 << 0)) == (1 << 0)) @@ -1350,6 +1428,40 @@ typedef volatile struct { #define CCTRL43239_XTAL_STRENGTH(ctl) ((ctl & 0x3F) << 12) +#define RES4331_REGULATOR 0 +#define RES4331_ILP_REQUEST 1 +#define RES4331_XTAL_PU 2 +#define RES4331_ALP_AVAIL 3 +#define RES4331_SI_PLL_ON 4 +#define RES4331_HT_SI_AVAIL 5 + + +#define CCTRL4331_BT_COEXIST (1<<0) +#define CCTRL4331_SECI (1<<1) +#define CCTRL4331_EXT_LNA_G (1<<2) +#define CCTRL4331_SPROM_GPIO13_15 (1<<3) +#define CCTRL4331_EXTPA_EN (1<<4) +#define CCTRL4331_GPIOCLK_ON_SPROMCS <1<<5) +#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) +#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) +#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) +#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) +#define CCTRL4331_PCIE_AUXCLKEN <1<<10) +#define CCTRL4331_PCIE_PIPE_PLLDOWN <1<<11) +#define CCTRL4331_EXTPA_EN2 (1<<12) +#define CCTRL4331_EXT_LNA_A (1<<13) +#define CCTRL4331_BT_SHD0_ON_GPIO4 <1<<16) +#define CCTRL4331_BT_SHD1_ON_GPIO5 <1<<17) +#define CCTRL4331_EXTPA_ANA_EN (1<<24) + + +#define CST4331_XTAL_FREQ 0x00000001 +#define CST4331_SPROM_OTP_SEL_MASK 0x00000006 +#define CST4331_SPROM_OTP_SEL_SHIFT 1 +#define CST4331_SPROM_PRESENT 0x00000002 +#define CST4331_OTP_PRESENT 0x00000004 +#define CST4331_LDO_RF 0x00000008 +#define CST4331_LDO_PAR 0x00000010 #define RES4315_CBUCK_LPOM 1 @@ -1547,6 +1659,9 @@ typedef volatile struct { #define CCTRL_4330_JTAG_DISABLE 0x00000008 +#define CCTRL_43239_GPIO_SEL 0x00000002 +#define CCTRL_43239_SDIO_HOST_WAKE 0x00000004 + #define RES4313_BB_PU_RSRC 0 #define RES4313_ILP_REQ_RSRC 1 #define RES4313_XTAL_PU_RSRC 2 @@ -1597,6 +1712,54 @@ typedef volatile struct { +#define SECI_MODE_UART 0x0 +#define SECI_MODE_SECI 0x1 +#define SECI_MODE_LEGACY_3WIRE_BT 0x2 +#define SECI_MODE_LEGACY_3WIRE_WLAN 0x3 +#define SECI_MODE_HALF_SECI 0x4 + +#define SECI_RESET (1 << 0) +#define SECI_RESET_BAR_UART (1 << 1) +#define SECI_ENAB_SECI_ECI (1 << 2) +#define SECI_ENAB_SECIOUT_DIS (1 << 3) +#define SECI_MODE_MASK 0x7 +#define SECI_MODE_SHIFT 4 +#define SECI_UPD_SECI (1 << 7) + + +#define CLKCTL_STS_SECI_CLK_REQ (1 << 8) +#define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) + +#define SECI_UART_MSR_CTS_STATE (1 << 0) +#define SECI_UART_MSR_RTS_STATE (1 << 1) +#define SECI_UART_SECI_IN_STATE (1 << 2) +#define SECI_UART_SECI_IN2_STATE (1 << 3) + + +#define SECI_UART_LCR_STOP_BITS (1 << 0) +#define SECI_UART_LCR_PARITY_EN (1 << 1) +#define SECI_UART_LCR_PARITY (1 << 2) +#define SECI_UART_LCR_RX_EN (1 << 3) +#define SECI_UART_LCR_LBRK_CTRL (1 << 4) +#define SECI_UART_LCR_TXO_EN (1 << 5) +#define SECI_UART_LCR_RTSO_EN (1 << 6) +#define SECI_UART_LCR_SLIPMODE_EN (1 << 7) +#define SECI_UART_LCR_RXCRC_CHK (1 << 8) +#define SECI_UART_LCR_TXCRC_INV (1 << 9) +#define SECI_UART_LCR_TXCRC_LSBF (1 << 10) +#define SECI_UART_LCR_TXCRC_EN (1 << 11) + +#define SECI_UART_MCR_TX_EN (1 << 0) +#define SECI_UART_MCR_PRTS (1 << 1) +#define SECI_UART_MCR_SWFLCTRL_EN (1 << 2) +#define SECI_UART_MCR_HIGHRATE_EN (1 << 3) +#define SECI_UART_MCR_LOOPBK_EN (1 << 4) +#define SECI_UART_MCR_AUTO_RTS (1 << 5) +#define SECI_UART_MCR_AUTO_TX_DIS (1 << 6) +#define SECI_UART_MCR_BAUD_ADJ_EN (1 << 7) +#define SECI_UART_MCR_XONOFF_RPT (1 << 9) + + #define ECI_BW_20 0x0 diff --git a/drivers/net/wireless/bcmdhd/include/sbconfig.h b/drivers/net/wireless/bcmdhd/include/sbconfig.h index 76f05ae34bd5..f45351a586cb 100644 --- a/drivers/net/wireless/bcmdhd/include/sbconfig.h +++ b/drivers/net/wireless/bcmdhd/include/sbconfig.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbconfig.h,v 13.70 2008-03-28 19:17:04 Exp $ + * $Id: sbconfig.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbhnddma.h b/drivers/net/wireless/bcmdhd/include/sbhnddma.h index 05d0587bc205..77c413f75f0d 100644 --- a/drivers/net/wireless/bcmdhd/include/sbhnddma.h +++ b/drivers/net/wireless/bcmdhd/include/sbhnddma.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbhnddma.h,v 13.20.2.3 2010-10-14 22:21:29 Exp $ + * $Id: sbhnddma.h 278779 2011-08-19 22:07:18Z $ */ @@ -217,7 +217,7 @@ typedef volatile struct { #define D64_XC_BL_SHIFT 18 -#define D64_XP_LD_MASK 0x00000fff +#define D64_XP_LD_MASK 0x00001fff #define D64_XS0_CD_MASK 0x00001fff @@ -260,7 +260,7 @@ typedef volatile struct { #define DMA_CTRL_USB_BOUNDRY4KB_WAR (1 << 4) -#define D64_RP_LD_MASK 0x00000fff +#define D64_RP_LD_MASK 0x00001fff #define D64_RS0_CD_MASK 0x00001fff diff --git a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h index aba914bd014f..d84f69ab5617 100644 --- a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h +++ b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbpcmcia.h,v 13.48.12.6 2010-11-04 09:39:42 Exp $ + * $Id: sbpcmcia.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbsdio.h b/drivers/net/wireless/bcmdhd/include/sbsdio.h index 4280d5bf9c1f..7aaeb73f0100 100644 --- a/drivers/net/wireless/bcmdhd/include/sbsdio.h +++ b/drivers/net/wireless/bcmdhd/include/sbsdio.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbsdio.h,v 13.34 2009-03-11 20:27:16 Exp $ + * $Id: sbsdio.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _SBSDIO_H diff --git a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h index 107a8b07c9e6..e5176483e9a1 100644 --- a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h +++ b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbsdpcmdev.h,v 13.38 2009-09-22 22:56:45 Exp $ + * $Id: sbsdpcmdev.h 282638 2011-09-08 21:18:10Z $ */ #ifndef _sbsdpcmdev_h_ diff --git a/drivers/net/wireless/bcmdhd/include/sbsocram.h b/drivers/net/wireless/bcmdhd/include/sbsocram.h index 1cba42238905..45c4dc208bda 100644 --- a/drivers/net/wireless/bcmdhd/include/sbsocram.h +++ b/drivers/net/wireless/bcmdhd/include/sbsocram.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbsocram.h,v 13.15 2009-10-02 16:55:44 Exp $ + * $Id: sbsocram.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/sdio.h b/drivers/net/wireless/bcmdhd/include/sdio.h index ca932266a1b2..c8ac7b773fb9 100644 --- a/drivers/net/wireless/bcmdhd/include/sdio.h +++ b/drivers/net/wireless/bcmdhd/include/sdio.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdio.h,v 13.27.14.1 2010-09-07 13:37:45 Exp $ + * $Id: sdio.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _SDIO_H @@ -366,7 +366,7 @@ typedef volatile struct { * SDIO Commands and responses * * I/O only commands are: - * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53 + * CMD0, CMD3, CMD5, CMD7, CMD14, CMD15, CMD52, CMD53 * ------------------------------------------------ */ @@ -412,6 +412,7 @@ typedef volatile struct { #define CMD7_RCA_M BITFIELD_MASK(16) #define CMD7_RCA_S 16 + #define CMD14_RCA_M BITFIELD_MASK(16) #define CMD14_RCA_S 16 #define CMD14_SLEEP_M BITFIELD_MASK(1) diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h index 3d37c7a7e30b..5f47d6f88cea 100644 --- a/drivers/net/wireless/bcmdhd/include/sdioh.h +++ b/drivers/net/wireless/bcmdhd/include/sdioh.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdioh.h,v 13.17.2.3 2011-01-08 05:28:21 Exp $ + * $Id: sdioh.h 300017 2011-12-01 20:30:27Z $ */ #ifndef _SDIOH_H @@ -68,6 +68,10 @@ #define SD_ADMA_SysAddr 0x58 #define SD_SlotInterruptStatus 0x0FC #define SD_HostControllerVersion 0x0FE +#define SD_GPIO_Reg 0x100 +#define SD_GPIO_OE 0x104 +#define SD_GPIO_Enable 0x108 + /* SD specific registers in PCI config space */ #define SD_SlotInfo 0x40 @@ -409,4 +413,30 @@ /* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */ /* SD_HostControllerVersion : Offset 0x0FE , size = bytes */ +/* SDIO Host Control Register DMA Mode Definitions */ +#define SDIOH_SDMA_MODE 0 +#define SDIOH_ADMA1_MODE 1 +#define SDIOH_ADMA2_MODE 2 +#define SDIOH_ADMA2_64_MODE 3 + +#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ +#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ +#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ +#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ +#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ +#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ +#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ +#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ + +/* ADMA2 Descriptor Table Entry for 32-bit Address */ +typedef struct adma2_dscr_32b { + uint32 len_attr; + uint32 phys_addr; +} adma2_dscr_32b_t; + +/* ADMA1 Descriptor Table Entry */ +typedef struct adma1_dscr { + uint32 phys_addr_attr; +} adma1_dscr_t; + #endif /* _SDIOH_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sdiovar.h b/drivers/net/wireless/bcmdhd/include/sdiovar.h index 2c5bcf97e910..55a3d3490c30 100644 --- a/drivers/net/wireless/bcmdhd/include/sdiovar.h +++ b/drivers/net/wireless/bcmdhd/include/sdiovar.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdiovar.h,v 13.9 2009-12-08 22:30:15 Exp $ + * $Id: sdiovar.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _sdiovar_h_ diff --git a/drivers/net/wireless/bcmdhd/include/siutils.h b/drivers/net/wireless/bcmdhd/include/siutils.h index c5a33832b585..6a7b93c7b977 100644 --- a/drivers/net/wireless/bcmdhd/include/siutils.h +++ b/drivers/net/wireless/bcmdhd/include/siutils.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: siutils.h,v 13.251.2.10 2011-02-04 05:06:32 Exp $ + * $Id: siutils.h 285387 2011-09-21 18:38:37Z $ */ @@ -60,6 +60,7 @@ struct si_pub { typedef const struct si_pub si_t; + #define SI_OSH NULL #define BADIDX (SI_MAXCORES + 1) @@ -213,8 +214,34 @@ extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevi #define si_eci(sih) 0 #define si_eci_init(sih) (0) #define si_eci_notify_bt(sih, type, val) (0) +#define si_seci(sih) 0 +static INLINE void * si_seci_init(si_t *sih, uint8 use_seci) {return NULL;} +#define si_seci_down(sih) do { } while (0) + + +extern bool si_is_otp_disabled(si_t *sih); +extern bool si_is_otp_powered(si_t *sih); +extern void si_otp_power(si_t *sih, bool on); + + +extern bool si_is_sprom_available(si_t *sih); +extern bool si_is_sprom_enabled(si_t *sih); +extern void si_sprom_enable(si_t *sih, bool enable); +extern int si_cis_source(si_t *sih); +#define CIS_DEFAULT 0 +#define CIS_SROM 1 +#define CIS_OTP 2 + + +#define DEFAULT_FAB 0x0 +#define CSM_FAB7 0x1 +#define TSMC_FAB12 0x2 +#define SMIC_FAB4 0x3 +extern int si_otp_fabid(si_t *sih, uint16 *fabid, bool rw); +extern uint16 si_fabid(si_t *sih); + extern int si_devpath(si_t *sih, char *path, int size); @@ -244,4 +271,5 @@ extern uint32 si_pcieserdesreg(si_t *sih, uint32 mdioslave, uint32 offset, uint3 char *si_getnvramflvar(si_t *sih, const char *name); + #endif diff --git a/drivers/net/wireless/bcmdhd/include/trxhdr.h b/drivers/net/wireless/bcmdhd/include/trxhdr.h index 397006ab005a..b52fb15ba5c0 100644 --- a/drivers/net/wireless/bcmdhd/include/trxhdr.h +++ b/drivers/net/wireless/bcmdhd/include/trxhdr.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: trxhdr.h,v 13.15.108.2 2010-11-15 17:57:30 Exp $ + * $Id: trxhdr.h 286295 2011-09-27 06:39:43Z $ */ #ifndef _TRX_HDR_H_ @@ -37,6 +37,7 @@ #define TRX_OVERLAYS 0x4 /* Contains an overlay header after the trx header */ #define TRX_MAX_OFFSET 3 /* Max number of individual files */ #define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed rtecdc.bin image */ +#define TRX_ROMSIM_IMAGE 0x10 /* Trx contains ROM simulation image */ struct trx_header { uint32 magic; /* "HDR0" */ diff --git a/drivers/net/wireless/bcmdhd/include/typedefs.h b/drivers/net/wireless/bcmdhd/include/typedefs.h index 228b5dcf11c7..d0902fe80891 100644 --- a/drivers/net/wireless/bcmdhd/include/typedefs.h +++ b/drivers/net/wireless/bcmdhd/include/typedefs.h @@ -18,7 +18,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: typedefs.h,v 1.103.2.1 2010-05-11 18:19:28 Exp $ + * $Id: typedefs.h 290055 2011-10-15 21:26:26Z $ */ @@ -305,5 +305,8 @@ typedef float64 float_t; #define UNUSED_PARAMETER(x) (void)(x) +#define DISCARD_QUAL(ptr, type) ((type *)(uintptr)(ptr)) + + #include <bcmdefs.h> #endif diff --git a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h index 7230d3b67ab0..d37105165bab 100644 --- a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h +++ b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h @@ -18,7 +18,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. -* $Id: wlfc_proto.h,v 1.1.6.2 2010-05-08 01:30:41 Exp $ +* $Id: wlfc_proto.h 277737 2011-08-16 17:54:59Z $ * */ #ifndef __wlfc_proto_definitions_h__ diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index 9357552c9194..91274a0c680b 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wlioctl.h,v 1.767.2.38 2011-02-01 23:04:28 Exp $ + * $Id: wlioctl.h 312596 2012-02-03 02:53:30Z $ */ @@ -65,8 +65,8 @@ typedef struct wl_action_frame { typedef struct ssid_info { - uint8 ssid_len; - uint8 ssid[32]; + uint8 ssid_len; + uint8 ssid[32]; } ssid_info_t; typedef struct wl_af_params { @@ -191,6 +191,7 @@ typedef struct wlc_ssid { #define WL_SCANFLAGS_PROHIBITED 0x04 #define WL_SCAN_PARAMS_SSID_MAX 10 + typedef struct wl_scan_params { wlc_ssid_t ssid; struct ether_addr bssid; @@ -376,6 +377,8 @@ typedef struct { #define NRATE_SGI_SHIFT 23 #define NRATE_LDPC_CODING 0x00400000 #define NRATE_LDPC_SHIFT 22 +#define NRATE_BCMC_OVERRIDE 0x00200000 +#define NRATE_BCMC_SHIFT 21 #define NRATE_STF_SISO 0 #define NRATE_STF_CDD 1 @@ -555,7 +558,7 @@ typedef enum sup_auth_status { #define CRYPTO_ALGO_AES_OCB_MSDU 5 #define CRYPTO_ALGO_AES_OCB_MPDU 6 #define CRYPTO_ALGO_NALG 7 -#define CRYPTO_ALGO_PMK 12 +#define CRYPTO_ALGO_PMK 12 #define WSEC_GEN_MIC_ERROR 0x0001 #define WSEC_GEN_REPLAY 0x0002 @@ -617,9 +620,9 @@ typedef struct { #define WPA2_AUTH_PSK 0x0080 #define BRCM_AUTH_PSK 0x0100 #define BRCM_AUTH_DPT 0x0200 -#define WPA2_AUTH_MFP 0x1000 -#define WPA2_AUTH_TPK 0x2000 -#define WPA2_AUTH_FT 0x4000 +#define WPA2_AUTH_MFP 0x1000 +#define WPA2_AUTH_TPK 0x2000 +#define WPA2_AUTH_FT 0x4000 #define MAXPMKID 16 @@ -649,12 +652,12 @@ typedef struct wl_assoc_info { uint32 resp_len; uint32 flags; struct dot11_assoc_req req; - struct ether_addr reassoc_bssid; + struct ether_addr reassoc_bssid; struct dot11_assoc_resp resp; } wl_assoc_info_t; -#define WLC_ASSOC_REQ_IS_REASSOC 0x01 +#define WLC_ASSOC_REQ_IS_REASSOC 0x01 typedef struct { @@ -818,8 +821,14 @@ typedef struct wlc_iov_trx_s { #define WLC_IOCTL_MEDLEN 1536 #ifdef WLC_HIGH_ONLY #define WLC_SAMPLECOLLECT_MAXLEN 1024 +#define WLC_SAMPLECOLLECT_MAXLEN_LCN40 1024 +#else +#if defined(LCNCONF) || defined(LCN40CONF) +#define WLC_SAMPLECOLLECT_MAXLEN 8192 #else -#define WLC_SAMPLECOLLECT_MAXLEN 10240 +#define WLC_SAMPLECOLLECT_MAXLEN 10240 +#endif +#define WLC_SAMPLECOLLECT_MAXLEN_LCN40 8192 #endif @@ -851,6 +860,7 @@ typedef struct wlc_iov_trx_s { #define WLC_GET_SSID 25 #define WLC_SET_SSID 26 #define WLC_RESTART 27 +#define WLC_TERMINATED 28 #define WLC_GET_CHANNEL 29 #define WLC_SET_CHANNEL 30 @@ -1203,7 +1213,7 @@ typedef struct { #define WL_AUTH_OPEN_SYSTEM 0 #define WL_AUTH_SHARED_KEY 1 -#define WL_AUTH_OPEN_SHARED 2 +#define WL_AUTH_OPEN_SHARED 2 #define WL_RADIO_SW_DISABLE (1<<0) @@ -1280,18 +1290,17 @@ typedef struct wl_po { #define WL_CHAN_FREQ_RANGE_5GM 2 #define WL_CHAN_FREQ_RANGE_5GH 3 -#define WL_CHAN_FREQ_RANGE_5GLL_VER2 4 -#define WL_CHAN_FREQ_RANGE_5GLH_VER2 5 -#define WL_CHAN_FREQ_RANGE_5GML_VER2 6 -#define WL_CHAN_FREQ_RANGE_5GMH_VER2 7 -#define WL_CHAN_FREQ_RANGE_5GH_VER2 8 - #define WL_CHAN_FREQ_RANGE_5GLL_5BAND 4 #define WL_CHAN_FREQ_RANGE_5GLH_5BAND 5 #define WL_CHAN_FREQ_RANGE_5GML_5BAND 6 #define WL_CHAN_FREQ_RANGE_5GMH_5BAND 7 #define WL_CHAN_FREQ_RANGE_5GH_5BAND 8 +#define WL_CHAN_FREQ_RANGE_5G_BAND0 1 +#define WL_CHAN_FREQ_RANGE_5G_BAND1 2 +#define WL_CHAN_FREQ_RANGE_5G_BAND2 3 +#define WL_CHAN_FREQ_RANGE_5G_BAND3 4 + #define WLC_PHY_TYPE_A 0 #define WLC_PHY_TYPE_B 1 @@ -1363,7 +1372,7 @@ typedef struct wl_po { #define PM_MAX 1 #define PM_FAST 2 -#define LISTEN_INTERVAL 10 +#define LISTEN_INTERVAL 10 #define INTERFERE_OVRRIDE_OFF -1 #define INTERFERE_NONE 0 @@ -1408,7 +1417,7 @@ typedef struct wl_aci_args { #define TRIGGER_BADPLCP 0x10 #define TRIGGER_CRSGLITCH 0x20 #define WL_ACI_ARGS_LEGACY_LENGTH 16 -#define WL_SAMPLECOLLECT_T_VERSION 1 +#define WL_SAMPLECOLLECT_T_VERSION 2 typedef struct wl_samplecollect_args { uint8 coll_us; @@ -1416,7 +1425,7 @@ typedef struct wl_samplecollect_args { uint16 version; uint16 length; - uint8 trigger; + int8 trigger; uint16 timeout; uint16 mode; uint32 pre_dur; @@ -1426,6 +1435,11 @@ typedef struct wl_samplecollect_args { bool be_deaf; bool agc; bool filter; + + uint8 trigger_state; + uint8 module_sel1; + uint8 module_sel2; + uint16 nsamps; } wl_samplecollect_args_t; #define WL_SAMPLEDATA_HEADER_TYPE 1 @@ -1493,6 +1507,7 @@ typedef struct wl_sampledata { #define WL_P2P_VAL 0x00000200 #define WL_TXRX_VAL 0x00000400 #define WL_MCHAN_VAL 0x00000800 +#define WL_TDLS_VAL 0x00001000 #define WL_LED_NUMGPIO 16 @@ -1546,7 +1561,7 @@ typedef struct wl_sampledata { #define WL_JOIN_PREF_WPA 2 #define WL_JOIN_PREF_BAND 3 #define WL_JOIN_PREF_RSSI_DELTA 4 -#define WL_JOIN_PREF_TRANS_PREF 5 +#define WL_JOIN_PREF_TRANS_PREF 5 #define WLJP_BAND_ASSOC_PREF 255 @@ -1797,17 +1812,19 @@ struct wl_msglevel2 { }; typedef struct wl_mkeep_alive_pkt { - uint16 version; - uint16 length; + uint16 version; + uint16 length; uint32 period_msec; uint16 len_bytes; - uint8 keep_alive_id; + uint8 keep_alive_id; uint8 data[1]; } wl_mkeep_alive_pkt_t; -#define WL_MKEEP_ALIVE_VERSION 1 -#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) -#define WL_MKEEP_ALIVE_PRECISION 500 +#define WL_MKEEP_ALIVE_VERSION 1 +#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) +#define WL_MKEEP_ALIVE_PRECISION 500 + + #define WLC_ROAM_TRIGGER_DEFAULT 0 #define WLC_ROAM_TRIGGER_BANDWIDTH 1 @@ -1897,7 +1914,7 @@ typedef struct wl_pfn_param { uint8 mscan; uint8 repeat; uint8 exp; - int32 slow_freq; + int32 slow_freq; } wl_pfn_param_t; typedef struct wl_pfn_bssid { @@ -2016,8 +2033,31 @@ typedef struct wl_keep_alive_pkt { +#define MAX_WAKE_PACKET_BYTES 128 + + +typedef struct pm_wake_packet { + uint32 status; + uint32 pattern_id; + uint32 original_packet_size; + uint32 saved_packet_size; + uchar packet[MAX_WAKE_PACKET_BYTES]; +} pm_wake_packet_t; + + + +#define PKT_FILTER_MODE_FORWARD_ON_MATCH 1 + +#define PKT_FILTER_MODE_DISABLE 2 + +#define PKT_FILTER_MODE_PKT_CACHE_ON_MATCH 4 + +#define PKT_FILTER_MODE_PKT_FORWARD_OFF_DEFAULT 8 + + typedef enum wl_pkt_filter_type { - WL_PKT_FILTER_TYPE_PATTERN_MATCH + WL_PKT_FILTER_TYPE_PATTERN_MATCH, + WL_PKT_FILTER_TYPE_MAGIC_PATTERN_MATCH } wl_pkt_filter_type_t; #define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t @@ -2550,6 +2590,12 @@ typedef struct wl_phycal_state { #define WL_PHYCAL_STAT_FIXED_LEN OFFSETOF(wl_phycal_state_t, phycal_core) #endif +#ifdef WLDSTA +typedef struct wl_dsta_if { + struct ether_addr addr; +} wl_dsta_if_t; +#endif + #ifdef WLP2P typedef struct wl_p2p_disc_st { diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c index 1a544378c1e6..4ef7bf7b24dc 100644 --- a/drivers/net/wireless/bcmdhd/linux_osl.c +++ b/drivers/net/wireless/bcmdhd/linux_osl.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: linux_osl.c,v 1.168.2.7 2011-01-27 17:01:13 Exp $ + * $Id: linux_osl.c,v 1.168.2.7 2011-01-27 17:01:13 $ */ @@ -47,7 +47,7 @@ #define OS_HANDLE_MAGIC 0x1234abcd #define BCM_MEM_FILENAME_LEN 24 -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF #define STATIC_BUF_MAX_NUM 16 #define STATIC_BUF_SIZE (PAGE_SIZE * 2) #define STATIC_BUF_TOTAL_LEN (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE) @@ -70,7 +70,7 @@ typedef struct bcm_static_pkt { } bcm_static_pkt_t; static bcm_static_pkt_t *bcm_static_skb = 0; -#endif +#endif typedef struct bcm_mem_link { struct bcm_mem_link *prev; @@ -211,7 +211,7 @@ osl_attach(void *pdev, uint bustype, bool pkttag) break; } -#if defined(DHD_USE_STATIC_BUF) +#if defined(CONFIG_DHD_USE_STATIC_BUF) if (!bcm_static_buf) { if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(osh, 3, STATIC_BUF_SIZE+ STATIC_BUF_TOTAL_LEN))) { @@ -220,7 +220,6 @@ osl_attach(void *pdev, uint bustype, bool pkttag) else printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); - sema_init(&bcm_static_buf->static_sem, 1); bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; @@ -238,7 +237,7 @@ osl_attach(void *pdev, uint bustype, bool pkttag) sema_init(&bcm_static_skb->osl_pkt_sem, 1); } -#endif +#endif return osh; } @@ -388,7 +387,7 @@ osl_ctfpool_stats(osl_t *osh, void *b) if ((osh == NULL) || (osh->ctfpool == NULL)) return; -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF if (bcm_static_buf) { bcm_static_buf = 0; } @@ -548,14 +547,14 @@ osl_pktfree(osl_t *osh, void *p, bool send) } } -#ifdef DHD_USE_STATIC_BUF +#ifdef CONFIG_DHD_USE_STATIC_BUF void * osl_pktget_static(osl_t *osh, uint len) { int i; struct sk_buff *skb; - if (len > (PAGE_SIZE * 2)) { + if (!bcm_static_skb || (len > (PAGE_SIZE * 2))) { printk("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len); return osl_pktget(osh, len); } @@ -570,10 +569,10 @@ osl_pktget_static(osl_t *osh, uint len) if (i != STATIC_PKT_MAX_NUM) { bcm_static_skb->pkt_use[i] = 1; - up(&bcm_static_skb->osl_pkt_sem); skb = bcm_static_skb->skb_4k[i]; skb->tail = skb->data + len; skb->len = len; + up(&bcm_static_skb->osl_pkt_sem); return skb; } } @@ -586,10 +585,10 @@ osl_pktget_static(osl_t *osh, uint len) if (i != STATIC_PKT_MAX_NUM) { bcm_static_skb->pkt_use[i+STATIC_PKT_MAX_NUM] = 1; - up(&bcm_static_skb->osl_pkt_sem); skb = bcm_static_skb->skb_8k[i]; skb->tail = skb->data + len; skb->len = len; + up(&bcm_static_skb->osl_pkt_sem); return skb; } @@ -603,9 +602,14 @@ osl_pktfree_static(osl_t *osh, void *p, bool send) { int i; + if (!bcm_static_skb) { + osl_pktfree(osh, p, send); + return; + } + + down(&bcm_static_skb->osl_pkt_sem); for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { if (p == bcm_static_skb->skb_4k[i]) { - down(&bcm_static_skb->osl_pkt_sem); bcm_static_skb->pkt_use[i] = 0; up(&bcm_static_skb->osl_pkt_sem); return; @@ -614,14 +618,15 @@ osl_pktfree_static(osl_t *osh, void *p, bool send) for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { if (p == bcm_static_skb->skb_8k[i]) { - down(&bcm_static_skb->osl_pkt_sem); bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 0; up(&bcm_static_skb->osl_pkt_sem); return; } } + up(&bcm_static_skb->osl_pkt_sem); - return osl_pktfree(osh, p, send); + osl_pktfree(osh, p, send); + return; } #endif diff --git a/drivers/net/wireless/bcmdhd/siutils.c b/drivers/net/wireless/bcmdhd/siutils.c index 22aa41265763..a655ac4ef141 100644 --- a/drivers/net/wireless/bcmdhd/siutils.c +++ b/drivers/net/wireless/bcmdhd/siutils.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: siutils.c,v 1.813.2.36 2011-02-10 23:43:55 Exp $ + * $Id: siutils.c,v 1.813.2.36 2011-02-10 23:43:55 $ */ #include <typedefs.h> @@ -213,11 +213,11 @@ si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, /* figure out bus/orignal core idx */ sii->pub.buscoretype = NODEV_CORE_ID; - sii->pub.buscorerev = NOREV; + sii->pub.buscorerev = (uint)NOREV; sii->pub.buscoreidx = BADIDX; pci = pcie = FALSE; - pcirev = pcierev = NOREV; + pcirev = pcierev = (uint)NOREV; pciidx = pcieidx = BADIDX; for (i = 0; i < sii->numcores; i++) { @@ -365,6 +365,19 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, return NULL; } +#if defined(HW_OOB) + if (CHIPID(sih->chip) == BCM43362_CHIP_ID) { + uint32 gpiocontrol, addr; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, gpiocontrol); + gpiocontrol = bcmsdh_reg_read(sdh, addr, 4); + gpiocontrol |= 0x2; + bcmsdh_reg_write(sdh, addr, 4, gpiocontrol); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10005, 0xf, NULL); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10006, 0x0, NULL); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10007, 0x2, NULL); + } +#endif + if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chiprev == 0) && (sih->chippkg != BCM4329_289PIN_PKG_ID)) { sih->chippkg = BCM4329_182PIN_PKG_ID; @@ -401,8 +414,9 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, } /* assume current core is CC */ - if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43236_CHIP_ID || + if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43234_CHIP_ID || CHIPID(sih->chip) == BCM43235_CHIP_ID || + CHIPID(sih->chip) == BCM43236_CHIP_ID || CHIPID(sih->chip) == BCM43238_CHIP_ID) && (CHIPREV(sii->pub.chiprev) == 0))) { @@ -1093,6 +1107,126 @@ si_watchdog_ms(si_t *sih, uint32 ms) +/* return the slow clock source - LPO, XTAL, or PCI */ +static uint +si_slowclk_src(si_info_t *sii) +{ + chipcregs_t *cc; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + if (sii->pub.ccrev < 6) { + if ((BUSTYPE(sii->pub.bustype) == PCI_BUS) && + (OSL_PCI_READ_CONFIG(sii->osh, PCI_GPIO_OUT, sizeof(uint32)) & + PCI_CFG_GPIO_SCS)) + return (SCC_SS_PCI); + else + return (SCC_SS_XTAL); + } else if (sii->pub.ccrev < 10) { + cc = (chipcregs_t *)si_setcoreidx(&sii->pub, sii->curidx); + return (R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_SS_MASK); + } else /* Insta-clock */ + return (SCC_SS_XTAL); +} + +/* return the ILP (slowclock) min or max frequency */ +static uint +si_slowclk_freq(si_info_t *sii, bool max_freq, chipcregs_t *cc) +{ + uint32 slowclk; + uint div; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + /* shouldn't be here unless we've established the chip has dynamic clk control */ + ASSERT(R_REG(sii->osh, &cc->capabilities) & CC_CAP_PWR_CTL); + + slowclk = si_slowclk_src(sii); + if (sii->pub.ccrev < 6) { + if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / 64) : (PCIMINFREQ / 64)); + else + return (max_freq ? (XTALMAXFREQ / 32) : (XTALMINFREQ / 32)); + } else if (sii->pub.ccrev < 10) { + div = 4 * + (((R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_CD_MASK) >> SCC_CD_SHIFT) + 1); + if (slowclk == SCC_SS_LPO) + return (max_freq ? LPOMAXFREQ : LPOMINFREQ); + else if (slowclk == SCC_SS_XTAL) + return (max_freq ? (XTALMAXFREQ / div) : (XTALMINFREQ / div)); + else if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / div) : (PCIMINFREQ / div)); + else + ASSERT(0); + } else { + /* Chipc rev 10 is InstaClock */ + div = R_REG(sii->osh, &cc->system_clk_ctl) >> SYCC_CD_SHIFT; + div = 4 * (div + 1); + return (max_freq ? XTALMAXFREQ : (XTALMINFREQ / div)); + } + return (0); +} + +static void +si_clkctl_setdelay(si_info_t *sii, void *chipcregs) +{ + chipcregs_t *cc = (chipcregs_t *)chipcregs; + uint slowmaxfreq, pll_delay, slowclk; + uint pll_on_delay, fref_sel_delay; + + pll_delay = PLL_DELAY; + + /* If the slow clock is not sourced by the xtal then add the xtal_on_delay + * since the xtal will also be powered down by dynamic clk control logic. + */ + + slowclk = si_slowclk_src(sii); + if (slowclk != SCC_SS_XTAL) + pll_delay += XTAL_ON_DELAY; + + /* Starting with 4318 it is ILP that is used for the delays */ + slowmaxfreq = si_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? FALSE : TRUE, cc); + + pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; + fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; + + W_REG(sii->osh, &cc->pll_on_delay, pll_on_delay); + W_REG(sii->osh, &cc->fref_sel_delay, fref_sel_delay); +} + +/* initialize power control delay registers */ +void +si_clkctl_init(si_t *sih) +{ + si_info_t *sii; + uint origidx = 0; + chipcregs_t *cc; + bool fast; + + if (!CCCTL_ENAB(sih)) + return; + + sii = SI_INFO(sih); + fast = SI_FAST(sii); + if (!fast) { + origidx = sii->curidx; + if ((cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0)) == NULL) + return; + } else if ((cc = (chipcregs_t *)CCREGS_FAST(sii)) == NULL) + return; + ASSERT(cc != NULL); + + /* set all Instaclk chip ILP to 1 MHz */ + if (sih->ccrev >= 10) + SET_REG(sii->osh, &cc->system_clk_ctl, SYCC_CD_MASK, + (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); + + si_clkctl_setdelay(sii, (void *)(uintptr)cc); + + if (!fast) + si_setcoreidx(sih, origidx); +} + /* change logical "focus" to the gpio core for optimized access */ void * si_gpiosetcore(si_t *sih) @@ -1718,3 +1852,64 @@ si_deviceremoved(si_t *sih) } return FALSE; } + +bool +si_is_sprom_available(si_t *sih) +{ + if (sih->ccrev >= 31) { + si_info_t *sii; + uint origidx; + chipcregs_t *cc; + uint32 sromctrl; + + if ((sih->cccaps & CC_CAP_SROM) == 0) + return FALSE; + + sii = SI_INFO(sih); + origidx = sii->curidx; + cc = si_setcoreidx(sih, SI_CC_IDX); + sromctrl = R_REG(sii->osh, &cc->sromcontrol); + si_setcoreidx(sih, origidx); + return (sromctrl & SRC_PRESENT); + } + + switch (CHIPID(sih->chip)) { + case BCM4312_CHIP_ID: + return ((sih->chipst & CST4312_SPROM_OTP_SEL_MASK) != CST4312_OTP_SEL); + case BCM4325_CHIP_ID: + return (sih->chipst & CST4325_SPROM_SEL) != 0; + case BCM4322_CHIP_ID: + case BCM43221_CHIP_ID: + case BCM43231_CHIP_ID: + case BCM43222_CHIP_ID: + case BCM43111_CHIP_ID: + case BCM43112_CHIP_ID: + case BCM4342_CHIP_ID: + { + uint32 spromotp; + spromotp = (sih->chipst & CST4322_SPROM_OTP_SEL_MASK) >> + CST4322_SPROM_OTP_SEL_SHIFT; + return (spromotp & CST4322_SPROM_PRESENT) != 0; + } + case BCM4329_CHIP_ID: + return (sih->chipst & CST4329_SPROM_SEL) != 0; + case BCM4315_CHIP_ID: + return (sih->chipst & CST4315_SPROM_SEL) != 0; + case BCM4319_CHIP_ID: + return (sih->chipst & CST4319_SPROM_SEL) != 0; + + case BCM4336_CHIP_ID: + case BCM43362_CHIP_ID: + return (sih->chipst & CST4336_SPROM_PRESENT) != 0; + + case BCM4330_CHIP_ID: + return (sih->chipst & CST4330_SPROM_PRESENT) != 0; + case BCM4313_CHIP_ID: + return (sih->chipst & CST4313_SPROM_PRESENT) != 0; + case BCM43239_CHIP_ID: + return ((sih->chipst & CST43239_SPROM_MASK) && + !(sih->chipst & CST43239_SFLASH_MASK)); + default: + return TRUE; + } +} diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 9ca3d6020239..4fcdcd32a3ff 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -2,13 +2,13 @@ * Linux cfg80211 driver - Android related functions * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -16,7 +16,7 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. @@ -67,13 +67,16 @@ #define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" #define CMD_BTCOEXMODE "BTCOEXMODE" #define CMD_SETSUSPENDOPT "SETSUSPENDOPT" +#define CMD_SETSUSPENDMODE "SETSUSPENDMODE" #define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" #define CMD_SETFWPATH "SETFWPATH" #define CMD_SETBAND "SETBAND" #define CMD_GETBAND "GETBAND" #define CMD_COUNTRY "COUNTRY" #define CMD_P2P_SET_NOA "P2P_SET_NOA" +#if !defined WL_ENABLE_P2P_IF #define CMD_P2P_GET_NOA "P2P_GET_NOA" +#endif #define CMD_P2P_SET_PS "P2P_SET_PS" #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" @@ -112,7 +115,7 @@ typedef struct android_wifi_priv_cmd { */ void dhd_customer_gpio_wlan_ctrl(int onoff); uint dhd_dev_reset(struct net_device *dev, uint8 flag); -void dhd_dev_init_ioctl(struct net_device *dev); +int dhd_dev_init_ioctl(struct net_device *dev); #ifdef WL_CFG80211 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); @@ -202,7 +205,7 @@ static int wl_android_set_suspendopt(struct net_device *dev, char *command, int ret_now = net_os_set_suspend_disable(dev, suspend_flag); if (ret_now != suspend_flag) { - if (!(ret = net_os_set_suspend(dev, ret_now))) + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) DHD_INFO(("%s: Suspend Flag %d -> %d\n", __FUNCTION__, ret_now, suspend_flag)); else @@ -211,6 +214,26 @@ static int wl_android_set_suspendopt(struct net_device *dev, char *command, int return ret; } +static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + DHD_INFO(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n",__FUNCTION__,ret)); +#endif + return ret; +} + static int wl_android_get_band(struct net_device *dev, char *command, int total_len) { uint band; @@ -224,7 +247,7 @@ static int wl_android_get_band(struct net_device *dev, char *command, int total_ return bytes_written; } -#ifdef PNO_SUPPORT +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) { wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; @@ -330,7 +353,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t exit_proc: return res; } -#endif /* PNO_SUPPORT */ +#endif /* PNO_SUPPORT && !WL_SCHED_SCAN */ static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) { @@ -364,8 +387,10 @@ int wl_android_wifi_on(struct net_device *dev) sdioh_start(NULL, 0); ret = dhd_dev_reset(dev, FALSE); sdioh_start(NULL, 1); - if (!ret) - dhd_dev_init_ioctl(dev); + if (!ret) { + if (dhd_dev_init_ioctl(dev) < 0) + ret = -EFAULT; + } g_wifi_on = 1; } dhd_net_if_unlock(dev); @@ -506,6 +531,9 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); } + else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) { + bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len); + } else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; bytes_written = wldev_set_band(net, band); @@ -517,7 +545,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) char *country_code = command + strlen(CMD_COUNTRY) + 1; bytes_written = wldev_set_country(net, country_code); } -#ifdef PNO_SUPPORT +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { bytes_written = dhd_dev_pno_reset(net); } @@ -537,9 +565,11 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, priv_cmd.total_len - skip); } +#if !defined WL_ENABLE_P2P_IF else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); } +#endif else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { int skip = strlen(CMD_P2P_SET_PS) + 1; bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, @@ -559,8 +589,10 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) bytes_written = strlen("OK"); } - if (bytes_written > 0) { - if (bytes_written > priv_cmd.total_len) { + if (bytes_written >= 0) { + if ((bytes_written == 0) && (priv_cmd.total_len > 0)) + command[0] = '\0'; + if (bytes_written >= priv_cmd.total_len) { DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); bytes_written = priv_cmd.total_len; } else { @@ -571,7 +603,8 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); ret = -EFAULT; } - } else { + } + else { ret = bytes_written; } @@ -588,7 +621,7 @@ int wl_android_init(void) { int ret = 0; - dhd_msg_level = DHD_ERROR_VAL; + dhd_msg_level |= DHD_ERROR_VAL; #ifdef ENABLE_INSMOD_NO_FW_LOAD dhd_download_fw_on_driverload = FALSE; #endif /* ENABLE_INSMOD_NO_FW_LOAD */ @@ -608,30 +641,14 @@ int wl_android_exit(void) return ret; } -int wl_android_post_init(void) +void wl_android_post_init(void) { - struct net_device *ndev; - int ret = 0; - char buf[IFNAMSIZ]; if (!dhd_download_fw_on_driverload) { /* Call customer gpio to turn off power with WL_REG_ON signal */ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); g_wifi_on = 0; - } else { - memset(buf, 0, IFNAMSIZ); -#ifdef CUSTOMER_HW2 - snprintf(buf, IFNAMSIZ, "%s%d", iface_name, 0); -#else - snprintf(buf, IFNAMSIZ, "%s%d", "eth", 0); -#endif - if ((ndev = dev_get_by_name (&init_net, buf)) != NULL) { - dhd_dev_init_ioctl(ndev); - dev_put(ndev); - } } - return ret; } - /** * Functions for Android WiFi card detection */ @@ -682,13 +699,14 @@ void* wl_android_prealloc(int section, unsigned long size) alloc_ptr = wifi_control_data->mem_prealloc(section, size); if (alloc_ptr) { DHD_INFO(("success alloc section %d\n", section)); - bzero(alloc_ptr, size); + if (size != 0L) + bzero(alloc_ptr, size); return alloc_ptr; } } DHD_ERROR(("can't alloc section %d\n", section)); - return 0; + return NULL; } int wifi_get_irq_number(unsigned long *irq_flags_ptr) @@ -787,7 +805,7 @@ static int wifi_remove(struct platform_device *pdev) static int wifi_suspend(struct platform_device *pdev, pm_message_t state) { DHD_TRACE(("##> %s\n", __FUNCTION__)); -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(0); #endif return 0; @@ -796,7 +814,7 @@ static int wifi_suspend(struct platform_device *pdev, pm_message_t state) static int wifi_resume(struct platform_device *pdev) { DHD_TRACE(("##> %s\n", __FUNCTION__)); -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) if (dhd_os_check_if_up(bcmsdh_get_drvdata())) bcmsdh_oob_intr_set(1); #endif diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h index 17373b7f6d5b..3983306cfe38 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.h +++ b/drivers/net/wireless/bcmdhd/wl_android.h @@ -40,7 +40,7 @@ */ int wl_android_init(void); int wl_android_exit(void); -int wl_android_post_init(void); +void wl_android_post_init(void); int wl_android_wifi_on(struct net_device *dev); int wl_android_wifi_off(struct net_device *dev); int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 04affb50b18b..f728c14622a1 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -2,13 +2,13 @@ * Linux cfg80211 driver * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -41,10 +41,9 @@ #include <dhd.h> #include <dhdioctl.h> #include <wlioctl.h> +#include <dhd_cfg80211.h> #include <proto/ethernet.h> -#include <dngl_stats.h> -#include <dhd.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/netdevice.h> @@ -54,54 +53,29 @@ #include <linux/ieee80211.h> #include <linux/wait.h> #include <net/cfg80211.h> - #include <net/rtnetlink.h> -#include <linux/mmc/sdio_func.h> -#include <linux/firmware.h> -#include <bcmsdbus.h> #include <wlioctl.h> #include <wldev_common.h> #include <wl_cfg80211.h> #include <wl_cfgp2p.h> -static struct sdio_func *cfg80211_sdio_func; -static struct wl_priv *wlcfg_drv_priv; +static struct device *cfg80211_parent_dev = NULL; +static int vsdb_supported = 0; +struct wl_priv *wlcfg_drv_priv = NULL; u32 wl_dbg_level = WL_DBG_ERR; -#define WL_4329_FW_FILE "brcm/bcm4329-fullmac-4-218-248-5.bin" -#define WL_4329_NVRAM_FILE "brcm/bcm4329-fullmac-4-218-248-5.txt" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define MAX_WAIT_TIME 1500 -static s8 ioctlbuf[WLC_IOCTL_MAXLEN]; +#define WL_SCAN_ACTIVE_TIME 40 +#define WL_SCAN_PASSIVE_TIME 130 +#define WL_FRAME_LEN 300 +#define DNGL_FUNC(func, parameters) func parameters; #define COEX_DHCP -#if defined(COEX_DHCP) -#define BT_DHCP_eSCO_FIX /* use New SCO/eSCO smart YG - * suppression - */ -#define BT_DHCP_USE_FLAGS /* this flag boost wifi pkt priority - * to max, caution: -not fair to sco - */ -#define BT_DHCP_OPPR_WIN_TIME 2500 /* T1 start SCO/ESCo priority - * suppression - */ -#define BT_DHCP_FLAG_FORCE_TIME 5500 /* T2 turn off SCO/SCO supperesion - * is (timeout) - */ -enum wl_cfg80211_btcoex_status { - BT_DHCP_IDLE, - BT_DHCP_START, - BT_DHCP_OPPR_WIN, - BT_DHCP_FLAG_FORCE_TIMEOUT -}; - -static int wl_cfg80211_btcoex_init(struct wl_priv *wl); -static void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); -#endif /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN @@ -121,8 +95,10 @@ static const struct ieee80211_regdomain brcm_regdom = { REG_RULE(2467-10, 2472+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), - /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only + /* + * IEEE 802.11 channel 14 - is for JP only, + * we need cfg80211 to allow it (reg_flags = 0); so that + * hostapd could request auto channel by sending down ch 14 */ REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | @@ -224,7 +200,8 @@ static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_pmksa *pmksa); static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); -static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted); +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, bool aborted, bool fw_abort); /* * event & event Q handlers for cfg80211 interfaces */ @@ -242,6 +219,8 @@ static s32 wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 type, const wl_event_msg_t *msg, void *data); static void wl_put_event(struct wl_event_q *e); static void wl_wakeup_event(struct wl_priv *wl); +static s32 wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); static s32 wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); @@ -256,22 +235,19 @@ static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); +#ifdef WL_SCHED_SCAN +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* WL_SCHED_SCAN */ +#ifdef PNO_SUPPORT +static s32 wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* PNO_SUPPORT */ /* - * register/deregister sdio function - */ -struct sdio_func *wl_cfg80211_get_sdio_func(void); -static void wl_clear_sdio_func(void); - -/* - * ioctl utilites + * register/deregister parent device */ -static s32 wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, - s32 buf_len); -static __used s32 wl_dev_bufvar_set(struct net_device *dev, s8 *name, - s8 *buf, s32 len); -static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val); -static s32 wl_dev_intvar_get(struct net_device *dev, s8 *name, - s32 *retval); +static void wl_cfg80211_clear_parent_dev(void); /* * cfg80211 set_wiphy_params utilities @@ -283,10 +259,10 @@ static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); /* * wl profile utilities */ -static s32 wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, - void *data, s32 item); -static void *wl_read_prof(struct wl_priv *wl, s32 item); -static void wl_init_prof(struct wl_priv *wl); +static s32 wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item); +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item); +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev); /* * cfg80211 connect utilites @@ -314,14 +290,14 @@ static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size); static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size); static u32 wl_get_ielen(struct wl_priv *wl); -static s32 wl_mode_to_nl80211_iftype(s32 mode); -static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev); +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev); static void wl_free_wdev(struct wl_priv *wl); static s32 wl_inform_bss(struct wl_priv *wl); static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev); +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, const u8 *mac_addr, @@ -347,42 +323,19 @@ static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev); static __used bool wl_is_ibssstarter(struct wl_priv *wl); /* - * dongle up/down , default configuration utilities + * link up/down , default configuration utilities */ +static s32 __wl_cfg80211_up(struct wl_priv *wl); +static s32 __wl_cfg80211_down(struct wl_priv *wl); +static s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev); static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e); static void wl_link_up(struct wl_priv *wl); static void wl_link_down(struct wl_priv *wl); -static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); -static s32 __wl_cfg80211_up(struct wl_priv *wl); -static s32 __wl_cfg80211_down(struct wl_priv *wl); -static s32 wl_dongle_probecap(struct wl_priv *wl); +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); static void wl_init_conf(struct wl_conf *conf); -static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); - -/* - * dongle configuration utilities - */ -#ifndef EMBEDDED_PLATFORM -static s32 wl_dongle_country(struct net_device *ndev, u8 ccode); -static s32 wl_dongle_up(struct net_device *ndev, u32 up); -static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode); -static s32 wl_dongle_glom(struct net_device *ndev, u32 glom, - u32 dongle_align); -static s32 wl_dongle_roam(struct net_device *ndev, u32 roamvar, - u32 bcn_timeout); -static s32 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, - s32 scan_unassoc_time); -static s32 wl_dongle_offload(struct net_device *ndev, s32 arpoe, - s32 arp_ol); -static s32 wl_pattern_atoh(s8 *src, s8 *dst); -static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode); static s32 wl_update_wiphybands(struct wl_priv *wl); -#endif /* !EMBEDDED_PLATFORM */ -static __used void wl_dongle_poweron(struct wl_priv *wl); -static __used void wl_dongle_poweroff(struct wl_priv *wl); -static s32 wl_config_dongle(struct wl_priv *wl, bool need_lock); /* * iscan handler @@ -406,33 +359,20 @@ static s32 wl_iscan_inprogress(struct wl_priv *wl); static s32 wl_iscan_aborted(struct wl_priv *wl); /* - * fw/nvram downloading handler - */ -static void wl_init_fw(struct wl_fw_ctrl *fw); - -/* * find most significant bit set */ static __used u32 wl_find_msb(u16 bit16); /* - * update pmklist to dongle - */ -static __used s32 wl_update_pmklist(struct net_device *dev, - struct wl_pmk_list *pmk_list, s32 err); - -/* - * debufs support - */ -static int wl_debugfs_add_netdev_params(struct wl_priv *wl); -static void wl_debugfs_remove_netdev(struct wl_priv *wl); - -/* * rfkill support */ static int wl_setup_rfkill(struct wl_priv *wl, bool setup); static int wl_rfkill_set(void *data, bool blocked); +static wl_scan_params_t *wl_cfg80211_scan_alloc_params(int channel, + int nprobes, int *out_params_size); +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac); + /* * Some external functions, TODO: move them to dhd_linux.h */ @@ -444,10 +384,10 @@ int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); #define CHECK_SYS_UP(wlpriv) \ do { \ - if (unlikely(!wl_get_drv_status(wlpriv, READY))) { \ - WL_INFO(("device is not ready : status (%d)\n", \ - (int)wlpriv->status)); \ - return -EIO; \ + struct net_device *ndev = wl_to_prmry_ndev(wlpriv); \ + if (unlikely(!wl_get_drv_status(wlpriv, READY, ndev))) { \ + WL_INFO(("device is not ready\n")); \ + return -EIO; \ } \ } while (0) @@ -740,6 +680,43 @@ wl_validate_wps_ie(char *wps_ie, bool *pbc) } } +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) +{ + if (vsdb_supported) { + return wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + else { + chanspec_t chspec; + int err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ether_addr bssid; + struct wl_bss_info *bss = NULL; + if ((err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), false))) { + /* STA interface is not associated. So start the new interface on a temp + * channel . Later proper channel will be applied by the above framework + * via set_channel (cfg80211 API). + */ + WL_DBG(("Not associated. Return a temp channel. \n")); + return wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + + + *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + if ((err = wldev_ioctl(dev, WLC_GET_BSS_INFO, wl->extra_buf, + sizeof(WL_EXTRA_BUF_MAX), false))) { + WL_ERR(("Failed to get associated bss info, use temp channel \n")); + chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + else { + bss = (struct wl_bss_info *) (wl->extra_buf + 4); + chspec = bss->chanspec; + WL_DBG(("Valid BSS Found. chanspec:%d \n", bss->chanspec)); + } + return chspec; + } +} + static struct net_device* wl_cfg80211_add_monitor_if(char *name) { int ret = 0; @@ -758,15 +735,20 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, s32 err; s32 timeout = -1; s32 wlif_type = -1; - s32 index = 0; s32 mode = 0; +#if defined(WL_ENABLE_P2P_IF) + s32 dhd_mode = 0; +#endif /* (WL_ENABLE_P2P_IF) */ chanspec_t chspec; struct wl_priv *wl = wiphy_priv(wiphy); struct net_device *_ndev; - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); - int (*net_attach)(dhd_pub_t *dhdp, int ifidx); + struct ether_addr primary_mac; + int (*net_attach)(void *dhdp, int ifidx); bool rollback_lock = false; + /* Use primary I/F for sending cmds down to firmware */ + _ndev = wl_to_prmry_ndev(wl); + WL_DBG(("if name: %s, type: %d\n", name, type)); switch (type) { case NL80211_IFTYPE_ADHOC: @@ -798,6 +780,8 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("name is NULL\n")); return NULL; } + if (wl->iface_cnt == IFACE_MAX_CNT) + return ERR_PTR(-ENOMEM); if (wl->p2p_supported && (wlif_type != -1)) { if (wl_get_p2p_status(wl, IF_DELETING)) { /* wait till IF_DEL is complete @@ -809,7 +793,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, } WL_INFO(("%s: Released the lock and wait till IF_DEL is complete\n", __func__)); - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, IF_DELETING) == false), msecs_to_jiffies(MAX_WAIT_TIME)); @@ -826,17 +810,25 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, return ERR_PTR(-EAGAIN); } } - if (!p2p_on(wl) && strstr(name, WL_P2P_INTERFACE_PREFIX)) { + if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { p2p_on(wl) = true; wl_cfgp2p_set_firm_p2p(wl); wl_cfgp2p_init_discovery(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); } + memset(wl->p2p->vir_ifname, 0, IFNAMSIZ); strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); - /* Temporary use channel 11, in case GO will be changed with set_channel API */ - chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + wldev_iovar_setint(_ndev, "mpc", 0); + wl_notify_escan_complete(wl, _ndev, true, true); + /* In concurrency case, STA may be already associated in a particular channel. + * so retrieve the current channel of primary interface and then start the virtual + * interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); /* For P2P mode, use P2P-specific driver features to create the * bss: "wl p2p_ifadd" @@ -844,10 +836,12 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, wl_set_p2p_status(wl, IF_ADD); err = wl_cfgp2p_ifadd(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR((" virtual iface add failed (%d) \n", err)); return ERR_PTR(-ENOMEM); + } - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, IF_ADD) == false), msecs_to_jiffies(MAX_WAIT_TIME)); if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { @@ -860,27 +854,37 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, } vwdev->wiphy = wl->wdev->wiphy; WL_INFO((" virtual interface(%s) is created memalloc done \n", - wl->p2p->vir_ifname)); - index = alloc_idx_vwdev(wl); - wl->vwdev[index] = vwdev; - vwdev->iftype = - (wlif_type == WL_P2P_IF_CLIENT) ? NL80211_IFTYPE_STATION - : NL80211_IFTYPE_AP; + wl->p2p->vir_ifname)); + vwdev->iftype = type; _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); _ndev->ieee80211_ptr = vwdev; SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); vwdev->netdev = _ndev; - wl_set_drv_status(wl, READY); + wl_set_drv_status(wl, READY, _ndev); wl->p2p->vif_created = true; - set_mode_by_netdev(wl, _ndev, mode); + wl_set_mode_by_netdev(wl, _ndev, mode); net_attach = wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION); if (rtnl_is_locked()) { rtnl_unlock(); rollback_lock = true; } - if (net_attach && !net_attach(dhd, _ndev->ifindex)) { - WL_DBG((" virtual interface(%s) is " + if (net_attach && !net_attach(wl->pub, _ndev->ifindex)) { + wl_alloc_netinfo(wl, _ndev, vwdev, mode); + WL_ERR((" virtual interface(%s) is " "created net attach done\n", wl->p2p->vir_ifname)); +#if defined(WL_ENABLE_P2P_IF) + if (type == NL80211_IFTYPE_P2P_CLIENT) + dhd_mode = P2P_GC_ENABLED; + else if (type == NL80211_IFTYPE_P2P_GO) + dhd_mode = P2P_GO_ENABLED; + DNGL_FUNC(dhd_cfg80211_set_p2p_info, (wl, dhd_mode)); +#endif /* (WL_ENABLE_P2P_IF) */ + /* Start the P2P I/F with PM disabled. Enable PM from + * the framework + */ + if ((type == NL80211_IFTYPE_P2P_CLIENT) || ( + type == NL80211_IFTYPE_P2P_GO)) + vwdev->ps = NL80211_PS_DISABLED; } else { /* put back the rtnl_lock again */ if (rollback_lock) @@ -911,34 +915,46 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) s32 timeout = -1; s32 ret = 0; WL_DBG(("Enter\n")); + + if (wl->p2p_net == dev) { + /* Since there is no ifidx corresponding to p2p0, cmds to + * firmware should be routed through primary I/F + */ + dev = wl_to_prmry_ndev(wl); + } + if (wl->p2p_supported) { memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); if (wl->p2p->vif_created) { - if (wl_get_drv_status(wl, SCANNING)) { - wl_cfg80211_scan_abort(wl, dev); + if (wl_get_drv_status(wl, SCANNING, dev)) { + wl_notify_escan_complete(wl, dev, true, true); } wldev_iovar_setint(dev, "mpc", 1); wl_set_p2p_status(wl, IF_DELETING); ret = wl_cfgp2p_ifdel(wl, &p2p_mac); - if (ret) { /* Firmware could not delete the interface so we will not get WLC_E_IF * event for cleaning the dhd virtual nw interace * So lets do it here. Failures from fw will ensure the application to do * ifconfig <inter> down and up sequnce, which will reload the fw * however we should cleanup the linux network virtual interfaces */ - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); - WL_ERR(("Firmware returned an error from p2p_ifdel\n")); - WL_ERR(("try to remove linux virtual interface %s\n", dev->name)); - dhd_del_if(dhd->info, dhd_net2idx(dhd->info, dev)); + /* Request framework to RESET and clean up */ + if (ret) { + struct net_device *ndev = wl_to_prmry_ndev(wl); + WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" + "HANG Notification sent to %s\n", ret, ndev->name)); + wl_cfg80211_hang(ndev, WLAN_REASON_UNSPECIFIED); } /* Wait for any pending scan req to get aborted from the sysioc context */ - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, - (wl_get_p2p_status(wl, IF_DELETING) == false), + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl->p2p->vif_created == false), msecs_to_jiffies(MAX_WAIT_TIME)); - if (timeout > 0 && !wl_get_p2p_status(wl, IF_DELETING)) { + if (timeout > 0 && (wl->p2p->vif_created == false)) { WL_DBG(("IFDEL operation done\n")); +#if defined(WL_ENABLE_P2P_IF) + DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (wl)); +#endif /* (WL_ENABLE_P2P_IF)) */ } else { WL_ERR(("IFDEL didn't complete properly\n")); } @@ -961,7 +977,8 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, s32 mode = 0; chanspec_t chspec; struct wl_priv *wl = wiphy_priv(wiphy); - WL_DBG(("Enter \n")); + + WL_DBG(("Enter type %d\n", type)); switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: @@ -988,28 +1005,35 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, return -EINVAL; } - if (ap) { - set_mode_by_netdev(wl, ndev, mode); + wl_set_mode_by_netdev(wl, ndev, mode); if (wl->p2p_supported && wl->p2p->vif_created) { WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", wl->p2p->vif_created, - p2p_on(wl))); - chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + p2p_on(wl))); + wldev_iovar_setint(ndev, "mpc", 0); + wl_notify_escan_complete(wl, ndev, true, true); + + /* In concurrency case, STA may be already associated in a particular + * channel. so retrieve the current channel of primary interface and + * then start the virtual interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); + wlif_type = ap ? WL_P2P_IF_GO : WL_P2P_IF_CLIENT; WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); wl_set_p2p_status(wl, IF_CHANGING); wl_clr_p2p_status(wl, IF_CHANGED); err = wl_cfgp2p_ifchange(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, IF_CHANGED) == true), msecs_to_jiffies(MAX_WAIT_TIME)); - set_mode_by_netdev(wl, ndev, mode); + wl_set_mode_by_netdev(wl, ndev, mode); wl_clr_p2p_status(wl, IF_CHANGING); wl_clr_p2p_status(wl, IF_CHANGED); } else if (ndev == wl_to_prmry_ndev(wl) && - !wl_get_drv_status(wl, AP_CREATED)) { - wl_set_drv_status(wl, AP_CREATING); + !wl_get_drv_status(wl, AP_CREATED, ndev)) { + wl_set_drv_status(wl, AP_CREATING, ndev); if (!wl->ap_info && !(wl->ap_info = kzalloc(sizeof(struct ap_info), GFP_KERNEL))) { WL_ERR(("struct ap_saved_ie allocation failed\n")); @@ -1027,15 +1051,16 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, -int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)) + void* _net_attach) { struct wl_priv *wl = wlcfg_drv_priv; s32 ret = BCME_OK; + WL_DBG(("Enter")); if (!ndev) { WL_ERR(("net is NULL\n")); return 0; } - if (wl->p2p_supported) { + if (wl->p2p_supported && wl_get_p2p_status(wl, IF_ADD)) { WL_DBG(("IF_ADD event called from dongle, old interface name: %s," "new name: %s\n", ndev->name, wl->p2p->vir_ifname)); /* Assign the net device to CONNECT BSSCFG */ @@ -1046,13 +1071,26 @@ int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)) ndev->ifindex = idx; wl_clr_p2p_status(wl, IF_ADD); - wake_up_interruptible(&wl->dongle_event_wait); + wake_up_interruptible(&wl->netif_change_event); + } else { + ret = BCME_NOTREADY; } return ret; } s32 -wl_cfg80211_notify_ifdel(struct net_device *ndev) +wl_cfg80211_notify_ifdel(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + wl_clr_p2p_status(wl, IF_DELETING); + + return 0; +} + +s32 +wl_cfg80211_ifdel_ops(struct net_device *ndev) { struct wl_priv *wl = wlcfg_drv_priv; bool rollback_lock = false; @@ -1063,8 +1101,10 @@ wl_cfg80211_notify_ifdel(struct net_device *ndev) return 0; } - if (p2p_is_on(wl) && wl->p2p->vif_created) { - if (wl->scan_request) { + if (p2p_is_on(wl) && wl->p2p->vif_created && + wl_get_p2p_status(wl, IF_DELETING)) { + if (wl->scan_request && + (wl->escan_info.ndev == ndev)) { /* Abort any pending scan requests */ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (!rtnl_is_locked()) { @@ -1072,7 +1112,7 @@ wl_cfg80211_notify_ifdel(struct net_device *ndev) rollback_lock = true; } WL_DBG(("ESCAN COMPLETED\n")); - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, ndev, true, false); if (rollback_lock) rtnl_unlock(); } @@ -1086,12 +1126,12 @@ wl_cfg80211_notify_ifdel(struct net_device *ndev) wl->p2p->vif_created = false; wl_cfgp2p_clear_management_ie(wl, index); - wl_clr_p2p_status(wl, IF_DELETING); WL_DBG(("index : %d\n", index)); } + /* Wake up any waiting thread */ - wake_up_interruptible(&wl->dongle_event_wait); + wake_up_interruptible(&wl->netif_change_event); return 0; } @@ -1123,15 +1163,15 @@ wl_cfg80211_notify_ifchange(void) struct wl_priv *wl = wlcfg_drv_priv; if (wl_get_p2p_status(wl, IF_CHANGING)) { wl_set_p2p_status(wl, IF_CHANGED); - wake_up_interruptible(&wl->dongle_event_wait); + wake_up_interruptible(&wl->netif_change_event); } return 0; } static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_request *request) { - u32 n_ssids = request->n_ssids; - u32 n_channels = request->n_channels; + u32 n_ssids; + u32 n_channels; u16 channel; chanspec_t chanspec; s32 i, offset; @@ -1160,6 +1200,13 @@ static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_req params->passive_time = htod32(params->passive_time); params->home_time = htod32(params->home_time); + /* if request is null just exit so it will be all channel broadcast scan */ + if (!request) + return; + + n_ssids = request->n_ssids; + n_channels = request->n_channels; + /* Copy channel array if applicable */ WL_SCAN(("### List of channelspecs to scan ###\n")); if (n_channels > 0) { @@ -1186,7 +1233,7 @@ static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_req params->channel_list[i] &= WL_CHANSPEC_CHAN_MASK; params->channel_list[i] |= chanspec; WL_SCAN(("Chan : %d, Channel spec: %x \n", - channel, params->channel_list[i])); + channel, params->channel_list[i])); params->channel_list[i] = htod16(params->channel_list[i]); } } else { @@ -1248,8 +1295,7 @@ wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, return -ENOMEM; } - if (request != NULL) - wl_scan_prep(¶ms->params, request); + wl_scan_prep(¶ms->params, request); params->version = htod32(ISCAN_REQ_VERSION); params->action = htod16(action); @@ -1261,7 +1307,7 @@ wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, goto done; } err = wldev_iovar_setbuf(iscan->dev, "iscan", params, params_size, - iscan->ioctl_buf, WLC_IOCTL_MEDLEN); + iscan->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); if (unlikely(err)) { if (err == -EBUSY) { WL_ERR(("system busy : iscan canceled\n")); @@ -1299,6 +1345,25 @@ static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request } static s32 +wl_get_valid_channels(struct net_device *ndev, u8 *valid_chan_list, s32 size) +{ + wl_uint32_list_t *list; + s32 err = BCME_OK; + if (valid_chan_list == NULL || size <= 0) + return -ENOMEM; + + memset(valid_chan_list, 0, size); + list = (wl_uint32_list_t *)(void *) valid_chan_list; + list->count = htod32(WL_NUMCHANNELS); + err = wldev_ioctl(ndev, WLC_GET_VALID_CHANNELS, valid_chan_list, size, false); + if (err != 0) { + WL_ERR(("get channels failed with %d\n", err)); + } + + return err; +} + +static s32 wl_run_escan(struct wl_priv *wl, struct net_device *ndev, struct cfg80211_scan_request *request, uint16 action) { @@ -1306,12 +1371,16 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, u32 n_channels; u32 n_ssids; s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)); - wl_escan_params_t *params; + wl_escan_params_t *params = NULL; struct cfg80211_scan_request *scan_request = wl->scan_request; + u8 chan_buf[sizeof(u32)*(WL_NUMCHANNELS + 1)]; u32 num_chans = 0; + s32 channel; + s32 n_valid_chan; s32 search_state = WL_P2P_DISC_ST_SCAN; - u32 i; + u32 i, j, n_nodfs = 0; u16 *default_chan_list = NULL; + wl_uint32_list_t *list; struct net_device *dev = NULL; WL_DBG(("Enter \n")); @@ -1340,8 +1409,7 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, goto exit; } - if (request != NULL) - wl_scan_prep(¶ms->params, request); + wl_scan_prep(¶ms->params, request); params->version = htod32(ESCAN_REQ_VERSION); params->action = htod16(action); params->sync_id = htod16(0x1234); @@ -1352,13 +1420,15 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, goto exit; } err = wldev_iovar_setbuf(ndev, "escan", params, params_size, - wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN); + wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL); if (unlikely(err)) WL_ERR((" Escan set error (%d)\n", err)); kfree(params); } - else if (p2p_on(wl) && p2p_scan(wl)) { + else if (p2p_is_on(wl) && p2p_scan(wl)) { /* P2P SCAN TRIGGER */ + s32 _freq = 0; + n_nodfs = 0; if (scan_request && scan_request->n_channels) { num_chans = scan_request->n_channels; WL_SCAN((" chann number : %d\n", num_chans)); @@ -1369,11 +1439,26 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, err = -ENOMEM; goto exit; } - for (i = 0; i < num_chans; i++) - { - default_chan_list[i] = - ieee80211_frequency_to_channel( - scan_request->channels[i]->center_freq); + if (!wl_get_valid_channels(ndev, chan_buf, sizeof(chan_buf))) { + list = (wl_uint32_list_t *) chan_buf; + n_valid_chan = dtoh32(list->count); + for (i = 0; i < num_chans; i++) + { + _freq = scan_request->channels[i]->center_freq; + channel = ieee80211_frequency_to_channel(_freq); + /* remove DFS channels */ + if (channel < 52 || channel > 140) { + for (j = 0; j < n_valid_chan; j++) { + /* allows only supported channel on + * current reguatory + */ + if (channel == (dtoh32(list->element[j]))) + default_chan_list[n_nodfs++] = + channel; + } + } + + } } if (num_chans == 3 && ( (default_chan_list[0] == SOCIAL_CHAN_1) && @@ -1383,12 +1468,15 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, search_state = WL_P2P_DISC_ST_SEARCH; WL_INFO(("P2P SEARCH PHASE START \n")); } else if ((dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)) && - (get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { + (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { /* If you are already a GO, then do SEARCH only */ WL_INFO(("Already a GO. Do SEARCH Only")); search_state = WL_P2P_DISC_ST_SEARCH; + num_chans = n_nodfs; + } else { WL_INFO(("P2P SCAN STATE START \n")); + num_chans = n_nodfs; } } @@ -1413,7 +1501,7 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, s32 passive_scan; wl_scan_results_t *results; WL_SCAN(("Enter \n")); - + wl->escan_info.ndev = ndev; wl->escan_info.wiphy = wiphy; wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; passive_scan = wl->active_scan ? 0 : 1; @@ -1440,36 +1528,41 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct wl_priv *wl = wiphy_priv(wiphy); struct cfg80211_ssid *ssids; struct wl_scan_req *sr = wl_to_sr(wl); + struct ether_addr primary_mac; wpa_ie_fixed_t *wps_ie; s32 passive_scan; bool iscan_req; - bool escan_req; - bool spec_scan; + bool escan_req = false; bool p2p_ssid; s32 err = 0; s32 i; u32 wpsie_len = 0; u8 wpsie[IE_MAX_LEN]; + /* If scan req comes for p2p0, send it over primary I/F + * Scan results will be delivered corresponding to cfg80211_scan_request + */ + if (ndev == wl->p2p_net) { + ndev = wl_to_prmry_ndev(wl); + } + WL_DBG(("Enter wiphy (%p)\n", wiphy)); - if (unlikely(wl_get_drv_status(wl, SCANNING))) { - WL_ERR(("Scanning already : status (%d)\n", (int)wl->status)); + if (wl_get_drv_status_all(wl, SCANNING)) { + WL_ERR(("Scanning already\n")); return -EAGAIN; } - if (unlikely(wl_get_drv_status(wl, SCAN_ABORTING))) { - WL_ERR(("Scanning being aborted : status (%d)\n", - (int)wl->status)); + if (wl_get_drv_status(wl, SCAN_ABORTING, ndev)) { + WL_ERR(("Scanning being aborted\n")); return -EAGAIN; } - if (request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) { - WL_ERR(("n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); + if (request && request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) { + WL_ERR(("request null or n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); return -EOPNOTSUPP; } /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); iscan_req = false; - spec_scan = false; if (request) { /* scan bss */ ssids = request->ssids; if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { @@ -1490,6 +1583,9 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* p2p on at the first time */ p2p_on(wl) = true; wl_cfgp2p_set_firm_p2p(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); } p2p_scan(wl) = true; } @@ -1543,7 +1639,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ssids = this_ssid; } wl->scan_request = request; - wl_set_drv_status(wl, SCANNING); + wl_set_drv_status(wl, SCANNING, ndev); if (iscan_req) { err = wl_do_iscan(wl, request); if (likely(!err)) @@ -1578,7 +1674,6 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, sr->ssid.SSID_len = htod32(sr->ssid.SSID_len); WL_SCAN(("Specific scan ssid=\"%s\" len=%d\n", sr->ssid.SSID, sr->ssid.SSID_len)); - spec_scan = true; } else { WL_SCAN(("Broadcast scan\n")); } @@ -1606,7 +1701,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return 0; scan_out: - wl_clr_drv_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, ndev); wl->scan_request = NULL; return err; } @@ -1630,52 +1725,11 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return err; } -static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val) -{ - s8 buf[WLC_IOCTL_SMLEN]; - u32 len; - s32 err = 0; - - val = htod32(val); - len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); - BUG_ON(unlikely(!len)); - - err = wldev_ioctl(dev, WLC_SET_VAR, buf, len, false); - if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - } - - return err; -} - -static s32 -wl_dev_intvar_get(struct net_device *dev, s8 *name, s32 *retval) -{ - union { - s8 buf[WLC_IOCTL_SMLEN]; - s32 val; - } var; - u32 len; - u32 data_null; - s32 err = 0; - - len = bcm_mkiovar(name, (char *)(&data_null), 0, - (char *)(&var), sizeof(var.buf)); - BUG_ON(unlikely(!len)); - err = wldev_ioctl(dev, WLC_GET_VAR, &var, len, false); - if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - } - *retval = dtoh32(var.val); - - return err; -} - static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold) { s32 err = 0; - err = wl_dev_intvar_set(dev, "rtsthresh", rts_threshold); + err = wldev_iovar_setint(dev, "rtsthresh", rts_threshold); if (unlikely(err)) { WL_ERR(("Error (%d)\n", err)); return err; @@ -1687,7 +1741,7 @@ static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold) { s32 err = 0; - err = wl_dev_intvar_set(dev, "fragthresh", frag_threshold); + err = wldev_iovar_setint_bsscfg(dev, "fragthresh", frag_threshold, 0); if (unlikely(err)) { WL_ERR(("Error (%d)\n", err)); return err; @@ -1716,6 +1770,7 @@ static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) s32 err = 0; CHECK_SYS_UP(wl); + WL_DBG(("Enter\n")); if (changed & WIPHY_PARAM_RTS_THRESHOLD && (wl->conf->rts_threshold != wiphy->rts_threshold)) { wl->conf->rts_threshold = wiphy->rts_threshold; @@ -1745,7 +1800,6 @@ static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) return err; } } - return err; } @@ -1862,7 +1916,7 @@ wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) WL_ERR(("set wpa_auth failed (%d)\n", err)); return err; } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->wpa_versions = sme->crypto.wpa_versions; return err; } @@ -1877,21 +1931,21 @@ wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) s32 bssidx = wl_cfgp2p_find_idx(wl, dev); switch (sme->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: - val = 0; + val = WL_AUTH_OPEN_SYSTEM; WL_DBG(("open system\n")); break; case NL80211_AUTHTYPE_SHARED_KEY: - val = 1; + val = WL_AUTH_SHARED_KEY; WL_DBG(("shared key\n")); break; case NL80211_AUTHTYPE_AUTOMATIC: - val = 2; + val = WL_AUTH_OPEN_SHARED; WL_DBG(("automatic\n")); break; case NL80211_AUTHTYPE_NETWORK_EAP: WL_DBG(("network eap\n")); default: - val = 2; + val = WL_AUTH_OPEN_SHARED; WL_ERR(("invalid auth type (%d)\n", sme->auth_type)); break; } @@ -1901,7 +1955,7 @@ wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) WL_ERR(("set auth failed (%d)\n", err)); return err; } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->auth_type = sme->auth_type; return err; } @@ -1962,7 +2016,11 @@ wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) WL_DBG(("pval (%d) gval (%d)\n", pval, gval)); if (is_wps_conn(sme)) { - err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); + if (sme->privacy) + err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); + else + /* WPS-2.0 allowes no security */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); } else { WL_DBG((" NO, is_wps_conn, Set pval | gval to WSEC")); err = wldev_iovar_setint_bsscfg(dev, "wsec", @@ -1973,7 +2031,7 @@ wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) return err; } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; sec->cipher_group = sme->crypto.cipher_group; @@ -1990,7 +2048,7 @@ wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) s32 bssidx = wl_cfgp2p_find_idx(wl, dev); if (sme->crypto.n_akm_suites) { - err = wl_dev_intvar_get(dev, "wpa_auth", &val); + err = wldev_iovar_getint(dev, "wpa_auth", &val); if (unlikely(err)) { WL_ERR(("could not get wpa_auth (%d)\n", err)); return err; @@ -2030,7 +2088,7 @@ wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) return err; } } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->wpa_auth = sme->crypto.akm_suites[0]; return err; @@ -2049,13 +2107,14 @@ wl_set_set_sharedkey(struct net_device *dev, WL_DBG(("key len (%d)\n", sme->key_len)); if (sme->key_len) { - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n", sec->wpa_versions, sec->cipher_pairwise)); if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) && (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | - WLAN_CIPHER_SUITE_WEP104))) { + WLAN_CIPHER_SUITE_WEP104))) + { memset(&key, 0, sizeof(key)); key.len = (u32) sme->key_len; key.index = (u32) sme->key_idx; @@ -2083,14 +2142,14 @@ wl_set_set_sharedkey(struct net_device *dev, WL_DBG(("key \"%s\"\n", key.data)); swap_key_from_BE(&key); err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), - ioctlbuf, sizeof(ioctlbuf), bssidx); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("WLC_SET_KEY error (%d)\n", err)); return err; } - if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { + if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { WL_DBG(("set auth_type to shared key\n")); - val = 1; /* shared key */ + val = WL_AUTH_SHARED_KEY; /* shared key */ err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); if (unlikely(err)) { WL_ERR(("set auth failed (%d)\n", err)); @@ -2129,15 +2188,15 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * Cancel ongoing scan to sync up with sme state machine of cfg80211. */ if (wl->scan_request) { - wl_cfg80211_scan_abort(wl, dev); + wl_notify_escan_complete(wl, dev, true, true); } /* Clean BSSID */ bzero(&bssid, sizeof(bssid)); - wl_update_prof(wl, NULL, (void *)&bssid, WL_PROF_BSSID); + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); if (IS_P2P_SSID(sme->ssid) && (dev != wl_to_prmry_ndev(wl))) { /* we only allow to connect using virtual interface in case of P2P */ - if (p2p_on(wl) && is_wps_conn(sme)) { + if (p2p_is_on(wl) && is_wps_conn(sme)) { WL_DBG(("ASSOC1 p2p index : %d sme->ie_len %d\n", wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); /* Have to apply WPS IE + P2P IE in assoc req frame */ @@ -2148,7 +2207,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len); wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); - } else if (p2p_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { + } else if (p2p_is_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { /* This is the connect req after WPS is done [credentials exchanged] * currently identified with WPA_VERSION_2 . * Update the previously set IEs with @@ -2158,6 +2217,8 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, WL_DBG(("ASSOC2 p2p index : %d sme->ie_len %d\n", wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_PRBREQ_FLAG, sme->ie, sme->ie_len); + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); } @@ -2177,10 +2238,10 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, wpaie_len = (wpa_ie != NULL) ? wpa_ie->length : wpa2_ie->len; wpaie_len += WPA_RSN_IE_TAG_FIXED_LEN; wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); } else { wldev_iovar_setbuf(dev, "wpaie", NULL, 0, - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); } /* find the WPSIE */ @@ -2249,7 +2310,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); if (ext_join_params == NULL) { err = -ENOMEM; - wl_clr_drv_status(wl, CONNECTING); + wl_clr_drv_status(wl, CONNECTING, dev); goto exit; } ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), sme->ssid_len); @@ -2258,12 +2319,13 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, /* Set up join scan parameters */ ext_join_params->scan.scan_type = -1; ext_join_params->scan.nprobes = 2; - /* increate dwell time to receive probe response - * from target AP at a noisy air + /* increate dwell time to receive probe response or detect Beacon + * from target AP at a noisy air only during connect command */ - ext_join_params->scan.active_time = 150; - ext_join_params->scan.passive_time = 300; + ext_join_params->scan.active_time = WL_SCAN_ACTIVE_TIME*3; + ext_join_params->scan.passive_time = WL_SCAN_PASSIVE_TIME*3; ext_join_params->scan.home_time = -1; + if (sme->bssid) memcpy(&ext_join_params->assoc.bssid, sme->bssid, ETH_ALEN); else @@ -2288,12 +2350,12 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, WL_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len)); } - wl_set_drv_status(wl, CONNECTING); - err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, ioctlbuf, - sizeof(ioctlbuf), wl_cfgp2p_find_idx(wl, dev)); + wl_set_drv_status(wl, CONNECTING, dev); + err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, wl_cfgp2p_find_idx(wl, dev), &wl->ioctl_buf_sync); kfree(ext_join_params); if (err) { - wl_clr_drv_status(wl, CONNECTING); + wl_clr_drv_status(wl, CONNECTING, dev); if (err == BCME_UNSUPPORTED) { WL_DBG(("join iovar is not supported\n")); goto set_ssid; @@ -2309,7 +2371,7 @@ set_ssid: join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len); memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); - wl_update_prof(wl, NULL, &join_params.ssid, WL_PROF_SSID); + wl_update_prof(wl, dev, NULL, &join_params.ssid, WL_PROF_SSID); if (sme->bssid) memcpy(&join_params.params.bssid, sme->bssid, ETH_ALEN); else @@ -2322,11 +2384,11 @@ set_ssid: WL_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, join_params.ssid.SSID_len)); } - wl_set_drv_status(wl, CONNECTING); + wl_set_drv_status(wl, CONNECTING, dev); err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); if (err) { WL_ERR(("error (%d)\n", err)); - wl_clr_drv_status(wl, CONNECTING); + wl_clr_drv_status(wl, CONNECTING, dev); } exit: return err; @@ -2343,23 +2405,23 @@ wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u8 *curbssid; WL_ERR(("Reason %d\n", reason_code)); CHECK_SYS_UP(wl); - act = *(bool *) wl_read_prof(wl, WL_PROF_ACT); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); - if (likely(act)) { + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { /* * Cancel ongoing scan to sync up with sme state machine of cfg80211. */ if (wl->scan_request) { - wl_cfg80211_scan_abort(wl, dev); + wl_notify_escan_complete(wl, dev, true, true); } - wl_set_drv_status(wl, DISCONNECTING); + wl_set_drv_status(wl, DISCONNECTING, dev); scbval.val = reason_code; memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); scbval.val = htod32(scbval.val); err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); if (unlikely(err)) { - wl_clr_drv_status(wl, DISCONNECTING); + wl_clr_drv_status(wl, DISCONNECTING, dev); WL_ERR(("error (%d)\n", err)); return err; } @@ -2409,7 +2471,7 @@ wl_cfg80211_set_tx_power(struct wiphy *wiphy, txpwrmw = 0xffff; else txpwrmw = (u16) dbm; - err = wl_dev_intvar_set(ndev, "qtxpower", + err = wldev_iovar_setint(ndev, "qtxpower", (s32) (bcm_mw_to_qdbm(txpwrmw))); if (unlikely(err)) { WL_ERR(("qtxpower error (%d)\n", err)); @@ -2429,7 +2491,7 @@ static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) s32 err = 0; CHECK_SYS_UP(wl); - err = wl_dev_intvar_get(ndev, "qtxpower", &txpwrdbm); + err = wldev_iovar_getint(ndev, "qtxpower", &txpwrdbm); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); return err; @@ -2478,7 +2540,7 @@ wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, struct wl_wsec_key key; s32 err = 0; s32 bssidx = wl_cfgp2p_find_idx(wl, dev); - s32 mode = get_mode_by_netdev(wl, dev); + s32 mode = wl_get_mode_by_netdev(wl, dev); memset(&key, 0, sizeof(key)); key.index = (u32) key_idx; @@ -2490,8 +2552,8 @@ wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, if (key.len == 0) { /* key delete */ swap_key_from_BE(&key); - wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("key delete error (%d)\n", err)); return err; @@ -2549,11 +2611,8 @@ wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, return -EINVAL; } swap_key_from_BE(&key); -#ifdef CONFIG_WIRELESS_EXT - dhd_wait_pend8021x(dev); -#endif - wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("WLC_SET_KEY error (%d)\n", err)); return err; @@ -2574,7 +2633,7 @@ wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 keybuf[8]; s32 bssidx = 0; struct wl_priv *wl = wiphy_priv(wiphy); - s32 mode = get_mode_by_netdev(wl, dev); + s32 mode = wl_get_mode_by_netdev(wl, dev); WL_DBG(("key index (%d)\n", key_idx)); CHECK_SYS_UP(wl); @@ -2635,8 +2694,8 @@ wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, /* Set the new key/index */ swap_key_from_BE(&key); - err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("WLC_SET_KEY error (%d)\n", err)); return err; @@ -2672,15 +2731,15 @@ wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, CHECK_SYS_UP(wl); memset(&key, 0, sizeof(key)); - key.index = (u32) key_idx; key.flags = WL_PRIMARY_KEY; key.algo = CRYPTO_ALGO_OFF; + key.index = (u32) key_idx; WL_DBG(("key index (%d)\n", key_idx)); /* Set the new key/index */ swap_key_from_BE(&key); - wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { if (err == -EINVAL) { if (key.index >= DOT11_MAX_DEFAULT_KEYS) { @@ -2724,7 +2783,7 @@ wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, } switch (wsec & ~SES_OW_ENABLED) { case WEP_ENABLED: - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { params.cipher = WLAN_CIPHER_SUITE_WEP40; WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); @@ -2774,15 +2833,15 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); CHECK_SYS_UP(wl); - if (get_mode_by_netdev(wl, dev) == WL_MODE_AP) { + if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac, - ETHER_ADDR_LEN, ioctlbuf, sizeof(ioctlbuf)); + ETHER_ADDR_LEN, wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (err < 0) { WL_ERR(("GET STA INFO failed, %d\n", err)); return err; } sinfo->filled = STATION_INFO_INACTIVE_TIME; - sta = (sta_info_t *)ioctlbuf; + sta = (sta_info_t *)wl->ioctl_buf; sta->len = dtoh16(sta->len); sta->cap = dtoh16(sta->cap); sta->flags = dtoh32(sta->flags); @@ -2798,50 +2857,48 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, bcm_ether_ntoa((const struct ether_addr *)mac, eabuf), sinfo->inactive_time, sta->idle * 1000)); #endif - } else if (get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { - u8 *curmacp = wl_read_prof(wl, WL_PROF_BSSID); - - if (!wl_get_drv_status(wl, CONNECTED) || - (dhd_is_associated(dhd, NULL) == FALSE)) { - WL_ERR(("NOT assoc\n")); - err = -ENODEV; - goto get_station_err; - } - if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { - WL_ERR(("Wrong Mac address: "MACSTR" != "MACSTR"\n", - MAC2STR(mac), MAC2STR(curmacp))); - } - - /* Report the current tx rate */ - err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); - if (err) { - WL_ERR(("Could not get rate (%d)\n", err)); - } else { - rate = dtoh32(rate); - sinfo->filled |= STATION_INFO_TX_BITRATE; - sinfo->txrate.legacy = rate * 5; - WL_DBG(("Rate %d Mbps\n", (rate / 2))); - } + } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); + err = -ENODEV; + if (!wl_get_drv_status(wl, CONNECTED, dev) || + (dhd_is_associated(dhd, NULL, &err) == FALSE)) { + WL_ERR(("NOT assoc: %d\n", err)); + goto get_station_err; + } + if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { + WL_ERR(("Wrong Mac address: "MACSTR" != "MACSTR"\n", + MAC2STR(mac), MAC2STR(curmacp))); + } - memset(&scb_val, 0, sizeof(scb_val)); - scb_val.val = 0; - err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, - sizeof(scb_val_t), false); - if (err) { - WL_ERR(("Could not get rssi (%d)\n", err)); - goto get_station_err; - } + /* Report the current tx rate */ + err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); + if (err) { + WL_ERR(("Could not get rate (%d)\n", err)); + } else { + rate = dtoh32(rate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + WL_DBG(("Rate %d Mbps\n", (rate / 2))); + } - rssi = dtoh32(scb_val.val); - sinfo->filled |= STATION_INFO_SIGNAL; - sinfo->signal = rssi; - WL_DBG(("RSSI %d dBm\n", rssi)); + memset(&scb_val, 0, sizeof(scb_val)); + scb_val.val = 0; + err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, + sizeof(scb_val_t), false); + if (err) { + WL_ERR(("Could not get rssi (%d)\n", err)); + goto get_station_err; + } + rssi = dtoh32(scb_val.val); + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); get_station_err: - if (err) { + if (err && (err != -ETIMEDOUT) && (err != -EIO)) { /* Disconnect due to zero BSSID or error to get RSSI */ - WL_ERR(("force cfg80211_disconnected\n")); - wl_clr_drv_status(wl, CONNECTED); + WL_ERR(("force cfg80211_disconnected: %d\n", err)); + wl_clr_drv_status(wl, CONNECTED, dev); cfg80211_disconnected(dev, 0, NULL, 0, GFP_KERNEL); wl_link_down(wl); } @@ -2859,14 +2916,14 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, struct wl_priv *wl = wiphy_priv(wiphy); CHECK_SYS_UP(wl); - pm = enabled ? PM_FAST : PM_OFF; - /* Do not enable the power save after assoc if it is p2p interface */ - if (wl->p2p && wl->p2p->vif_created) { - WL_DBG(("Do not enable the power save for p2p interfaces even after assoc\n")); - pm = PM_OFF; + + WL_DBG(("Enter : power save %s\n", (enabled ? "enable" : "disable"))); + if (wl->p2p_net == dev) { + return err; } + + pm = enabled ? PM_FAST : PM_OFF; pm = htod32(pm); - WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); if (unlikely(err)) { if (err == -ENODEV) @@ -2875,6 +2932,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, WL_ERR(("error (%d)\n", err)); return err; } + WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); return err; } @@ -2908,11 +2966,11 @@ static __used u32 wl_find_msb(u16 bit16) static s32 wl_cfg80211_resume(struct wiphy *wiphy) { struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); s32 err = 0; - if (unlikely(!wl_get_drv_status(wl, READY))) { - WL_INFO(("device is not ready : status (%d)\n", - (int)wl->status)); + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { + WL_INFO(("device is not ready\n")); return 0; } @@ -2929,33 +2987,38 @@ static s32 wl_cfg80211_suspend(struct wiphy *wiphy) { #ifdef DHD_CLEAR_ON_SUSPEND struct wl_priv *wl = wiphy_priv(wiphy); + struct net_info *iter, *next; struct net_device *ndev = wl_to_prmry_ndev(wl); unsigned long flags; - if (unlikely(!wl_get_drv_status(wl, READY))) { + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { WL_INFO(("device is not ready : status (%d)\n", (int)wl->status)); return 0; } - - wl_set_drv_status(wl, SCAN_ABORTING); + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); wl_term_iscan(wl); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { cfg80211_scan_done(wl->scan_request, true); wl->scan_request = NULL; } - wl_clr_drv_status(wl, SCANNING); - wl_clr_drv_status(wl, SCAN_ABORTING); - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); - if (wl_get_drv_status(wl, CONNECTING)) { - wl_bss_connect_done(wl, ndev, NULL, NULL, false); + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); } -#endif + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + for_each_ndev(wl, iter, next) { + if (wl_get_drv_status(wl, CONNECTING, iter->ndev)) { + wl_bss_connect_done(wl, iter->ndev, NULL, NULL, false); + } + } +#endif /* DHD_CLEAR_ON_SUSPEND */ return 0; } -static __used s32 +static s32 wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, s32 err) { @@ -2963,10 +3026,13 @@ wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, struct wl_priv *wl = wlcfg_drv_priv; struct net_device *primary_dev = wl_to_prmry_ndev(wl); - /* Firmware is supporting pmk list only for STA interface i.e. primary interface - * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init - * Do we really need to support PMK cache in P2P in firmware? - */ + if (!pmk_list) { + printk("pmk_list is NULL\n"); + return -EINVAL; + } + /* pmk list is supported only for STA interface i.e. primary interface + * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init + */ if (primary_dev != dev) { WL_INFO(("Not supporting Flushing pmklist on virtual" " interfaces than primary interface\n")); @@ -2982,8 +3048,8 @@ wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, } } if (likely(!err)) { - err = wl_dev_bufvar_set(dev, "pmkid_info", (char *)pmk_list, - sizeof(*pmk_list)); + err = wldev_iovar_setbuf(dev, "pmkid_info", (char *)pmk_list, + sizeof(*pmk_list), wl->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); } return err; @@ -3084,7 +3150,7 @@ wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) } -wl_scan_params_t * +static wl_scan_params_t * wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) { wl_scan_params_t *params; @@ -3116,47 +3182,12 @@ wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) /* Our scan params have 1 channel and 0 ssids */ params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | - (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); + (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); *out_params_size = params_size; /* rtn size to the caller */ return params; } -s32 -wl_cfg80211_scan_abort(struct wl_priv *wl, struct net_device *ndev) -{ - wl_scan_params_t *params = NULL; - s32 params_size = 0; - s32 err = BCME_OK; - unsigned long flags; - - WL_DBG(("Enter\n")); - - /* Our scan params only need space for 1 channel and 0 ssids */ - params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); - if (params == NULL) { - WL_ERR(("scan params allocation failed \n")); - err = -ENOMEM; - } else { - /* Do a scan abort to stop the driver's scan engine */ - err = wldev_ioctl(ndev, WLC_SCAN, params, params_size, true); - if (err < 0) { - WL_ERR(("scan abort failed \n")); - } - } - del_timer_sync(&wl->scan_timeout); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); - if (wl->scan_request) { - cfg80211_scan_done(wl->scan_request, true); - wl->scan_request = NULL; - } - wl_clr_drv_status(wl, SCANNING); - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); - if (params) - kfree(params); - return err; -} - static s32 wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel * channel, @@ -3164,36 +3195,48 @@ wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, unsigned int duration, u64 *cookie) { s32 target_channel; + u32 id; + struct ether_addr primary_mac; + struct net_device *ndev = NULL; s32 err = BCME_OK; struct wl_priv *wl = wiphy_priv(wiphy); - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); WL_DBG(("Enter, netdev_ifidx: %d \n", dev->ifindex)); - if (likely(wl_get_drv_status(wl, SCANNING))) { - wl_cfg80211_scan_abort(wl, dev); + + if (wl->p2p_net == dev) { + ndev = wl_to_prmry_ndev(wl); + } else { + ndev = dev; } + if (wl_get_drv_status(wl, SCANNING, ndev)) { + wl_notify_escan_complete(wl, ndev, true, true); + } target_channel = ieee80211_frequency_to_channel(channel->center_freq); memcpy(&wl->remain_on_chan, channel, sizeof(struct ieee80211_channel)); wl->remain_on_chan_type = channel_type; - wl->cache_cookie = *cookie; + id = ++wl->last_roc_id; + if (id == 0) + id = ++wl->last_roc_id; + *cookie = id; cfg80211_ready_on_channel(dev, *cookie, channel, channel_type, duration, GFP_KERNEL); - if (!p2p_on(wl)) { - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + if (wl->p2p && !wl->p2p->on) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); /* In case of p2p_listen command, supplicant send remain_on_channel * without turning on P2P */ p2p_on(wl) = true; - err = wl_cfgp2p_enable_discovery(wl, dev, NULL, 0); + err = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); if (unlikely(err)) { goto exit; } } - if (p2p_on(wl)) + if (p2p_is_on(wl)) wl_cfgp2p_discover_listen(wl, target_channel, duration); @@ -3211,39 +3254,143 @@ wl_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev } static s32 -wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, +wl_cfg80211_send_pending_tx_act_frm(struct wl_priv *wl) +{ + wl_af_params_t *tx_act_frm; + struct net_device *dev = wl->afx_hdl->dev; + if (!p2p_is_on(wl)) + return -1; + + if (dev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } + + tx_act_frm = wl->afx_hdl->pending_tx_act_frm; + WL_DBG(("Sending the action frame\n")); + wl->afx_hdl->pending_tx_act_frm = NULL; + if (tx_act_frm != NULL) { + /* Suspend P2P discovery's search-listen to prevent it from + * starting a scan or changing the channel. + */ + wl_clr_drv_status(wl, SENDING_ACT_FRM, wl->afx_hdl->dev); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_notify_escan_complete(wl, dev, true, true); + wl_cfgp2p_discover_enable_search(wl, false); + tx_act_frm->channel = wl->afx_hdl->peer_chan; + wl->afx_hdl->ack_recv = (wl_cfgp2p_tx_action_frame(wl, dev, + tx_act_frm, wl->afx_hdl->bssidx)) ? false : true; + } + return 0; +} +static void +wl_cfg80211_afx_handler(struct work_struct *work) +{ + + struct afx_hdl *afx_instance; + struct wl_priv *wl = wlcfg_drv_priv; + afx_instance = container_of(work, struct afx_hdl, work); + if (afx_instance != NULL) { + wl_cfgp2p_act_frm_search(wl, wl->afx_hdl->dev, + wl->afx_hdl->bssidx, 0); + } +} + +static bool +wl_cfg80211_send_at_common_channel(struct wl_priv *wl, + struct net_device *dev, + wl_af_params_t *af_params) +{ + WL_DBG((" enter ) \n")); + /* initialize afx_hdl */ + wl->afx_hdl->pending_tx_act_frm = af_params; + wl->afx_hdl->bssidx = wl_cfgp2p_find_idx(wl, dev); + wl->afx_hdl->dev = dev; + wl->afx_hdl->retry = 0; + wl->afx_hdl->peer_chan = WL_INVALID; + wl->afx_hdl->ack_recv = false; + memcpy(wl->afx_hdl->pending_tx_dst_addr.octet, + af_params->action_frame.da.octet, + sizeof(wl->afx_hdl->pending_tx_dst_addr.octet)); + /* Loop to wait until we have sent the pending tx action frame or the + * pending action frame tx is cancelled. + */ + while ((wl->afx_hdl->retry < WL_CHANNEL_SYNC_RETRY) && + (wl->afx_hdl->peer_chan == WL_INVALID)) { + wl_set_drv_status(wl, SENDING_ACT_FRM, dev); + wl_set_drv_status(wl, SCANNING, dev); + WL_DBG(("Scheduling the action frame for sending.. retry %d\n", + wl->afx_hdl->retry)); + /* Do find_peer_for_action */ + schedule_work(&wl->afx_hdl->work); + wait_for_completion(&wl->act_frm_scan); + wl->afx_hdl->retry++; + } + if (wl->afx_hdl->peer_chan != WL_INVALID) + wl_cfg80211_send_pending_tx_act_frm(wl); + else { + WL_ERR(("Couldn't find the peer after %d retries\n", + wl->afx_hdl->retry)); + } + wl->afx_hdl->dev = NULL; + wl->afx_hdl->bssidx = WL_INVALID; + wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); + if (wl->afx_hdl->ack_recv) + return true; /* ACK */ + else + return false; /* NO ACK */ +} + +static s32 +wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_channel *channel, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8* buf, size_t len, u64 *cookie) + const u8* buf, size_t len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif + u64 *cookie) { wl_action_frame_t *action_frame; wl_af_params_t *af_params; wifi_p2p_ie_t *p2p_ie; wpa_ie_fixed_t *wps_ie; + wifi_wfd_ie_t *wfd_ie; + scb_val_t scb_val; const struct ieee80211_mgmt *mgmt; struct wl_priv *wl = wiphy_priv(wiphy); - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + struct net_device *dev = NULL; s32 err = BCME_OK; s32 bssidx = 0; u32 p2pie_len = 0; u32 wpsie_len = 0; - u16 fc; + u32 wfdie_len = 0; + u32 id; + u32 retry = 0; bool ack = false; - wifi_p2p_pub_act_frame_t *act_frm; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; + s8 eabuf[ETHER_ADDR_STR_LEN]; + int retry_cnt = 0; + WL_DBG(("Enter \n")); + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + /* If TX req is for any valid ifidx. Use as is */ + dev = ndev; + } + /* find bssidx based on ndev */ bssidx = wl_cfgp2p_find_idx(wl, dev); - /* cookie generation */ - *cookie = (unsigned long) buf; - if (bssidx == -1) { WL_ERR(("Can not find the bssidx for dev( %p )\n", dev)); return -ENODEV; } - if (wl->p2p_supported && p2p_on(wl)) { - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + if (p2p_is_on(wl)) { /* Suspend P2P discovery search-listen to prevent it from changing the * channel. */ @@ -3252,50 +3399,74 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, return -EFAULT; } } - - mgmt = (const struct ieee80211_mgmt *) buf; - fc = mgmt->frame_control; - if (fc != IEEE80211_STYPE_ACTION) { - if (fc == IEEE80211_STYPE_PROBE_RESP) { + *cookie = 0; + id = wl->send_action_id++; + if (id == 0) + id = wl->send_action_id++; + *cookie = id; + mgmt = (const struct ieee80211_mgmt *)buf; + if (ieee80211_is_mgmt(mgmt->frame_control)) { + if (ieee80211_is_probe_resp(mgmt->frame_control)) { s32 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; s32 ie_len = len - ie_offset; if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Total length of P2P Information Element */ p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); - /* Have to change p2p device address in dev_info attribute - * because Supplicant use primary eth0 address - */ - #ifdef ENABLE_DRIVER_CHANGE_IFADDR /* We are now doing this in supplicant */ - wl_cfg80211_change_ifaddr((u8 *)p2p_ie, - &wl->p2p_dev_addr, P2P_SEID_DEV_INFO); - #endif + } + if ((wfd_ie = wl_cfgp2p_find_wfdie((u8 *)(buf + ie_offset), ie_len)) + != NULL) { + /* Total length of WFD Information Element */ + wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); } if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Order of Vendor IE is 1) WPS IE + * 2) P2P IE created by supplicant * So, it is ok to find start address of WPS IE - * to save IEs to firmware + * to save IEs */ wpsie_len = wps_ie->length + sizeof(wps_ie->length) + sizeof(wps_ie->tag); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_PRBRSP_FLAG, - (u8 *)wps_ie, wpsie_len + p2pie_len); + (u8 *)wps_ie, wpsie_len + p2pie_len + wfdie_len); } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + } else if (ieee80211_is_disassoc(mgmt->frame_control) || + ieee80211_is_deauth(mgmt->frame_control)) { + memcpy(scb_val.ea.octet, mgmt->da, ETH_ALEN); + scb_val.val = mgmt->u.disassoc.reason_code; + wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), + scb_val.val)); + /* Wait for the deauth event to come, supplicant will do the delete iface immediately + * and we will have problem in sending deauth frame if we delete the bss in firmware + */ + wl_delay(400); + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + + } else if (ieee80211_is_action(mgmt->frame_control)) { + /* Abort the dwell time of any previous off-channel + * action frame that may be still in effect. Sending + * off-channel action frames relies on the driver's + * scan engine. If a previous off-channel action frame + * tx is still in progress (including the dwell time), + * then this new action frame will not be sent out. + */ + wl_notify_escan_complete(wl, dev, true, true); + } - cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_KERNEL); - goto exit; + } else { - /* Abort the dwell time of any previous off-channel action frame that may - * be still in effect. Sending off-channel action frames relies on the - * driver's scan engine. If a previous off-channel action frame tx is - * still in progress (including the dwell time), then this new action - * frame will not be sent out. - */ - wl_cfg80211_scan_abort(wl, dev); + WL_ERR(("Driver only allows MGMT packet type\n")); + goto exit; } + af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL); if (af_params == NULL) @@ -3307,7 +3478,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, action_frame = &af_params->action_frame; /* Add the packet Id */ - action_frame->packetId = (u32) action_frame; + action_frame->packetId = *cookie; WL_DBG(("action frame %d\n", action_frame->packetId)); /* Add BSSID */ memcpy(&action_frame->da, &mgmt->da[0], ETHER_ADDR_LEN); @@ -3321,6 +3492,15 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, af_params->channel = ieee80211_frequency_to_channel(channel->center_freq); + if (channel->band == IEEE80211_BAND_5GHZ) { + WL_DBG(("5GHz channel %d", af_params->channel)); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &af_params->channel, sizeof(af_params->channel), true); + if (err < 0) { + WL_ERR(("WLC_SET_CHANNEL error %d\n", err)); + } + } + /* Add the dwell time * Dwell time to stay off-channel to wait for a response action frame * after transmitting an GO Negotiation action frame @@ -3328,28 +3508,74 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, af_params->dwell_time = WL_DWELL_TIME; memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], action_frame->len); + if (wl_cfgp2p_is_pub_action(action_frame->data, action_frame->len)) { + act_frm = (wifi_p2p_pub_act_frame_t *) (action_frame->data); + WL_DBG(("P2P PUB action_frame->len: %d chan %d category %d subtype %d\n", + action_frame->len, af_params->channel, + act_frm->category, act_frm->subtype)); + } else if (wl_cfgp2p_is_p2p_action(action_frame->data, action_frame->len)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) (action_frame->data); + WL_DBG(("P2P action_frame->len: %d chan %d category %d subtype %d\n", + action_frame->len, af_params->channel, + p2p_act_frm->category, p2p_act_frm->subtype)); + } else if (wl_cfgp2p_is_gas_action(action_frame->data, action_frame->len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) (action_frame->data); + WL_DBG(("Service Discovery action_frame->len: %d chan %d category %d action %d\n", + action_frame->len, af_params->channel, + sd_act_frm->category, sd_act_frm->action)); + + } + wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); + /* + * To make sure to send successfully action frame, we have to turn off mpc + */ - act_frm = (wifi_p2p_pub_act_frame_t *) (action_frame->data); - WL_DBG(("action_frame->len: %d chan %d category %d subtype %d\n", - action_frame->len, af_params->channel, - act_frm->category, act_frm->subtype)); - if (wl->p2p->vif_created) { - /* - * To make sure to send successfully action frame, we have to turn off mpc + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_REQ) || + (act_frm->subtype == P2P_PAF_GON_RSP) || + (act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_REQ))) { + wldev_iovar_setint(dev, "mpc", 0); + } + if (act_frm->subtype == P2P_PAF_GON_RSP) + retry_cnt = 1; + else retry_cnt = WL_ACT_FRAME_RETRY; + + if (act_frm && act_frm->subtype == P2P_PAF_DEVDIS_REQ) { + af_params->dwell_time = WL_LONG_DWELL_TIME; + } else if (act_frm && + (act_frm->subtype == P2P_PAF_PROVDIS_REQ || + act_frm->subtype == P2P_PAF_PROVDIS_RSP || + act_frm->subtype == P2P_PAF_GON_RSP)) { + af_params->dwell_time = WL_MED_DWELL_TIME; + } + + if (IS_P2P_SOCIAL(af_params->channel) && + (IS_P2P_PUB_ACT_REQ(act_frm, &act_frm->elts[0], action_frame->len) || + IS_GAS_REQ(sd_act_frm, action_frame->len)) && + wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len) { + /* channel offload require P2P IE for Probe request + * otherwise, we will use wl_cfgp2p_tx_action_frame directly. + * channel offload for action request frame */ - if ((act_frm->subtype == P2P_PAF_GON_REQ)|| - (act_frm->subtype == P2P_PAF_GON_RSP)) { - wldev_iovar_setint(dev, "mpc", 0); - } else if (act_frm->subtype == P2P_PAF_GON_CONF) { - wldev_iovar_setint(dev, "mpc", 1); - } else if (act_frm->subtype == P2P_PAF_DEVDIS_REQ) { - af_params->dwell_time = WL_LONG_DWELL_TIME; - } - } - ack = (wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx)) ? false : true; - cfg80211_mgmt_tx_status(dev, *cookie, buf, len, ack, GFP_KERNEL); + /* channel offload for action request frame */ + ack = wl_cfg80211_send_at_common_channel(wl, dev, af_params); + } else { + ack = (wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx)) ? false : true; + if (!ack) { + for (retry = 1; retry < retry_cnt; retry++) { + ack = (wl_cfgp2p_tx_action_frame(wl, dev, + af_params, bssidx)) ? false : true; + if (ack) + break; + } + } + } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, ack, GFP_KERNEL); + if (act_frm && act_frm->subtype == P2P_PAF_GON_CONF) { + wldev_iovar_setint(dev, "mpc", 1); + } kfree(af_params); exit: return err; @@ -3403,8 +3629,21 @@ wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, { s32 channel; s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } channel = ieee80211_frequency_to_channel(chan->center_freq); + + if (wl_get_drv_status(wl, AP_CREATING, dev)) { + WL_TRACE(("<0> %s: as!!! in AP creating mode, save chan num:%d\n", + __FUNCTION__, channel)); + wl->hostapd_chan = channel; + if (channel == 14) + return err; /* hostapd requested ch auto-select, will be done later */ + } + WL_DBG(("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", dev->ifindex, channel_type, channel)); err = wldev_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel), true); @@ -3419,8 +3658,7 @@ wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) { s32 len = 0; s32 err = BCME_OK; - u16 auth = 0; /* d11 open authentication */ - u16 count; + u16 auth = WL_AUTH_OPEN_SYSTEM; /* d11 open authentication */ u32 wsec; u32 pval = 0; u32 gval = 0; @@ -3458,7 +3696,7 @@ wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) len -= WPA_SUITE_LEN; /* check the unicast cipher */ ucast = (wpa_suite_ucast_t *)&mcast[1]; - count = ltoh16_ua(&ucast->count); + ltoh16_ua(&ucast->count); tmp = ucast->list[0].oui; switch (tmp[DOT11_OUI_LEN]) { case WPA_CIPHER_NONE: @@ -3481,7 +3719,7 @@ wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) wsec = (pval | gval | SES_OW_ENABLED); /* check the AKM */ mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[1]; - count = ltoh16_ua(&mgmt->count); + ltoh16_ua(&mgmt->count); tmp = (u8 *)&mgmt->list[0]; switch (tmp[DOT11_OUI_LEN]) { case RSN_AKM_NONE: @@ -3524,7 +3762,7 @@ wl_validate_wpaie(struct net_device *dev, wpa_ie_fixed_t *wpaie, s32 bssidx) wpa_suite_mcast_t *mcast; wpa_suite_ucast_t *ucast; wpa_suite_auth_key_mgmt_t *mgmt; - u16 auth = 0; /* d11 open authentication */ + u16 auth = WL_AUTH_OPEN_SYSTEM; /* d11 open authentication */ u16 count; s32 err = BCME_OK; s32 len = 0; @@ -3677,20 +3915,28 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, wpa_ie_fixed_t *wpa_ie; bcm_tlv_t *wpa2_ie; wifi_p2p_ie_t *p2p_ie; + wifi_wfd_ie_t *wfd_ie; bool is_bssup = false; bool update_bss = false; bool pbc = false; u16 wpsie_len = 0; u16 p2pie_len = 0; + u32 wfdie_len = 0; u8 beacon_ie[IE_MAX_LEN]; s32 ie_offset = 0; - s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + s32 bssidx = 0; s32 infra = 1; s32 join_params_size = 0; s32 ap = 0; WL_DBG(("interval (%d) dtim_period (%d) head_len (%d) tail_len (%d)\n", info->interval, info->dtim_period, info->head_len, info->tail_len)); - if (wl->p2p_supported && p2p_on(wl) && + + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } + + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && (bssidx == wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION))) { memset(beacon_ie, 0, sizeof(beacon_ie)); @@ -3728,21 +3974,29 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)info->tail, info->tail_len)) != NULL) { /* Total length of P2P Information Element */ p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); - #ifdef ENABLE_DRIVER_CHANGE_IFADDR /* We are now doing this in supplicant */ - /* Have to change device address in dev_id attribute because Supplicant - * use primary eth0 address - */ - wl_cfg80211_change_ifaddr((u8 *)p2p_ie, &wl->p2p_dev_addr, P2P_SEID_DEV_ID); - #endif memcpy(&beacon_ie[wpsie_len], p2p_ie, p2pie_len); } else { WL_ERR(("No P2PIE in beacon \n")); } + /* find the WFD IEs */ + if ((wfd_ie = wl_cfgp2p_find_wfdie((u8 *)info->tail, info->tail_len)) != NULL) { + /* Total length of P2P Information Element */ + wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); + if ((wpsie_len + p2pie_len + wfdie_len) < IE_MAX_LEN) { + memcpy(&beacon_ie[wpsie_len + p2pie_len], wfd_ie, wfdie_len); + } else { + WL_ERR(("Found WFD IE but there is no space, (%d)(%d)(%d)\n", + wpsie_len, p2pie_len, wfdie_len)); + wfdie_len = 0; + } + } else { + WL_ERR(("No WFDIE in beacon \n")); + } /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, - beacon_ie, wpsie_len + p2pie_len); + beacon_ie, wpsie_len + p2pie_len + wfdie_len); /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -3763,17 +4017,18 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, goto exit; } err = wldev_iovar_setbuf_bsscfg(dev, "ssid", &wl->p2p->ssid, - sizeof(wl->p2p->ssid), ioctlbuf, sizeof(ioctlbuf), bssidx); + sizeof(wl->p2p->ssid), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); if (err < 0) { WL_ERR(("GO SSID setting error %d\n", err)); goto exit; } - if ((err = wl_cfgp2p_bss(dev, bssidx, 1)) < 0) { + if ((err = wl_cfgp2p_bss(wl, dev, bssidx, 1)) < 0) { WL_ERR(("GO Bring up error %d\n", err)); goto exit; } } - } else if (wl_get_drv_status(wl, AP_CREATING)) { + } else if (wl_get_drv_status(wl, AP_CREATING, dev)) { ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ap = 1; /* find the SSID */ @@ -3791,6 +4046,26 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, WL_ERR(("setting AP mode failed %d \n", err)); return err; } + + /* if requested, do softap ch autoselect */ + if (wl->hostapd_chan == 14) { + int auto_chan; + if ((err = wldev_get_auto_channel(dev, &auto_chan)) != 0) { + WL_ERR(("softap: auto chan select failed," + " will use ch 6\n")); + auto_chan = 6; + } else { + printf("<0>softap: got auto ch:%d\n", auto_chan); + } + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &auto_chan, sizeof(auto_chan), true); + if (err < 0) { + WL_ERR(("softap: WLC_SET_CHANNEL error %d chip" + " may not be supporting this channel\n", err)); + return err; + } + } + /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, DOT11_MNG_RSN_ID)) != NULL) { @@ -3846,9 +4121,9 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, memcpy(beacon_ie, wps_ie, wpsie_len); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len); - wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } else { WL_DBG(("No WPSIE in beacon \n")); } @@ -3879,11 +4154,11 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, /* create softap */ if ((err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true)) == 0) { - wl_clr_drv_status(wl, AP_CREATING); - wl_set_drv_status(wl, AP_CREATED); + wl_clr_drv_status(wl, AP_CREATING, dev); + wl_set_drv_status(wl, AP_CREATED, dev); } } - } else if (wl_get_drv_status(wl, AP_CREATED)) { + } else if (wl_get_drv_status(wl, AP_CREATED, dev)) { ap = 1; /* find the WPSIE */ if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)info->tail, info->tail_len)) != NULL) { @@ -3899,14 +4174,14 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, memcmp(wl->ap_info->wps_ie, wps_ie, wpsie_len)) { WL_DBG((" WPS IE is changed\n")); kfree(wl->ap_info->wps_ie); - wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } else if (wl->ap_info->wps_ie == NULL) { WL_DBG((" WPS IE is added\n")); - wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -3972,12 +4247,12 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, } if (update_bss) { wl->ap_info->security_mode = true; - wl_cfgp2p_bss(dev, bssidx, 0); + wl_cfgp2p_bss(wl, dev, bssidx, 0); if (wl_validate_wpa2ie(dev, wpa2_ie, bssidx) < 0 || wl_validate_wpaie(dev, wpa_ie, bssidx) < 0) { return BCME_ERROR; } - wl_cfgp2p_bss(dev, bssidx, 1); + wl_cfgp2p_bss(wl, dev, bssidx, 1); } } } else { @@ -3990,6 +4265,108 @@ exit: return err; } +#ifdef WL_SCHED_SCAN +#define PNO_TIME 30 +#define PNO_REPEAT 4 +#define PNO_FREQ_EXPO_MAX 3 +int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + ushort pno_time = PNO_TIME; + int pno_repeat = PNO_REPEAT; + int pno_freq_expo_max = PNO_FREQ_EXPO_MAX; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssid = NULL; + int ssid_count = 0; + int i; + int ret = 0; + + WL_DBG(("Enter n_match_sets:%d n_ssids:%d \n", + request->n_match_sets, request->n_ssids)); + WL_DBG(("ssids:%d pno_time:%d pno_repeat:%d pno_freq:%d \n", + request->n_ssids, pno_time, pno_repeat, pno_freq_expo_max)); + +#if defined(WL_ENABLE_P2P_IF) + /* While GO is operational, PNO is not supported */ + if (dhd_cfg80211_get_opmode(wl) & P2P_GO_ENABLED) { + WL_DBG(("PNO not enabled! op_mode: P2P GO")); + return -1; + } +#endif + + if (!request || !request->n_ssids || !request->n_match_sets) { + WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids)); + return -EINVAL; + } + + memset(&ssids_local, 0, sizeof(ssids_local)); + + if (request->n_match_sets > 0) { + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + memcpy(ssids_local[i].SSID, ssid->ssid, ssid->ssid_len); + ssids_local[i].SSID_len = ssid->ssid_len; + WL_DBG((">>> PNO filter set for ssid (%s) \n", ssid->ssid)); + ssid_count++; + } + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + WL_DBG((">>> Active scan req for ssid (%s) \n", request->ssids[i].ssid)); + + /* match_set ssids is a supert set of n_ssid list, so we need + * not add these set seperately + */ + } + } + + if (ssid_count) { + if ((ret = dhd_dev_pno_set(dev, ssids_local, request->n_match_sets, + pno_time, pno_repeat, pno_freq_expo_max)) < 0) { + WL_ERR(("PNO setup failed!! ret=%d \n", ret)); + return -EINVAL; + } + + /* Enable the PNO */ + if (dhd_dev_pno_enable(dev, 1) < 0) { + WL_ERR(("PNO enable failed!! ret=%d \n", ret)); + return -EINVAL; + } + wl->sched_scan_req = request; + } else { + return -EINVAL; + } + + return 0; +} + +int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + + if (dhd_dev_pno_enable(dev, 0) < 0) + WL_ERR(("PNO disable failed")); + + if (dhd_dev_pno_reset(dev) < 0) + WL_ERR(("PNO reset failed")); + + if (wl->scan_request && wl->sched_scan_running) { + wl_notify_escan_complete(wl, dev, true, true); + } + + wl->sched_scan_req = NULL; + wl->sched_scan_running = FALSE; + + return 0; +} +#endif /* WL_SCHED_SCAN */ + static struct cfg80211_ops wl_cfg80211_ops = { .add_virtual_intf = wl_cfg80211_add_virtual_iface, .del_virtual_intf = wl_cfg80211_del_virtual_iface, @@ -4022,9 +4399,13 @@ static struct cfg80211_ops wl_cfg80211_ops = { .set_channel = wl_cfg80211_set_channel, .set_beacon = wl_cfg80211_add_set_beacon, .add_beacon = wl_cfg80211_add_set_beacon, +#ifdef WL_SCHED_SCAN + .sched_scan_start = wl_cfg80211_sched_scan_start, + .sched_scan_stop = wl_cfg80211_sched_scan_stop, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) */ }; -static s32 wl_mode_to_nl80211_iftype(s32 mode) +s32 wl_mode_to_nl80211_iftype(s32 mode) { s32 err = 0; @@ -4042,30 +4423,30 @@ static s32 wl_mode_to_nl80211_iftype(s32 mode) return err; } -static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev) +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev) { - struct wireless_dev *wdev; s32 err = 0; - wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); - if (unlikely(!wdev)) { - WL_ERR(("Could not allocate wireless device\n")); - return ERR_PTR(-ENOMEM); - } wdev->wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv)); if (unlikely(!wdev->wiphy)) { WL_ERR(("Couldn not allocate wiphy device\n")); err = -ENOMEM; - goto wiphy_new_out; + return err; } set_wiphy_dev(wdev->wiphy, sdiofunc_dev); wdev->wiphy->max_scan_ie_len = WL_SCAN_IE_LEN_MAX; /* Report how many SSIDs Driver can support per Scan request */ wdev->wiphy->max_scan_ssids = WL_SCAN_PARAMS_SSID_MAX; wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; +#ifdef WL_SCHED_SCAN + wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* WL_SCHED_SCAN */ wdev->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) - | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR); + BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR); wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; @@ -4085,6 +4466,9 @@ static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev) WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS | #endif WIPHY_FLAG_4ADDR_STATION; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; +#endif #ifdef ENABLE_CUSTOM_REGULATORY_DOMAIN WL_DBG(("Registering custom regulatory)\n")); @@ -4095,39 +4479,28 @@ static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev) err = wiphy_register(wdev->wiphy); if (unlikely(err < 0)) { WL_ERR(("Couldn not register wiphy device (%d)\n", err)); - goto wiphy_register_out; + wiphy_free(wdev->wiphy); } - return wdev; - -wiphy_register_out: - wiphy_free(wdev->wiphy); - -wiphy_new_out: - kfree(wdev); - - return ERR_PTR(err); + return err; } static void wl_free_wdev(struct wl_priv *wl) { - int i; struct wireless_dev *wdev = wl->wdev; - - if (unlikely(!wdev)) { + struct wiphy *wiphy; + if (!wdev) { WL_ERR(("wdev is invalid\n")); return; } - - for (i = 0; i < VWDEV_CNT; i++) { - if ((wl->vwdev[i] != NULL)) { - kfree(wl->vwdev[i]); - wl->vwdev[i] = NULL; - } - } + wiphy = wdev->wiphy; wiphy_unregister(wdev->wiphy); wdev->wiphy->dev.parent = NULL; - wiphy_free(wdev->wiphy); - kfree(wdev); + + wl_delete_all_netinfo(wl); + wiphy_free(wiphy); + /* PLEASE do NOT call any function after wiphy_free, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wiphy_free !!!!!!!!!!! + */ } static s32 wl_inform_bss(struct wl_priv *wl) @@ -4157,6 +4530,7 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) struct wl_cfg80211_bss_info *notif_bss_info; struct wl_scan_req *sr = wl_to_sr(wl); struct beacon_proberesp *beacon_proberesp; + struct cfg80211_bss *cbss = NULL; s32 mgmt_type; s32 signal; u32 freq; @@ -4180,6 +4554,11 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + kfree(notif_bss_info); + return -EINVAL; + } notif_bss_info->rssi = dtoh16(bi->RSSI); memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); mgmt_type = wl->active_scan ? @@ -4206,8 +4585,13 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) freq = ieee80211_channel_to_frequency(notif_bss_info->channel, band->band); #endif channel = ieee80211_get_channel(wiphy, freq); + if (!channel) { + WL_ERR(("No valid channel")); + kfree(notif_bss_info); + return -EINVAL; + } - WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM" + WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM " "mgmt_type %d frame_len %d\n", bi->SSID, notif_bss_info->rssi, notif_bss_info->channel, mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, @@ -4215,13 +4599,41 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) signal = notif_bss_info->rssi * 100; - if (unlikely(!cfg80211_inform_bss_frame(wiphy, channel, mgmt, - le16_to_cpu(notif_bss_info->frame_len), - signal, GFP_KERNEL))) { +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p && wl->p2p_net && wl->scan_request && + ((wl->scan_request->dev == wl->p2p_net) || + (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))){ +#else + if (p2p_is_on(wl) && ( p2p_scan(wl) || + (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))) { +#endif + /* find the P2PIE, if we do not find it, we will discard this frame */ + wifi_p2p_ie_t * p2p_ie; + if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)beacon_proberesp->variable, + wl_get_ielen(wl))) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } + } + + if (!mgmt->u.probe_resp.timestamp) { + struct timeval tv; + + do_gettimeofday(&tv); + mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec * 1000000) + + tv.tv_usec; + } + + cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, + le16_to_cpu(notif_bss_info->frame_len), signal, GFP_KERNEL); + if (unlikely(!cbss)) { WL_ERR(("cfg80211_inform_bss_frame error\n")); kfree(notif_bss_info); return -EINVAL; } + + cfg80211_put_bss(cbss); kfree(notif_bss_info); return err; @@ -4233,7 +4645,7 @@ static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net u32 status = ntoh32(e->status); u16 flags = ntoh16(e->flags); - WL_DBG(("event %d, status %d\n", event, status)); + WL_DBG(("event %d, status %d flags %x\n", event, status, flags)); if (event == WLC_E_SET_SSID) { if (status == WLC_E_STATUS_SUCCESS) { if (!wl_is_ibssmode(wl, ndev)) @@ -4279,162 +4691,225 @@ static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e) return false; } +/* The mainline kernel >= 3.2.0 has support for indicating new/del station + * to AP/P2P GO via events. If this change is backported to kernel for which + * this driver is being built, then define WL_CFG80211_STA_EVENT. You + * should use this new/del sta event mechanism for BRCM supplicant >= 22. + */ static s32 -wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, +wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) { - bool act; - bool isfree = false; s32 err = 0; - s32 freq; - s32 channel; - u8 body[200]; u32 event = ntoh32(e->event_type); u32 reason = ntoh32(e->reason); u32 len = ntoh32(e->datalen); - u16 fc = 0; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) + bool isfree = false; u8 *mgmt_frame; u8 bsscfgidx = e->bsscfgidx; + s32 freq; + s32 channel; + u8 body[WL_FRAME_LEN]; + u16 fc = 0; struct ieee80211_supported_band *band; struct ether_addr da; struct ether_addr bssid; struct wiphy *wiphy = wl_to_wiphy(wl); channel_info_t ci; +#else + struct station_info sinfo; +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !WL_CFG80211_STA_EVENT */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) memset(body, 0, sizeof(body)); memset(&bssid, 0, ETHER_ADDR_LEN); - WL_DBG(("Enter \n")); + WL_DBG(("Enter event %d ndev %p\n", event, ndev)); + if (wl_get_mode_by_netdev(wl, ndev) == WL_INVALID) + return WL_INVALID; - if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { - memcpy(body, data, len); - wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", - NULL, 0, ioctlbuf, sizeof(ioctlbuf), bsscfgidx); - memcpy(da.octet, ioctlbuf, ETHER_ADDR_LEN); - err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); - switch (event) { - case WLC_E_ASSOC_IND: - fc = FC_ASSOC_REQ; - break; - case WLC_E_REASSOC_IND: - fc = FC_REASSOC_REQ; - break; - case WLC_E_DISASSOC_IND: - fc = FC_DISASSOC; - break; - case WLC_E_DEAUTH_IND: - fc = FC_DISASSOC; - break; - case WLC_E_DEAUTH: - fc = FC_DISASSOC; - break; - default: - fc = 0; - goto exit; - } - if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) - return err; - - channel = dtoh32(ci.hw_channel); - if (channel <= CH_MAX_2G_CHANNEL) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (len > WL_FRAME_LEN) { + WL_ERR(("Received frame length %d from dongle is greater than" + " allocated body buffer len %d", len, WL_FRAME_LEN)); + goto exit; + } + memcpy(body, data, len); + wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bsscfgidx, &wl->ioctl_buf_sync); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); + err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + switch (event) { + case WLC_E_ASSOC_IND: + fc = FC_ASSOC_REQ; + break; + case WLC_E_REASSOC_IND: + fc = FC_REASSOC_REQ; + break; + case WLC_E_DISASSOC_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH: + fc = FC_DISASSOC; + break; + default: + fc = 0; + goto exit; + } + if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) + return err; + channel = dtoh32(ci.hw_channel); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) - freq = ieee80211_channel_to_frequency(channel); + freq = ieee80211_channel_to_frequency(channel); #else - freq = ieee80211_channel_to_frequency(channel, band->band); + freq = ieee80211_channel_to_frequency(channel, band->band); #endif - err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, + err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, &mgmt_frame, &len, body); - if (err < 0) - goto exit; - isfree = true; + if (err < 0) + goto exit; + isfree = true; - if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { - cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); - } else if (event == WLC_E_DISASSOC_IND) { - cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); - } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { - cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } + +exit: + if (isfree) + kfree(mgmt_frame); + return err; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + sinfo.filled = 0; + if (((event == WLC_E_ASSOC_IND) || (event == WLC_E_REASSOC_IND)) && + reason == DOT11_SC_SUCCESS) { + sinfo.filled = STATION_INFO_ASSOC_REQ_IES; + if (!data) { + WL_ERR(("No IEs present in ASSOC/REASSOC_IND")); + return -EINVAL; } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = len; + cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + return err; +} +static s32 +wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + bool act; + s32 err = 0; + u32 event = ntoh32(e->event_type); + u32 reason; + + if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + wl_notify_connect_status_ap(wl, ndev, e, data); } else { - WL_DBG(("wl_notify_connect_status : event %d status : %d \n", - ntoh32(e->event_type), ntoh32(e->status))); + WL_DBG(("wl_notify_connect_status : event %d status : %d ndev %p\n", + ntoh32(e->event_type), ntoh32(e->status), ndev)); + if((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DISASSOC_IND)) { + reason = ntoh32(e->reason); + wl->deauth_reason = reason; + WL_ERR(("Received %s event with reason code: %d\n", (event == WLC_E_DEAUTH_IND)? "WLC_E_DEAUTH_IND":"WLC_E_DISASSOC_IND", reason)); + } if (wl_is_linkup(wl, e, ndev)) { wl_link_up(wl); act = true; - wl_update_prof(wl, e, &act, WL_PROF_ACT); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + wl->deauth_reason = 0; if (wl_is_ibssmode(wl, ndev)) { printk("cfg80211_ibss_joined\n"); cfg80211_ibss_joined(ndev, (s8 *)&e->addr, GFP_KERNEL); WL_DBG(("joined in IBSS network\n")); } else { - if (!wl_get_drv_status(wl, DISCONNECTING)) { - printk("wl_bss_connect_done succeeded status=(0x%x)\n", - (int)wl->status); + if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { + printk("wl_bss_connect_done succeeded\n"); wl_bss_connect_done(wl, ndev, e, data, true); WL_DBG(("joined in BSS network \"%s\"\n", ((struct wlc_ssid *) - wl_read_prof(wl, WL_PROF_SSID))->SSID)); + wl_read_prof(wl, ndev, WL_PROF_SSID))->SSID)); } } } else if (wl_is_linkdown(wl, e)) { if (wl->scan_request) { - del_timer_sync(&wl->scan_timeout); if (wl->escan_on) { - wl_notify_escan_complete(wl, true); - } else + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); wl_iscan_aborted(wl); + } } - if (wl_get_drv_status(wl, CONNECTED)) { + if (wl_get_drv_status(wl, CONNECTED, ndev)) { scb_val_t scbval; - u8 *curbssid = wl_read_prof(wl, WL_PROF_BSSID); - printk("link down, call cfg80211_disconnected\n"); - wl_clr_drv_status(wl, CONNECTED); - /* To make sure disconnect, explictly send dissassoc - * for BSSID 00:00:00:00:00:00 issue - */ - scbval.val = WLAN_REASON_DEAUTH_LEAVING; - - memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); - scbval.val = htod32(scbval.val); - wldev_ioctl(ndev, WLC_DISASSOC, &scbval, - sizeof(scb_val_t), true); - cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); - wl_link_down(wl); - wl_init_prof(wl); - } else if (wl_get_drv_status(wl, CONNECTING)) { + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_clr_drv_status(wl, CONNECTED, ndev); + if (! wl_get_drv_status(wl, DISCONNECTING, ndev)) { + /* To make sure disconnect, explictly send dissassoc + * for BSSID 00:00:00:00:00:00 issue + */ + scbval.val = WLAN_REASON_DEAUTH_LEAVING; + + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + wldev_ioctl(ndev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + WL_ERR(("link down, calling cfg80211_disconnected with deauth_reason:%d\n", wl->deauth_reason)); + cfg80211_disconnected(ndev, wl->deauth_reason , NULL, 0, GFP_KERNEL); + wl_link_down(wl); + wl_init_prof(wl, ndev); + } + } + else if (wl_get_drv_status(wl, CONNECTING, ndev)) { printk("link down, during connecting\n"); wl_bss_connect_done(wl, ndev, e, data, false); } - wl_clr_drv_status(wl, DISCONNECTING); + wl_clr_drv_status(wl, DISCONNECTING, ndev); } else if (wl_is_nonetwork(wl, e)) { printk("connect failed event=%d e->status 0x%x\n", event, (int)ntoh32(e->status)); /* Clean up any pending scan request */ if (wl->scan_request) { - del_timer_sync(&wl->scan_timeout); if (wl->escan_on) { - wl_notify_escan_complete(wl, true); - } else + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); wl_iscan_aborted(wl); + } } - if (wl_get_drv_status(wl, CONNECTING)) + if (wl_get_drv_status(wl, CONNECTING, ndev)) wl_bss_connect_done(wl, ndev, e, data, false); } else { printk("%s nothing\n", __FUNCTION__); } } -exit: - if (isfree) - kfree(mgmt_frame); return err; } @@ -4448,50 +4923,17 @@ wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, u32 status = be32_to_cpu(e->status); WL_DBG(("Enter \n")); if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { - if (wl_get_drv_status(wl, CONNECTED)) + if (wl_get_drv_status(wl, CONNECTED, ndev)) wl_bss_roaming_done(wl, ndev, e, data); else wl_bss_connect_done(wl, ndev, e, data, true); act = true; - wl_update_prof(wl, e, &act, WL_PROF_ACT); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); } return err; } -static __used s32 -wl_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len) -{ - struct wl_priv *wl = wlcfg_drv_priv; - u32 buflen; - - buflen = bcm_mkiovar(name, buf, len, wl->ioctl_buf, WL_IOCTL_LEN_MAX); - BUG_ON(unlikely(!buflen)); - - return wldev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen, true); -} - -static s32 -wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, - s32 buf_len) -{ - struct wl_priv *wl = wlcfg_drv_priv; - u32 len; - s32 err = 0; - - len = bcm_mkiovar(name, NULL, 0, wl->ioctl_buf, WL_IOCTL_LEN_MAX); - BUG_ON(unlikely(!len)); - err = wldev_ioctl(dev, WLC_GET_VAR, (void *)wl->ioctl_buf, - WL_IOCTL_LEN_MAX, false); - if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - return err; - } - memcpy(buf, wl->ioctl_buf, buf_len); - - return err; -} - static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) { wl_assoc_info_t assoc_info; @@ -4499,8 +4941,8 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) s32 err = 0; WL_DBG(("Enter \n")); - err = wl_dev_bufvar_get(ndev, "assoc_info", wl->extra_buf, - WL_ASSOC_INFO_MAX); + err = wldev_iovar_getbuf(ndev, "assoc_info", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); if (unlikely(err)) { WL_ERR(("could not get assoc info (%d)\n", err)); return err; @@ -4518,8 +4960,8 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) bzero(conn_info->resp_ie, sizeof(conn_info->resp_ie)); } if (assoc_info.req_len) { - err = wl_dev_bufvar_get(ndev, "assoc_req_ies", wl->extra_buf, - WL_ASSOC_INFO_MAX); + err = wldev_iovar_getbuf(ndev, "assoc_req_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); if (unlikely(err)) { WL_ERR(("could not get assoc req (%d)\n", err)); return err; @@ -4539,8 +4981,8 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) conn_info->req_ie_len = 0; } if (assoc_info.resp_len) { - err = wl_dev_bufvar_get(ndev, "assoc_resp_ies", wl->extra_buf, - WL_ASSOC_INFO_MAX); + err = wldev_iovar_getbuf(ndev, "assoc_resp_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); if (unlikely(err)) { WL_ERR(("could not get assoc resp (%d)\n", err)); return err; @@ -4602,26 +5044,27 @@ static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) struct wl_bss_info *bi; struct wlc_ssid *ssid; struct bcm_tlv *tim; - u16 beacon_interval; - u8 dtim_period; + s32 beacon_interval; + s32 dtim_period; size_t ie_len; u8 *ie; u8 *curbssid; s32 err = 0; struct wiphy *wiphy; + wiphy = wl_to_wiphy(wl); if (wl_is_ibssmode(wl, ndev)) return err; - ssid = (struct wlc_ssid *)wl_read_prof(wl, WL_PROF_SSID); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); + ssid = (struct wlc_ssid *)wl_read_prof(wl, ndev, WL_PROF_SSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); bss = cfg80211_get_bss(wiphy, NULL, curbssid, ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); mutex_lock(&wl->usr_sync); - if (unlikely(!bss)) { + if (!bss) { WL_DBG(("Could not find the AP\n")); *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, @@ -4657,7 +5100,7 @@ static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) /* * active scan was done so we could not get dtim * information out of probe response. - * so we speficially query dtim information to dongle. + * so we speficially query dtim information. */ err = wldev_ioctl(ndev, WLC_GET_DTIMPRD, &dtim_period, sizeof(dtim_period), false); @@ -4667,8 +5110,8 @@ static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) } } - wl_update_prof(wl, NULL, &beacon_interval, WL_PROF_BEACONINT); - wl_update_prof(wl, NULL, &dtim_period, WL_PROF_DTIMPERIOD); + wl_update_prof(wl, ndev, NULL, &beacon_interval, WL_PROF_BEACONINT); + wl_update_prof(wl, ndev, NULL, &dtim_period, WL_PROF_DTIMPERIOD); update_bss_info_out: mutex_unlock(&wl->usr_sync); @@ -4684,8 +5127,8 @@ wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, u8 *curbssid; wl_get_assoc_ies(wl, ndev); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); wl_update_bss_info(wl, ndev); wl_update_pmklist(ndev, wl->pmk_list, err); cfg80211_roamed(ndev, @@ -4697,7 +5140,7 @@ wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); WL_DBG(("Report roaming result\n")); - wl_set_drv_status(wl, CONNECTED); + wl_set_drv_status(wl, CONNECTED, ndev); return err; } @@ -4708,20 +5151,21 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, { struct wl_connect_info *conn_info = wl_to_conn(wl); s32 err = 0; - u8 *curbssid = wl_read_prof(wl, WL_PROF_BSSID); + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + WL_DBG((" enter\n")); if (wl->scan_request) { - wl_cfg80211_scan_abort(wl, ndev); + wl_notify_escan_complete(wl, ndev, true, true); } - if (wl_get_drv_status(wl, CONNECTING)) { - wl_clr_drv_status(wl, CONNECTING); + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); if (completed) { wl_get_assoc_ies(wl, ndev); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); wl_update_bss_info(wl, ndev); wl_update_pmklist(ndev, wl->pmk_list, err); - wl_set_drv_status(wl, CONNECTED); + wl_set_drv_status(wl, CONNECTED, ndev); } cfg80211_connect_result(ndev, curbssid, @@ -4759,6 +5203,28 @@ wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, return 0; } +#ifdef PNO_SUPPORT +static s32 +wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + WL_ERR((" PNO Event\n")); + + mutex_lock(&wl->usr_sync); +#ifndef WL_SCHED_SCAN + /* TODO: Use cfg80211_sched_scan_results(wiphy); */ + cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); +#else + /* If cfg80211 scheduled scan is supported, report the pno results via sched + * scan results + */ + wl_notify_sched_scan_results(wl, ndev, e, data); +#endif /* WL_SCHED_SCAN */ + mutex_unlock(&wl->usr_sync); + return 0; +} +#endif /* PNO_SUPPORT */ + static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) @@ -4770,11 +5236,15 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, unsigned long flags; WL_DBG(("Enter \n")); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("scan is not ready \n")); + return err; + } if (wl->iscan_on && wl->iscan_kickstart) return wl_wakeup_iscan(wl_to_iscan(wl)); mutex_lock(&wl->usr_sync); - wl_clr_drv_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, ndev); err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, sizeof(channel_inform), false); if (unlikely(err)) { @@ -4805,13 +5275,13 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, scan_done_out: del_timer_sync(&wl->scan_timeout); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { WL_DBG(("cfg80211_scan_done\n")); cfg80211_scan_done(wl->scan_request, false); wl->scan_request = NULL; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); mutex_unlock(&wl->usr_sync); return err; } @@ -4867,7 +5337,10 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, bool isfree = false; s32 err = 0; s32 freq; - wifi_p2p_pub_act_frame_t *act_frm; + struct net_device *dev = NULL; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; wl_event_rx_frame_data_t *rxframe = (wl_event_rx_frame_data_t*)data; u32 event = ntoh32(e->event_type); @@ -4877,22 +5350,32 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, u16 channel = ((ntoh16(rxframe->channel) & WL_CHANSPEC_CHAN_MASK)); memset(&bssid, 0, ETHER_ADDR_LEN); + + if (wl->p2p_net == ndev) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + if (channel <= CH_MAX_2G_CHANNEL) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; - + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) freq = ieee80211_channel_to_frequency(channel); #else freq = ieee80211_channel_to_frequency(channel, band->band); #endif if (event == WLC_E_ACTION_FRAME_RX) { - wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", - NULL, 0, ioctlbuf, sizeof(ioctlbuf), bsscfgidx); + wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bsscfgidx, &wl->ioctl_buf_sync); - wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); - memcpy(da.octet, ioctlbuf, ETHER_ADDR_LEN); + wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, &mgmt_frame, &mgmt_frame_len, (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1)); @@ -4902,13 +5385,29 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, goto exit; } isfree = true; - act_frm = - (wifi_p2p_pub_act_frame_t *) (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + if (wl_cfgp2p_is_pub_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + act_frm = (wifi_p2p_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + } else if (wl_cfgp2p_is_p2p_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) p2p_act_frm; + } else if (wl_cfgp2p_is_gas_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) sd_act_frm; + } + wl_cfgp2p_print_actframe(false, &mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN); /* * After complete GO Negotiation, roll back to mpc mode */ - if (act_frm->subtype == P2P_PAF_GON_CONF) { - wldev_iovar_setint(ndev, "mpc", 1); + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_RSP))) { + wldev_iovar_setint(dev, "mpc", 1); } } else { mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); @@ -4925,14 +5424,122 @@ exit: return 0; } +#ifdef WL_SCHED_SCAN +/* If target scan is not reliable, set the below define to "1" to do a + * full escan + */ +#define FULL_ESCAN_ON_PFN_NET_FOUND 0 +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + wl_pfn_net_info_t *netinfo, *pnetinfo; + struct cfg80211_scan_request request; + struct wiphy *wiphy = wl_to_wiphy(wl); + int err = 0; + struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT]; + struct ieee80211_channel *channel = NULL; + int channel_req = 0; + int band = 0; + struct wl_pfn_scanresults *pfn_result = (struct wl_pfn_scanresults *)data; + + WL_DBG(("Enter\n")); + + if (e->event_type == WLC_E_PFN_NET_LOST) { + WL_DBG(("PFN NET LOST event. Do Nothing \n")); + return 0; + } + WL_DBG(("PFN NET FOUND event. count:%d \n", pfn_result->count)); + if (pfn_result->count > 0) { + int i; + + memset(&request, 0x00, sizeof(struct cfg80211_scan_request)); + memset(&ssid, 0x00, sizeof(ssid)); + request.wiphy = wiphy; + + pnetinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) + - sizeof(wl_pfn_net_info_t)); + channel = (struct ieee80211_channel *)kzalloc( + (sizeof(struct ieee80211_channel) * MAX_PFN_LIST_COUNT), + GFP_KERNEL); + if (!channel) { + WL_ERR(("No memory")); + err = -ENOMEM; + goto out_err; + } + + for (i = 0; i < pfn_result->count; i++) { + netinfo = &pnetinfo[i]; + if (!netinfo) { + WL_ERR(("Invalid netinfo ptr. index:%d", i)); + err = -EINVAL; + goto out_err; + } + WL_DBG(("SSID:%s Channel:%d \n", + netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.channel)); + /* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, + netinfo->pfnsubnet.SSID_len); + ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; + request.n_ssids++; + + channel_req = netinfo->pfnsubnet.channel; + band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ + : NL80211_BAND_5GHZ; + channel[i].center_freq = ieee80211_channel_to_frequency(channel_req, band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request.channels[i] = &channel[i]; + request.n_channels++; + } + + /* assign parsed ssid array */ + if (request.n_ssids) + request.ssids = &ssid[0]; + + if (wl_get_drv_status_all(wl, SCANNING)) { + /* Abort any on-going scan */ + wl_notify_escan_complete(wl, ndev, true, true); + } + + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + err = wl_cfgp2p_discover_enable_search(wl, false); + if (unlikely(err)) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + } + + wl_set_drv_status(wl, SCANNING, ndev); +#if FULL_ESCAN_ON_PFN_NET_FOUND + err = wl_do_escan(wl, wiphy, ndev, NULL); +#else + err = wl_do_escan(wl, wiphy, ndev, &request); +#endif + if (err) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + wl->sched_scan_running = TRUE; + } + else { + WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n")); + } +out_err: + if (channel) + kfree(channel); + return err; +} +#endif /* WL_SCHED_SCAN */ + static void wl_init_conf(struct wl_conf *conf) { - s32 i = 0; WL_DBG(("Enter \n")); - for (i = 0; i <= VWDEV_CNT; i++) { - conf->mode[i].type = -1; - conf->mode[i].ndev = NULL; - } conf->frag_threshold = (u32)-1; conf->rts_threshold = (u32)-1; conf->retry_short = (u32)-1; @@ -4940,13 +5547,14 @@ static void wl_init_conf(struct wl_conf *conf) conf->tx_power = -1; } -static void wl_init_prof(struct wl_priv *wl) +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev) { unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); - memset(wl->profile, 0, sizeof(struct wl_profile)); - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + memset(profile, 0, sizeof(struct wl_profile)); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); } static void wl_init_event_handler(struct wl_priv *wl) @@ -4969,7 +5577,9 @@ static void wl_init_event_handler(struct wl_priv *wl) wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete; wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete; wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete; - +#ifdef PNO_SUPPORT + wl->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status; +#endif /* PNO_SUPPORT */ } static s32 wl_init_priv_mem(struct wl_priv *wl) @@ -4985,23 +5595,13 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("wl_conf alloc failed\n")); goto init_priv_mem_out; } - wl->profile = (void *)kzalloc(sizeof(*wl->profile), GFP_KERNEL); - if (unlikely(!wl->profile)) { - WL_ERR(("wl_profile alloc failed\n")); - goto init_priv_mem_out; - } - wl->bss_info = (void *)kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (unlikely(!wl->bss_info)) { - WL_ERR(("Bss information alloc failed\n")); - goto init_priv_mem_out; - } wl->scan_req_int = (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); if (unlikely(!wl->scan_req_int)) { WL_ERR(("Scan req alloc failed\n")); goto init_priv_mem_out; } - wl->ioctl_buf = (void *)kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL); + wl->ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); if (unlikely(!wl->ioctl_buf)) { WL_ERR(("Ioctl buf alloc failed\n")); goto init_priv_mem_out; @@ -5021,11 +5621,6 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("Iscan buf alloc failed\n")); goto init_priv_mem_out; } - wl->fw = (void *)kzalloc(sizeof(*wl->fw), GFP_KERNEL); - if (unlikely(!wl->fw)) { - WL_ERR(("fw object alloc failed\n")); - goto init_priv_mem_out; - } wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); if (unlikely(!wl->pmk_list)) { WL_ERR(("pmk list alloc failed\n")); @@ -5036,6 +5631,14 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("sta info alloc failed\n")); goto init_priv_mem_out; } + wl->afx_hdl = (void *)kzalloc(sizeof(*wl->afx_hdl), GFP_KERNEL); + if (unlikely(!wl->afx_hdl)) { + WL_ERR(("afx hdl alloc failed\n")); + goto init_priv_mem_out; + } else { + init_completion(&wl->act_frm_scan); + INIT_WORK(&wl->afx_hdl->work, wl_cfg80211_afx_handler); + } return 0; init_priv_mem_out: @@ -5048,12 +5651,8 @@ static void wl_deinit_priv_mem(struct wl_priv *wl) { kfree(wl->scan_results); wl->scan_results = NULL; - kfree(wl->bss_info); - wl->bss_info = NULL; kfree(wl->conf); wl->conf = NULL; - kfree(wl->profile); - wl->profile = NULL; kfree(wl->scan_req_int); wl->scan_req_int = NULL; kfree(wl->ioctl_buf); @@ -5064,12 +5663,16 @@ static void wl_deinit_priv_mem(struct wl_priv *wl) wl->extra_buf = NULL; kfree(wl->iscan); wl->iscan = NULL; - kfree(wl->fw); - wl->fw = NULL; kfree(wl->pmk_list); wl->pmk_list = NULL; kfree(wl->sta_info); wl->sta_info = NULL; + if (wl->afx_hdl) { + cancel_work_sync(&wl->afx_hdl->work); + kfree(wl->afx_hdl); + wl->afx_hdl = NULL; + } + if (wl->ap_info) { kfree(wl->ap_info->wpa_ie); kfree(wl->ap_info->rsn_ie); @@ -5084,7 +5687,8 @@ static s32 wl_create_event_handler(struct wl_priv *wl) int ret = 0; WL_DBG(("Enter \n")); - wl->event_tsk.thr_pid = DHD_PID_KT_INVALID; + /* Do not use DHD in cfg driver */ + wl->event_tsk.thr_pid = -1; PROC_START(wl_event_handler, wl, &wl->event_tsk, 0); if (wl->event_tsk.thr_pid < 0) ret = -ENOMEM; @@ -5114,21 +5718,22 @@ static void wl_term_iscan(struct wl_priv *wl) static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) { struct wl_priv *wl = iscan_to_wl(iscan); + struct net_device *ndev = wl_to_prmry_ndev(wl); unsigned long flags; WL_DBG(("Enter \n")); - if (unlikely(!wl_get_drv_status(wl, SCANNING))) { - wl_clr_drv_status(wl, SCANNING); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + wl_clr_drv_status(wl, SCANNING, ndev); WL_ERR(("Scan complete while device not scanning\n")); return; } - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); - wl_clr_drv_status(wl, SCANNING); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + wl_clr_drv_status(wl, SCANNING, ndev); if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); wl->iscan_kickstart = false; } @@ -5164,7 +5769,7 @@ wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, list.results.buflen = htod32(WL_ISCAN_BUF_MAX); err = wldev_iovar_getbuf(iscan->dev, "iscanresults", &list, WL_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, - WL_ISCAN_BUF_MAX); + WL_ISCAN_BUF_MAX, NULL); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); return err; @@ -5237,13 +5842,11 @@ static s32 wl_iscan_aborted(struct wl_priv *wl) static s32 wl_iscan_thread(void *data) { - struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 }; struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; struct wl_priv *wl = iscan_to_wl(iscan); u32 status; int err = 0; - sched_setscheduler(current, SCHED_FIFO, ¶m); allow_signal(SIGTERM); status = WL_SCAN_RESULTS_PARTIAL; while (likely(!down_interruptible(&iscan->sync))) { @@ -5278,7 +5881,7 @@ static void wl_scan_timeout(unsigned long data) if (wl->scan_request) { WL_ERR(("timer expired\n")); if (wl->escan_on) - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, wl->escan_info.ndev, true, false); else wl_notify_iscan_complete(wl_to_iscan(wl), true); } @@ -5324,21 +5927,109 @@ static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan) iscan->iscan_handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; } -static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted) +static s32 +wl_cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, + void *ndev) { + struct net_device *dev = ndev; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + if (!wdev || !wl || dev == wl_to_prmry_ndev(wl)) + return NOTIFY_DONE; + switch (state) { + case NETDEV_UNREGISTER: + /* after calling list_del_rcu(&wdev->list) */ + wl_dealloc_netinfo(wl, ndev); + break; + case NETDEV_GOING_DOWN: + /* At NETDEV_DOWN state, wdev_cleanup_work work will be called. + * In front of door, the function checks + * whether current scan is working or not. + * If the scanning is still working, wdev_cleanup_work call WARN_ON and + * make the scan done forcibly. + */ + if (wl_get_drv_status(wl, SCANNING, dev)) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, dev, true, true); + } + } + break; + } + return NOTIFY_DONE; +} +static struct notifier_block wl_cfg80211_netdev_notifier = { + .notifier_call = wl_cfg80211_netdev_notifier_call, +}; + +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, + bool aborted, bool fw_abort) +{ + wl_scan_params_t *params = NULL; + s32 params_size = 0; + s32 err = BCME_OK; unsigned long flags; + struct net_device *dev; WL_DBG(("Enter \n")); - wl_clr_drv_status(wl, SCANNING); - if (wl->p2p_supported && p2p_on(wl)) - wl_clr_p2p_status(wl, SCANNING); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (wl->scan_request) { + if (wl->scan_request->dev == wl->p2p_net) + dev = wl_to_prmry_ndev(wl); + else + dev = wl->scan_request->dev; + } + else { + WL_ERR(("wl->scan_request is NULL may be internal scan." + "doing scan_abort for ndev %p primary %p p2p_net %p", + ndev, wl_to_prmry_ndev(wl), wl->p2p_net)); + dev = ndev; + } + if (fw_abort && !in_atomic()) { + /* Our scan params only need space for 1 channel and 0 ssids */ + params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); + if (params == NULL) { + WL_ERR(("scan params allocation failed \n")); + err = -ENOMEM; + } else { + /* Do a scan abort to stop the driver's scan engine */ + err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true); + if (err < 0) { + WL_ERR(("scan abort failed \n")); + } + } + } + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + +#ifdef WL_SCHED_SCAN + if (wl->sched_scan_req && !wl->scan_request) { + WL_DBG((" REPORTING SCHED SCAN RESULTS \n")); + if (aborted) + cfg80211_sched_scan_stopped(wl->sched_scan_req->wiphy); + else + cfg80211_sched_scan_results(wl->sched_scan_req->wiphy); + wl->sched_scan_running = FALSE; + wl->sched_scan_req = NULL; + } +#endif /* WL_SCHED_SCAN */ + if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + if (p2p_is_on(wl)) + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, dev); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (params) + kfree(params); + + return err; } static s32 wl_escan_handler(struct wl_priv *wl, @@ -5353,11 +6044,21 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl_scan_results_t *list; u32 bi_length; u32 i; + WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); - if (!wl->escan_on && - !wl_get_drv_status(wl, SCANNING)) { - WL_ERR(("escan is not ready \n")); + /* P2P SCAN is coming from primary interface */ + if (wl_get_p2p_status(wl, SCANNING)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) + ndev = wl->afx_hdl->dev; + else + ndev = wl->escan_info.ndev; + + } + if (!ndev || !wl->escan_on || + !wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", + ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); return err; } @@ -5382,77 +6083,114 @@ static s32 wl_escan_handler(struct wl_priv *wl, WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length)); goto exit; } - list = (wl_scan_results_t *)wl->escan_info.escan_buf; - if (bi_length > ESCAN_BUF_SIZE - list->buflen) { - WL_ERR(("Buffer is too small: ignoring\n")); - goto exit; + + if (!(wl_to_wiphy(wl)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { + if (dtoh16(bi->capability) & DOT11_CAP_IBSS) { + WL_ERR(("Ignoring IBSS result\n")); + goto exit; + } } -#define WLC_BSS_RSSI_ON_CHANNEL 0x0002 - for (i = 0; i < list->count; i++) { - bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) - : list->bss_info; - - if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && - CHSPEC_BAND(bi->chanspec) == CHSPEC_BAND(bss->chanspec) && - bi->SSID_len == bss->SSID_len && - !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { - if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == - (bi->flags & WLC_BSS_RSSI_ON_CHANNEL)) { - /* preserve max RSSI if the measurements are - * both on-channel or both off-channel - */ - bss->RSSI = MAX(bss->RSSI, bi->RSSI); - } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && - (bi->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { - /* preserve the on-channel rssi measurement - * if the new measurement is off channel - */ - bss->RSSI = bi->RSSI; - bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; - } + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + if (!memcmp(bi->BSSID.octet, + wl->afx_hdl->pending_tx_dst_addr.octet, ETHER_ADDR_LEN)) { + s32 channel = CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); + WL_DBG(("ACTION FRAME SCAN : Peer found, channel : %d\n", channel)); + wl_clr_p2p_status(wl, SCANNING); + wl->afx_hdl->peer_chan = channel; + complete(&wl->act_frm_scan); goto exit; } + + } else { + list = (wl_scan_results_t *)wl->escan_info.escan_buf; + if (bi_length > ESCAN_BUF_SIZE - list->buflen) { + WL_ERR(("Buffer is too small: ignoring\n")); + goto exit; + } +#define WLC_BSS_RSSI_ON_CHANNEL 0x0002 + for (i = 0; i < list->count; i++) { + bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) + : list->bss_info; + + if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && + CHSPEC_BAND(bi->chanspec) == CHSPEC_BAND(bss->chanspec) && + bi->SSID_len == bss->SSID_len && + !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { + if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == + (bi->flags & WLC_BSS_RSSI_ON_CHANNEL)) { + /* preserve max RSSI if the measurements are + * both on-channel or both off-channel + */ + bss->RSSI = MAX(bss->RSSI, bi->RSSI); + } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && + (bi->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { + /* preserve the on-channel rssi measurement + * if the new measurement is off channel + */ + bss->RSSI = bi->RSSI; + bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; + } + + goto exit; + } + } + memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); + list->version = dtoh32(bi->version); + list->buflen += bi_length; + list->count++; + } - memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); - list->version = dtoh32(bi->version); - list->buflen += bi_length; - list->count++; } else if (status == WLC_E_STATUS_SUCCESS) { wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (likely(wl->scan_request)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { mutex_lock(&wl->usr_sync); - del_timer_sync(&wl->scan_timeout); WL_INFO(("ESCAN COMPLETED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); - wl_notify_escan_complete(wl, false); + wl_notify_escan_complete(wl, ndev, false, false); mutex_unlock(&wl->usr_sync); } } else if (status == WLC_E_STATUS_ABORT) { wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (likely(wl->scan_request)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_clr_p2p_status(wl, SCANNING); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { mutex_lock(&wl->usr_sync); - del_timer_sync(&wl->scan_timeout); WL_INFO(("ESCAN ABORTED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, ndev, true, false); mutex_unlock(&wl->usr_sync); } } else { WL_ERR(("unexpected Escan Event %d : abort\n", status)); wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (likely(wl->scan_request)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { mutex_lock(&wl->usr_sync); - del_timer_sync(&wl->scan_timeout); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, ndev, true, false); mutex_unlock(&wl->usr_sync); } } @@ -5493,16 +6231,11 @@ static s32 wl_init_scan(struct wl_priv *wl) return err; } -static void wl_init_fw(struct wl_fw_ctrl *fw) -{ - fw->status = 0; -} - static s32 wl_init_priv(struct wl_priv *wl) { struct wiphy *wiphy = wl_to_wiphy(wl); + struct net_device *ndev = wl_to_prmry_ndev(wl); s32 err = 0; - s32 i = 0; wl->scan_request = NULL; wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); @@ -5511,60 +6244,78 @@ static s32 wl_init_priv(struct wl_priv *wl) wl->roam_on = false; wl->iscan_kickstart = false; wl->active_scan = true; - wl->dongle_up = false; wl->rf_blocked = false; - - for (i = 0; i < VWDEV_CNT; i++) - wl->vwdev[i] = NULL; - - init_waitqueue_head(&wl->dongle_event_wait); + wl->deauth_reason = 0; + spin_lock_init(&wl->cfgdrv_lock); + mutex_init(&wl->ioctl_buf_sync); + init_waitqueue_head(&wl->netif_change_event); wl_init_eq(wl); err = wl_init_priv_mem(wl); - if (unlikely(err)) + if (err) return err; - if (unlikely(wl_create_event_handler(wl))) + if (wl_create_event_handler(wl)) return -ENOMEM; wl_init_event_handler(wl); mutex_init(&wl->usr_sync); err = wl_init_scan(wl); - if (unlikely(err)) + if (err) return err; - wl_init_fw(wl->fw); wl_init_conf(wl->conf); - wl_init_prof(wl); + wl_init_prof(wl, ndev); wl_link_down(wl); + DNGL_FUNC(dhd_cfg80211_init, (wl)); return err; } static void wl_deinit_priv(struct wl_priv *wl) { + DNGL_FUNC(dhd_cfg80211_deinit, (wl)); wl_destroy_event_handler(wl); - wl->dongle_up = false; /* dongle down */ wl_flush_eq(wl); wl_link_down(wl); del_timer_sync(&wl->scan_timeout); wl_term_iscan(wl); wl_deinit_priv_mem(wl); + unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); } -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) -s32 wl_cfg80211_sysctl_export_devaddr(void *data) +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static s32 wl_cfg80211_attach_p2p(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_TRACE(("Enter \n")); + + if (wl_cfgp2p_register_ndev(wl) < 0) { + WL_ERR(("%s: P2P attach failed. \n", __func__)); + return -ENODEV; + } + + return 0; +} + +static s32 wl_cfg80211_detach_p2p(void) { - /* Export the p2p_dev_addr via sysctl interface - * so that wpa_supplicant can access it - */ - dhd_pub_t *dhd = (dhd_pub_t *)data; struct wl_priv *wl = wlcfg_drv_priv; + struct wireless_dev *wdev = wl->p2p_wdev; - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + WL_DBG(("Enter \n")); + if (!wdev || !wl) { + WL_ERR(("Invalid Ptr\n")); + return -EINVAL; + } - sprintf((char *)&wl_sysctl_macstring[0], MACSTR, MAC2STR(wl->p2p->dev_addr.octet)); - sprintf((char *)&wl_sysctl_macstring[1], MACSTR, MAC2STR(wl->p2p->int_addr.octet)); + wl_cfgp2p_unregister_ndev(wl); + + wl->p2p_wdev = NULL; + wl->p2p_net = NULL; + WL_DBG(("Freeing 0x%08x \n", (unsigned int)wdev)); + kfree(wdev); return 0; } -#endif /* CONFIG_SYSCTL */ +#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ s32 wl_cfg80211_attach_post(struct net_device *ndev) { @@ -5576,75 +6327,107 @@ s32 wl_cfg80211_attach_post(struct net_device *ndev) return -ENODEV; } wl = wlcfg_drv_priv; - if (wl && !wl_get_drv_status(wl, READY)) { + if (wl && !wl_get_drv_status(wl, READY, ndev)) { if (wl->wdev && wl_cfgp2p_supported(wl, ndev)) { +#if !defined(WL_ENABLE_P2P_IF) wl->wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT)| BIT(NL80211_IFTYPE_P2P_GO)); +#endif if ((err = wl_cfgp2p_init_priv(wl)) != 0) goto fail; -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) - wl_cfg80211_sysctl_export_devaddr(wl->pub); -#endif + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net) { + /* Update MAC addr for p2p0 interface here. */ + memcpy(wl->p2p_net->dev_addr, ndev->dev_addr, ETH_ALEN); + wl->p2p_net->dev_addr[0] |= 0x02; + printk("%s: p2p_dev_addr="MACSTR "\n", + wl->p2p_net->name, MAC2STR(wl->p2p_net->dev_addr)); + } else { + WL_ERR(("p2p_net not yet populated." + " Couldn't update the MAC Address for p2p0 \n")); + return -ENODEV; + } +#endif /* defined(WLP2P) && (WL_ENABLE_P2P_IF) */ + wl->p2p_supported = true; } } else return -ENODEV; - - wl_set_drv_status(wl, READY); + wl_set_drv_status(wl, READY, ndev); fail: return err; } + s32 wl_cfg80211_attach(struct net_device *ndev, void *data) { struct wireless_dev *wdev; struct wl_priv *wl; s32 err = 0; + struct device *dev; WL_TRACE(("In\n")); - if (unlikely(!ndev)) { + if (!ndev) { WL_ERR(("ndev is invaild\n")); return -ENODEV; } - WL_DBG(("func %p\n", wl_cfg80211_get_sdio_func())); - wdev = wl_alloc_wdev(&wl_cfg80211_get_sdio_func()->dev); - if (unlikely(IS_ERR(wdev))) - return -ENOMEM; + WL_DBG(("func %p\n", wl_cfg80211_get_parent_dev())); + dev = wl_cfg80211_get_parent_dev(); + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + err = wl_setup_wiphy(wdev, dev); + if (unlikely(err)) { + kfree(wdev); + return -ENOMEM; + } wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); wl = (struct wl_priv *)wiphy_priv(wdev->wiphy); wl->wdev = wdev; wl->pub = data; - + INIT_LIST_HEAD(&wl->net_list); ndev->ieee80211_ptr = wdev; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; - + err = wl_alloc_netinfo(wl, ndev, wdev, WL_MODE_BSS); + if (err) { + WL_ERR(("Failed to alloc net_info (%d)\n", err)); + goto cfg80211_attach_out; + } err = wl_init_priv(wl); - if (unlikely(err)) { + if (err) { WL_ERR(("Failed to init iwm_priv (%d)\n", err)); goto cfg80211_attach_out; } err = wl_setup_rfkill(wl, TRUE); - if (unlikely(err)) { + if (err) { WL_ERR(("Failed to setup rfkill %d\n", err)); goto cfg80211_attach_out; } - -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) - if (!(wl_sysctl_hdr = register_sysctl_table(wl_sysctl_table))) { - WL_ERR(("%s: sysctl register failed!! \n", __func__)); + err = register_netdevice_notifier(&wl_cfg80211_netdev_notifier); + if (err) { + WL_ERR(("Failed to register notifierl %d\n", err)); goto cfg80211_attach_out; } -#endif #if defined(COEX_DHCP) if (wl_cfg80211_btcoex_init(wl)) goto cfg80211_attach_out; -#endif /* COEX_DHCP */ +#endif wlcfg_drv_priv = wl; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + err = wl_cfg80211_attach_p2p(); + if (err) + goto cfg80211_attach_out; +#endif + return err; cfg80211_attach_out: @@ -5653,7 +6436,7 @@ cfg80211_attach_out: return err; } -void wl_cfg80211_detach(void) +void wl_cfg80211_detach(void *para) { struct wl_priv *wl; @@ -5663,19 +6446,21 @@ void wl_cfg80211_detach(void) #if defined(COEX_DHCP) wl_cfg80211_btcoex_deinit(wl); -#endif /* COEX_DHCP */ +#endif -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) - if (wl_sysctl_hdr) - unregister_sysctl_table(wl_sysctl_hdr); +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + wl_cfg80211_detach_p2p(); #endif wl_setup_rfkill(wl, FALSE); if (wl->p2p_supported) wl_cfgp2p_deinit_priv(wl); wl_deinit_priv(wl); wlcfg_drv_priv = NULL; - wl_clear_sdio_func(); + wl_cfg80211_clear_parent_dev(); wl_free_wdev(wl); + /* PLEASE do NOT call any function after wl_free_wdev, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wl_free_wdev !!!!!!!!!!! + */ } static void wl_wakeup_event(struct wl_priv *wl) @@ -5686,6 +6471,42 @@ static void wl_wakeup_event(struct wl_priv *wl) } } +static int wl_is_p2p_event(struct wl_event_q *e) +{ + switch (e->etype) { + /* We have to seperate out the P2P events received + * on primary interface so that it can be send up + * via p2p0 interface. + */ + case WLC_E_P2P_PROBREQ_MSG: + case WLC_E_P2P_DISC_LISTEN_COMPLETE: + case WLC_E_ACTION_FRAME_RX: + case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: + case WLC_E_ACTION_FRAME_COMPLETE: + + if (e->emsg.ifidx != 0) { + WL_TRACE(("P2P Event on Virtual I/F (ifidx:%d) \n", + e->emsg.ifidx)); + /* We are only bothered about the P2P events received + * on primary interface. For rest of them return false + * so that it is sent over the interface corresponding + * to the ifidx. + */ + return FALSE; + } else { + WL_TRACE(("P2P Event on Primary I/F (ifidx:%d)." + " Sent it to p2p0 \n", e->emsg.ifidx)); + return TRUE; + } + break; + + default: + WL_TRACE(("NON-P2P Event %d on ifidx (ifidx:%d) \n", + e->etype, e->emsg.ifidx)); + return FALSE; + } +} + static s32 wl_event_handler(void *data) { struct net_device *netdev; @@ -5694,6 +6515,7 @@ static s32 wl_event_handler(void *data) tsk_ctl_t *tsk = (tsk_ctl_t *)data; wl = (struct wl_priv *)tsk->parent; + DAEMONIZE("dhd_cfg80211_event"); complete(&tsk->completed); while (down_interruptible (&tsk->sema) == 0) { @@ -5702,7 +6524,15 @@ static s32 wl_event_handler(void *data) break; while ((e = wl_deq_event(wl))) { WL_DBG(("event type (%d), if idx: %d\n", e->etype, e->emsg.ifidx)); - netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); + /* All P2P device address related events comes on primary interface since + * there is no corresponding bsscfg for P2P interface. Map it to p2p0 + * interface. + */ + if ((wl_is_p2p_event(e) == TRUE) && (wl->p2p_net)) { + netdev = wl->p2p_net; + } else { + netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); + } if (!netdev) netdev = wl_to_prmry_ndev(wl); if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) { @@ -5714,7 +6544,7 @@ static s32 wl_event_handler(void *data) } DHD_OS_WAKE_UNLOCK(wl->pub); } - WL_DBG(("%s was terminated\n", __func__)); + WL_ERR(("%s was terminated\n", __func__)); complete_and_exit(&tsk->completed, 0); return 0; } @@ -5731,9 +6561,6 @@ wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) WL_DBG(("event_type (%d):" "WLC_E_" "%s\n", event_type, estr)); #endif /* (WL_DBG_LEVEL > 0) */ - if (event_type == WLC_E_PFN_NET_FOUND) - WL_ERR((" PNO Event\n")); - if (likely(!wl_enq_event(wl, ndev, event_type, e, data))) wl_wakeup_event(wl); } @@ -5818,22 +6645,7 @@ static void wl_put_event(struct wl_event_q *e) kfree(e); } -void wl_cfg80211_set_sdio_func(void *func) -{ - cfg80211_sdio_func = (struct sdio_func *)func; -} - -static void wl_clear_sdio_func(void) -{ - cfg80211_sdio_func = NULL; -} - -struct sdio_func *wl_cfg80211_get_sdio_func(void) -{ - return cfg80211_sdio_func; -} - -static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) { s32 infra = 0; s32 err = 0; @@ -5870,11 +6682,12 @@ static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftyp return err; } - set_mode_by_netdev(wl, ndev, mode); + wl_set_mode_by_netdev(wl, ndev, mode); return 0; } -static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) + +static s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) { s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; @@ -5887,7 +6700,7 @@ static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, boo err = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); if (unlikely(err)) { WL_ERR(("Get event_msgs error (%d)\n", err)); - goto dongle_eventmsg_out; + goto eventmsg_out; } memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); if (add) { @@ -5900,389 +6713,103 @@ static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, boo err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("Set event_msgs error (%d)\n", err)); - goto dongle_eventmsg_out; + goto eventmsg_out; } -dongle_eventmsg_out: +eventmsg_out: return err; } - -#ifndef EMBEDDED_PLATFORM -static s32 wl_dongle_country(struct net_device *ndev, u8 ccode) -{ - - s32 err = 0; - - return err; -} - -static s32 wl_dongle_up(struct net_device *ndev, u32 up) -{ - s32 err = 0; - - err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); - if (unlikely(err)) { - WL_ERR(("WLC_UP error (%d)\n", err)); - } - return err; -} - -static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode) +s32 wl_update_wiphybands(struct wl_priv *wl) { + struct wiphy *wiphy; + u32 bandlist[3]; + u32 nband = 0; + u32 i = 0; s32 err = 0; - - WL_TRACE(("In\n")); - err = wldev_ioctl(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode), true); + int nmode = 0; + int bw_40 = 0; + int index = 0; + + WL_DBG(("Entry")); + memset(bandlist, 0, sizeof(bandlist)); + err = wldev_ioctl(wl_to_prmry_ndev(wl), WLC_GET_BANDLIST, bandlist, + sizeof(bandlist), false); if (unlikely(err)) { - WL_ERR(("WLC_SET_PM error (%d)\n", err)); + WL_ERR(("error read bandlist (%d)\n", err)); + return err; } - return err; -} - -static s32 -wl_dongle_glom(struct net_device *ndev, u32 glom, u32 dongle_align) -{ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - s32 err = 0; + wiphy = wl_to_wiphy(wl); + nband = bandlist[0]; + wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - /* Match Host and Dongle rx alignment */ - bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, - sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); + err = wldev_iovar_getint(wl_to_prmry_ndev(wl), "nmode", &nmode); if (unlikely(err)) { - WL_ERR(("txglomalign error (%d)\n", err)); - goto dongle_glom_out; + WL_ERR(("error reading nmode (%d)\n", err)); } - /* disable glom option per default */ - bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (unlikely(err)) { - WL_ERR(("txglom error (%d)\n", err)); - goto dongle_glom_out; - } -dongle_glom_out: - return err; -} - -static s32 -wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) -{ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - s32 err = 0; - - /* Setup timeout if Beacons are lost and roam is off to report link down */ - if (roamvar) { - bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, - sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); + else { + /* For nmodeonly check bw cap */ + err = wldev_iovar_getint(wl_to_prmry_ndev(wl), "mimo_bw_cap", &bw_40); if (unlikely(err)) { - WL_ERR(("bcn_timeout error (%d)\n", err)); - goto dongle_rom_out; + WL_ERR(("error get mimo_bw_cap (%d)\n", err)); } } - /* Enable/Disable built-in roaming to allow supplicant to take care of roaming */ - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (unlikely(err)) { - WL_ERR(("roam_off error (%d)\n", err)); - goto dongle_rom_out; - } -dongle_rom_out: - return err; -} -static s32 -wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, - s32 scan_unassoc_time) -{ - s32 err = 0; - - err = wldev_ioctl(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time, - sizeof(scan_assoc_time), true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("Scan assoc time is not supported\n")); - } else { - WL_ERR(("Scan assoc time error (%d)\n", err)); - } - goto dongle_scantime_out; - } - err = wldev_ioctl(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time, - sizeof(scan_unassoc_time), true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("Scan unassoc time is not supported\n")); - } else { - WL_ERR(("Scan unassoc time error (%d)\n", err)); + for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { + index = -1; + if (bandlist[i] == WLC_BAND_5G) { + wiphy->bands[IEEE80211_BAND_5GHZ] = + &__wl_band_5ghz_a; + index = IEEE80211_BAND_5GHZ; + } else if (bandlist[i] == WLC_BAND_2G) { + wiphy->bands[IEEE80211_BAND_2GHZ] = + &__wl_band_2ghz; + index = IEEE80211_BAND_2GHZ; } - goto dongle_scantime_out; - } - -dongle_scantime_out: - return err; -} - -static s32 -wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol) -{ - /* Room for "event_msgs" + '\0' + bitvec */ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - s32 err = 0; - - /* Set ARP offload */ - bcm_mkiovar("arpoe", (char *)&arpoe, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (err) { - if (err == -EOPNOTSUPP) - WL_INFO(("arpoe is not supported\n")); - else - WL_ERR(("arpoe error (%d)\n", err)); - - goto dongle_offload_out; - } - bcm_mkiovar("arp_ol", (char *)&arp_ol, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (err) { - if (err == -EOPNOTSUPP) - WL_INFO(("arp_ol is not supported\n")); - else - WL_ERR(("arp_ol error (%d)\n", err)); - - goto dongle_offload_out; - } - -dongle_offload_out: - return err; -} - -static s32 wl_pattern_atoh(s8 *src, s8 *dst) -{ - int i; - if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { - WL_ERR(("Mask invalid format. Needs to start with 0x\n")); - return -1; - } - src = src + 2; /* Skip past 0x */ - if (strlen(src) % 2 != 0) { - WL_ERR(("Mask invalid format. Needs to be of even length\n")); - return -1; - } - for (i = 0; *src != '\0'; i++) { - char num[3]; - strncpy(num, src, 2); - num[2] = '\0'; - dst[i] = (u8) simple_strtoul(num, NULL, 16); - src += 2; - } - return i; -} - -static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode) -{ - /* Room for "event_msgs" + '\0' + bitvec */ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - const s8 *str; - struct wl_pkt_filter pkt_filter; - struct wl_pkt_filter *pkt_filterp; - s32 buf_len; - s32 str_len; - u32 mask_size; - u32 pattern_size; - s8 buf[256]; - s32 err = 0; - - /* add a default packet filter pattern */ - str = "pkt_filter_add"; - str_len = strlen(str); - strncpy(buf, str, str_len); - buf[str_len] = '\0'; - buf_len = str_len + 1; - - pkt_filterp = (struct wl_pkt_filter *)(buf + str_len + 1); - - /* Parse packet filter id. */ - pkt_filter.id = htod32(100); - - /* Parse filter polarity. */ - pkt_filter.negate_match = htod32(0); - - /* Parse filter type. */ - pkt_filter.type = htod32(0); - - /* Parse pattern filter offset. */ - pkt_filter.u.pattern.offset = htod32(0); - - /* Parse pattern filter mask. */ - mask_size = htod32(wl_pattern_atoh("0xff", - (char *)pkt_filterp->u.pattern. - mask_and_pattern)); - - /* Parse pattern filter pattern. */ - pattern_size = htod32(wl_pattern_atoh("0x00", - (char *)&pkt_filterp->u.pattern.mask_and_pattern[mask_size])); - - if (mask_size != pattern_size) { - WL_ERR(("Mask and pattern not the same size\n")); - err = -EINVAL; - goto dongle_filter_out; - } - - pkt_filter.u.pattern.size_bytes = mask_size; - buf_len += WL_PKT_FILTER_FIXED_LEN; - buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); - - /* Keep-alive attributes are set in local - * variable (keep_alive_pkt), and - * then memcpy'ed into buffer (keep_alive_pktp) since there is no - * guarantee that the buffer is properly aligned. - */ - memcpy((char *)pkt_filterp, &pkt_filter, - WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); - - err = wldev_ioctl(ndev, WLC_SET_VAR, buf, buf_len, true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("filter not supported\n")); - } else { - WL_ERR(("filter (%d)\n", err)); + if ((index >= 0) && nmode) { + wiphy->bands[index]->ht_cap.cap = + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_MAX_AMSDU; + wiphy->bands[index]->ht_cap.ht_supported = TRUE; + wiphy->bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + wiphy->bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; } - goto dongle_filter_out; - } - /* set mode to allow pattern */ - bcm_mkiovar("pkt_filter_mode", (char *)&filter_mode, 4, iovbuf, - sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("filter_mode not supported\n")); - } else { - WL_ERR(("filter_mode (%d)\n", err)); + if ((index >= 0) && bw_40) { + wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } - goto dongle_filter_out; } -dongle_filter_out: + wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); return err; } -#endif /* !EMBEDDED_PLATFORM */ -s32 wl_config_dongle(struct wl_priv *wl, bool need_lock) +static s32 __wl_cfg80211_up(struct wl_priv *wl) { -#ifndef DHD_SDALIGN -#define DHD_SDALIGN 32 -#endif - struct net_device *ndev; - struct wireless_dev *wdev; s32 err = 0; + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wireless_dev *wdev = ndev->ieee80211_ptr; - WL_TRACE(("In\n")); - if (wl->dongle_up) { - WL_ERR(("Dongle is already up\n")); - return err; - } + WL_DBG(("In\n")); - ndev = wl_to_prmry_ndev(wl); - wdev = ndev->ieee80211_ptr; - if (need_lock) - rtnl_lock(); -#ifndef EMBEDDED_PLATFORM - err = wl_dongle_up(ndev, 0); - if (unlikely(err)) { - WL_ERR(("wl_dongle_up failed\n")); - goto default_conf_out; - } - err = wl_dongle_country(ndev, 0); - if (unlikely(err)) { - WL_ERR(("wl_dongle_country failed\n")); - goto default_conf_out; - } - err = wl_dongle_power(ndev, PM_FAST); - if (unlikely(err)) { - WL_ERR(("wl_dongle_power failed\n")); - goto default_conf_out; - } - err = wl_dongle_glom(ndev, 0, DHD_SDALIGN); - if (unlikely(err)) { - WL_ERR(("wl_dongle_glom failed\n")); - goto default_conf_out; - } - err = wl_dongle_roam(ndev, (wl->roam_on ? 0 : 1), 3); - if (unlikely(err)) { - WL_ERR(("wl_dongle_roam failed\n")); - goto default_conf_out; - } - wl_dongle_scantime(ndev, 40, 80); - wl_dongle_offload(ndev, 1, 0xf); - wl_dongle_filter(ndev, 1); -#endif /* !EMBEDDED_PLATFORM */ + err = dhd_config_dongle(wl, false); + if (unlikely(err)) + return err; - err = wl_dongle_mode(wl, ndev, wdev->iftype); + err = wl_config_ifmode(wl, ndev, wdev->iftype); if (unlikely(err && err != -EINPROGRESS)) { - WL_ERR(("wl_dongle_mode failed\n")); - goto default_conf_out; + WL_ERR(("wl_config_ifmode failed\n")); } - err = wl_dongle_probecap(wl); - if (unlikely(err)) { - WL_ERR(("wl_dongle_probecap failed\n")); - goto default_conf_out; - } - - /* -EINPROGRESS: Call commit handler */ - -default_conf_out: - if (need_lock) - rtnl_unlock(); - - wl->dongle_up = true; - - return err; - -} - -static s32 wl_update_wiphybands(struct wl_priv *wl) -{ - struct wiphy *wiphy; - s8 phylist_buf[128]; - s8 *phy; - s32 err = 0; - - err = wldev_ioctl(wl_to_prmry_ndev(wl), WLC_GET_PHYLIST, phylist_buf, - sizeof(phylist_buf), false); + err = wl_update_wiphybands(wl); if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - return err; + WL_ERR(("wl_update_wiphybands failed\n")); } - phy = phylist_buf; - for (; *phy; phy++) { - if (*phy == 'a' || *phy == 'n') { - wiphy = wl_to_wiphy(wl); - wiphy->bands[IEEE80211_BAND_5GHZ] = - &__wl_band_5ghz_a; - } - } - return err; -} - -static s32 __wl_cfg80211_up(struct wl_priv *wl) -{ - s32 err = 0; - - WL_TRACE(("In\n")); - wl_debugfs_add_netdev_params(wl); - err = wl_config_dongle(wl, false); - if (unlikely(err)) - return err; - dhd_monitor_init(wl->pub); - wl_invoke_iscan(wl); - wl_set_drv_status(wl, READY); + err = dhd_monitor_init(wl->pub); + err = wl_invoke_iscan(wl); + wl_set_drv_status(wl, READY, ndev); return err; } @@ -6290,52 +6817,65 @@ static s32 __wl_cfg80211_down(struct wl_priv *wl) { s32 err = 0; unsigned long flags; + struct net_info *iter, *next; + struct net_device *ndev = wl_to_prmry_ndev(wl); +#ifdef WL_ENABLE_P2P_IF + struct wiphy *wiphy = wl_to_prmry_ndev(wl)->ieee80211_ptr->wiphy; + struct net_device *p2p_net = wl->p2p_net; +#endif - WL_TRACE(("In\n")); + WL_DBG(("In\n")); /* Check if cfg80211 interface is already down */ - if (!wl_get_drv_status(wl, READY)) + if (!wl_get_drv_status(wl, READY, ndev)) return err; /* it is even not ready */ - - wl_set_drv_status(wl, SCAN_ABORTING); + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); wl_term_iscan(wl); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { cfg80211_scan_done(wl->scan_request, true); wl->scan_request = NULL; } - wl_clr_drv_status(wl, READY); - wl_clr_drv_status(wl, SCANNING); - wl_clr_drv_status(wl, SCAN_ABORTING); - wl_clr_drv_status(wl, CONNECTING); - wl_clr_drv_status(wl, CONNECTED); - wl_clr_drv_status(wl, DISCONNECTING); - if (wl_get_drv_status(wl, AP_CREATED)) { - wl_clr_drv_status(wl, AP_CREATED); - wl_clr_drv_status(wl, AP_CREATING); + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, READY, iter->ndev); + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTED, iter->ndev); + wl_clr_drv_status(wl, DISCONNECTING, iter->ndev); + wl_clr_drv_status(wl, AP_CREATED, iter->ndev); + wl_clr_drv_status(wl, AP_CREATING, iter->ndev); } wl_to_prmry_ndev(wl)->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); - - wl->dongle_up = false; +#ifdef WL_ENABLE_P2P_IF + wiphy->interface_modes = (wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + if ((p2p_net) && (p2p_net->flags & IFF_UP)) { + /* p2p0 interface is still UP. Bring it down */ + p2p_net->flags &= ~IFF_UP; + } +#endif /* WL_ENABLE_P2P_IF */ + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + + DNGL_FUNC(dhd_cfg80211_down, (wl)); wl_flush_eq(wl); wl_link_down(wl); if (wl->p2p_supported) wl_cfgp2p_down(wl); dhd_monitor_uninit(); - wl_debugfs_remove_netdev(wl); - return err; } -s32 wl_cfg80211_up(void) +s32 wl_cfg80211_up(void *para) { struct wl_priv *wl; s32 err = 0; - WL_TRACE(("In\n")); + WL_DBG(("In\n")); wl = wlcfg_drv_priv; mutex_lock(&wl->usr_sync); wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); @@ -6343,17 +6883,16 @@ s32 wl_cfg80211_up(void) if (err) WL_ERR(("__wl_cfg80211_up failed\n")); mutex_unlock(&wl->usr_sync); - return err; } -/* Private Event to Supplicant with indication that FW hangs */ +/* Private Event to Supplicant with indication that chip hangs */ int wl_cfg80211_hang(struct net_device *dev, u16 reason) { struct wl_priv *wl; wl = wlcfg_drv_priv; - WL_ERR(("In : FW crash Eventing\n")); + WL_ERR(("In : chip crash eventing\n")); cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); if (wl != NULL) { wl_link_down(wl); @@ -6361,12 +6900,12 @@ int wl_cfg80211_hang(struct net_device *dev, u16 reason) return 0; } -s32 wl_cfg80211_down(void) +s32 wl_cfg80211_down(void *para) { struct wl_priv *wl; s32 err = 0; - WL_TRACE(("In\n")); + WL_DBG(("In\n")); wl = wlcfg_drv_priv; mutex_lock(&wl->usr_sync); err = __wl_cfg80211_down(wl); @@ -6375,84 +6914,79 @@ s32 wl_cfg80211_down(void) return err; } -static s32 wl_dongle_probecap(struct wl_priv *wl) -{ - s32 err = 0; - - err = wl_update_wiphybands(wl); - if (unlikely(err)) - return err; - - return err; -} - -static void *wl_read_prof(struct wl_priv *wl, s32 item) +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item) { unsigned long flags; void *rptr = NULL; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (!profile) + return NULL; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); switch (item) { case WL_PROF_SEC: - rptr = &wl->profile->sec; + rptr = &profile->sec; break; case WL_PROF_ACT: - rptr = &wl->profile->active; + rptr = &profile->active; break; case WL_PROF_BSSID: - rptr = &wl->profile->bssid; + rptr = profile->bssid; break; case WL_PROF_SSID: - rptr = &wl->profile->ssid; + rptr = &profile->ssid; break; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); if (!rptr) WL_ERR(("invalid item (%d)\n", item)); return rptr; } static s32 -wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, void *data, - s32 item) +wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item) { s32 err = 0; struct wlc_ssid *ssid; unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (!profile) + return WL_INVALID; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); switch (item) { case WL_PROF_SSID: ssid = (wlc_ssid_t *) data; - memset(wl->profile->ssid.SSID, 0, - sizeof(wl->profile->ssid.SSID)); - memcpy(wl->profile->ssid.SSID, ssid->SSID, ssid->SSID_len); - wl->profile->ssid.SSID_len = ssid->SSID_len; + memset(profile->ssid.SSID, 0, + sizeof(profile->ssid.SSID)); + memcpy(profile->ssid.SSID, ssid->SSID, ssid->SSID_len); + profile->ssid.SSID_len = ssid->SSID_len; break; case WL_PROF_BSSID: if (data) - memcpy(wl->profile->bssid, data, ETHER_ADDR_LEN); + memcpy(profile->bssid, data, ETHER_ADDR_LEN); else - memset(wl->profile->bssid, 0, ETHER_ADDR_LEN); + memset(profile->bssid, 0, ETHER_ADDR_LEN); break; case WL_PROF_SEC: - memcpy(&wl->profile->sec, data, sizeof(wl->profile->sec)); + memcpy(&profile->sec, data, sizeof(profile->sec)); break; case WL_PROF_ACT: - wl->profile->active = *(bool *)data; + profile->active = *(bool *)data; break; case WL_PROF_BEACONINT: - wl->profile->beacon_interval = *(u16 *)data; + profile->beacon_interval = *(u16 *)data; break; case WL_PROF_DTIMPERIOD: - wl->profile->dtim_period = *(u8 *)data; + profile->dtim_period = *(u8 *)data; break; default: WL_ERR(("unsupported item (%d)\n", item)); err = -EOPNOTSUPP; break; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); return err; } @@ -6469,7 +7003,7 @@ void wl_cfg80211_dbg_level(u32 level) static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev) { - return get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; + return wl_get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; } static __used bool wl_is_ibssstarter(struct wl_priv *wl) @@ -6580,114 +7114,28 @@ static void wl_delay(u32 ms) } } -s32 wl_cfg80211_read_fw(s8 *buf, u32 size) -{ - const struct firmware *fw_entry; - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - - fw_entry = wl->fw->fw_entry; - - if (fw_entry->size < wl->fw->ptr + size) - size = fw_entry->size - wl->fw->ptr; - - memcpy(buf, &fw_entry->data[wl->fw->ptr], size); - wl->fw->ptr += size; - return size; -} - -void wl_cfg80211_release_fw(void) -{ - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - release_firmware(wl->fw->fw_entry); - wl->fw->ptr = 0; -} - -void *wl_cfg80211_request_fw(s8 *file_name) -{ - struct wl_priv *wl; - const struct firmware *fw_entry = NULL; - s32 err = 0; - - WL_TRACE(("In\n")); - WL_DBG(("file name : \"%s\"\n", file_name)); - wl = wlcfg_drv_priv; - - if (!test_bit(WL_FW_LOADING_DONE, &wl->fw->status)) { - err = request_firmware(&wl->fw->fw_entry, file_name, - &wl_cfg80211_get_sdio_func()->dev); - if (unlikely(err)) { - WL_ERR(("Could not download fw (%d)\n", err)); - goto req_fw_out; - } - set_bit(WL_FW_LOADING_DONE, &wl->fw->status); - fw_entry = wl->fw->fw_entry; - if (fw_entry) { - WL_DBG(("fw size (%zd), data (%p)\n", fw_entry->size, - fw_entry->data)); - } - } else if (!test_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status)) { - err = request_firmware(&wl->fw->fw_entry, file_name, - &wl_cfg80211_get_sdio_func()->dev); - if (unlikely(err)) { - WL_ERR(("Could not download nvram (%d)\n", err)); - goto req_fw_out; - } - set_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status); - fw_entry = wl->fw->fw_entry; - if (fw_entry) { - WL_DBG(("nvram size (%zd), data (%p)\n", fw_entry->size, - fw_entry->data)); - } - } else { - WL_DBG(("Downloading already done. Nothing to do more\n")); - err = -EPERM; - } - -req_fw_out: - if (unlikely(err)) { - return NULL; - } - wl->fw->ptr = 0; - return (void *)fw_entry->data; -} - -s8 *wl_cfg80211_get_fwname(void) -{ - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - strcpy(wl->fw->fw_name, WL_4329_FW_FILE); - return wl->fw->fw_name; -} - -s8 *wl_cfg80211_get_nvramname(void) -{ - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - strcpy(wl->fw->nvram_name, WL_4329_NVRAM_FILE); - return wl->fw->nvram_name; -} - s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) { - struct wl_priv *wl; - dhd_pub_t *dhd_pub; + struct wl_priv *wl = wlcfg_drv_priv; struct ether_addr p2pif_addr; + struct ether_addr primary_mac; - wl = wlcfg_drv_priv; - dhd_pub = (dhd_pub_t *)wl->pub; - wl_cfgp2p_generate_bss_mac(&dhd_pub->mac, p2pdev_addr, &p2pif_addr); + if (!wl->p2p) + return -1; + if (!p2p_is_on(wl)) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, p2pdev_addr, &p2pif_addr); + } else { + memcpy(p2pdev_addr->octet, + wl->p2p->dev_addr.octet, ETHER_ADDR_LEN); + } return 0; } s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) { struct wl_priv *wl; + wl = wlcfg_drv_priv; return wl_cfgp2p_set_p2p_noa(wl, net, buf, len); @@ -6721,8 +7169,8 @@ s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, if (wl->p2p && wl->p2p->vif_created) { ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); - } else if (wl_get_drv_status(wl, AP_CREATING) || - wl_get_drv_status(wl, AP_CREATED)) { + } else if (wl_get_drv_status(wl, AP_CREATING, net) || + wl_get_drv_status(wl, AP_CREATED, net)) { ndev = net; bssidx = 0; } @@ -6745,69 +7193,6 @@ s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, return ret; } -static __used void wl_dongle_poweron(struct wl_priv *wl) -{ - WL_DBG(("Enter \n")); - dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); - -#if defined(BCMLXSDMMC) - sdioh_start(NULL, 0); -#endif -#if defined(BCMLXSDMMC) - sdioh_start(NULL, 1); -#endif - wl_cfg80211_resume(wl_to_wiphy(wl)); -} - -static __used void wl_dongle_poweroff(struct wl_priv *wl) -{ - WL_DBG(("Enter \n")); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) - wl_cfg80211_suspend(wl_to_wiphy(wl), NULL); -#else - wl_cfg80211_suspend(wl_to_wiphy(wl)); -#endif - -#if defined(BCMLXSDMMC) - sdioh_stop(NULL); -#endif - /* clean up dtim_skip setting */ - dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); -} - -static int wl_debugfs_add_netdev_params(struct wl_priv *wl) -{ - char buf[10+IFNAMSIZ]; - struct dentry *fd; - s32 err = 0; - - WL_TRACE(("In\n")); - sprintf(buf, "netdev:%s", wl_to_prmry_ndev(wl)->name); - wl->debugfsdir = debugfs_create_dir(buf, wl_to_wiphy(wl)->debugfsdir); - - fd = debugfs_create_u16("beacon_int", S_IRUGO, wl->debugfsdir, - (u16 *)&wl->profile->beacon_interval); - if (!fd) { - err = -ENOMEM; - goto err_out; - } - - fd = debugfs_create_u8("dtim_period", S_IRUGO, wl->debugfsdir, - (u8 *)&wl->profile->dtim_period); - if (!fd) { - err = -ENOMEM; - goto err_out; - } - -err_out: - return err; -} - -static void wl_debugfs_remove_netdev(struct wl_priv *wl) -{ - WL_DBG(("Enter \n")); -} - static const struct rfkill_ops wl_rfkill_ops = { .set_block = wl_rfkill_set }; @@ -6836,7 +7221,7 @@ static int wl_setup_rfkill(struct wl_priv *wl, bool setup) return -EINVAL; if (setup) { wl->rfkill = rfkill_alloc("brcmfmac-wifi", - &wl_cfg80211_get_sdio_func()->dev, + wl_cfg80211_get_parent_dev(), RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); if (!wl->rfkill) { @@ -6862,471 +7247,42 @@ err_out: return err; } -#if defined(COEX_DHCP) -/* - * get named driver variable to uint register value and return error indication - * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) - */ -static int -dev_wlc_intvar_get_reg(struct net_device *dev, char *name, - uint reg, int *retval) +struct device *wl_cfg80211_get_parent_dev(void) { - union { - char buf[WLC_IOCTL_SMLEN]; - int val; - } var; - int error; - - bcm_mkiovar(name, (char *)(®), sizeof(reg), - (char *)(&var), sizeof(var.buf)); - error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); - - *retval = dtoh32(var.val); - return (error); + return cfg80211_parent_dev; } -static int -dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) +void wl_cfg80211_set_parent_dev(void *dev) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) - char ioctlbuf[1024]; -#else - static char ioctlbuf[1024]; -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ - - bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf)); - - return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf, sizeof(ioctlbuf), true)); -} -/* -get named driver variable to uint register value and return error indication -calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) -*/ -static int -dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) -{ - char reg_addr[8]; - - memset(reg_addr, 0, sizeof(reg_addr)); - memcpy((char *)®_addr[0], (char *)addr, 4); - memcpy((char *)®_addr[4], (char *)val, 4); - - return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); + cfg80211_parent_dev = dev; } -static bool btcoex_is_sco_active(struct net_device *dev) +static void wl_cfg80211_clear_parent_dev(void) { - int ioc_res = 0; - bool res = FALSE; - int sco_id_cnt = 0; - int param27; - int i; - - for (i = 0; i < 12; i++) { - - ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); - - WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", - __FUNCTION__, i, param27)); - - if (ioc_res < 0) { - WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); - break; - } - - if ((param27 & 0x6) == 2) { /* count both sco & esco */ - sco_id_cnt++; - } - - if (sco_id_cnt > 2) { - WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", - __FUNCTION__, sco_id_cnt, i)); - res = TRUE; - break; - } - - msleep(5); - } - - return res; + cfg80211_parent_dev = NULL; } -#if defined(BT_DHCP_eSCO_FIX) -/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ -static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac) { - static bool saved_status = FALSE; - - char buf_reg50va_dhcp_on[8] = - { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; - char buf_reg51va_dhcp_on[8] = - { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - char buf_reg64va_dhcp_on[8] = - { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - char buf_reg65va_dhcp_on[8] = - { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - char buf_reg71va_dhcp_on[8] = - { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - uint32 regaddr; - static uint32 saved_reg50; - static uint32 saved_reg51; - static uint32 saved_reg64; - static uint32 saved_reg65; - static uint32 saved_reg71; - - if (trump_sco) { - /* this should reduce eSCO agressive retransmit - * w/o breaking it - */ - - /* 1st save current */ - WL_TRACE(("Do new SCO/eSCO coex algo {save &" - "override}\n")); - if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { - saved_status = TRUE; - WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" - "0x%x 0x%x 0x%x 0x%x 0x%x\n", - __FUNCTION__, saved_reg50, saved_reg51, - saved_reg64, saved_reg65, saved_reg71)); - } else { - WL_ERR((":%s: save btc_params failed\n", - __FUNCTION__)); - saved_status = FALSE; - return -1; - } - - WL_TRACE(("override with [50,51,64,65,71]:" - "0x%x 0x%x 0x%x 0x%x 0x%x\n", - *(u32 *)(buf_reg50va_dhcp_on+4), - *(u32 *)(buf_reg51va_dhcp_on+4), - *(u32 *)(buf_reg64va_dhcp_on+4), - *(u32 *)(buf_reg65va_dhcp_on+4), - *(u32 *)(buf_reg71va_dhcp_on+4))); - - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg50va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg51va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg64va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg65va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg71va_dhcp_on[0], 8); - - saved_status = TRUE; - } else if (saved_status) { - /* restore previously saved bt params */ - WL_TRACE(("Do new SCO/eSCO coex algo {save &" - "override}\n")); - - regaddr = 50; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg50); - regaddr = 51; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg51); - regaddr = 64; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg64); - regaddr = 65; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg65); - regaddr = 71; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg71); - - WL_TRACE(("restore bt_params[50,51,64,65,71]:" - "0x%x 0x%x 0x%x 0x%x 0x%x\n", - saved_reg50, saved_reg51, saved_reg64, - saved_reg65, saved_reg71)); - - saved_status = FALSE; - } else { - WL_ERR((":%s att to restore not saved BTCOEX params\n", - __FUNCTION__)); - return -1; - } - return 0; -} -#endif /* BT_DHCP_eSCO_FIX */ - -static void -wl_cfg80211_bt_setflag(struct net_device *dev, bool set) -{ -#if defined(BT_DHCP_USE_FLAGS) - char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; - char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; -#endif - -#if defined(BT_DHCP_eSCO_FIX) - /* set = 1, save & turn on 0 - off & restore prev settings */ - set_btc_esco_params(dev, set); -#endif - -#if defined(BT_DHCP_USE_FLAGS) - WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); - if (set == TRUE) - /* Forcing bt_flag7 */ - dev_wlc_bufvar_set(dev, "btc_flags", - (char *)&buf_flag7_dhcp_on[0], - sizeof(buf_flag7_dhcp_on)); - else - /* Restoring default bt flag7 */ - dev_wlc_bufvar_set(dev, "btc_flags", - (char *)&buf_flag7_default[0], - sizeof(buf_flag7_default)); -#endif -} - -static void wl_cfg80211_bt_timerfunc(ulong data) -{ - struct btcoex_info *bt_local = (struct btcoex_info *)data; - WL_TRACE(("%s\n", __FUNCTION__)); - bt_local->timer_on = 0; - schedule_work(&bt_local->work); -} - -static void wl_cfg80211_bt_handler(struct work_struct *work) -{ - struct btcoex_info *btcx_inf; - - btcx_inf = container_of(work, struct btcoex_info, work); - - if (btcx_inf->timer_on) { - btcx_inf->timer_on = 0; - del_timer_sync(&btcx_inf->timer); - } - - switch (btcx_inf->bt_state) { - case BT_DHCP_START: - /* DHCP started - * provide OPPORTUNITY window to get DHCP address - */ - WL_TRACE(("%s bt_dhcp stm: started \n", - __FUNCTION__)); - btcx_inf->bt_state = BT_DHCP_OPPR_WIN; - mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); - btcx_inf->timer_on = 1; - break; - - case BT_DHCP_OPPR_WIN: - if (btcx_inf->dhcp_done) { - WL_TRACE(("%s DHCP Done before T1 expiration\n", - __FUNCTION__)); - goto btc_coex_idle; - } - - /* DHCP is not over yet, start lowering BT priority - * enforce btc_params + flags if necessary - */ - WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, - BT_DHCP_OPPR_WIN_TIME)); - if (btcx_inf->dev) - wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); - btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); - btcx_inf->timer_on = 1; - break; - - case BT_DHCP_FLAG_FORCE_TIMEOUT: - if (btcx_inf->dhcp_done) { - WL_TRACE(("%s DHCP Done before T2 expiration\n", - __FUNCTION__)); - } else { - /* Noo dhcp during T1+T2, restore BT priority */ - WL_TRACE(("%s DHCP wait interval T2:%d" - "msec expired\n", __FUNCTION__, - BT_DHCP_FLAG_FORCE_TIME)); - } - - /* Restoring default bt priority */ - if (btcx_inf->dev) - wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); -btc_coex_idle: - btcx_inf->bt_state = BT_DHCP_IDLE; - btcx_inf->timer_on = 0; - break; - - default: - WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, - btcx_inf->bt_state)); - if (btcx_inf->dev) - wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); - btcx_inf->bt_state = BT_DHCP_IDLE; - btcx_inf->timer_on = 0; - break; - } - - net_os_wake_unlock(btcx_inf->dev); + wldev_iovar_getbuf_bsscfg(wl_to_prmry_ndev(wl), "cur_etheraddr", NULL, + 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); + memcpy(mac->octet, wl->ioctl_buf, ETHER_ADDR_LEN); } -static int wl_cfg80211_btcoex_init(struct wl_priv *wl) +int wl_cfg80211_do_driver_init(struct net_device *net) { - struct btcoex_info *btco_inf = NULL; + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); - btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); - if (!btco_inf) - return -ENOMEM; - - btco_inf->bt_state = BT_DHCP_IDLE; - btco_inf->ts_dhcp_start = 0; - btco_inf->ts_dhcp_ok = 0; - /* Set up timer for BT */ - btco_inf->timer_ms = 10; - init_timer(&btco_inf->timer); - btco_inf->timer.data = (ulong)btco_inf; - btco_inf->timer.function = wl_cfg80211_bt_timerfunc; - - btco_inf->dev = wl->wdev->netdev; + if (!wl || !wl->wdev) + return -EINVAL; - INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + if (dhd_do_driver_init(wl->wdev->netdev) < 0) + return -1; - wl->btcoex_info = btco_inf; return 0; } -static void -wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +void wl_cfg80211_enable_trace(int level) { - if (!wl->btcoex_info) - return; - - if (!wl->btcoex_info->timer_on) { - wl->btcoex_info->timer_on = 0; - del_timer_sync(&wl->btcoex_info->timer); - } - - cancel_work_sync(&wl->btcoex_info->work); - - kfree(wl->btcoex_info); - wl->btcoex_info = NULL; -} -#endif /* COEX_DHCP */ - -int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) -{ - char powermode_val = 0; - char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; - char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; - char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; - - uint32 regaddr; - static uint32 saved_reg66; - static uint32 saved_reg41; - static uint32 saved_reg68; - static bool saved_status = FALSE; - -#ifdef COEX_DHCP - char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; - struct btcoex_info *btco_inf = wlcfg_drv_priv->btcoex_info; -#endif /* COEX_DHCP */ - - /* Figure out powermode 1 or o command */ - strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); - - if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { - - WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); - - /* Retrieve and saved orig regs value */ - if ((saved_status == FALSE) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { - saved_status = TRUE; - WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", - saved_reg66, saved_reg41, saved_reg68)); - - /* Disable PM mode during dhpc session */ - - /* Disable PM mode during dhpc session */ -#ifdef COEX_DHCP - /* Start BT timer only for SCO connection */ - if (btcoex_is_sco_active(dev)) { - /* btc_params 66 */ - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg66va_dhcp_on[0], - sizeof(buf_reg66va_dhcp_on)); - /* btc_params 41 0x33 */ - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg41va_dhcp_on[0], - sizeof(buf_reg41va_dhcp_on)); - /* btc_params 68 0x190 */ - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg68va_dhcp_on[0], - sizeof(buf_reg68va_dhcp_on)); - saved_status = TRUE; - - btco_inf->bt_state = BT_DHCP_START; - btco_inf->timer_on = 1; - mod_timer(&btco_inf->timer, btco_inf->timer.expires); - WL_TRACE(("%s enable BT DHCP Timer\n", - __FUNCTION__)); - } -#endif /* COEX_DHCP */ - } - else if (saved_status == TRUE) { - WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); - } - } - else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { - - - /* Restoring PM mode */ - -#ifdef COEX_DHCP - /* Stop any bt timer because DHCP session is done */ - WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); - if (btco_inf->timer_on) { - btco_inf->timer_on = 0; - del_timer_sync(&btco_inf->timer); - - if (btco_inf->bt_state != BT_DHCP_IDLE) { - /* need to restore original btc flags & extra btc params */ - WL_TRACE(("%s bt->bt_state:%d\n", - __FUNCTION__, btco_inf->bt_state)); - /* wake up btcoex thread to restore btlags+params */ - schedule_work(&btco_inf->work); - } - } - - /* Restoring btc_flag paramter anyway */ - if (saved_status == TRUE) - dev_wlc_bufvar_set(dev, "btc_flags", - (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); -#endif /* COEX_DHCP */ - - /* Restore original values */ - if (saved_status == TRUE) { - regaddr = 66; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg66); - regaddr = 41; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg41); - regaddr = 68; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg68); - - WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", - saved_reg66, saved_reg41, saved_reg68)); - } - saved_status = FALSE; - - } - else { - WL_ERR(("%s Unkwown yet power setting, ignored\n", - __FUNCTION__)); - } - - snprintf(command, 3, "OK"); - - return (strlen("OK")); + wl_dbg_level |= WL_DBG_DBG; } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 262335ef99c2..fba853149c35 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -68,6 +68,9 @@ do { \ printk args; \ } \ } while (0) +#ifdef WL_INFO +#undef WL_INFO +#endif #define WL_INFO(args) \ do { \ if (wl_dbg_level & WL_DBG_INFO) { \ @@ -75,6 +78,9 @@ do { \ printk args; \ } \ } while (0) +#ifdef WL_SCAN +#undef WL_SCAN +#endif #define WL_SCAN(args) \ do { \ if (wl_dbg_level & WL_DBG_SCAN) { \ @@ -82,6 +88,9 @@ do { \ printk args; \ } \ } while (0) +#ifdef WL_TRACE +#undef WL_TRACE +#endif #define WL_TRACE(args) \ do { \ if (wl_dbg_level & WL_DBG_TRACE) { \ @@ -102,39 +111,32 @@ do { \ #endif /* (WL_DBG_LEVEL > 0) */ -#define WL_SCAN_RETRY_MAX 3 /* used for ibss scan */ -#define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used - * for 2.6.33 kernel - * or later - */ -#define WL_SCAN_BUF_MAX (1024 * 8) -#define WL_TLV_INFO_MAX 1024 +#define WL_SCAN_RETRY_MAX 3 +#define WL_NUM_PMKIDS_MAX MAXPMKID +#define WL_SCAN_BUF_MAX (1024 * 8) +#define WL_TLV_INFO_MAX 1024 #define WL_SCAN_IE_LEN_MAX 2048 -#define WL_BSS_INFO_MAX 2048 -#define WL_ASSOC_INFO_MAX 512 /* - * needs to grab assoc info from dongle to - * report it to cfg80211 through "connect" - * event - */ +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 #define WL_IOCTL_LEN_MAX 1024 #define WL_EXTRA_BUF_MAX 2048 -#define WL_ISCAN_BUF_MAX 2048 /* - * the buf lengh can be WLC_IOCTL_MAXLEN (8K) - * to reduce iteration - */ +#define WL_ISCAN_BUF_MAX 2048 #define WL_ISCAN_TIMER_INTERVAL_MS 3000 #define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1) -#define WL_AP_MAX 256 /* virtually unlimitted as long - * as kernel memory allows - */ +#define WL_AP_MAX 256 #define WL_FILE_NAME_MAX 256 -#define WL_DWELL_TIME 200 -#define WL_LONG_DWELL_TIME 1000 -#define VWDEV_CNT 3 +#define WL_DWELL_TIME 200 +#define WL_MED_DWELL_TIME 400 +#define WL_LONG_DWELL_TIME 1000 +#define IFACE_MAX_CNT 2 #define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ +#define WL_CHANNEL_SYNC_RETRY 3 +#define WL_ACT_FRAME_RETRY 4 -/* dongle status */ +#define WL_INVALID -1 + +/* driver status */ enum wl_status { WL_STATUS_READY = 0, WL_STATUS_SCANNING, @@ -143,7 +145,8 @@ enum wl_status { WL_STATUS_CONNECTED, WL_STATUS_DISCONNECTING, WL_STATUS_AP_CREATING, - WL_STATUS_AP_CREATED + WL_STATUS_AP_CREATED, + WL_STATUS_SENDING_ACT_FRM }; /* wi-fi mode */ @@ -153,7 +156,7 @@ enum wl_mode { WL_MODE_AP }; -/* dongle profile list */ +/* driver profile list */ enum wl_prof_list { WL_PROF_MODE, WL_PROF_SSID, @@ -166,7 +169,7 @@ enum wl_prof_list { WL_PROF_DTIMPERIOD }; -/* dongle iscan state */ +/* driver iscan state */ enum wl_iscan_state { WL_ISCAN_STATE_IDLE, WL_ISCAN_STATE_SCANING @@ -196,12 +199,8 @@ struct beacon_proberesp { u8 variable[0]; } __attribute__ ((packed)); -/* dongle configuration */ +/* driver configuration */ struct wl_conf { - struct net_mode { - struct net_device *ndev; - s32 type; - } mode [VWDEV_CNT + 1]; /* adhoc , infrastructure or ap */ u32 frag_threshold; u32 rts_threshold; u32 retry_short; @@ -259,22 +258,30 @@ struct wl_ibss { u8 channel; }; -/* dongle profile */ +/* wl driver profile */ struct wl_profile { u32 mode; + s32 band; struct wlc_ssid ssid; + struct wl_security sec; + struct wl_ibss ibss; u8 bssid[ETHER_ADDR_LEN]; u16 beacon_interval; u8 dtim_period; - struct wl_security sec; - struct wl_ibss ibss; - s32 band; bool active; }; +struct net_info { + struct net_device *ndev; + struct wireless_dev *wdev; + struct wl_profile profile; + s32 mode; + unsigned long sme_state; + struct list_head list; /* list of all net_info structure */ +}; typedef s32(*ISCAN_HANDLER) (struct wl_priv *wl); -/* dongle iscan controller */ +/* iscan controller */ struct wl_iscan_ctrl { struct net_device *dev; struct timer_list timer; @@ -323,9 +330,10 @@ struct wl_pmk_list { #define ESCAN_BUF_SIZE (64 * 1024) struct escan_info { - u32 escan_state; - u8 escan_buf[ESCAN_BUF_SIZE]; - struct wiphy *wiphy; + u32 escan_state; + u8 escan_buf[ESCAN_BUF_SIZE]; + struct wiphy *wiphy; + struct net_device *ndev; }; struct ap_info { @@ -341,14 +349,14 @@ struct ap_info { }; struct btcoex_info { struct timer_list timer; - uint32 timer_ms; - uint32 timer_on; - uint32 ts_dhcp_start; /* ms ts ecord time stats */ - uint32 ts_dhcp_ok; /* ms ts ecord time stats */ - bool dhcp_done; /* flag, indicates that host done with - * dhcp before t1/t2 expiration - */ - int bt_state; + u32 timer_ms; + u32 timer_on; + u32 ts_dhcp_start; /* ms ts ecord time stats */ + u32 ts_dhcp_ok; /* ms ts ecord time stats */ + bool dhcp_done; /* flag, indicates that host done with + * dhcp before t1/t2 expiration + */ + s32 bt_state; struct work_struct work; struct net_device *dev; }; @@ -360,40 +368,50 @@ struct sta_info { u32 probe_req_ie_len; u32 assoc_req_ie_len; }; -/* dongle private data of cfg80211 interface */ + +struct afx_hdl { + wl_af_params_t *pending_tx_act_frm; + struct ether_addr pending_tx_dst_addr; + struct net_device *dev; + struct work_struct work; + u32 bssidx; + u32 retry; + s32 peer_chan; + bool ack_recv; +}; + +/* private data of cfg80211 interface */ struct wl_priv { struct wireless_dev *wdev; /* representing wl cfg80211 device */ - struct wireless_dev *vwdev[VWDEV_CNT]; - struct wl_conf *conf; /* dongle configuration */ + + struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ + struct net_device *p2p_net; /* reference to p2p0 interface */ + + struct wl_conf *conf; struct cfg80211_scan_request *scan_request; /* scan request object */ EVENT_HANDLER evt_handler[WLC_E_LAST]; struct list_head eq_list; /* used for event queue */ + struct list_head net_list; /* used for struct net_info */ spinlock_t eq_lock; /* for event queue synchronization */ - struct mutex usr_sync; /* maily for dongle up/down synchronization */ + spinlock_t cfgdrv_lock; /* to protect scan status (and others if needed) */ + struct completion act_frm_scan; + struct mutex usr_sync; /* maily for up/down synchronization */ struct wl_scan_results *bss_list; struct wl_scan_results *scan_results; /* scan request object for internal purpose */ struct wl_scan_req *scan_req_int; - - /* bss information for cfg80211 layer */ - struct wl_cfg80211_bss_info *bss_info; /* information element object for internal purpose */ struct wl_ie ie; - - /* for synchronization of main event thread */ - struct wl_profile *profile; /* holding dongle profile */ struct wl_iscan_ctrl *iscan; /* iscan controller */ /* association information container */ struct wl_connect_info conn_info; - /* control firwmare and nvram paramter downloading */ - struct wl_fw_ctrl *fw; struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ tsk_ctl_t event_tsk; /* task of main event handler thread */ - unsigned long status; /* current dongle status */ void *pub; + u32 iface_cnt; u32 channel; /* current channel */ bool iscan_on; /* iscan on/off switch */ bool iscan_kickstart; /* indicate iscan already started */ @@ -403,12 +421,12 @@ struct wl_priv { bool ibss_starter; /* indicates this sta is ibss starter */ bool link_up; /* link/connection up flag */ - /* indicate whether dongle to support power save mode */ + /* indicate whether chip to support power save mode */ bool pwr_save; - bool dongle_up; /* indicate whether dongle up or not */ - bool roam_on; /* on/off switch for dongle self-roaming */ + bool roam_on; /* on/off switch for self-roaming */ bool scan_tried; /* indicates if first scan attempted */ - u8 *ioctl_buf; /* ioctl buffer */ + u8 *ioctl_buf; /* ioctl buffer */ + struct mutex ioctl_buf_sync; u8 *escan_ioctl_buf; u8 *extra_buf; /* maily to grab assoc information */ struct dentry *debugfsdir; @@ -416,131 +434,224 @@ struct wl_priv { bool rf_blocked; struct ieee80211_channel remain_on_chan; enum nl80211_channel_type remain_on_chan_type; - u64 cache_cookie; - wait_queue_head_t dongle_event_wait; + u64 send_action_id; + u64 last_roc_id; + wait_queue_head_t netif_change_event; + struct afx_hdl *afx_hdl; struct ap_info *ap_info; struct sta_info *sta_info; struct p2p_info *p2p; bool p2p_supported; struct btcoex_info *btcoex_info; struct timer_list scan_timeout; /* Timer for catch scan event timeout */ +#ifdef WL_SCHED_SCAN + struct cfg80211_sched_scan_request *sched_scan_req; /* scheduled scan req */ +#endif /* WL_SCHED_SCAN */ + bool sched_scan_running; /* scheduled scan req status */ + u16 hostapd_chan; /* remember chan requested by framework for hostapd */ + u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ }; -#define wl_to_wiphy(w) (w->wdev->wiphy) -#define wl_to_prmry_ndev(w) (w->wdev->netdev) -#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) -#define wl_to_sr(w) (w->scan_req_int) -#define wl_to_ie(w) (&w->ie) -#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) -#define wl_to_iscan(w) (w->iscan) -#define wl_to_conn(w) (&w->conn_info) -#define wiphy_from_scan(w) (w->escan_info.wiphy) -#define wl_get_drv_status(wl, stat) (test_bit(WL_STATUS_ ## stat, &(wl)->status)) -#define wl_set_drv_status(wl, stat) (set_bit(WL_STATUS_ ## stat, &(wl)->status)) -#define wl_clr_drv_status(wl, stat) (clear_bit(WL_STATUS_ ## stat, &(wl)->status)) -#define wl_chg_drv_status(wl, stat) (change_bit(WL_STATUS_ ## stat, &(wl)->status)) - static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) { return bss = bss ? (struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info; } -static inline s32 alloc_idx_vwdev(struct wl_priv *wl) + +static inline s32 +wl_alloc_netinfo(struct wl_priv *wl, struct net_device *ndev, + struct wireless_dev * wdev, s32 mode) { - s32 i = 0; - for (i = 0; i < VWDEV_CNT; i++) { - if (wl->vwdev[i] == NULL) - return i; + struct net_info *_net_info; + s32 err = 0; + if (wl->iface_cnt == IFACE_MAX_CNT) + return -ENOMEM; + _net_info = kzalloc(sizeof(struct net_info), GFP_KERNEL); + if (!_net_info) + err = -ENOMEM; + else { + _net_info->mode = mode; + _net_info->ndev = ndev; + _net_info->wdev = wdev; + wl->iface_cnt++; + list_add(&_net_info->list, &wl->net_list); } - return -1; + return err; } -static inline s32 get_idx_vwdev_by_netdev(struct wl_priv *wl, struct net_device *ndev) +static inline void +wl_dealloc_netinfo(struct wl_priv *wl, struct net_device *ndev) { - s32 i = 0; - for (i = 0; i < VWDEV_CNT; i++) { - if ((wl->vwdev[i] != NULL) && (wl->vwdev[i]->netdev == ndev)) - return i; + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + list_del(&_net_info->list); + wl->iface_cnt--; + if (_net_info->wdev) { + kfree(_net_info->wdev); + ndev->ieee80211_ptr = NULL; + } + kfree(_net_info); + } } - return -1; } -static inline s32 get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) +static inline void +wl_delete_all_netinfo(struct wl_priv *wl) { - s32 i = 0; - for (i = 0; i <= VWDEV_CNT; i++) { - if (wl->conf->mode[i].ndev != NULL && (wl->conf->mode[i].ndev == ndev)) - return wl->conf->mode[i].type; + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + list_del(&_net_info->list); + if (_net_info->wdev) + kfree(_net_info->wdev); + kfree(_net_info); } - return -1; + wl->iface_cnt = 0; } -static inline void set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, s32 type) + +static inline bool +wl_get_status_all(struct wl_priv *wl, s32 status) + { - s32 i = 0; - for (i = 0; i <= VWDEV_CNT; i++) { - if (type == -1) { - /* free the info of netdev */ - if (wl->conf->mode[i].ndev == ndev) { - wl->conf->mode[i].ndev = NULL; - wl->conf->mode[i].type = -1; - break; - } + struct net_info *_net_info, *next; + u32 cnt = 0; + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (_net_info->ndev && + test_bit(status, &_net_info->sme_state)) + cnt++; + } + return cnt? true: false; +} - } else { - if ((wl->conf->mode[i].ndev != NULL)&& - (wl->conf->mode[i].ndev == ndev)) { - /* update type of ndev */ - wl->conf->mode[i].type = type; - break; - } - else if ((wl->conf->mode[i].ndev == NULL)&& - (wl->conf->mode[i].type == -1)) { - wl->conf->mode[i].ndev = ndev; - wl->conf->mode[i].type = type; - break; +static inline void +wl_set_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev, u32 op) +{ + + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + switch (op) { + case 1: + set_bit(status, &_net_info->sme_state); + break; + case 2: + clear_bit(status, &_net_info->sme_state); + break; + case 4: + change_bit(status, &_net_info->sme_state); + break; } } + + } +} + +static inline u32 +wl_get_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return test_bit(status, &_net_info->sme_state); + } + return 0; +} + +static inline s32 +wl_get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return _net_info->mode; + } + return -1; +} + +static inline void +wl_set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, + s32 mode) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + _net_info->mode = mode; + } +} + +static inline struct wl_profile * +wl_get_profile_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return &_net_info->profile; } + return NULL; } -#define free_vwdev_by_index(wl, __i) do { \ - if (wl->vwdev[__i] != NULL) \ - kfree(wl->vwdev[__i]); \ - wl->vwdev[__i] = NULL; \ - } while (0) +#define wl_to_wiphy(w) (w->wdev->wiphy) +#define wl_to_prmry_ndev(w) (w->wdev->netdev) +#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) +#define wl_to_sr(w) (w->scan_req_int) +#define wl_to_ie(w) (&w->ie) +#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) +#define wl_to_iscan(w) (w->iscan) +#define wl_to_conn(w) (&w->conn_info) +#define wiphy_from_scan(w) (w->escan_info.wiphy) +#define wl_get_drv_status_all(wl, stat) \ + (wl_get_status_all(wl, WL_STATUS_ ## stat)) +#define wl_get_drv_status(wl, stat, ndev) \ + (wl_get_status_by_netdev(wl, WL_STATUS_ ## stat, ndev)) +#define wl_set_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 1)) +#define wl_clr_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 2)) +#define wl_chg_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 4)) #define for_each_bss(list, bss, __i) \ for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss)) +#define for_each_ndev(wl, iter, next) \ + list_for_each_entry_safe(iter, next, &wl->net_list, list) + + /* In case of WPS from wpa_supplicant, pairwise siute and group suite is 0. * In addtion to that, wpa_version is WPA_VERSION_1 */ #define is_wps_conn(_sme) \ - ((_sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) && \ + ((wl_cfgp2p_find_wpsie((u8 *)_sme->ie, _sme->ie_len) != NULL) && \ (!_sme->crypto.n_ciphers_pairwise) && \ (!_sme->crypto.cipher_group)) extern s32 wl_cfg80211_attach(struct net_device *ndev, void *data); extern s32 wl_cfg80211_attach_post(struct net_device *ndev); -extern void wl_cfg80211_detach(void); -/* event handler from dongle */ +extern void wl_cfg80211_detach(void *para); + extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, void *data); -extern void wl_cfg80211_set_sdio_func(void *func); /* set sdio function info */ -extern struct sdio_func *wl_cfg80211_get_sdio_func(void); /* set sdio function info */ -extern s32 wl_cfg80211_up(void); /* dongle up */ -extern s32 wl_cfg80211_down(void); /* dongle down */ -extern s32 wl_cfg80211_notify_ifadd(struct net_device *net, s32 idx, s32 bssidx, -int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)); -extern s32 wl_cfg80211_notify_ifdel(struct net_device *ndev); +void wl_cfg80211_set_parent_dev(void *dev); +struct device *wl_cfg80211_get_parent_dev(void); + +extern s32 wl_cfg80211_up(void *para); +extern s32 wl_cfg80211_down(void *para); +extern s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, + void* _net_attach); +extern s32 wl_cfg80211_ifdel_ops(struct net_device *net); +extern s32 wl_cfg80211_notify_ifdel(void); extern s32 wl_cfg80211_is_progress_ifadd(void); extern s32 wl_cfg80211_is_progress_ifchange(void); extern s32 wl_cfg80211_is_progress_ifadd(void); extern s32 wl_cfg80211_notify_ifchange(void); extern void wl_cfg80211_dbg_level(u32 level); -extern void *wl_cfg80211_request_fw(s8 *file_name); -extern s32 wl_cfg80211_read_fw(s8 *buf, u32 size); -extern void wl_cfg80211_release_fw(void); -extern s8 *wl_cfg80211_get_fwname(void); -extern s8 *wl_cfg80211_get_nvramname(void); extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); extern s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len); extern s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len); @@ -548,11 +659,8 @@ extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len enum wl_management_type type); extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); - -/* do scan abort */ -extern s32 -wl_cfg80211_scan_abort(struct wl_priv *wl, struct net_device *ndev); - -extern s32 -wl_cfg80211_if_is_group_owner(void); +extern s32 wl_mode_to_nl80211_iftype(s32 mode); +int wl_cfg80211_do_driver_init(struct net_device *net); +void wl_cfg80211_enable_trace(int level); +extern s32 wl_cfg80211_if_is_group_owner(void); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index 4ee6557e17dd..a34c4ad17c25 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -30,6 +30,7 @@ #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/types.h> #include <linux/string.h> #include <linux/timer.h> @@ -39,26 +40,199 @@ #include <bcmutils.h> #include <bcmendian.h> #include <proto/ethernet.h> -#include <dngl_stats.h> -#include <dhd.h> -#include <dhdioctl.h> -#include <wlioctl.h> #include <wl_cfg80211.h> #include <wl_cfgp2p.h> #include <wldev_common.h> +#include <wl_android.h> - -static s8 ioctlbuf[WLC_IOCTL_MAXLEN]; static s8 scanparambuf[WLC_IOCTL_SMLEN]; -static s8 *smbuf = ioctlbuf; static bool wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type); static s32 -wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, +wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete); + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); +static int wl_cfgp2p_if_open(struct net_device *net); +static int wl_cfgp2p_if_stop(struct net_device *net); + +static const struct net_device_ops wl_cfgp2p_if_ops = { + .ndo_open = wl_cfgp2p_if_open, + .ndo_stop = wl_cfgp2p_if_stop, + .ndo_do_ioctl = wl_cfgp2p_do_ioctl, + .ndo_start_xmit = wl_cfgp2p_start_xmit, +}; + +bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + + if (frame == NULL) + return false; + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_pub_act_frame_t) -1) + return false; + + if (pact_frm->category == P2P_PUB_AF_CATEGORY && + pact_frm->action == P2P_PUB_AF_ACTION && + pact_frm->oui_type == P2P_VER && + memcmp(pact_frm->oui, P2P_OUI, sizeof(pact_frm->oui)) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len) +{ + wifi_p2p_action_frame_t *act_frm; + + if (frame == NULL) + return false; + act_frm = (wifi_p2p_action_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_action_frame_t) -1) + return false; + + if (act_frm->category == P2P_AF_CATEGORY && + act_frm->type == P2P_VER && + memcmp(act_frm->OUI, P2P_OUI, DOT11_OUI_LEN) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) +{ + + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + + if (frame == NULL) + return false; + + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1) + return false; + if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) + return false; + + if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) + return true; + else + return false; + +} + +void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + wifi_p2p_action_frame_t *act_frm; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + if (!frame || frame_len <= 2) + return; + + if (wl_cfgp2p_is_pub_action(frame, frame_len)) { + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + switch (pact_frm->subtype) { + case P2P_PAF_GON_REQ: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Req Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_RSP: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Rsp Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_CONF: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Confirm Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_REQ: + CFGP2P_DBG(("%s P2P Invitation Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_RSP: + CFGP2P_DBG(("%s P2P Invitation Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_REQ: + CFGP2P_DBG(("%s P2P Device Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_RSP: + CFGP2P_DBG(("%s P2P Device Discoverability Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_REQ: + CFGP2P_DBG(("%s P2P Provision Discovery Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_RSP: + CFGP2P_DBG(("%s P2P Provision Discovery Response Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P Public Action Frame\n", + (tx)? "TX": "RX")); + + } + + } else if (wl_cfgp2p_is_p2p_action(frame, frame_len)) { + act_frm = (wifi_p2p_action_frame_t *)frame; + switch (act_frm->subtype) { + case P2P_AF_NOTICE_OF_ABSENCE: + CFGP2P_DBG(("%s P2P Notice of Absence Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_REQ: + CFGP2P_DBG(("%s P2P Presence Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_RSP: + CFGP2P_DBG(("%s P2P Presence Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_GO_DISC_REQ: + CFGP2P_DBG(("%s P2P Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P Action Frame\n", + (tx)? "TX": "RX")); + } + + } else if (wl_cfgp2p_is_gas_action(frame, frame_len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + switch (sd_act_frm->action) { + case P2PSD_ACTION_ID_GAS_IREQ: + CFGP2P_DBG(("%s P2P GAS Initial Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_IRESP: + CFGP2P_DBG(("%s P2P GAS Initial Response\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CREQ: + CFGP2P_DBG(("%s P2P GAS Comback Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CRESP: + CFGP2P_DBG(("%s P2P GAS Comback Response\n", + (tx)? "TX" : "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P GAS Frame\n", + (tx)? "TX" : "RX")); + } + } +} + /* * Initialize variables related to P2P * @@ -99,7 +273,6 @@ wl_cfgp2p_init_priv(struct wl_priv *wl) wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; - spin_lock_init(&wl->p2p->timer_lock); return BCME_OK; } @@ -110,6 +283,8 @@ wl_cfgp2p_init_priv(struct wl_priv *wl) void wl_cfgp2p_deinit_priv(struct wl_priv *wl) { + CFGP2P_DBG(("In\n")); + if (wl->p2p) { kfree(wl->p2p); wl->p2p = NULL; @@ -142,9 +317,9 @@ wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) * firmware for P2P device address */ ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", &null_eth_addr, - sizeof(null_eth_addr), ioctlbuf, sizeof(ioctlbuf), 0); + sizeof(null_eth_addr), wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); if (ret && ret != BCME_UNSUPPORTED) { - CFGP2P_ERR(("failed to update device address\n")); + CFGP2P_ERR(("failed to update device address ret %d\n", ret)); } return ret; } @@ -168,14 +343,14 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, ifreq.chspec = chspec; memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); - CFGP2P_INFO(("---wl p2p_ifadd %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", + CFGP2P_DBG(("---wl p2p_ifadd %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", ifreq.addr.octet[0], ifreq.addr.octet[1], ifreq.addr.octet[2], ifreq.addr.octet[3], ifreq.addr.octet[4], ifreq.addr.octet[5], (if_type == WL_P2P_IF_GO) ? "go" : "client", (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); return err; } @@ -194,7 +369,7 @@ wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) netdev->ifindex, mac->octet[0], mac->octet[1], mac->octet[2], mac->octet[3], mac->octet[4], mac->octet[5])); ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (unlikely(ret < 0)) { printk("'wl p2p_ifdel' error %d\n", ret); } @@ -225,7 +400,7 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (unlikely(err < 0)) { printk("'wl p2p_ifupd' error %d\n", err); @@ -251,8 +426,8 @@ wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) mac->octet[0], mac->octet[1], mac->octet[2], mac->octet[3], mac->octet[4], mac->octet[5])); - ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), - getbuf, sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY)); + ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), getbuf, + sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY), NULL); if (ret == 0) { memcpy(index, getbuf, sizeof(index)); @@ -262,7 +437,7 @@ wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) return ret; } -s32 +static s32 wl_cfgp2p_set_discovery(struct wl_priv *wl, s32 on) { s32 ret = BCME_OK; @@ -311,13 +486,14 @@ wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, u32 channel, u16 listen_ms, discovery_mode.chspec = CH20MHZ_CHSPEC(channel); discovery_mode.dwell = listen_ms; ret = wldev_iovar_setbuf_bsscfg(dev, "p2p_state", &discovery_mode, - sizeof(discovery_mode), ioctlbuf, sizeof(ioctlbuf), bssidx); + sizeof(discovery_mode), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); return ret; } /* Get the index of the P2P Discovery BSS */ -s32 +static s32 wl_cfgp2p_get_disc_idx(struct wl_priv *wl, s32 *index) { s32 ret; @@ -364,7 +540,7 @@ wl_cfgp2p_init_discovery(struct wl_priv *wl) /* Set the initial discovery state to SCAN */ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, - wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); if (unlikely(ret != 0)) { CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); @@ -381,7 +557,7 @@ wl_cfgp2p_init_discovery(struct wl_priv *wl) * @wl : wl_private data * Returns 0 if succes */ -s32 +static s32 wl_cfgp2p_deinit_discovery(struct wl_priv *wl) { s32 ret = BCME_OK; @@ -419,7 +595,8 @@ wl_cfgp2p_deinit_discovery(struct wl_priv *wl) * Returns 0 if success. */ s32 -wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 *ie, u32 ie_len) +wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, + const u8 *ie, u32 ie_len) { s32 ret = BCME_OK; if (wl_get_p2p_status(wl, DISCOVERY_ON)) { @@ -511,10 +688,10 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, wl_escan_params_t *eparams; wlc_ssid_t ssid; /* Scan parameters */ -#define P2PAPI_SCAN_NPROBES 4 -#define P2PAPI_SCAN_DWELL_TIME_MS 80 -#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 100 -#define P2PAPI_SCAN_HOME_TIME_MS 10 +#define P2PAPI_SCAN_NPROBES 1 +#define P2PAPI_SCAN_DWELL_TIME_MS 50 +#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 +#define P2PAPI_SCAN_HOME_TIME_MS 60 struct net_device *pri_dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); wl_set_p2p_status(wl, SCANNING); /* Allocate scan params which need space for 3 channels and 0 ssids */ @@ -530,7 +707,7 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, return -1; } memset(memblk, 0, memsize); - memset(ioctlbuf, 0, sizeof(ioctlbuf)); + memset(wl->ioctl_buf, 0, WLC_IOCTL_MAXLEN); if (search_state == WL_P2P_DISC_ST_SEARCH) { /* * If we in SEARCH STATE, we don't need to set SSID explictly @@ -569,7 +746,7 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, eparams->params.nprobes = htod32(P2PAPI_SCAN_NPROBES); eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS); - if (wl_get_drv_status(wl, CONNECTED)) + if (wl_get_drv_status_all(wl, CONNECTED)) eparams->params.active_time = htod32(-1); else if (num_chans == 3) eparams->params.active_time = htod32(P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS); @@ -595,9 +772,54 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, CFGP2P_INFO(("\n")); ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", - memblk, memsize, smbuf, sizeof(ioctlbuf), bssidx); + memblk, memsize, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); return ret; } + +/* search function to reach at common channel to send action frame + * Parameters: + * @wl : wl_private data + * @ndev : net device for bssidx + * @bssidx : bssidx for BSS + * Returns 0 if success. + */ +s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel) +{ + s32 ret = 0; + u32 chan_cnt = 0; + u16 *default_chan_list = NULL; + if (!p2p_is_on(wl)) + return -BCME_ERROR; + CFGP2P_ERR((" Enter\n")); + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + if (channel) + chan_cnt = 1; + else + chan_cnt = SOCIAL_CHAN_CNT; + default_chan_list = kzalloc(chan_cnt * sizeof(*default_chan_list), GFP_KERNEL); + if (default_chan_list == NULL) { + CFGP2P_ERR(("channel list allocation failed \n")); + ret = -ENOMEM; + goto exit; + } + if (channel) { + default_chan_list[0] = channel; + } else { + default_chan_list[0] = SOCIAL_CHAN_1; + default_chan_list[1] = SOCIAL_CHAN_2; + default_chan_list[2] = SOCIAL_CHAN_3; + } + ret = wl_cfgp2p_escan(wl, ndev, true, SOCIAL_CHAN_CNT, + default_chan_list, WL_P2P_DISC_ST_SEARCH, + WL_SCAN_ACTION_START, bssidx); + kfree(default_chan_list); +exit: + return ret; +} + /* Check whether pointed-to IE looks like WPA. */ #define wl_cfgp2p_is_wpa_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPA_OUI_TYPE) @@ -607,6 +829,10 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, /* Check whether the given IE looks like WFA P2P IE. */ #define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) +/* Check whether the given IE looks like WFA WFDisplay IE. */ +#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */ +#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_WFD) /* Delete and Set a management vndr ie to firmware * Parameters: * @wl : wl_private data @@ -635,7 +861,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss u8 delete = 0; #define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) #define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) - if (wl->p2p_supported && p2p_on(wl) && bssidx != -1) { + if (p2p_is_on(wl) && bssidx != -1) { if (bssidx == P2PAPI_BSSCFG_PRIMARY) bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); switch (pktflag) { @@ -670,7 +896,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss CFGP2P_ERR(("not suitable type\n")); return -1; } - } else if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { switch (pktflag) { case VNDR_IE_PRBRSP_FLAG : mgmt_ie_buf = wl->ap_info->probe_res_ie; @@ -689,7 +915,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss return -1; } bssidx = 0; - } else if (bssidx == -1 && get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { + } else if (bssidx == -1 && wl_get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { switch (pktflag) { case VNDR_IE_PRBREQ_FLAG : mgmt_ie_buf = wl->sta_info->probe_req_ie; @@ -731,12 +957,14 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss ie_len = ie_buf[pos++]; if ((ie_id == DOT11_MNG_VS_ID) && (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || - wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); - ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, - VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); + ret = wl_cfgp2p_vndr_ie(wl, ndev, bssidx, pktflag, + ie_buf+pos, VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, + ie_len-3, delete); } pos += ie_len; } @@ -756,12 +984,14 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss ie_len = ie_buf[pos++]; if ((ie_id == DOT11_MNG_VS_ID) && (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || - wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { CFGP2P_INFO(("ADDED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); - ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, - VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); + ret = wl_cfgp2p_vndr_ie(wl, ndev, bssidx, pktflag, + ie_buf+pos, VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, + ie_len-3, delete); } pos += ie_len; } @@ -865,9 +1095,21 @@ wl_cfgp2p_find_p2pie(u8 *parse, u32 len) } return NULL; } +wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wfd_ie((uint8*)ie, &parse, &len)) { + return (wifi_wfd_ie_t *)ie; + } + } + return NULL; +} static s32 -wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, +wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete) { s32 err = BCME_OK; @@ -909,7 +1151,7 @@ wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui, oui, 3); memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data, data, data_len); err = wldev_iovar_setbuf_bsscfg(ndev, "vndr_ie", ie_setbuf, buf_len, - ioctlbuf, sizeof(ioctlbuf), bssidx); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); CFGP2P_INFO(("vndr_ie iovar returns %d\n", err)); kfree(ie_setbuf); @@ -957,14 +1199,17 @@ wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, s32 ret = BCME_OK; CFGP2P_DBG((" Enter\n")); + + /* If p2p_info is de-initialized, do nothing */ + if (!wl->p2p) + return ret; + if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { wl_set_p2p_status(wl, LISTEN_EXPIRED); if (timer_pending(&wl->p2p->listen_timer)) { - spin_lock_bh(&wl->p2p->timer_lock); del_timer_sync(&wl->p2p->listen_timer); - spin_unlock_bh(&wl->p2p->timer_lock); } - cfg80211_remain_on_channel_expired(ndev, wl->cache_cookie, &wl->remain_on_chan, + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); } else wl_clr_p2p_status(wl, LISTEN_EXPIRED); @@ -990,6 +1235,33 @@ wl_cfgp2p_listen_expired(unsigned long data) } /* + * Routine for cancelling the P2P LISTEN + */ +s32 +wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify) +{ + WL_DBG(("Enter \n")); + + /* Irrespective of whether timer is running or not, reset + * the LISTEN state. + */ + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (timer_pending(&wl->p2p->listen_timer)) { + del_timer_sync(&wl->p2p->listen_timer); + + if (notify) + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, + &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); + } + + + return 0; +} + +/* * Do a P2P Listen on the given channel for the given duration. * A listen consists of sitting idle and responding to P2P probe requests * with a P2P probe response. @@ -1093,10 +1365,10 @@ wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, wl_set_p2p_status(wl, ACTION_TX_NOACK); CFGP2P_ERR(("WLC_E_ACTION_FRAME_COMPLETE : NO ACK\n")); } - wake_up_interruptible(&wl->dongle_event_wait); } else { CFGP2P_INFO((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received," "status : %d\n", status)); + wake_up_interruptible(&wl->netif_change_event); } return ret; } @@ -1126,15 +1398,15 @@ wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, if (bssidx == P2PAPI_BSSCFG_PRIMARY) bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); - ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", - af_params, sizeof(*af_params), ioctlbuf, sizeof(ioctlbuf), bssidx); + ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", af_params, sizeof(*af_params), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (ret < 0) { CFGP2P_ERR((" sending action frame is failed\n")); goto exit; } - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || wl_get_p2p_status(wl, ACTION_TX_NOACK)), msecs_to_jiffies(MAX_WAIT_TIME)); @@ -1243,7 +1515,7 @@ wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) /* Check if the BSS is up */ *(int*)getbuf = -1; result = wldev_iovar_getbuf_bsscfg(ndev, "bss", &bsscfg_idx, - sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0); + sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0, NULL); if (result != 0) { CFGP2P_ERR(("'wl bss -C %d' failed: %d\n", bsscfg_idx, result)); CFGP2P_ERR(("NOTE: this ioctl error is normal " @@ -1260,7 +1532,7 @@ wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) /* Bring up or down a BSS */ s32 -wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up) +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up) { s32 ret = BCME_OK; s32 val = up ? 1 : 0; @@ -1274,7 +1546,7 @@ wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up) bss_setbuf.val = htod32(val); CFGP2P_INFO(("---wl bss -C %d %s\n", bsscfg_idx, up ? "up" : "down")); ret = wldev_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (ret != 0) { CFGP2P_ERR(("'bss %d' failed with %d\n", up, ret)); @@ -1308,13 +1580,16 @@ wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) s32 wl_cfgp2p_down(struct wl_priv *wl) { - if (timer_pending(&wl->p2p->listen_timer)) - del_timer_sync(&wl->p2p->listen_timer); + + wl_cfgp2p_cancel_listen(wl, + wl->p2p_net ? wl->p2p_net : wl_to_prmry_ndev(wl), TRUE); + wl_cfgp2p_deinit_priv(wl); return 0; } -s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +s32 +wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) { s32 ret = -1; int count, start, duration; @@ -1330,7 +1605,7 @@ s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf sscanf(buf, "%d %d %d", &count, &start, &duration); CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", - count, start, duration)); + count, start, duration)); if (count != -1) wl->p2p->noa.desc[0].count = count; @@ -1374,7 +1649,8 @@ s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf dongle_noa.desc[0].interval = htod32(wl->p2p->noa.desc[0].interval*1000); ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), - "p2p_noa", &dongle_noa, sizeof(dongle_noa), ioctlbuf, sizeof(ioctlbuf)); + "p2p_noa", &dongle_noa, sizeof(dongle_noa), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + &wl->ioctl_buf_sync); if (ret < 0) { CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret)); @@ -1386,7 +1662,8 @@ s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf return ret; } -s32 wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) +s32 +wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) { wifi_p2p_noa_desc_t *noa_desc; int len = 0, i; @@ -1428,7 +1705,8 @@ s32 wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf return len * 2; } -s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +s32 +wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) { int ps, ctw; int ret = -1; @@ -1446,7 +1724,7 @@ s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, wl->p2p->ops.ops = ps; ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), "p2p_ops", &wl->p2p->ops, sizeof(wl->p2p->ops), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (ret < 0) { CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret)); } @@ -1467,3 +1745,257 @@ s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, } return ret; } + +u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id) +{ + wifi_p2p_ie_t *ie = NULL; + u16 len = 0; + u8 *subel; + u8 subelt_id; + u16 subelt_len; + + if (!buf) { + WL_ERR(("P2P IE not present")); + return 0; + } + + ie = (wifi_p2p_ie_t*) buf; + len = ie->len; + + /* Point subel to the P2P IE's subelt field. + * Subtract the preceding fields (id, len, OUI, oui_type) from the length. + */ + subel = ie->subelts; + len -= 4; /* exclude OUI + OUI_TYPE */ + + while (len >= 3) { + /* attribute id */ + subelt_id = *subel; + subel += 1; + len -= 1; + + /* 2-byte little endian */ + subelt_len = *subel++; + subelt_len |= *subel++ << 8; + + len -= 2; + len -= subelt_len; /* for the remaining subelt fields */ + + if (subelt_id == element_id) { + /* This will point to start of subelement attrib after + * attribute id & len + */ + return subel; + } + + /* Go to next subelement */ + subel += subelt_len; + } + + /* Not Found */ + return NULL; +} + +#define P2P_GROUP_CAPAB_GO_BIT 0x01 +u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length) +{ + wifi_p2p_ie_t * p2p_ie = NULL; + u8 *capability = NULL; + bool p2p_go = 0; + u8 *ptr = NULL; + + if (!(p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, bi->ie_length))) { + WL_ERR(("P2P IE not found")); + return NULL; + } + + if (!(capability = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_P2P_INFO))) { + WL_ERR(("P2P Capability attribute not found")); + return NULL; + } + + /* Check Group capability for Group Owner bit */ + p2p_go = capability[1] & P2P_GROUP_CAPAB_GO_BIT; + if (!p2p_go) { + return bi->BSSID.octet; + } + + /* In probe responses, DEVICE INFO attribute will be present */ + if (!(ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO))) { + /* If DEVICE_INFO is not found, this might be a beacon frame. + * check for DEVICE_ID in the beacon frame. + */ + ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_ID); + } + + if (!ptr) + WL_ERR((" Both DEVICE_ID & DEVICE_INFO attribute not present in P2P IE ")); + + return ptr; +} + +s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl) +{ + int ret = 0; + struct net_device* net = NULL; + struct wireless_dev *wdev; + uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x33, 0x22, 0x11 }; + + /* Allocate etherdev, including space for private structure */ + if (!(net = alloc_etherdev(sizeof(wl)))) { + CFGP2P_ERR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + goto fail; + } + + strcpy(net->name, "p2p%d"); + net->name[IFNAMSIZ - 1] = '\0'; + + /* Copy the reference to wl_priv */ + memcpy((void *)netdev_priv(net), &wl, sizeof(wl)); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + ASSERT(!net->open); + net->do_ioctl = wl_cfgp2p_do_ioctl; + net->hard_start_xmit = wl_cfgp2p_start_xmit; + net->open = wl_cfgp2p_if_open; + net->stop = wl_cfgp2p_if_stop; +#else + ASSERT(!net->netdev_ops); + net->netdev_ops = &wl_cfgp2p_if_ops; +#endif + + /* Register with a dummy MAC addr */ + memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + + wdev->wiphy = wl->wdev->wiphy; + + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + + net->ieee80211_ptr = wdev; + + SET_NETDEV_DEV(net, wiphy_dev(wdev->wiphy)); + + /* Associate p2p0 network interface with new wdev */ + wdev->netdev = net; + + /* store p2p net ptr for further reference. Note that iflist won't have this + * entry as there corresponding firmware interface is a "Hidden" interface. + */ + if (wl->p2p_net) { + CFGP2P_ERR(("p2p_net defined already.\n")); + return -EINVAL; + } else { + wl->p2p_wdev = wdev; + wl->p2p_net = net; + } + + ret = register_netdev(net); + if (ret) { + CFGP2P_ERR((" register_netdevice failed (%d)\n", ret)); + goto fail; + } + + printk("%s: P2P Interface Registered\n", net->name); + + return ret; +fail: + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + + if (net) { + unregister_netdev(net); + free_netdev(net); + } + + return -ENODEV; +} + +s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl) +{ + + if (!wl || !wl->p2p_net) { + CFGP2P_ERR(("Invalid Ptr\n")); + return -EINVAL; + } + + unregister_netdev(wl->p2p_net); + free_netdev(wl->p2p_net); + + return 0; +} + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + CFGP2P_DBG(("(%s) is not used for data operations. Droping the packet. \n", ndev->name)); + return 0; +} + +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd) +{ + int ret = 0; + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); + struct net_device *ndev = wl_to_prmry_ndev(wl); + + /* There is no ifidx corresponding to p2p0 in our firmware. So we should + * not Handle any IOCTL cmds on p2p0 other than ANDROID PRIVATE CMDs. + * For Android PRIV CMD handling map it to primary I/F + */ + if (cmd == SIOCDEVPRIVATE+1) { + ret = wl_android_priv_cmd(ndev, ifr, cmd); + + } else { + CFGP2P_ERR(("%s: IOCTL req 0x%x on p2p0 I/F. Ignoring. \n", + __FUNCTION__, cmd)); + return -1; + } + + return ret; +} + +static int wl_cfgp2p_if_open(struct net_device *net) +{ + struct wireless_dev *wdev = net->ieee80211_ptr; + + if (!wdev) + return -EINVAL; + + /* If suppose F/W download (ifconfig wlan0 up) hasn't been done by now, + * do it here. This will make sure that in concurrent mode, supplicant + * is not dependent on a particular order of interface initialization. + * i.e you may give wpa_supp -iwlan0 -N -ip2p0 or wpa_supp -ip2p0 -N + * -iwlan0. + */ + wl_cfg80211_do_driver_init(net); + + wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO)); + + return 0; +} + +static int wl_cfgp2p_if_stop(struct net_device *net) +{ + struct wireless_dev *wdev = net->ieee80211_ptr; + + if (!wdev) + return -EINVAL; + + wdev->wiphy->interface_modes = (wdev->wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index 5a69168c6a3a..668198d31a2e 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -30,7 +30,7 @@ struct wl_priv; extern u32 wl_dbg_level; - +typedef struct wifi_p2p_ie wifi_wfd_ie_t; /* Enumeration of the usages of the BSSCFGs used by the P2P Library. Do not * confuse this with a bsscfg index. This value is an index into the * saved_ie[] array of structures which in turn contains a bsscfg index field. @@ -77,7 +77,6 @@ struct p2p_info { wl_p2p_sched_t noa; wl_p2p_ops_t ops; wlc_ssid_t ssid; - spinlock_t timer_lock; }; /* dongle status */ @@ -101,13 +100,13 @@ enum wl_cfgp2p_status { #define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p->bss_idx[type].saved_ie) #define wl_to_p2p_bss_private(w, type) ((wl)->p2p->bss_idx[type].private_data) #define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) -#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \ +#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:test_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) -#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : set_bit(WLP2P_STATUS_ ## stat, \ +#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:set_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) -#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : clear_bit(WLP2P_STATUS_ ## stat, \ +#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:clear_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) -#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : change_bit(WLP2P_STATUS_ ## stat, \ +#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:change_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) #define p2p_on(wl) ((wl)->p2p->on) #define p2p_scan(wl) ((wl)->p2p->scan) @@ -140,7 +139,14 @@ enum wl_cfgp2p_status { } \ } while (0) - +extern bool +wl_cfgp2p_is_pub_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_gas_action(void *frame, u32 frame_len); +extern void +wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len); extern s32 wl_cfgp2p_init_priv(struct wl_priv *wl); extern void @@ -172,6 +178,10 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, u32 num_ u16 *channels, s32 search_state, u16 action, u32 bssidx); +extern s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel); + extern wpa_ie_fixed_t * wl_cfgp2p_find_wpaie(u8 *parse, u32 len); @@ -181,6 +191,9 @@ wl_cfgp2p_find_wpsie(u8 *parse, u32 len); extern wifi_p2p_ie_t * wl_cfgp2p_find_p2pie(u8 *parse, u32 len); +extern wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len); + extern s32 wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); @@ -217,7 +230,7 @@ extern bool wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx); extern s32 -wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up); +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up); extern s32 @@ -235,13 +248,38 @@ wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, in extern s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); +extern u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id); + +extern u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length); + +extern s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl); + +extern s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl); + /* WiFi Direct */ #define SOCIAL_CHAN_1 1 #define SOCIAL_CHAN_2 6 #define SOCIAL_CHAN_3 11 +#define SOCIAL_CHAN_CNT 3 #define WL_P2P_WILDCARD_SSID "DIRECT-" #define WL_P2P_WILDCARD_SSID_LEN 7 #define WL_P2P_INTERFACE_PREFIX "p2p" #define WL_P2P_TEMP_CHAN "11" + +/* If the provision discovery is for JOIN operations, then we need not do an internal scan to find GO */ +#define IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len) (wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_GROUP_ID) == NULL ) + +#define IS_GAS_REQ(frame, len) (wl_cfgp2p_is_gas_action(frame, len) && \ + ((frame->action == P2PSD_ACTION_ID_GAS_IREQ) || \ + (frame->action == P2PSD_ACTION_ID_GAS_CREQ))) +#define IS_P2P_PUB_ACT_REQ(frame, p2p_ie, len) (wl_cfgp2p_is_pub_action(frame, len) && \ + ((frame->subtype == P2P_PAF_GON_REQ) || \ + (frame->subtype == P2P_PAF_INVITE_REQ) || \ + ((frame->subtype == P2P_PAF_PROVDIS_REQ) && IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len)))) +#define IS_P2P_SOCIAL(ch) ((ch == SOCIAL_CHAN_1) || (ch == SOCIAL_CHAN_2) || (ch == SOCIAL_CHAN_3)) #define IS_P2P_SSID(ssid) (memcmp(ssid, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN) == 0) #endif /* _wl_cfgp2p_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 457372b62b3a..059929340354 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.c,v 1.132.2.18 2011-02-05 01:44:47 Exp $ + * $Id: wl_iw.c,v 1.132.2.18 2011-02-05 01:44:47 $ */ #include <wlioctl.h> @@ -148,7 +148,7 @@ static struct mutex wl_softap_lock; #include <bcmsdbus.h> extern void dhd_customer_gpio_wlan_ctrl(int onoff); extern uint dhd_dev_reset(struct net_device *dev, uint8 flag); -extern void dhd_dev_init_ioctl(struct net_device *dev); +extern int dhd_dev_init_ioctl(struct net_device *dev); uint wl_msg_level = WL_ERROR_VAL; @@ -162,11 +162,8 @@ uint wl_msg_level = WL_ERROR_VAL; #define htodchanspec(i) i #define dtohchanspec(i) i -#ifdef CONFIG_WIRELESS_EXT - extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); extern int dhd_wait_pend8021x(struct net_device *dev); -#endif #if WIRELESS_EXT < 19 #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) @@ -1122,7 +1119,7 @@ wl_iw_set_btcoex_dhcp( } static int -wl_iw_set_suspend( +wl_iw_set_suspend_opt( struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, @@ -1133,16 +1130,15 @@ char *extra int ret_now; int ret = 0; - suspend_flag = *(extra + strlen(SETSUSPEND_CMD) + 1) - '0'; + suspend_flag = *(extra + strlen(SETSUSPENDOPT_CMD) + 1) - '0'; if (suspend_flag != 0) suspend_flag = 1; ret_now = net_os_set_suspend_disable(dev, suspend_flag); - if (ret_now != suspend_flag) { - if (!(ret = net_os_set_suspend(dev, ret_now))) + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) WL_ERROR(("%s: Suspend Flag %d -> %d\n", __FUNCTION__, ret_now, suspend_flag)); else @@ -1153,6 +1149,32 @@ char *extra } static int +wl_iw_set_suspend_mode( +struct net_device *dev, +struct iw_request_info *info, +union iwreq_data *wrqu, +char *extra +) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(extra + strlen(SETSUSPENDMODE_CMD) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + WL_ERROR(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); + else + WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); +#endif + return ret; +} + +static int wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) { int i, c; @@ -1564,6 +1586,63 @@ exit_proc: net_os_wake_unlock(dev); return res; } + +static int +wl_iw_set_pno_setadd( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int ret = -1; + char *tmp_ptr; + int size, tmp_size; + + net_os_wake_lock(dev); + WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto exit_proc; + } + + if (wrqu->data.length <= strlen(PNOSETADD_SET_CMD) + sizeof(cmd_tlv_t)) { + WL_ERROR(("%s argument=%d less than %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(PNOSETADD_SET_CMD) + sizeof(cmd_tlv_t)))); + goto exit_proc; + } + + + bcopy(PNOSETUP_SET_CMD, extra, strlen(PNOSETUP_SET_CMD)); + + tmp_ptr = extra + strlen(PNOSETUP_SET_CMD); + size = wrqu->data.length - strlen(PNOSETUP_SET_CMD); + tmp_size = size; + + while (*tmp_ptr && tmp_size > 0) { + if ((*tmp_ptr == 'S') && (size - tmp_size) >= sizeof(cmd_tlv_t)) { + *(tmp_ptr + 1) = ((*(tmp_ptr + 1) - '0') << 4) + (*(tmp_ptr + 2) - '0'); + memmove(tmp_ptr + 2, tmp_ptr + 3, tmp_size - 3); + tmp_size -= 2 + *(tmp_ptr + 1); + tmp_ptr += 2 + *(tmp_ptr + 1); + size--; + } else { + tmp_ptr++; + tmp_size--; + } + } + + wrqu->data.length = strlen(PNOSETUP_SET_CMD) + size; + ret = wl_iw_set_pno_set(dev, info, wrqu, extra); + +exit_proc: + net_os_wake_unlock(dev); + return ret; + +} #endif static int @@ -1626,7 +1705,7 @@ wl_iw_send_priv_event( strcpy(extra, flag); wrqu.data.length = strlen(extra); wireless_send_event(dev, cmd, &wrqu, extra); - net_os_wake_lock_timeout_enable(dev, DHD_EVENT_TIMEOUT); + net_os_wake_lock_ctrl_timeout_enable(dev, DHD_EVENT_TIMEOUT_MS); WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra)); return 0; @@ -2567,6 +2646,8 @@ wl_iw_get_range( IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE); IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND); #endif @@ -5473,7 +5554,15 @@ wl_iw_set_wpaauth( switch (paramid) { case IW_AUTH_WPA_VERSION: - iw->wpaversion = paramval; + if (paramval & IW_AUTH_WPA_VERSION_DISABLED) + val = WPA_AUTH_DISABLED; + else if (paramval & (IW_AUTH_WPA_VERSION_WPA)) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (paramval & IW_AUTH_WPA_VERSION_WPA2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + WL_ERROR(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + return error; break; case IW_AUTH_CIPHER_PAIRWISE: @@ -5491,7 +5580,27 @@ wl_iw_set_wpaauth( break; case IW_AUTH_KEY_MGMT: - if (paramval & IW_AUTH_KEY_MGMT_PSK) { + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA_AUTH_PSK; + else + val = WPA_AUTH_UNSPECIFIED; + if (paramval & 0x04) + val |= WPA2_AUTH_FT; + } + else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA2_AUTH_PSK; + else + val = WPA2_AUTH_UNSPECIFIED; + if (paramval & 0x04) + val |= WPA2_AUTH_FT; + } + + else if (paramval & IW_AUTH_KEY_MGMT_PSK) { if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA) val = WPA_AUTH_PSK; else if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA2) @@ -7517,8 +7626,10 @@ wl_iw_set_priv( ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) - ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPENDOPT_CMD, strlen(SETSUSPENDOPT_CMD)) == 0) + ret = wl_iw_set_suspend_opt(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPENDMODE_CMD, strlen(SETSUSPENDMODE_CMD)) == 0) + ret = wl_iw_set_suspend_mode(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0) ret = wl_iw_set_txpower(dev, info, (union iwreq_data *)dwrq, extra); #if defined(PNO_SUPPORT) @@ -7526,6 +7637,8 @@ wl_iw_set_priv( ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOSETADD_SET_CMD, strlen(PNOSETADD_SET_CMD)) == 0) + ret = wl_iw_set_pno_setadd(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); #endif @@ -8251,6 +8364,21 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) break; } + + case WLC_E_ASSOC_REQ_IE: + cmd = IWEVASSOCREQIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + + case WLC_E_ASSOC_RESP_IE: + cmd = IWEVASSOCRESPIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + case WLC_E_PMKID_CACHE: { if (data) { diff --git a/drivers/net/wireless/bcmdhd/wl_iw.h b/drivers/net/wireless/bcmdhd/wl_iw.h index c0cc14bdde4e..9cdb53dfef8c 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.h +++ b/drivers/net/wireless/bcmdhd/wl_iw.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.h,v 1.15.80.6 2010-12-23 01:13:23 Exp $ + * $Id: wl_iw.h,v 1.15.80.6 2010-12-23 01:13:23 $ */ @@ -47,13 +47,15 @@ #define BAND_SET_CMD "SETBAND" #define DTIM_SKIP_GET_CMD "DTIMSKIPGET" #define DTIM_SKIP_SET_CMD "DTIMSKIPSET" -#define SETSUSPEND_CMD "SETSUSPENDOPT" +#define SETSUSPENDOPT_CMD "SETSUSPENDOPT" +#define SETSUSPENDMODE_CMD "SETSUSPENDMODE" #define PNOSSIDCLR_SET_CMD "PNOSSIDCLR" #define PNOSETUP_SET_CMD "PNOSETUP " +#define PNOSETADD_SET_CMD "PNOSETADD" #define PNOENABLE_SET_CMD "PNOFORCE" #define PNODEBUG_SET_CMD "PNODEBUG" -#define TXPOWER_SET_CMD "TXPOWER" +#define TXPOWER_SET_CMD "TXPOWER" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" @@ -203,11 +205,10 @@ void wl_iw_detach(void); extern int net_os_wake_lock(struct net_device *dev); extern int net_os_wake_unlock(struct net_device *dev); extern int net_os_wake_lock_timeout(struct net_device *dev); -extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); +extern int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val); extern int net_os_set_suspend_disable(struct net_device *dev, int val); -extern int net_os_set_suspend(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); extern int net_os_set_dtim_skip(struct net_device *dev, int val); -extern int net_os_send_hang_message(struct net_device *dev); extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) @@ -226,6 +227,18 @@ extern void get_customized_country_code(char *country_iso_code, wl_country_t *cs iwe_stream_add_point(stream, ends, iwe, extra) #endif +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_dev_pno_reset(struct net_device *dev); +extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, + int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); +extern int dhd_dev_get_pno_status(struct net_device *dev); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); + void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); #define PNO_TLV_PREFIX 'S' diff --git a/drivers/net/wireless/bcmdhd/wl_linux_mon.c b/drivers/net/wireless/bcmdhd/wl_linux_mon.c new file mode 100644 index 000000000000..f44b4b04bb96 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_linux_mon.c @@ -0,0 +1,409 @@ +/* + * Broadcom Dongle Host Driver (DHD), Linux monitor network interface + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_linux_mon.c 303266 2011-12-16 00:15:23Z $ + */ + +#include <linux/string.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> +#include <linux/ieee80211.h> +#include <linux/rtnetlink.h> +#include <net/ieee80211_radiotap.h> + +#include <wlioctl.h> +#include <bcmutils.h> +#include <linux_osl.h> +#include <dhd_dbg.h> +#include <dngl_stats.h> +#include <dhd.h> + +typedef enum monitor_states +{ + MONITOR_STATE_DEINIT = 0x0, + MONITOR_STATE_INIT = 0x1, + MONITOR_STATE_INTERFACE_ADDED = 0x2, + MONITOR_STATE_INTERFACE_DELETED = 0x4 +} monitor_states_t; +extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); + +/** + * Local declarations and defintions (not exposed) + */ +#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__) +#define MON_TRACE MON_PRINT + +typedef struct monitor_interface { + int radiotap_enabled; + struct net_device* real_ndev; /* The real interface that the monitor is on */ + struct net_device* mon_ndev; +} monitor_interface; + +typedef struct dhd_linux_monitor { + void *dhd_pub; + monitor_states_t monitor_state; + monitor_interface mon_if[DHD_MAX_IFS]; + struct mutex lock; /* lock to protect mon_if */ +} dhd_linux_monitor_t; + +static dhd_linux_monitor_t g_monitor; + +static struct net_device* lookup_real_netdev(char *name); +static monitor_interface* ndev_to_monif(struct net_device *ndev); +static int dhd_mon_if_open(struct net_device *ndev); +static int dhd_mon_if_stop(struct net_device *ndev); +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static void dhd_mon_if_set_multicast_list(struct net_device *ndev); +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr); + +static const struct net_device_ops dhd_mon_if_ops = { + .ndo_open = dhd_mon_if_open, + .ndo_stop = dhd_mon_if_stop, + .ndo_start_xmit = dhd_mon_if_subif_start_xmit, + .ndo_set_multicast_list = dhd_mon_if_set_multicast_list, + .ndo_set_mac_address = dhd_mon_if_change_mac, +}; + +/** + * Local static function defintions + */ + +/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0" + * "p2p-eth0-0" is a match for "mon.p2p-eth0-0") + */ +static struct net_device* lookup_real_netdev(char *name) +{ + int i; + int len = 0; + int last_name_len = 0; + struct net_device *ndev; + struct net_device *ndev_found = NULL; + + /* We need to find interface "p2p-p2p-0" corresponding to monitor interface "mon-p2p-0", + * Once mon iface name reaches IFNAMSIZ, it is reset to p2p0-0 and corresponding mon + * iface would be mon-p2p0-0. + */ + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = dhd_idx2net(g_monitor.dhd_pub, i); + + /* Skip "p2p" and look for "-p2p0-x" in monitor interface name. If it + * it matches, then this netdev is the corresponding real_netdev. + */ + if (ndev && strstr(ndev->name, "p2p-p2p0")) { + len = strlen("p2p"); + } else { + /* if p2p- is not present, then the IFNAMSIZ have reached and name + * would have got reset. In this casse,look for p2p0-x in mon-p2p0-x + */ + len = 0; + } + if (ndev && strstr(name, (ndev->name + len))) { + if (strlen(ndev->name) > last_name_len) { + ndev_found = ndev; + last_name_len = strlen(ndev->name); + } + } + } + + return ndev_found; +} + +static monitor_interface* ndev_to_monif(struct net_device *ndev) +{ + int i; + + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev) + return &g_monitor.mon_if[i]; + } + + return NULL; +} + +static int dhd_mon_if_open(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_stop(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int ret = 0; + int rtap_len; + int qos_len = 0; + int dot11_hdr_len = 24; + int snap_len = 6; + unsigned char *pdata; + unsigned short frame_ctl; + unsigned char src_mac_addr[6]; + unsigned char dst_mac_addr[6]; + struct ieee80211_hdr *dot11_hdr; + struct ieee80211_radiotap_header *rtap_hdr; + monitor_interface* mon_if; + + MON_PRINT("enter\n"); + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + goto fail; + } + + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + goto fail; + + rtap_hdr = (struct ieee80211_radiotap_header *)skb->data; + if (unlikely(rtap_hdr->it_version)) + goto fail; + + rtap_len = ieee80211_get_radiotap_len(skb->data); + if (unlikely(skb->len < rtap_len)) + goto fail; + + MON_PRINT("radiotap len (should be 14): %d\n", rtap_len); + + /* Skip the ratio tap header */ + skb_pull(skb, rtap_len); + + dot11_hdr = (struct ieee80211_hdr *)skb->data; + frame_ctl = le16_to_cpu(dot11_hdr->frame_control); + /* Check if the QoS bit is set */ + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + /* Check if this ia a Wireless Distribution System (WDS) frame + * which has 4 MAC addresses + */ + if (dot11_hdr->frame_control & 0x0080) + qos_len = 2; + if ((dot11_hdr->frame_control & 0x0300) == 0x0300) + dot11_hdr_len += 6; + + memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); + memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); + + /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for + * for two MAC addresses + */ + skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); + pdata = (unsigned char*)skb->data; + memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); + memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); + + MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); + + /* Use the real net device to transmit the packet */ + ret = dhd_start_xmit(skb, mon_if->real_ndev); + + return ret; + } +fail: + dev_kfree_skb(skb); + return 0; +} + +static void dhd_mon_if_set_multicast_list(struct net_device *ndev) +{ + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); + } +} + +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) +{ + int ret = 0; + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); + } + return ret; +} + +/** + * Global function definitions (declared in dhd_linux_mon.h) + */ + +int dhd_add_monitor(char *name, struct net_device **new_ndev) +{ + int i; + int idx = -1; + int ret = 0; + struct net_device* ndev = NULL; + dhd_linux_monitor_t **dhd_mon; + + mutex_lock(&g_monitor.lock); + + MON_TRACE("enter, if name: %s\n", name); + if (!name || !new_ndev) { + MON_PRINT("invalid parameters\n"); + ret = -EINVAL; + goto out; + } + + /* + * Find a vacancy + */ + for (i = 0; i < DHD_MAX_IFS; i++) + if (g_monitor.mon_if[i].mon_ndev == NULL) { + idx = i; + break; + } + if (idx == -1) { + MON_PRINT("exceeds maximum interfaces\n"); + ret = -EFAULT; + goto out; + } + + ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*)); + if (!ndev) { + MON_PRINT("failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } + + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + strncpy(ndev->name, name, IFNAMSIZ); + ndev->name[IFNAMSIZ - 1] = 0; + ndev->netdev_ops = &dhd_mon_if_ops; + + ret = register_netdevice(ndev); + if (ret) { + MON_PRINT(" register_netdevice failed (%d)\n", ret); + goto out; + } + + *new_ndev = ndev; + g_monitor.mon_if[idx].radiotap_enabled = TRUE; + g_monitor.mon_if[idx].mon_ndev = ndev; + g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); + dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); + *dhd_mon = &g_monitor; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; + MON_PRINT("net device returned: 0x%p\n", ndev); + MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); + +out: + if (ret && ndev) + free_netdev(ndev); + + mutex_unlock(&g_monitor.lock); + return ret; + +} + +int dhd_del_monitor(struct net_device *ndev) +{ + int i; + bool rollback_lock = false; + if (!ndev) + return -EINVAL; + mutex_lock(&g_monitor.lock); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev || + g_monitor.mon_if[i].real_ndev == ndev) { + g_monitor.mon_if[i].real_ndev = NULL; + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(g_monitor.mon_if[i].mon_ndev); + free_netdev(g_monitor.mon_if[i].mon_ndev); + g_monitor.mon_if[i].mon_ndev = NULL; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; + break; + } + } + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + + if (g_monitor.monitor_state != + MONITOR_STATE_INTERFACE_DELETED) + MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", + ndev); + mutex_unlock(&g_monitor.lock); + + return 0; +} + +int dhd_monitor_init(void *dhd_pub) +{ + if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { + g_monitor.dhd_pub = dhd_pub; + mutex_init(&g_monitor.lock); + g_monitor.monitor_state = MONITOR_STATE_INIT; + } + return 0; +} + +int dhd_monitor_uninit(void) +{ + int i; + struct net_device *ndev; + bool rollback_lock = false; + mutex_lock(&g_monitor.lock); + if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = g_monitor.mon_if[i].mon_ndev; + if (ndev) { + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(ndev); + free_netdev(ndev); + g_monitor.mon_if[i].real_ndev = NULL; + g_monitor.mon_if[i].mon_ndev = NULL; + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + } + } + g_monitor.monitor_state = MONITOR_STATE_DEINIT; + } + mutex_unlock(&g_monitor.lock); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index bb3eaea90d0f..827033185035 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -24,12 +24,13 @@ * $Id: wldev_common.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ */ -#include <linux/module.h> +#include <osl.h> +#include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/netdevice.h> #include <wldev_common.h> #include <bcmutils.h> -#include <dhd_dbg.h> #define htod32(i) i #define htod16(i) i @@ -37,6 +38,13 @@ #define dtoh16(i) i #define htodchanspec(i) i #define dtohchanspec(i) i + +#define WLDEV_ERROR(args) \ + do { \ + printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__); \ + printk args; \ + } while (0) + extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); s32 wldev_ioctl( @@ -71,26 +79,34 @@ static s32 wldev_mkiovar( s32 wldev_iovar_getbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen) + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len = 0; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) + mutex_unlock(buf_sync); return ret; } s32 wldev_iovar_setbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen) + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + if (buf_sync) + mutex_unlock(buf_sync); return ret; } @@ -102,7 +118,7 @@ s32 wldev_iovar_setint( val = htod32(val); memset(iovar_buf, 0, sizeof(iovar_buf)); return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf, - sizeof(iovar_buf)); + sizeof(iovar_buf), NULL); } @@ -114,7 +130,7 @@ s32 wldev_iovar_getint( memset(iovar_buf, 0, sizeof(iovar_buf)); err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf, - sizeof(iovar_buf)); + sizeof(iovar_buf), NULL); if (err == 0) { memcpy(pval, iovar_buf, sizeof(*pval)); @@ -148,7 +164,7 @@ s32 wldev_mkiovar_bsscfg( if (buflen < 0 || iolen > (u32)buflen) { - DHD_ERROR(("%s: buffer is too short\n", __FUNCTION__)); + WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__)); return BCME_BUFTOOSHORT; } @@ -177,26 +193,37 @@ s32 wldev_mkiovar_bsscfg( s32 wldev_iovar_getbuf_bsscfg( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx) + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len = 0; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) { + mutex_unlock(buf_sync); + } return ret; } s32 wldev_iovar_setbuf_bsscfg( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx) + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); + ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + if (buf_sync) { + mutex_unlock(buf_sync); + } return ret; } @@ -208,7 +235,7 @@ s32 wldev_iovar_setint_bsscfg( val = htod32(val); memset(iovar_buf, 0, sizeof(iovar_buf)); return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf, - sizeof(iovar_buf), bssidx); + sizeof(iovar_buf), bssidx, NULL); } @@ -220,7 +247,7 @@ s32 wldev_iovar_getint_bsscfg( memset(iovar_buf, 0, sizeof(iovar_buf)); err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf, - sizeof(iovar_buf), bssidx); + sizeof(iovar_buf), bssidx, NULL); if (err == 0) { memcpy(pval, iovar_buf, sizeof(*pval)); @@ -309,16 +336,16 @@ int wldev_set_country( return error; error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf)); + smbuf, sizeof(smbuf), NULL); if (error < 0) - DHD_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); + WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); if ((error < 0) || (strncmp(country_code, smbuf, WLC_CNTRY_BUF_SZ) != 0)) { bzero(&scbval, sizeof(scb_val_t)); error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), 1); if (error < 0) { - DHD_ERROR(("%s: set country failed due to Disassoc error %d\n", + WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n", __FUNCTION__, error)); return error; } @@ -328,14 +355,64 @@ int wldev_set_country( memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); get_customized_country_code((char *)&cspec.country_abbrev, &cspec); error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf)); + smbuf, sizeof(smbuf), NULL); if (error < 0) { - DHD_ERROR(("%s: set country for %s as %s rev %d failed\n", + WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); return error; } dhd_bus_country_set(dev, &cspec); - DHD_INFO(("%s: set country for %s as %s rev %d\n", + WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); return 0; } + +/* + * softap channel autoselect + */ +int wldev_get_auto_channel(struct net_device *dev, int *chan) +{ + int chosen = 0; + wl_uint32_list_t request; + int retry = 0; + int updown = 0; + int ret = 0; + wlc_ssid_t null_ssid; + + memset(&null_ssid, 0, sizeof(wlc_ssid_t)); + ret |= wldev_ioctl(dev, WLC_UP, &updown, sizeof(updown), true); + + ret |= wldev_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid), true); + + request.count = htod32(0); + ret = wldev_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request), true); + if (ret < 0) { + WLDEV_ERROR(("can't start auto channel scan:%d\n", ret)); + goto fail; + } + + while (retry++ < 15) { + + bcm_mdelay(350); + + ret = wldev_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen), false); + + if ((ret == 0) && (dtoh32(chosen) != 0)) { + *chan = (uint16)chosen & 0x00FF; /* covert chanspec --> chan number */ + printf("%s: Got channel = %d, attempt:%d\n", + __FUNCTION__, *chan, retry); + break; + } + } + + if ((ret = wldev_ioctl(dev, WLC_DOWN, &updown, sizeof(updown), true)) < 0) { + WLDEV_ERROR(("%s fail to WLC_DOWN ioctl err =%d\n", __FUNCTION__, ret)); + goto fail; + } + +fail : + if (ret < 0) { + WLDEV_ERROR(("%s: return value %d\n", __FUNCTION__, ret)); + } + return ret; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h index 46326803e216..f58660944420 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.h +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -21,33 +21,33 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 Exp $ + * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ */ #ifndef __WLDEV_COMMON_H__ #define __WLDEV_COMMON_H__ #include <wlioctl.h> -/** wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or +/* wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or * netdev_ops->ndo_do_ioctl in new kernels) * @dev: the net_device handle */ s32 wldev_ioctl( struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set); -/** Retrieve named IOVARs, this function calls wl_dev_ioctl with +/** Retrieve named IOVARs, this function calls wl_dev_ioctl with * WLC_GET_VAR IOCTL code */ s32 wldev_iovar_getbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen); + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); /** Set named IOVARs, this function calls wl_dev_ioctl with * WLC_SET_VAR IOCTL code */ s32 wldev_iovar_setbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen); + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); s32 wldev_iovar_setint( struct net_device *dev, s8 *iovar, s32 val); @@ -67,15 +67,15 @@ s32 wldev_mkiovar_bsscfg( * WLC_GET_VAR IOCTL code */ s32 wldev_iovar_getbuf_bsscfg( - struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx); + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); /** Set named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with * WLC_SET_VAR IOCTL code */ s32 wldev_iovar_setbuf_bsscfg( - struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx); + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); s32 wldev_iovar_getint_bsscfg( struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx); @@ -92,9 +92,9 @@ extern int net_os_wake_lock_timeout(struct net_device *dev); extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); extern int net_os_set_dtim_skip(struct net_device *dev, int val); extern int net_os_set_suspend_disable(struct net_device *dev, int val); -extern int net_os_set_suspend(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, - int max, int *bytes_left); + int max, int *bytes_left); /* Get the link speed from dongle, speed is in kpbs */ int wldev_get_link_speed(struct net_device *dev, int *plink_speed); @@ -107,4 +107,6 @@ int wldev_get_band(struct net_device *dev, uint *pband); int wldev_set_band(struct net_device *dev, uint band); +int wldev_get_auto_channel(struct net_device *dev, int *chan); + #endif /* __WLDEV_COMMON_H__ */ diff --git a/drivers/net/wireless/sd8797/Makefile b/drivers/net/wireless/sd8797/Makefile index d5eb7872a232..3ef70380dd82 100644 --- a/drivers/net/wireless/sd8797/Makefile +++ b/drivers/net/wireless/sd8797/Makefile @@ -52,6 +52,7 @@ EXTRA_CFLAGS += -DPROC_DEBUG EXTRA_CFLAGS += -DSDIO_MULTI_PORT_TX_AGGR EXTRA_CFLAGS += -DSDIO_MULTI_PORT_RX_AGGR EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME +EXTRA_CFLAGS += -DMMC_PM_KEEP_POWER EXTRA_CFLAGS += -DDFS_TESTING_SUPPORT EXTRA_CFLAGS += -DMFG_CMD_SUPPORT diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11d.c b/drivers/net/wireless/sd8797/mlan/mlan_11d.c index 1727c5890fc8..fdc0d993e381 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_11d.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_11d.c @@ -2,7 +2,7 @@ * * @brief This file contains functions for 802.11D. * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -55,6 +55,7 @@ static region_code_mapping_t region_code_mapping[] = { {"JP ", 0x40}, /* Japan */ {"JP ", 0x41}, /* Japan */ {"CN ", 0x50}, /* China */ + {"JP ", 0xFE}, /* Japan */ {"JP ", 0xFF}, /* Japan special */ }; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11h.c b/drivers/net/wireless/sd8797/mlan/mlan_11h.c index 7e08f4cd5d8a..3a38979a8dd2 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_11h.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_11h.c @@ -2,7 +2,7 @@ * * @brief This file contains functions for 802.11H. * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -298,6 +298,11 @@ wlan_11h_set_supp_channels_ie(mlan_private * priv, sizeof(IEEEtypes_SupportedChannels_t)); cfp_bg = cfp_a = priv->adapter->region_code; + if (!priv->adapter->region_code) { + /* Invalid region code, use CFP code */ + cfp_bg = priv->adapter->cfp_code_bg; + cfp_a = priv->adapter->cfp_code_a; + } if ((band & BAND_B) || (band & BAND_G)) { /* @@ -355,6 +360,23 @@ wlan_11h_set_supp_channels_ie(mlan_private * priv, psup_chan->subband[num_subbands++] = wlan_11h_unii_middle_band; psup_chan->subband[num_subbands++] = wlan_11h_unii_mid_upper_band; break; + case 0x1: /* Low band (5150-5250 MHz) channels */ + psup_chan->subband[num_subbands++] = wlan_11h_unii_lower_band; + break; + case 0x2: /* Lower middle band (5250-5350 MHz) channels */ + psup_chan->subband[num_subbands++] = wlan_11h_unii_middle_band; + break; + case 0x3: /* Upper middle band (5470-5725 MHz) channels */ + psup_chan->subband[num_subbands++] = wlan_11h_unii_mid_upper_band; + break; + case 0x4: /* High band (5725-5850 MHz) channels */ + psup_chan->subband[num_subbands++] = wlan_11h_unii_upper_band; + break; + case 0x5: /* Low band (5150-5250 MHz) and High band + (5725-5850 MHz) channels */ + psup_chan->subband[num_subbands++] = wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = wlan_11h_unii_upper_band; + break; } } @@ -1799,9 +1821,15 @@ wlan_11h_radar_detect_required(mlan_private * priv, t_u8 channel) required = wlan_get_cfp_radar_detect(priv, channel); - PRINTM(MINFO, "11h: Radar detection in region %#02x " - "is %srequired for channel %d\n", - priv->adapter->region_code, (required ? "" : "not "), channel); + if (!priv->adapter->region_code) + PRINTM(MINFO, "11h: Radar detection in CFP code[BG:%#x, A:%#x] " + "is %srequired for channel %d\n", + priv->adapter->cfp_code_bg, priv->adapter->cfp_code_a, + (required ? "" : "not "), channel); + else + PRINTM(MINFO, "11h: Radar detection in region %#02x " + "is %srequired for channel %d\n", + priv->adapter->region_code, (required ? "" : "not "), channel); if (required == MTRUE && priv->media_connected == MTRUE && priv->curr_bss_params.bss_descriptor.channel == channel) { @@ -3153,13 +3181,13 @@ wlan_11h_switch_non_dfs_chan(mlan_private * priv, t_u8 * chan) def_chan = (t_u8) chn_tbl->pcfp[rand_entry].channel; rand_tries++; } while ((wlan_11h_is_channel_under_nop(pmadapter, def_chan) || - chn_tbl->pcfp[rand_entry].radar_detect == MTRUE) && - (rand_tries < MAX_SWITCH_CHANNEL_RETRIES)); + chn_tbl->pcfp[rand_entry].passive_scan_or_radar_detect == MTRUE) + && (rand_tries < MAX_SWITCH_CHANNEL_RETRIES)); /* meet max retries, use the lowest non-dfs channel */ if (rand_tries == MAX_SWITCH_CHANNEL_RETRIES) { for (i = 0; i < chn_tbl->num_cfp; i++) { - if (chn_tbl->pcfp[i].radar_detect == MFALSE && + if (chn_tbl->pcfp[i].passive_scan_or_radar_detect == MFALSE && !wlan_11h_is_channel_under_nop(pmadapter, (t_u8) chn_tbl->pcfp[i]. channel)) { diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n.c b/drivers/net/wireless/sd8797/mlan/mlan_11n.c index 48b51a5bf7e0..5e78748e4a3b 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_11n.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n.c @@ -1335,6 +1335,7 @@ wlan_ret_tx_bf_cfg(IN pmlan_private pmpriv, } #ifdef STA_SUPPORT + /** * @brief This function append the 802_11N tlv * @@ -1426,12 +1427,12 @@ wlan_cmd_append_11n_tlv(IN mlan_private * pmpriv, pbss_desc->pht_info->ht_info.pri_chan; pchan_list->chan_scan_param[0].radio_type = wlan_band_to_radio_type((t_u8) pbss_desc->bss_band); - if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && - ISALLOWED_CHANWIDTH40(pbss_desc->pht_info->ht_info.field2)) + ISALLOWED_CHANWIDTH40(pbss_desc->pht_info->ht_info.field2)) { SET_SECONDARYCHAN(pchan_list->chan_scan_param[0].radio_type, GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info. field2)); + } HEXDUMP("ChanList", (t_u8 *) pchan_list, sizeof(MrvlIEtypes_ChanListParamSet_t)); @@ -1467,15 +1468,13 @@ wlan_cmd_append_11n_tlv(IN mlan_private * pmpriv, memcpy(pmadapter, (t_u8 *) pext_cap + sizeof(MrvlIEtypesHeader_t), (t_u8 *) pbss_desc->pext_cap + sizeof(IEEEtypes_Header_t), - pext_cap->header.len); - + pbss_desc->pext_cap->ieee_hdr.len); HEXDUMP("Extended Capabilities IE", (t_u8 *) pext_cap, sizeof(MrvlIETypes_ExtCap_t)); *ppbuffer += sizeof(MrvlIETypes_ExtCap_t); ret_len += sizeof(MrvlIETypes_ExtCap_t); pext_cap->header.len = wlan_cpu_to_le16(pext_cap->header.len); } - LEAVE(); return ret_len; } diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c b/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c index 6af4b6a698e8..6125262d5b7a 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c @@ -333,7 +333,9 @@ wlan_11n_aggregate_pkt(mlan_private * priv, raListTbl * pra_list, t_u8 *data; int pad = 0; mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 t_u32 sec, usec; +#endif mlan_tx_param tx_param; #ifdef STA_SUPPORT TxPD *ptx_pd = MNULL; @@ -497,8 +499,7 @@ wlan_11n_aggregate_pkt(mlan_private * priv, raListTbl * pra_list, pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); } - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); PRINTM_NETINTF(MDATA, priv); PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c b/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c index 870081799c07..af689b7bab49 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c @@ -4,7 +4,7 @@ * driver. * * Copyright (C) 2008-2011, Marvell International Ltd. - * + * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in @@ -639,6 +639,9 @@ wlan_cmd_11n_addba_rspgen(mlan_private * priv, BLOCKACKPARAM_WINSIZE_POS); win_size = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_WINSIZE_MASK) >> BLOCKACKPARAM_WINSIZE_POS; + if (win_size == 0) + padd_ba_rsp->status_code = wlan_cpu_to_le16(ADDBA_RSP_STATUS_DECLINED); + padd_ba_rsp->block_ack_param_set = wlan_cpu_to_le16(padd_ba_rsp->block_ack_param_set); diff --git a/drivers/net/wireless/sd8797/mlan/mlan_cfp.c b/drivers/net/wireless/sd8797/mlan/mlan_cfp.c index 23e849f04b63..ccbb5aa1f666 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_cfp.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_cfp.c @@ -4,7 +4,7 @@ * @brief This file contains WLAN client mode channel, frequency and power * related code * - * Copyright (C) 2009-2011, Marvell International Ltd. + * Copyright (C) 2009-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -52,6 +52,30 @@ Change Log: /** 2000mW */ #define WLAN_TX_PWR_CN_2000MW 33 +/** Region code mapping */ +typedef struct _country_code_mapping +{ + /** Region */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Code for B/G CFP table */ + t_u8 cfp_code_bg; + /** Code for A CFP table */ + t_u8 cfp_code_a; +} country_code_mapping_t; + +/** Region code mapping table */ +static country_code_mapping_t country_code_mapping[] = { + {"US", 0x10, 0x10}, /* US FCC */ + {"CA", 0x10, 0x20}, /* IC Canada */ + {"SG", 0x10, 0x10}, /* Singapore */ + {"EU", 0x30, 0x30}, /* ETSI */ + {"AU", 0x30, 0x30}, /* Australia */ + {"KR", 0x30, 0x30}, /* Republic Of Korea */ + {"FR", 0x32, 0x32}, /* France */ + {"JP", 0xFF, 0x40}, /* Japan */ + {"CN", 0x30, 0x50}, /* China */ +}; + /** * The structure for Channel-Frequency-Power table */ @@ -68,91 +92,108 @@ typedef struct _cfp_table /* Format { Channel, Frequency (MHz), MaxTxPower } */ /** Band: 'B/G', Region: USA FCC/Canada IC */ static chan_freq_power_t channel_freq_power_US_BG[] = { - {1, 2412, WLAN_TX_PWR_US_DEFAULT}, - {2, 2417, WLAN_TX_PWR_US_DEFAULT}, - {3, 2422, WLAN_TX_PWR_US_DEFAULT}, - {4, 2427, WLAN_TX_PWR_US_DEFAULT}, - {5, 2432, WLAN_TX_PWR_US_DEFAULT}, - {6, 2437, WLAN_TX_PWR_US_DEFAULT}, - {7, 2442, WLAN_TX_PWR_US_DEFAULT}, - {8, 2447, WLAN_TX_PWR_US_DEFAULT}, - {9, 2452, WLAN_TX_PWR_US_DEFAULT}, - {10, 2457, WLAN_TX_PWR_US_DEFAULT}, - {11, 2462, WLAN_TX_PWR_US_DEFAULT} + {1, 2412, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT, MFALSE} }; /** Band: 'B/G', Region: Europe ETSI/China */ static chan_freq_power_t channel_freq_power_EU_BG[] = { - {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, - {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, - {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, - {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, - {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, - {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, - {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, - {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, - {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, - {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, - {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, - {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, - {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE} }; /** Band: 'B/G', Region: France */ static chan_freq_power_t channel_freq_power_FR_BG[] = { - {1, 2412, WLAN_TX_PWR_FR_100MW}, - {2, 2417, WLAN_TX_PWR_FR_100MW}, - {3, 2422, WLAN_TX_PWR_FR_100MW}, - {4, 2427, WLAN_TX_PWR_FR_100MW}, - {5, 2432, WLAN_TX_PWR_FR_100MW}, - {6, 2437, WLAN_TX_PWR_FR_100MW}, - {7, 2442, WLAN_TX_PWR_FR_100MW}, - {8, 2447, WLAN_TX_PWR_FR_100MW}, - {9, 2452, WLAN_TX_PWR_FR_100MW}, - {10, 2457, WLAN_TX_PWR_FR_10MW}, - {11, 2462, WLAN_TX_PWR_FR_10MW}, - {12, 2467, WLAN_TX_PWR_FR_10MW}, - {13, 2472, WLAN_TX_PWR_FR_10MW} + {1, 2412, WLAN_TX_PWR_FR_100MW, MFALSE}, + {2, 2417, WLAN_TX_PWR_FR_100MW, MFALSE}, + {3, 2422, WLAN_TX_PWR_FR_100MW, MFALSE}, + {4, 2427, WLAN_TX_PWR_FR_100MW, MFALSE}, + {5, 2432, WLAN_TX_PWR_FR_100MW, MFALSE}, + {6, 2437, WLAN_TX_PWR_FR_100MW, MFALSE}, + {7, 2442, WLAN_TX_PWR_FR_100MW, MFALSE}, + {8, 2447, WLAN_TX_PWR_FR_100MW, MFALSE}, + {9, 2452, WLAN_TX_PWR_FR_100MW, MFALSE}, + {10, 2457, WLAN_TX_PWR_FR_10MW, MFALSE}, + {11, 2462, WLAN_TX_PWR_FR_10MW, MFALSE}, + {12, 2467, WLAN_TX_PWR_FR_10MW, MFALSE}, + {13, 2472, WLAN_TX_PWR_FR_10MW, MFALSE} }; /** Band: 'B/G', Region: Japan */ static chan_freq_power_t channel_freq_power_JPN41_BG[] = { - {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT}, - {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT}, - {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT}, - {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT}, - {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT}, - {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT}, - {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT}, - {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT}, - {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT}, - {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT}, - {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT}, - {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT}, - {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT} + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE} }; /** Band: 'B/G', Region: Japan */ static chan_freq_power_t channel_freq_power_JPN40_BG[] = { - {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT} + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE} +}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPNFE_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE} }; /** Band : 'B/G', Region: Special */ static chan_freq_power_t channel_freq_power_SPECIAL_BG[] = { - {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT}, - {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT}, - {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT}, - {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT}, - {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT}, - {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT}, - {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT}, - {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT}, - {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT}, - {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT}, - {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT}, - {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT}, - {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT}, - {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT} + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE} }; /** @@ -194,6 +235,12 @@ static cfp_table_t cfp_table_BG[] = { sizeof(channel_freq_power_EU_BG) / sizeof(chan_freq_power_t), } , + { + 0xfe, /* JAPAN */ + channel_freq_power_JPNFE_BG, + sizeof(channel_freq_power_JPNFE_BG) / sizeof(chan_freq_power_t), + } + , {0xff, /* Special */ channel_freq_power_SPECIAL_BG, sizeof(channel_freq_power_SPECIAL_BG) / sizeof(chan_freq_power_t), @@ -317,6 +364,64 @@ static chan_freq_power_t channel_freq_power_CN_A[] = { {165, 5825, WLAN_TX_PWR_CN_2000MW, MFALSE} }; +/** Band: 'A', NULL */ +static chan_freq_power_t channel_freq_power_NULL_A[] = { +}; + +/** Band: 'A', Code: 1, Low band (5150-5250 MHz) channels */ +static chan_freq_power_t channel_freq_power_low_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, +}; + +/** Band: 'A', Code: 2, Lower middle band (5250-5350 MHz) channels */ +static chan_freq_power_t channel_freq_power_lower_middle_band[] = { + {52, 5260, WLAN_TX_PWR_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MTRUE}, +}; + +/** Band: 'A', Code: 3, Upper middle band (5470-5725 MHz) channels */ +static chan_freq_power_t channel_freq_power_upper_middle_band[] = { + {100, 5500, WLAN_TX_PWR_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_DEFAULT, MTRUE}, +}; + +/** Band: 'A', Code: 4, High band (5725-5850 MHz) channels */ +static chan_freq_power_t channel_freq_power_high_band[] = { + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE} +}; + +/** Band: 'A', Code: 5, Low band (5150-5250 MHz) and + * High band (5725-5850 MHz) channels */ +static chan_freq_power_t channel_freq_power_low_high_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE} +}; + /** * The 5GHz CFP tables */ @@ -356,11 +461,42 @@ static cfp_table_t cfp_table_A[] = { sizeof(channel_freq_power_CN_A) / sizeof(chan_freq_power_t), } , + {0xfe, /* JAPAN */ + channel_freq_power_NULL_A, + sizeof(channel_freq_power_NULL_A) / sizeof(chan_freq_power_t), + } + , {0xff, /* Special */ channel_freq_power_JPN_A, sizeof(channel_freq_power_JPN_A) / sizeof(chan_freq_power_t), } , + {0x1, /* Low band (5150-5250 MHz) channels */ + channel_freq_power_low_band, + sizeof(channel_freq_power_low_band) / sizeof(chan_freq_power_t) + } + , + {0x2, /* Lower middle band (5250-5350 MHz) channels */ + channel_freq_power_lower_middle_band, + sizeof(channel_freq_power_lower_middle_band) / sizeof(chan_freq_power_t) + } + , + {0x3, /* Upper middle band (5470-5725 MHz) channels */ + channel_freq_power_upper_middle_band, + sizeof(channel_freq_power_upper_middle_band) / sizeof(chan_freq_power_t) + } + , + {0x4, /* High band (5725-5850 MHz) channels */ + channel_freq_power_high_band, + sizeof(channel_freq_power_high_band) / sizeof(chan_freq_power_t) + } + , + {0x5, /* Low band (5150-5250 MHz) and High band + (5725-5850 MHz) channels */ + channel_freq_power_low_high_band, + sizeof(channel_freq_power_low_high_band) / sizeof(chan_freq_power_t) + } + , /* Add new region here */ }; @@ -374,7 +510,13 @@ static cfp_table_t cfp_table_A[] = { * The table to keep region code */ t_u16 region_code_index[MRVDRV_MAX_REGION_CODE] = - { 0x10, 0x20, 0x30, 0x32, 0x40, 0x41, 0x50, 0xff }; + { 0x10, 0x20, 0x30, 0x32, 0x40, 0x41, 0x50, 0xfe, 0xff }; + +/** The table to keep CFP code for BG */ +t_u16 cfp_code_index_bg[MRVDRV_MAX_CFP_CODE_BG] = { }; + +/** The table to keep CFP code for A */ +t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A] = { 0x1, 0x2, 0x3, 0x4, 0x5 }; /** * The rates supported for ad-hoc B mode @@ -494,6 +636,11 @@ wlan_get_region_cfp_table(pmlan_adapter pmadapter, t_u8 region, t_u8 band, ENTER(); cfp_bg = cfp_a = region; + if (!region) { + /* Invalid region code, use CFP code */ + cfp_bg = pmadapter->cfp_code_bg; + cfp_a = pmadapter->cfp_code_a; + } if (band & (BAND_B | BAND_G | BAND_GN)) { for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) { @@ -521,7 +668,11 @@ wlan_get_region_cfp_table(pmlan_adapter pmadapter, t_u8 region, t_u8 band, } } - PRINTM(MERROR, "Error Band[0x%x] or region[%#x]\n", band, region); + if (!region) + PRINTM(MERROR, "Error Band[0x%x] or code[BG:%#x, A:%#x]\n", + band, cfp_bg, cfp_a); + else + PRINTM(MERROR, "Error Band[0x%x] or region[%#x]\n", band, region); LEAVE(); return MNULL; @@ -530,6 +681,38 @@ wlan_get_region_cfp_table(pmlan_adapter pmadapter, t_u8 region, t_u8 band, /******************************************************** Global Functions ********************************************************/ +/** + * @brief This function converts region string to integer code + * + * @param pmadapter A pointer to mlan_adapter structure + * @param country_code Country string + * @param cfp_bg Pointer to buffer + * @param cfp_a Pointer to buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_country_2_cfp_table_code(pmlan_adapter pmadapter, t_u8 * country_code, + t_u8 * cfp_bg, t_u8 * cfp_a) +{ + t_u8 i; + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < NELEMENTS(country_code_mapping); i++) { + if (!memcmp(pmadapter, country_code_mapping[i].country_code, + country_code, COUNTRY_CODE_LEN - 1)) { + *cfp_bg = country_code_mapping[i].cfp_code_bg; + *cfp_a = country_code_mapping[i].cfp_code_a; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + LEAVE(); + return MLAN_STATUS_FAILURE; +} #ifdef STA_SUPPORT #endif /* STA_SUPPORT */ @@ -1137,7 +1320,7 @@ wlan_get_cfp_radar_detect(mlan_private * priv, t_u8 chnl) /* get the radar detection requirements according to chan num */ for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { if (pcfp[j].channel == chnl) { - required = pcfp[j].radar_detect; + required = pcfp[j].passive_scan_or_radar_detect; break; } } @@ -1146,3 +1329,50 @@ wlan_get_cfp_radar_detect(mlan_private * priv, t_u8 chnl) LEAVE(); return required; } + +/** + * @brief Get if scan type is passive or not on a certain channel for b/g band + * + * @param priv Private driver information structure + * @param chnl Channel to determine scan type + * + * @return + * - MTRUE if scan type is passive + * - MFALSE otherwise + */ + +t_bool +wlan_bg_scan_type_is_passive(mlan_private * priv, t_u8 chnl) +{ + int i, j; + t_bool passive = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /* get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & (BAND_B | BAND_G)) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (!pcfp) { + /* This means operation in BAND-B or BAND_G is not support, we can + just return false here */ + goto done; + } + + /* get the bg scan type according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chnl) { + passive = pcfp[j].passive_scan_or_radar_detect; + break; + } + } + + done: + LEAVE(); + return passive; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c b/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c index f3db9e662565..a3e5ef0517f0 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c @@ -344,7 +344,9 @@ wlan_dnld_cmd_to_fw(IN mlan_private * pmpriv, IN cmd_ctrl_node * pcmd_node) mlan_ioctl_req *pioctl_buf = MNULL; t_u16 cmd_code; t_u16 cmd_size; +#ifdef DEBUG_LEVEL1 t_u32 sec, usec; +#endif ENTER(); @@ -389,8 +391,7 @@ wlan_dnld_cmd_to_fw(IN mlan_private * pmpriv, IN cmd_ctrl_node * pcmd_node) pcmd_node->cmdbuf->data_len = cmd_size; - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); PRINTM_NETINTF(MCMND, pmpriv); PRINTM(MCMND, "DNLD_CMD (%lu.%06lu): 0x%x, act 0x%x, len %d, seqno 0x%x\n", sec, usec, cmd_code, @@ -690,8 +691,9 @@ wlan_process_event(pmlan_adapter pmadapter) pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; t_u32 eventcause = pmadapter->event_cause; - t_u32 in_ts_sec; - t_u32 in_ts_usec; +#ifdef DEBUG_LEVEL1 + t_u32 in_ts_sec, in_ts_usec; +#endif ENTER(); /* Save the last event to debug log */ @@ -726,8 +728,7 @@ wlan_process_event(pmlan_adapter pmadapter) if (MTRUE && (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) ) { - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, - &in_ts_sec, &in_ts_usec); + PRINTM_GET_SYS_TIME(MEVENT, &in_ts_sec, &in_ts_usec); PRINTM_NETINTF(MEVENT, priv); PRINTM(MEVENT, "%lu.%06lu : Event: 0x%x\n", in_ts_sec, in_ts_usec, eventcause); @@ -1109,7 +1110,10 @@ wlan_process_cmdresp(mlan_adapter * pmadapter) t_u16 cmdresp_result; mlan_ioctl_req *pioctl_buf = MNULL; mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; - t_u32 sec, usec, i; +#ifdef DEBUG_LEVEL1 + t_u32 sec, usec; +#endif + t_u32 i; ENTER(); @@ -1189,8 +1193,7 @@ wlan_process_cmdresp(mlan_adapter * pmadapter) pmadapter->dbg.last_cmd_resp_id[pmadapter->dbg.last_cmd_resp_index] = orig_cmdresp_no; - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); PRINTM_NETINTF(MCMND, pmadapter->curr_cmd->priv); PRINTM(MCMND, "CMD_RESP (%lu.%06lu): 0x%x, result %d, len %d, seqno 0x%x\n", sec, usec, orig_cmdresp_no, cmdresp_result, resp->size, @@ -1301,7 +1304,9 @@ wlan_cmd_timeout_func(t_void * function_context) mlan_adapter *pmadapter = (mlan_adapter *) function_context; cmd_ctrl_node *pcmd_node = MNULL; mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef DEBUG_LEVEL1 t_u32 sec, usec; +#endif t_u8 i; mlan_private *pmpriv = MNULL; @@ -1325,8 +1330,7 @@ wlan_cmd_timeout_func(t_void * function_context) pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index]; pmadapter->dbg.timeout_cmd_act = pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index]; - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MERROR, &sec, &usec); PRINTM(MERROR, "Timeout cmd id (%lu.%06lu) = 0x%x, act = 0x%x \n", sec, usec, pmadapter->dbg.timeout_cmd_id, pmadapter->dbg.timeout_cmd_act); @@ -2719,6 +2723,9 @@ wlan_ret_get_hw_spec(IN pmlan_private pmpriv, PRINTM(MWARN, "unidentified region code, use the default (0x%02x)\n", MRVDRV_DEFAULT_REGION_CODE); } + /* Synchronize CFP code with region code */ + pmadapter->cfp_code_bg = pmadapter->region_code; + pmadapter->cfp_code_a = pmadapter->region_code; if (wlan_set_regiontable(pmpriv, (t_u8) pmadapter->region_code, pmadapter->fw_bands)) { diff --git a/drivers/net/wireless/sd8797/mlan/mlan_decl.h b/drivers/net/wireless/sd8797/mlan/mlan_decl.h index d0ca7acae937..41cf2bf5f2f1 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_decl.h +++ b/drivers/net/wireless/sd8797/mlan/mlan_decl.h @@ -27,7 +27,7 @@ Change log: #define _MLAN_DECL_H_ /** MLAN release version */ -#define MLAN_RELEASE_VERSION "303" +#define MLAN_RELEASE_VERSION "311" /** Re-define generic data types for MLAN/MOAL */ /** Signed char (1-byte) */ @@ -136,10 +136,18 @@ typedef t_s32 t_sval; /** This is current limit on Maximum Rx AMPDU allowed */ #define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 +#ifdef STA_SUPPORT /** Default Win size attached during ADDBA request */ -#define MLAN_AMPDU_DEF_TXWINSIZE 32 +#define MLAN_STA_AMPDU_DEF_TXWINSIZE 16 /** Default Win size attached during ADDBA response */ -#define MLAN_AMPDU_DEF_RXWINSIZE 16 +#define MLAN_STA_AMPDU_DEF_RXWINSIZE 32 +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_UAP_AMPDU_DEF_TXWINSIZE 32 +/** Default Win size attached during ADDBA response */ +#define MLAN_UAP_AMPDU_DEF_RXWINSIZE 16 +#endif /* UAP_SUPPORT */ /** Block ack timeout value */ #define MLAN_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff /** Maximum Tx Win size configured for ADDBA request [10 bits] */ @@ -626,6 +634,8 @@ typedef MLAN_PACK_START struct _custom_ie t_u8 ie_buffer[MAX_IE_SIZE]; } MLAN_PACK_END custom_ie; +/** Max IE index to FW */ +#define MAX_MGMT_IE_INDEX_TO_FW 4 /** Max IE index per BSS */ #define MAX_MGMT_IE_INDEX 16 @@ -659,7 +669,7 @@ typedef MLAN_PACK_START struct _tlvbuf_custom_ie /** Length */ t_u16 len; /** IE data */ - custom_ie ie_data_list[MAX_MGMT_IE_INDEX]; + custom_ie ie_data_list[MAX_MGMT_IE_INDEX_TO_FW]; /** Max mgmt IE TLV */ tlvbuf_max_mgmt_ie max_mgmt_ie; } MLAN_PACK_END mlan_ds_misc_custom_ie; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_fw.h b/drivers/net/wireless/sd8797/mlan/mlan_fw.h index 1b0af8d8c0ea..fda2e7285dfd 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_fw.h +++ b/drivers/net/wireless/sd8797/mlan/mlan_fw.h @@ -148,6 +148,11 @@ typedef enum _KEY_TYPE_ID KEY_TYPE_ID_WAPI, } KEY_TYPE_ID; +/** Key Info flag for multicast key */ +#define KEY_INFO_MCAST_KEY 0x01 +/** Key Info flag for unicast key */ +#define KEY_INFO_UCAST_KEY 0x02 + /** KEY_INFO_WEP*/ typedef enum _KEY_INFO_WEP { @@ -311,9 +316,10 @@ typedef enum _WLAN_802_11_WEP_STATUS #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 0x16) // 0x0116 /** TLV type : Beacon SNR high */ #define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 0x17) // 0x0117 - /** TLV type : Start BG scan later */ #define TLV_TYPE_STARTBGSCANLATER (PROPRIETARY_TLV_BASE_ID + 0x1e) // 0x011e +/** TLV type: BG scan repeat count */ +#define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 0xb0) // 0x01b0 /** TLV type : Authentication type */ #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 0x1f) // 0x011f /** TLV type : BSSID */ @@ -397,7 +403,7 @@ typedef enum _WLAN_802_11_WEP_STATUS #define BA_STREAM_NOT_ALLOWED 0xff /** Test if 11n is enabled by checking the HTCap IE */ -#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN ||priv->adapter->config_bands & BAND_AN) \ +#define IS_11N_ENABLED(priv) ((priv->config_bands & BAND_GN ||priv->config_bands & BAND_AN) \ && priv->curr_bss_params.bss_descriptor.pht_cap) /** Find out if we are the initiator or not */ #define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) & \ @@ -848,6 +854,9 @@ typedef enum _WLAN_802_11_WEP_STATUS #define HostCmd_CMD_802_11_REMAIN_ON_CHANNEL 0x010d #endif +/** Host Command ID : OTP user data */ +#define HostCmd_CMD_OTP_READ_USER_DATA 0x0114 + /** Enhanced PS modes */ typedef enum _ENH_PS_MODES { @@ -2806,6 +2815,25 @@ typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_QUERY_RSP HostCmd_DS_802_11_SCAN_RSP scan_resp; } MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_QUERY_RSP; +/** MrvlIEtypes_StartLater_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_StartLater_t +{ + /** Header */ + MrvlIEtypesHeader_t header; + /* 0 - BGScan start immediately, 1 - BGScan will start later after "Scan + Interval" */ + t_u16 value; +} MLAN_PACK_END MrvlIEtypes_StartLater_t; + +/** MrvlIEtypes_RepeatCount_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RepeatCount_t +{ + /** Header */ + MrvlIEtypesHeader_t header; + /* Repeat count */ + t_u16 repeat_count; +} MLAN_PACK_END MrvlIEtypes_RepeatCount_t; + /** MrvlIEtypes_DomainParamSet_t */ typedef MLAN_PACK_START struct _MrvlIEtypes_DomainParamSet { @@ -3540,6 +3568,19 @@ typedef MLAN_PACK_START struct _HostCmd_DS_SUBSCRIBE_EVENT t_u16 event_bitmap; } MLAN_PACK_END HostCmd_DS_SUBSCRIBE_EVENT; +/** HostCmd_DS_OTP_USER_DATA */ +typedef MLAN_PACK_START struct _HostCmd_DS_OTP_USER_DATA +{ + /** Action */ + t_u16 action; + /** Reserved field */ + t_u16 reserved; + /** User data length */ + t_u16 user_data_length; + /** User data */ + t_u8 user_data[1]; +} MLAN_PACK_END HostCmd_DS_OTP_USER_DATA; + /** HostCmd_DS_INACTIVITY_TIMEOUT_EXT */ typedef MLAN_PACK_START struct _HostCmd_DS_INACTIVITY_TIMEOUT_EXT { @@ -4495,6 +4536,7 @@ typedef struct MLAN_PACK_START _HostCmd_DS_COMMAND HostCmd_DS_802_11_BG_SCAN_QUERY bg_scan_query; HostCmd_DS_802_11_BG_SCAN_QUERY_RSP bg_scan_query_resp; HostCmd_DS_SUBSCRIBE_EVENT subscribe_event; + HostCmd_DS_OTP_USER_DATA otp_user_data; /** Associate */ HostCmd_DS_802_11_ASSOCIATE associate; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_ieee.h b/drivers/net/wireless/sd8797/mlan/mlan_ieee.h index 9473c498b973..2431e06f48fa 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_ieee.h +++ b/drivers/net/wireless/sd8797/mlan/mlan_ieee.h @@ -27,6 +27,8 @@ Change log: #ifndef _MLAN_IEEE_H_ #define _MLAN_IEEE_H_ +/** FIX IES size in beacon buffer */ +#define WLAN_802_11_FIXED_IE_SIZE 12 /** WLAN supported rates */ #define WLAN_SUPPORTED_RATES 14 @@ -1179,6 +1181,8 @@ typedef MLAN_PACK_START struct t_u8 rssi_threshold; /** SNR threshold */ t_u8 snr_threshold; + /** repeat count */ + t_u16 repeat_count; /** SSID filter list used in the to limit the scan results */ wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; /** Variable number (fixed maximum) of channels to scan up */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_init.c b/drivers/net/wireless/sd8797/mlan/mlan_init.c index 563f74cd3980..48681b3e3ed9 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_init.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_init.c @@ -312,10 +312,17 @@ wlan_init_priv(pmlan_private priv) priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; priv->bcn_nf_last = 0; + + priv->sec_info.ewpa_enabled = MFALSE; + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; memset(pmadapter, &priv->wpa_ie, 0, sizeof(priv->wpa_ie)); memset(pmadapter, &priv->aes_key, 0, sizeof(priv->aes_key)); priv->wpa_ie_len = 0; priv->wpa_is_gtk_set = MFALSE; + priv->sec_info.wapi_enabled = MFALSE; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = MFALSE; memset(pmadapter, &priv->wps, 0, sizeof(priv->wps)); memset(pmadapter, &priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); @@ -362,7 +369,6 @@ wlan_init_priv(pmlan_private priv) t_void wlan_init_adapter(pmlan_adapter pmadapter) { - int i; opt_sleep_confirm_buffer *sleep_cfm_buf = MNULL; ENTER(); @@ -391,9 +397,6 @@ wlan_init_adapter(pmlan_adapter pmadapter) pmadapter->mp_wr_bitmap = 0; pmadapter->curr_rd_port = 1; pmadapter->curr_wr_port = 1; - for (i = 0; i < MAX_NUM_TID; i++) { - pmadapter->tx_eligibility[i] = 1; - } pmadapter->mp_data_port_mask = DATA_PORT_MASK; #ifdef SDIO_MULTI_PORT_TX_AGGR @@ -559,6 +562,8 @@ wlan_init_adapter(pmlan_adapter pmadapter) memset(pmadapter, &pmadapter->region_channel, 0, sizeof(pmadapter->region_channel)); pmadapter->region_code = 0; + memcpy(pmadapter, pmadapter->country_code, MRVDRV_DEFAULT_COUNTRY_CODE, + COUNTRY_CODE_LEN); pmadapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; pmadapter->adhoc_awake_period = 0; #ifdef STA_SUPPORT diff --git a/drivers/net/wireless/sd8797/mlan/mlan_ioctl.h b/drivers/net/wireless/sd8797/mlan/mlan_ioctl.h index 15e88e631067..964de9197ffe 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_ioctl.h +++ b/drivers/net/wireless/sd8797/mlan/mlan_ioctl.h @@ -82,6 +82,7 @@ enum _mlan_ioctl_req_id MLAN_OID_SNMP_MIB_DOT11D, MLAN_OID_SNMP_MIB_DOT11H, #endif + MLAN_OID_SNMP_MIB_DTIM_PERIOD, /* Status Information Group */ MLAN_IOCTL_GET_INFO = 0x00050000, @@ -204,12 +205,15 @@ enum _mlan_ioctl_req_id MLAN_OID_MISC_IP_ADDR, MLAN_OID_MISC_MAC_CONTROL, MLAN_OID_MISC_MEF_CFG, + MLAN_OID_MISC_CFP_CODE, + MLAN_OID_MISC_COUNTRY_CODE, MLAN_OID_MISC_THERMAL, MLAN_OID_MISC_RX_MGMT_IND, MLAN_OID_MISC_SUBSCRIBE_EVENT, #ifdef DEBUG_LEVEL1 MLAN_OID_MISC_DRVDBG, #endif + MLAN_OID_MISC_OTP_USER_DATA, }; /** Sub command size */ @@ -1041,6 +1045,8 @@ typedef struct _mlan_ds_snmp_mib /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ t_u32 oid_value; #endif + /** DTIM period for MLAN_OID_SNMP_MIB_DTIM_PERIOD */ + t_u32 dtim_period; } param; } mlan_ds_snmp_mib, *pmlan_ds_snmp_mib; @@ -1266,6 +1272,8 @@ typedef struct _mlan_bss_info #ifdef STA_SUPPORT /** Capability Info */ t_u16 capability_info; + /** Beacon Interval */ + t_u16 beacon_interval; /** Listen Interval */ t_u16 listen_interval; /** Association Id */ @@ -1442,7 +1450,7 @@ typedef struct _mlan_debug_info #ifdef UAP_SUPPORT /** Maximum number of clients supported by AP */ -#define MAX_NUM_CLIENTS 16 +#define MAX_NUM_CLIENTS MAX_STA_COUNT /** station info */ typedef struct _sta_info @@ -1553,7 +1561,7 @@ enum _mlan_psk_type /** key flag for rx_seq */ #define KEY_FLAG_RX_SEQ_VALID 0x00000002 /** key flag for group key */ -#define KEY_FLAG_GROUP_KEY 0x00000004 +#define KEY_FLAG_GROUP_KEY 0x00000004 /** key flag for tx and rx */ #define KEY_FLAG_SET_TX_KEY 0x00000008 /** key flag for remove key */ @@ -1600,6 +1608,24 @@ typedef struct _mlan_pmk_t t_u8 pmk[MLAN_MAX_KEY_LENGTH]; } mlan_pmk_t; +/** Embedded supplicant RSN type: No RSN */ +#define RSN_TYPE_NO_RSN MBIT(0) +/** Embedded supplicant RSN type: WPA */ +#define RSN_TYPE_WPA MBIT(3) +/** Embedded supplicant RSN type: WPA-NONE */ +#define RSN_TYPE_WPANONE MBIT(4) +/** Embedded supplicant RSN type: WPA2 */ +#define RSN_TYPE_WPA2 MBIT(5) +/** Embedded supplicant RSN type: RFU */ +#define RSN_TYPE_VALID_BITS (RSN_TYPE_NO_RSN | RSN_TYPE_WPA | RSN_TYPE_WPANONE | RSN_TYPE_WPA2) + +/** Embedded supplicant cipher type: TKIP */ +#define EMBED_CIPHER_TKIP MBIT(2) +/** Embedded supplicant cipher type: AES */ +#define EMBED_CIPHER_AES MBIT(3) +/** Embedded supplicant cipher type: RFU */ +#define EMBED_CIPHER_VALID_BITS (EMBED_CIPHER_TKIP | EMBED_CIPHER_AES) + /** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ typedef struct _mlan_ds_passphrase { @@ -2545,7 +2571,7 @@ enum _mlan_reg_type MLAN_REG_MAC = 1, MLAN_REG_BBP, MLAN_REG_RF, - MLAN_REG_CAU, + MLAN_REG_CAU = 5, }; /** Type definition of mlan_ds_reg_rw for MLAN_OID_REG_RW */ @@ -2782,6 +2808,22 @@ typedef struct _mlan_ds_misc_mef_cfg } param; } mlan_ds_misc_mef_cfg; +/** Type definition of mlan_ds_misc_cfp_code for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_misc_cfp_code +{ + /** CFP table code for 2.4GHz */ + t_u32 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u32 cfp_code_a; +} mlan_ds_misc_cfp_code; + +/** Type definition of mlan_ds_misc_country_code for MLAN_OID_MISC_COUNTRY_CODE */ +typedef struct _mlan_ds_misc_country_code +{ + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; +} mlan_ds_misc_country_code; + /** BITMAP for subscribe event rssi low */ #define SUBSCRIBE_EVT_RSSI_LOW MBIT(0) /** BITMAP for subscribe event snr low */ @@ -2806,6 +2848,8 @@ typedef struct _mlan_ds_misc_mef_cfg #define SUBSCRIBE_EVT_LINK_QUALITY MBIT(10) /** BITMAP for subscribe event pre_beacon_lost */ #define SUBSCRIBE_EVT_PRE_BEACON_LOST MBIT(11) +/** default PRE_BEACON_MISS_COUNT */ +#define DEFAULT_PRE_BEACON_MISS 30 /** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ typedef struct _mlan_ds_subscribe_evt @@ -2868,6 +2912,20 @@ typedef struct _mlan_ds_subscribe_evt t_u8 pre_beacon_miss; } mlan_ds_subscribe_evt; +/** Max OTP user data length */ +#define MAX_OTP_USER_DATA_LEN 252 + +/** Type definition of mlan_ds_misc_otp_user_data for MLAN_OID_MISC_OTP_USER_DATA */ +typedef struct _mlan_ds_misc_otp_user_data +{ + /** Reserved */ + t_u16 reserved; + /** OTP user data length */ + t_u16 user_data_length; + /** User data buffer */ + t_u8 user_data[MAX_OTP_USER_DATA_LEN]; +} mlan_ds_misc_otp_user_data; + /** Type definition of mlan_ds_misc_cfg for MLAN_IOCTL_MISC_CFG */ typedef struct _mlan_ds_misc_cfg { @@ -2902,6 +2960,10 @@ typedef struct _mlan_ds_misc_cfg t_u32 mac_ctrl; /** MEF configuration for MLAN_OID_MISC_MEF_CFG */ mlan_ds_misc_mef_cfg mef_cfg; + /** CFP code for MLAN_OID_MISC_CFP_CODE */ + mlan_ds_misc_cfp_code cfp_code; + /** Country code for MLAN_OID_MISC_COUNTRY_CODE */ + mlan_ds_misc_country_code country_code; /** Thermal reading for MLAN_OID_MISC_THERMAL */ t_u32 thermal; /** Mgmt subtype mask for MLAN_OID_MISC_RX_MGMT_IND */ @@ -2912,6 +2974,7 @@ typedef struct _mlan_ds_misc_cfg /** Driver debug bit masks */ t_u32 drvdbg; #endif + mlan_ds_misc_otp_user_data otp_user_data; } param; } mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_main.h b/drivers/net/wireless/sd8797/mlan/mlan_main.h index fb15c7226c38..44e4f8001201 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_main.h +++ b/drivers/net/wireless/sd8797/mlan/mlan_main.h @@ -4,7 +4,7 @@ * structures and declares global function prototypes used * in MLAN module. * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -31,6 +31,11 @@ Change log: #ifdef DEBUG_LEVEL1 extern t_void(*print_callback) (IN t_void * pmoal_handle, IN t_u32 level, IN t_s8 * pformat, IN ...); + +extern mlan_status(*get_sys_time_callback) (IN t_void * pmoal_handle, + OUT t_u32 * psec, + OUT t_u32 * pusec); + extern t_u32 drvdbg; #ifdef DEBUG_LEVEL2 @@ -40,10 +45,35 @@ extern t_u32 drvdbg; print_callback(MNULL, MWARN, msg);} while(0) #define PRINTM_MENTRY(msg...) do {if ((drvdbg & MENTRY) && (print_callback)) \ print_callback(MNULL, MENTRY, msg);} while(0) +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ +do { \ + if ((level & drvdbg) && (get_sys_time_callback)) \ + get_sys_time_callback(MNULL, psec, pusec); \ +} while (0) + +/** Hexdump for level-2 debugging */ +#define HEXDUMP(x,y,z) \ +do { \ + if ((drvdbg & (MHEX_DUMP | MINFO)) && (print_callback)) \ + print_callback(MNULL, MHEX_DUMP | MINFO, x, y, z); \ +} while (0) + #else + #define PRINTM_MINFO(msg...) do {} while (0) #define PRINTM_MWARN(msg...) do {} while (0) #define PRINTM_MENTRY(msg...) do {} while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ +do { \ + if ((level & drvdbg) && (get_sys_time_callback) \ + && (level != MINFO) && (level != MWARN)) \ + get_sys_time_callback(MNULL, psec, pusec); \ +} while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x,y,z) do {} while (0) + #endif /* DEBUG_LEVEL2 */ #define PRINTM_MFW_D(msg...) do {if ((drvdbg & MFW_D) && (print_callback)) \ @@ -74,20 +104,6 @@ extern t_u32 drvdbg; #define PRINTM(level,msg...) PRINTM_##level(msg) -#ifdef DEBUG_LEVEL2 - -/** Hexdump for level-2 debugging */ -#define HEXDUMP(x,y,z) \ -do { \ - if ((drvdbg & (MHEX_DUMP | MINFO)) && (print_callback)) \ - print_callback(MNULL, MHEX_DUMP | MINFO, x, y, z); \ -} while (0) -#else - -/** Hexdump for debugging */ -#define HEXDUMP(x,y,z) do {} while (0) -#endif /* DEBUG_LEVEL2 */ - /** Log debug message */ #ifdef __GNUC__ #define PRINTM_NETINTF(level, pmpriv) \ @@ -122,6 +138,8 @@ do { \ /** Hexdump for debugging */ #define HEXDUMP(x,y,z) do {} while (0) +#define PRINTM_GET_SYS_TIME(level, psec, pusec) do { } while(0) + #endif /* DEBUG_LEVEL1 */ /** Log entry point for debugging */ @@ -330,11 +348,19 @@ do { \ #define MLAN_DEFAULT_LISTEN_INTERVAL 10 /** Maximum number of region codes */ -#define MRVDRV_MAX_REGION_CODE 8 +#define MRVDRV_MAX_REGION_CODE 9 + +/** Maximum number of CFP codes for BG */ +#define MRVDRV_MAX_CFP_CODE_BG 0 +/** Maximum number of CFP codes for A */ +#define MRVDRV_MAX_CFP_CODE_A 5 /** Default region code */ #define MRVDRV_DEFAULT_REGION_CODE 0x10 +/** Default country code */ +#define MRVDRV_DEFAULT_COUNTRY_CODE "US" + /** Default factor for calculating beacon average */ #define DEFAULT_BCN_AVG_FACTOR 8 /** Default factor for calculating data average */ @@ -722,8 +748,9 @@ typedef struct _chan_freq_power_t t_u32 freq; /** Max allowed Tx power level */ t_u16 max_tx_power; - /** TRUE:radar detect required; FALSE:radar detect not required*/ - t_bool radar_detect; + /** TRUE:radar detect required for BAND A or passive scan for BAND B/G; + * FALSE:radar detect not required for BAND A or active scan for BAND B/G*/ + t_bool passive_scan_or_radar_detect; /** TRUE:channel unsupported; FALSE:supported */ t_u8 unsupported; } chan_freq_power_t; @@ -1368,7 +1395,7 @@ typedef struct _sdio_mpa_tx /** multiport tx aggregation packet count */ t_u32 pkt_cnt; /** multiport tx aggregation ports */ - t_u16 ports; + t_u32 ports; /** multiport tx aggregation starting port */ t_u16 start_port; /** multiport tx aggregation enable/disable flag */ @@ -1393,7 +1420,7 @@ typedef struct _sdio_mpa_rx /** multiport rx aggregation packet count */ t_u32 pkt_cnt; /** multiport rx aggregation ports */ - t_u16 ports; + t_u32 ports; /** multiport rx aggregation starting port */ t_u16 start_port; @@ -1511,8 +1538,6 @@ typedef struct _mlan_adapter t_u8 *mp_regs; /** allocated buf to read SDIO multiple port group registers */ t_u8 *mp_regs_buf; - /** Array to store data transfer eligibility based on tid (QoS-over-SDIO) */ - t_u8 tx_eligibility[MAX_NUM_TID]; #ifdef SDIO_MULTI_PORT_TX_AGGR /** data structure for SDIO MPA TX */ @@ -1596,6 +1621,10 @@ typedef struct _mlan_adapter t_u16 region_code; /** Region Channel data */ region_chan_t region_channel[MAX_REGION_CHANNEL_NUM]; + /** CFP table code for 2.4GHz */ + t_u8 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u8 cfp_code_a; #ifdef STA_SUPPORT /** Universal Channel data */ region_chan_t universal_channel[MAX_REGION_CHANNEL_NUM]; @@ -1604,6 +1633,8 @@ typedef struct _mlan_adapter #endif /* STA_SUPPORT */ /** 11D and Domain Regulatory Data */ wlan_802_11d_domain_reg_t domain_reg; + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; /** FSM variable for 11h support */ wlan_11h_device_state_t state_11h; /** FSM variable for DFS support */ @@ -1620,6 +1651,7 @@ typedef struct _mlan_adapter BSSDescriptor_t *pscan_table; /** scan age in secs */ t_u32 age_in_secs; + t_u8 bgscan_reported; /** Number of records in the scan table */ t_u32 num_in_scan_table; @@ -2177,6 +2209,9 @@ mlan_status wlan_cmd_bgscan_config(IN mlan_private * pmpriv, mlan_status wlan_ret_bgscan_config(IN mlan_private * pmpriv, IN HostCmd_DS_COMMAND * resp, IN mlan_ioctl_req * pioctl_buf); +mlan_status wlan_ret_802_11_bgscan_query(IN mlan_private * pmpriv, + IN HostCmd_DS_COMMAND * resp, + IN mlan_ioctl_req * pioctl_buf); /** Get Channel-Frequency-Power by band and channel */ chan_freq_power_t *wlan_get_cfp_by_band_and_channel(pmlan_adapter pmadapter, @@ -2221,10 +2256,16 @@ int wlan_get_rate_index(pmlan_adapter pmadapter, t_u16 * rateBitmap, int size); /* CFP related functions */ /** Region code index table */ extern t_u16 region_code_index[MRVDRV_MAX_REGION_CODE]; +/** The table to keep CFP code for BG */ +extern t_u16 cfp_code_index_bg[MRVDRV_MAX_CFP_CODE_BG]; +/** The table to keep CFP code for A */ +extern t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A]; /** Set region table */ mlan_status wlan_set_regiontable(mlan_private * pmpriv, t_u8 region, t_u8 band); /** Get radar detection requirements*/ t_bool wlan_get_cfp_radar_detect(mlan_private * priv, t_u8 chnl); +/** check if scan type is passive for b/g band*/ +t_bool wlan_bg_scan_type_is_passive(mlan_private * priv, t_u8 chnl); /* 802.11D related functions */ /** Initialize 11D */ @@ -2280,6 +2321,12 @@ mlan_status wlan_11d_handle_uap_domain_info(mlan_private * pmpriv, t_void * pioctl_buf); #endif +/** This function converts region string to CFP table code */ +mlan_status wlan_misc_country_2_cfp_table_code(IN pmlan_adapter pmadapter, + IN t_u8 * country_code, + OUT t_u8 * cfp_bg, + OUT t_u8 * cfp_a); + /** check if station list is empty */ t_u8 wlan_is_station_list_empty(mlan_private * priv); /** get station node */ @@ -2316,7 +2363,6 @@ wlan_is_tx_pause(mlan_private * priv, t_u8 * ra) t_void wlan_updata_ralist_tx_pause(pmlan_private priv, t_u8 * mac, t_u8 tx_pause); -sta_node *wlan_get_tx_pause_station_entry(mlan_private * priv); #ifdef UAP_SUPPORT mlan_status wlan_process_uap_rx_packet(IN mlan_private * priv, @@ -2366,6 +2412,9 @@ mlan_status wlan_set_drvdbg(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req); #endif +mlan_status wlan_misc_otp_user_data(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + /** * @brief RA based queueing * diff --git a/drivers/net/wireless/sd8797/mlan/mlan_misc.c b/drivers/net/wireless/sd8797/mlan/mlan_misc.c index 929a38ab33f6..f41dcea3d206 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_misc.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_misc.c @@ -970,7 +970,7 @@ wlan_misc_ioctl_custom_ie_list(IN pmlan_adapter pmadapter, goto done; } memset(pmadapter, ie_data, 0, - sizeof(custom_ie) * MAX_MGMT_IE_INDEX); + sizeof(custom_ie) * MAX_MGMT_IE_INDEX_TO_FW); len = 0; for (i = 0; i < pmadapter->max_mgmt_ie_index; i++) { memcpy(pmadapter, (t_u8 *) ie_data + len, &i, @@ -1042,7 +1042,7 @@ wlan_misc_ioctl_custom_ie_list(IN pmlan_adapter pmadapter, goto done; } memset(pmadapter, ie_data, 0, - sizeof(custom_ie) * MAX_MGMT_IE_INDEX); + sizeof(custom_ie) * MAX_MGMT_IE_INDEX_TO_FW); memcpy(pmadapter, (t_u8 *) ie_data, &pmpriv->mgmt_ie[index], pmpriv->mgmt_ie[index].ie_length + MLAN_CUSTOM_IE_HDR_SIZE); @@ -1281,41 +1281,6 @@ wlan_delete_station_list(pmlan_private priv) } /** - * @brief This function will return the pointer to station entry in station list - * table which in tx_pause state - * - * @param priv A pointer to mlan_private - * - * @return A pointer to structure sta_node - */ -sta_node * -wlan_get_tx_pause_station_entry(mlan_private * priv) -{ - sta_node *sta_ptr; - - ENTER(); - - if (!(sta_ptr = (sta_node *) util_peek_list(priv->adapter->pmoal_handle, - &priv->sta_list, - priv->adapter->callbacks. - moal_spin_lock, - priv->adapter->callbacks. - moal_spin_unlock))) { - LEAVE(); - return MNULL; - } - while (sta_ptr != (sta_node *) & priv->sta_list) { - if (sta_ptr->tx_pause) { - LEAVE(); - return sta_ptr; - } - sta_ptr = sta_ptr->pnext; - } - LEAVE(); - return MNULL; -} - -/** * @brief Get extended version information * * @param pmadapter A pointer to mlan_adapter structure @@ -1496,6 +1461,44 @@ wlan_process_802dot11_mgmt_pkt(IN mlan_private * priv, } /** + * @brief Get OTP user data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_otp_user_data(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *) pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + + if (misc->param.otp_user_data.user_data_length > MAX_OTP_USER_DATA_LEN) { + PRINTM(MERROR, "Invalid OTP user data length\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return ret; + } + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_OTP_READ_USER_DATA, + HostCmd_ACT_GEN_GET, + 0, + (t_void *) pioctl_req, &misc->param.otp_user_data); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** * @brief This function will search for the specific ie * * @@ -1718,26 +1721,26 @@ wlan_rate_ioctl_get_rate_value(IN pmlan_adapter pmadapter, /* If not connected, set rate to the lowest in each band */ if (pmpriv->media_connected != MTRUE) { - if (pmadapter->config_bands & (BAND_B | BAND_G)) { + if (pmpriv->config_bands & (BAND_B | BAND_G)) { /* Return the lowest supported rate for BG band */ rate->param.rate_cfg.rate = SupportedRates_BG[0] & 0x7f; - } else if (pmadapter->config_bands & (BAND_A | BAND_B)) { + } else if (pmpriv->config_bands & (BAND_A | BAND_B)) { /* Return the lowest supported rate for A band */ rate->param.rate_cfg.rate = SupportedRates_BG[0] & 0x7f; - } else if (pmadapter->config_bands & BAND_A) { + } else if (pmpriv->config_bands & BAND_A) { /* Return the lowest supported rate for A band */ rate->param.rate_cfg.rate = SupportedRates_A[0] & 0x7f; - } else if (pmadapter->config_bands & BAND_G) { + } else if (pmpriv->config_bands & BAND_G) { /* Return the lowest supported rate for G band */ rate->param.rate_cfg.rate = SupportedRates_G[0] & 0x7f; - } else if (pmadapter->config_bands & BAND_B) { + } else if (pmpriv->config_bands & BAND_B) { /* Return the lowest supported rate for B band */ rate->param.rate_cfg.rate = SupportedRates_B[0] & 0x7f; - } else if (pmadapter->config_bands & BAND_GN) { + } else if (pmpriv->config_bands & BAND_GN) { /* Return the lowest supported rate for N band */ rate->param.rate_cfg.rate = SupportedRates_N[0] & 0x7f; } else { - PRINTM(MMSG, "Invalid Band 0x%x\n", pmadapter->config_bands); + PRINTM(MMSG, "Invalid Band 0x%x\n", pmpriv->config_bands); } } else { @@ -1793,7 +1796,7 @@ wlan_rate_ioctl_set_rate_value(IN pmlan_adapter pmadapter, memset(pmadapter, rates, 0, sizeof(rates)); wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) ? - pmadapter->config_bands : pmadapter-> + pmpriv->config_bands : pmadapter-> adhoc_start_band, rates); rate = rates; for (i = 0; (rate[i] && i < WLAN_SUPPORTED_RATES); i++) { diff --git a/drivers/net/wireless/sd8797/mlan/mlan_scan.c b/drivers/net/wireless/sd8797/mlan/mlan_scan.c index 2c4eadf04a2e..895fb38a5786 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_scan.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_scan.c @@ -5,7 +5,7 @@ * IOCTL handlers as well as command preparation and response routines * for sending scan commands to the firmware. * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -378,6 +378,9 @@ wlan_scan_create_channel_list(IN mlan_private * pmpriv, break; case BAND_B: case BAND_G: + if (wlan_bg_scan_type_is_passive(pmpriv, (t_u8) cfp->channel)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } default: pscan_chan_list[chan_idx].radio_type = HostCmd_SCAN_RADIO_TYPE_BG; @@ -825,7 +828,7 @@ wlan_scan_setup_scan_config(IN mlan_private * pmpriv, rates_size = wlan_get_supported_rates(pmpriv, pmpriv->bss_mode, (pmpriv->bss_mode == - MLAN_BSS_MODE_INFRA) ? pmadapter-> + MLAN_BSS_MODE_INFRA) ? pmpriv-> config_bands : pmadapter-> adhoc_start_band, rates); @@ -838,13 +841,12 @@ wlan_scan_setup_scan_config(IN mlan_private * pmpriv, PRINTM(MINFO, "SCAN_CMD: Rates size = %d\n", rates_size); if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) - && (pmpriv->adapter->config_bands & BAND_GN - || pmpriv->adapter->config_bands & BAND_AN)) { + && (pmpriv->config_bands & BAND_GN || pmpriv->config_bands & BAND_AN)) { pht_cap = (MrvlIETypes_HTCap_t *) ptlv_pos; memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); pht_cap->header.len = sizeof(HTCap_t); - wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pmpriv->adapter->config_bands); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pmpriv->config_bands); HEXDUMP("SCAN: HT_CAPABILITIES IE", (t_u8 *) pht_cap, sizeof(MrvlIETypes_HTCap_t)); ptlv_pos += sizeof(MrvlIETypes_HTCap_t); @@ -898,6 +900,11 @@ wlan_scan_setup_scan_config(IN mlan_private * pmpriv, scan_type = MLAN_SCAN_TYPE_PASSIVE; } } + if (radio_type == HostCmd_SCAN_RADIO_TYPE_BG) { + if (wlan_bg_scan_type_is_passive(pmpriv, channel)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { (pscan_chan_list + chan_idx)->chan_scan_mode.passive_scan = MTRUE; @@ -2938,7 +2945,8 @@ wlan_ret_802_11_scan(IN mlan_private * pmpriv, pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &pmadapter->age_in_secs, &age_ts_usec); - + if (is_bgscan_resp) + goto done; if (!util_peek_list (pmadapter->pmoal_handle, &pmadapter->scan_pending_q, pcb->moal_spin_lock, pcb->moal_spin_unlock)) { @@ -2961,6 +2969,7 @@ wlan_ret_802_11_scan(IN mlan_private * pmpriv, (pmlan_ioctl_req) pioctl_buf, MLAN_STATUS_SUCCESS); } + pmadapter->bgscan_reported = MFALSE; wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); } else { /* If firmware not ready, do not issue any more scan commands */ @@ -3360,6 +3369,7 @@ wlan_handle_event_ext_scan_report(IN mlan_private * pmpriv, (pmlan_ioctl_req) pioctl_req, MLAN_STATUS_SUCCESS); } + pmadapter->bgscan_reported = MFALSE; wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); } else { /* If firmware not ready, do not issue any more scan commands */ @@ -3508,6 +3518,8 @@ wlan_bgscan_create_channel_list(IN mlan_private * pmpriv, break; case BAND_B: case BAND_G: + if (wlan_bg_scan_type_is_passive(pmpriv, (t_u8) cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; default: tlv_chan_list->chan_scan_param[chan_idx].radio_type = HostCmd_SCAN_RADIO_TYPE_BG; @@ -3575,6 +3587,8 @@ wlan_cmd_bgscan_config(IN mlan_private * pmpriv, MrvlIEtypes_BeaconLowSnrThreshold_t *snr_tlv = MNULL; MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv = MNULL; MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + MrvlIEtypes_StartLater_t *tlv_start_later = MNULL; + MrvlIEtypes_RepeatCount_t *tlv_repeat = MNULL; t_u8 *tlv = MNULL; t_u16 num_probes = 0; t_u32 ssid_idx; @@ -3640,6 +3654,16 @@ wlan_cmd_bgscan_config(IN mlan_private * pmpriv, tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); } + if (bg_scan_in->repeat_count) { + tlv_repeat = (MrvlIEtypes_RepeatCount_t *) tlv; + tlv_repeat->header.type = wlan_cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + tlv_repeat->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_RepeatCount_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_repeat->repeat_count = wlan_cpu_to_le16(bg_scan_in->repeat_count); + tlv += sizeof(MrvlIEtypes_RepeatCount_t); + cmd_size += sizeof(MrvlIEtypes_RepeatCount_t); + } for (ssid_idx = 0; ((ssid_idx < NELEMENTS(bg_scan_in->ssid_list)) && (*bg_scan_in->ssid_list[ssid_idx].ssid || bg_scan_in->ssid_list[ssid_idx].max_len)); @@ -3683,6 +3707,12 @@ wlan_cmd_bgscan_config(IN mlan_private * pmpriv, scan_type = MLAN_SCAN_TYPE_PASSIVE; } } + if (radio_type == HostCmd_SCAN_RADIO_TYPE_BG) { + if (wlan_bg_scan_type_is_passive + (pmpriv, bg_scan_in->chan_list[chan_idx].chan_number)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } tlv_chan_list->chan_scan_param[chan_num].chan_number = bg_scan_in->chan_list[chan_idx].chan_number; tlv_chan_list->chan_scan_param[chan_num].radio_type = @@ -3729,6 +3759,14 @@ wlan_cmd_bgscan_config(IN mlan_private * pmpriv, cmd_size += sizeof(MrvlIEtypesHeader_t) + sizeof(ChanScanParamSet_t) * chan_num; } + tlv_start_later = (MrvlIEtypes_StartLater_t *) tlv; + tlv_start_later->header.type = wlan_cpu_to_le16(TLV_TYPE_STARTBGSCANLATER); + tlv_start_later->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_StartLater_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_start_later->value = 0; + tlv += sizeof(MrvlIEtypes_StartLater_t); + cmd_size += sizeof(MrvlIEtypes_StartLater_t); done: pcmd->size = wlan_cpu_to_le16(cmd_size); LEAVE(); @@ -3776,6 +3814,36 @@ wlan_ret_bgscan_config(IN mlan_private * pmpriv, } /** + * @brief This function handles the command response of bgscan_query + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_bgscan_query(IN mlan_private * pmpriv, + IN HostCmd_DS_COMMAND * resp, + IN mlan_ioctl_req * pioctl_buf) +{ + mlan_ds_scan *pscan = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + ENTER(); + wlan_ret_802_11_scan(pmpriv, resp, MNULL); + if (pioctl_buf) { + pscan = (mlan_ds_scan *) pioctl_buf->pbuf; + pscan->param.scan_resp.pscan_table = (t_u8 *) pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = pmadapter->age_in_secs; + pioctl_buf->data_read_written = sizeof(mlan_scan_resp) + + MLAN_SUB_COMMAND_SIZE; + + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** * @brief This function finds ssid in ssid list. * * @param pmpriv A pointer to mlan_private structure @@ -3812,7 +3880,7 @@ wlan_find_ssid_in_list(IN mlan_private * pmpriv, MLAN_MAC_ADDR_LENGTH))) { if (((mode == MLAN_BSS_MODE_INFRA) && - !wlan_is_band_compatible(pmadapter->config_bands, + !wlan_is_band_compatible(pmpriv->config_bands, pmadapter->pscan_table[i].bss_band)) || (wlan_find_cfp_by_band_and_channel @@ -3896,7 +3964,7 @@ wlan_find_bssid_in_list(IN mlan_private * pmpriv, (pmadapter, pmadapter->pscan_table[i].mac_address, bssid, MLAN_MAC_ADDR_LENGTH)) { if (((mode == MLAN_BSS_MODE_INFRA) && - !wlan_is_band_compatible(pmadapter->config_bands, + !wlan_is_band_compatible(pmpriv->config_bands, pmadapter->pscan_table[i].bss_band)) || (wlan_find_cfp_by_band_and_channel diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sdio.c b/drivers/net/wireless/sd8797/mlan/mlan_sdio.c index 0718cec12ee2..02e73477b619 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sdio.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sdio.c @@ -218,8 +218,9 @@ wlan_get_wr_port_data(mlan_adapter * pmadapter, t_u8 * pport) PRINTM(MIF_D, "wlan_get_wr_port_data: mp_wr_bitmap=0x%08x\n", wr_bitmap); if (!(wr_bitmap & pmadapter->mp_data_port_mask)) { + pmadapter->data_sent = MTRUE; LEAVE(); - return MLAN_STATUS_FAILURE; + return MLAN_STATUS_RESOURCE; } if (pmadapter->mp_wr_bitmap & (1 << pmadapter->curr_wr_port)) { @@ -1431,7 +1432,6 @@ wlan_sdio_host_to_card(mlan_adapter * pmadapter, t_u8 type, mlan_buffer * pmbuf, ret = wlan_get_wr_port_data(pmadapter, &port); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "no wr_port available: %d\n", ret); - pmbuf->status_code = MLAN_ERROR_PKT_INVALID; goto exit; } /* Transfer data to card */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_shim.c b/drivers/net/wireless/sd8797/mlan/mlan_shim.c index 1e06a52b87cb..07f7db5c7227 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_shim.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_shim.c @@ -108,6 +108,12 @@ t_void(*assert_callback) (IN t_void * pmoal_handle, IN t_u32 cond) = MNULL; /** Global moal_print callback */ t_void(*print_callback) (IN t_void * pmoal_handle, IN t_u32 level, IN t_s8 * pformat, IN ...) = MNULL; + +/** Global moal_get_system_time callback */ +mlan_status(*get_sys_time_callback) (IN t_void * pmoal_handle, + OUT t_u32 * psec, + OUT t_u32 * pusec) = MNULL; + /** Global driver debug mit masks */ t_u32 drvdbg = DEFAULT_DEBUG_MASK; #endif @@ -161,6 +167,7 @@ mlan_register(IN pmlan_device pmdevice, OUT t_void ** ppmlan_adapter) MASSERT(pmdevice->callbacks.moal_print); #ifdef DEBUG_LEVEL1 print_callback = pmdevice->callbacks.moal_print; + get_sys_time_callback = pmdevice->callbacks.moal_get_system_time; #endif assert_callback = pmdevice->callbacks.moal_assert; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c index c91fcc9f6e12..7c426595117e 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c @@ -151,6 +151,16 @@ wlan_cmd_802_11_snmp_mib(IN pmlan_private pmpriv, } switch (cmd_oid) { + case DtimPeriod_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16) DtimPeriod_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + ul_temp = *((t_u32 *) pdata_buf); + psnmp_mib->value[0] = (t_u8) ul_temp; + cmd->size += sizeof(t_u8); + } + break; case FragThresh_i: psnmp_mib->oid = wlan_cpu_to_le16((t_u16) FragThresh_i); if (cmd_action == HostCmd_ACT_GEN_SET) { @@ -712,7 +722,22 @@ wlan_cmd_802_11_key_material(IN pmlan_private pmpriv, pkey_material->action = wlan_cpu_to_le16(cmd_action); if (cmd_action == HostCmd_ACT_GEN_GET) { - cmd->size = wlan_cpu_to_le16(sizeof(pkey_material->action) + S_DS_GEN); + cmd->size = + wlan_cpu_to_le16(sizeof(pkey_material->action) + S_DS_GEN + + KEYPARAMSET_FIXED_LEN + + sizeof(MrvlIEtypesHeader_t)); + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + sizeof(MrvlIEtype_KeyParamSet_t)); + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEYPARAMSET_FIXED_LEN); + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_UCAST_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); goto done; } @@ -963,10 +988,14 @@ wlan_cmd_802_11_supplicant_pmk(IN pmlan_private pmpriv, static mlan_status wlan_cmd_802_11_supplicant_profile(IN pmlan_private pmpriv, IN HostCmd_DS_COMMAND * cmd, - IN t_u16 cmd_action) + IN t_u16 cmd_action, IN t_void * pdata_buf) { HostCmd_DS_802_11_SUPPLICANT_PROFILE *sup_profile = &cmd->params.esupplicant_profile; + MrvlIEtypes_EncrProto_t *encr_proto_tlv = MNULL; + MrvlIEtypes_Cipher_t *pcipher_tlv = MNULL; + t_u8 *ptlv_buffer = (t_u8 *) sup_profile->tlv_buf; + mlan_ds_esupp_mode *esupp = MNULL; ENTER(); @@ -975,6 +1004,44 @@ wlan_cmd_802_11_supplicant_profile(IN pmlan_private pmpriv, S_DS_GEN - 1); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SUPPLICANT_PROFILE); sup_profile->action = wlan_cpu_to_le16(cmd_action); + if ((cmd_action == HostCmd_ACT_GEN_SET) && pdata_buf) { + esupp = (mlan_ds_esupp_mode *) pdata_buf; + if (esupp->rsn_mode) { + encr_proto_tlv = (MrvlIEtypes_EncrProto_t *) ptlv_buffer; + encr_proto_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ENCRYPTION_PROTO); + encr_proto_tlv->header.len = + (t_u16) sizeof(encr_proto_tlv->rsn_mode); + encr_proto_tlv->rsn_mode = wlan_cpu_to_le16(esupp->rsn_mode); + ptlv_buffer += + (encr_proto_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (encr_proto_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + encr_proto_tlv->header.len = + wlan_cpu_to_le16(encr_proto_tlv->header.len); + } + if (esupp->act_paircipher || esupp->act_groupcipher) { + pcipher_tlv = (MrvlIEtypes_Cipher_t *) ptlv_buffer; + pcipher_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CIPHER); + pcipher_tlv->header.len = + (t_u16) (sizeof(pcipher_tlv->pair_cipher) + + sizeof(pcipher_tlv->group_cipher)); + if (esupp->act_paircipher) { + pcipher_tlv->pair_cipher = + wlan_cpu_to_le16(esupp->act_paircipher); + } + if (esupp->act_groupcipher) { + pcipher_tlv->group_cipher = + wlan_cpu_to_le16(esupp->act_groupcipher); + } + ptlv_buffer += + (pcipher_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (pcipher_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + pcipher_tlv->header.len = wlan_cpu_to_le16(pcipher_tlv->header.len); + } + } + cmd->size = wlan_cpu_to_le16(cmd->size); LEAVE(); return MLAN_STATUS_SUCCESS; } @@ -1113,7 +1180,8 @@ wlan_cmd_mgmt_ie_list(IN pmlan_private pmpriv, } cmd->size -= - (MAX_MGMT_IE_INDEX * sizeof(custom_ie)) + sizeof(tlvbuf_max_mgmt_ie); + (MAX_MGMT_IE_INDEX_TO_FW * sizeof(custom_ie)) + + sizeof(tlvbuf_max_mgmt_ie); cmd->size += cust_ie->len; cmd->size = wlan_cpu_to_le16(cmd->size); @@ -1460,6 +1528,42 @@ wlan_cmd_subscribe_event(IN pmlan_private pmpriv, } /** + * @brief This function prepares command of OTP user data. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_otp_user_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * cmd, + IN t_u16 cmd_action, IN t_void * pdata_buf) +{ + mlan_ds_misc_otp_user_data *user_data = + (mlan_ds_misc_otp_user_data *) pdata_buf; + HostCmd_DS_OTP_USER_DATA *cmd_user_data = + (HostCmd_DS_OTP_USER_DATA *) & cmd->params.otp_user_data; + t_u16 cmd_size = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_OTP_READ_USER_DATA); + cmd_size = sizeof(HostCmd_DS_OTP_USER_DATA) + S_DS_GEN - 1; + + cmd_user_data->action = wlan_cpu_to_le16(cmd_action); + cmd_user_data->reserved = 0; + cmd_user_data->user_data_length = + wlan_cpu_to_le16(user_data->user_data_length); + cmd_size += user_data->user_data_length; + cmd->size = wlan_cpu_to_le16(cmd_size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** * @brief This function prepares inactivity timeout command * * @param cmd A pointer to HostCmd_DS_COMMAND structure @@ -1679,7 +1783,8 @@ wlan_ops_sta_prepare_cmd(IN t_void * priv, pdata_buf); break; case HostCmd_CMD_SUPPLICANT_PROFILE: - ret = wlan_cmd_802_11_supplicant_profile(pmpriv, cmd_ptr, cmd_action); + ret = wlan_cmd_802_11_supplicant_profile(pmpriv, cmd_ptr, cmd_action, + pdata_buf); break; case HostCmd_CMD_802_11D_DOMAIN_INFO: @@ -1786,6 +1891,9 @@ wlan_ops_sta_prepare_cmd(IN t_void * priv, case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: ret = wlan_cmd_subscribe_event(pmpriv, cmd_ptr, cmd_action, pdata_buf); break; + case HostCmd_CMD_OTP_READ_USER_DATA: + ret = wlan_cmd_otp_user_data(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; default: PRINTM(MERROR, "PREP_CMD: unknown command- %#x\n", cmd_no); ret = MLAN_STATUS_FAILURE; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c index be56a16615b9..086d16ae5a62 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c @@ -3,7 +3,7 @@ * @brief This file contains the handling of command * responses generated by firmware. * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -231,6 +231,12 @@ wlan_ret_802_11_snmp_mib(IN pmlan_private pmpriv, wlan_le16_to_cpu(psmib->buf_size)); if (query_type == HostCmd_ACT_GEN_GET) { switch (oid) { + case DtimPeriod_i: + ul_temp = psmib->value[0]; + PRINTM(MINFO, "SNMP_RESP: DTIM Period =%u\n", ul_temp); + if (mib) + mib->param.dtim_period = ul_temp; + break; case FragThresh_i: ul_temp = wlan_le16_to_cpu(*((t_u16 *) (psmib->value))); PRINTM(MINFO, "SNMP_RESP: FragThsd =%u\n", ul_temp); @@ -765,6 +771,7 @@ wlan_ret_802_11_key_material(IN pmlan_private pmpriv, IN mlan_ioctl_req * pioctl_buf) { HostCmd_DS_802_11_KEY_MATERIAL *pkey = &resp->params.key_material; + mlan_ds_sec_cfg *sec = MNULL; ENTER(); @@ -780,8 +787,50 @@ wlan_ret_802_11_key_material(IN pmlan_private pmpriv, } pmpriv->scan_block = MFALSE; } + } else { + if (pioctl_buf && + (wlan_le16_to_cpu(pkey->key_param_set.type) == + TLV_TYPE_KEY_MATERIAL)) { + PRINTM(MIOCTL, "key_type_id=%d, key_len=%d, key_info=0x%x\n", + wlan_le16_to_cpu(pkey->key_param_set.key_type_id), + wlan_le16_to_cpu(pkey->key_param_set.key_len), + wlan_le16_to_cpu(pkey->key_param_set.key_info)); + sec = (mlan_ds_sec_cfg *) pioctl_buf->pbuf; +#define WAPI_KEY_SIZE 32 + switch (wlan_le16_to_cpu(pkey->key_param_set.key_type_id)) { + case KEY_TYPE_ID_WEP: + sec->param.encrypt_key.key_index = pkey->key_param_set.key[0]; + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set.key_len); + memcpy(pmpriv->adapter, sec->param.encrypt_key.key_material, + &pkey->key_param_set.key[2], + sec->param.encrypt_key.key_len); + break; + case KEY_TYPE_ID_TKIP: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set.key_len); + memcpy(pmpriv->adapter, sec->param.encrypt_key.key_material, + pkey->key_param_set.key, sec->param.encrypt_key.key_len); + break; + case KEY_TYPE_ID_AES: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set.key_len); + memcpy(pmpriv->adapter, sec->param.encrypt_key.key_material, + pkey->key_param_set.key, sec->param.encrypt_key.key_len); + break; + case KEY_TYPE_ID_WAPI: + sec->param.encrypt_key.is_wapi_key = MTRUE; + sec->param.encrypt_key.key_index = pkey->key_param_set.key[0]; + sec->param.encrypt_key.key_len = WAPI_KEY_SIZE; + memcpy(pmpriv->adapter, sec->param.encrypt_key.key_material, + &pkey->key_param_set.key[2], + sec->param.encrypt_key.key_len); + memcpy(pmpriv->adapter, sec->param.encrypt_key.pn, + &pkey->key_param_set.key[2 + WAPI_KEY_SIZE], PN_SIZE); + break; + } + } } - LEAVE(); return MLAN_STATUS_SUCCESS; } @@ -1374,6 +1423,41 @@ wlan_ret_subscribe_event(IN pmlan_private pmpriv, return MLAN_STATUS_SUCCESS; } +/** + * @brief This function handles the command response of + * OTP user data + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_otp_user_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * resp, + IN mlan_ioctl_req * pioctl_buf) +{ + + HostCmd_DS_OTP_USER_DATA *cmd_user_data = + (HostCmd_DS_OTP_USER_DATA *) & resp->params.otp_user_data; + mlan_ds_misc_otp_user_data *user_data = MNULL; + + ENTER(); + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + user_data = (mlan_ds_misc_otp_user_data *) pioctl_buf->pbuf; + user_data->user_data_length = MIN(MAX_OTP_USER_DATA_LEN, + wlan_le16_to_cpu(cmd_user_data-> + user_data_length)); + memcpy(pmpriv->adapter, user_data->user_data, cmd_user_data->user_data, + user_data->user_data_length); + pioctl_buf->data_read_written = + sizeof(mlan_ds_misc_otp_user_data) + user_data->user_data_length; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + /******************************************************** Global Functions ********************************************************/ @@ -1442,8 +1526,7 @@ wlan_ops_sta_process_cmdresp(IN t_void * priv, ret = wlan_ret_bgscan_config(pmpriv, resp, pioctl_buf); break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: - ret = wlan_ret_802_11_scan(pmpriv, resp, pioctl_buf); - wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN, MNULL); + ret = wlan_ret_802_11_bgscan_query(pmpriv, resp, pioctl_buf); PRINTM(MINFO, "CMD_RESP: BG_SCAN result is ready!\n"); break; case HostCmd_CMD_TXPWR_CFG: @@ -1629,6 +1712,9 @@ wlan_ops_sta_process_cmdresp(IN t_void * priv, case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: ret = wlan_ret_subscribe_event(pmpriv, resp, pioctl_buf); break; + case HostCmd_CMD_OTP_READ_USER_DATA: + ret = wlan_ret_otp_user_data(pmpriv, resp, pioctl_buf); + break; default: PRINTM(MERROR, "CMD_RESP: Unknown command response %#x\n", resp->command); diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c index f638241f9632..1ad77818995f 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c @@ -341,14 +341,8 @@ wlan_ops_sta_process_event(IN t_void * priv) case EVENT_BG_SCAN_REPORT: PRINTM(MEVENT, "EVENT: BGS_REPORT\n"); - /* Clear the previous scan result */ - memset(pmadapter, pmadapter->pscan_table, 0x00, - sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST); - pmadapter->num_in_scan_table = 0; - pmadapter->pbcn_buf_end = pmadapter->bcn_buf; - ret = wlan_prepare_cmd(pmpriv, - HostCmd_CMD_802_11_BG_SCAN_QUERY, - HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + pmadapter->bgscan_reported = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN, MNULL); break; case EVENT_PORT_RELEASE: diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c index 72dd525ca4af..eb715cb804c8 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c @@ -222,6 +222,9 @@ wlan_get_info_bss_info(IN pmlan_adapter pmadapter, /* Channel */ info->param.bss_info.bss_chan = pbss_desc->channel; + /* Beacon interval */ + info->param.bss_info.beacon_interval = pbss_desc->beacon_period; + /* Band */ info->param.bss_info.bss_band = (t_u8) pbss_desc->bss_band; @@ -396,6 +399,10 @@ wlan_snmp_mib_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) value = mib->param.retry_count; cmd_oid = ShortRetryLim_i; break; + case MLAN_OID_SNMP_MIB_DTIM_PERIOD: + value = mib->param.dtim_period; + cmd_oid = DtimPeriod_i; + break; } /* Send request to firmware */ @@ -509,6 +516,7 @@ wlan_radio_ioctl_band_cfg(IN pmlan_adapter pmadapter, } pmpriv->adhoc_channel = (t_u8) adhoc_channel; } + if ((adhoc_band & BAND_GN) || (adhoc_band & BAND_AN) ) { @@ -993,6 +1001,7 @@ wlan_bss_ioctl_start(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) CLOSED state */ if (pmpriv->port_ctrl_mode == MTRUE) { PRINTM(MINFO, "bss_ioctl_start(): port_state=CLOSED\n"); + pmpriv->prior_port_status = pmpriv->port_open; pmpriv->port_open = MFALSE; } pmpriv->scan_block = MFALSE; @@ -1437,7 +1446,7 @@ wlan_rate_ioctl_get_supported_rate(IN pmlan_adapter pmadapter, else wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) ? - pmadapter->config_bands : pmadapter-> + pmpriv->config_bands : pmadapter-> adhoc_start_band, rate->param.rates); pioctl_req->data_read_written = MLAN_SUPPORTED_RATES + MLAN_SUB_COMMAND_SIZE; @@ -3306,20 +3315,61 @@ wlan_sec_ioctl_esupp_mode(IN pmlan_adapter pmadapter, { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 cmd_action = 0; + mlan_ds_sec_cfg *sec = MNULL; ENTER(); - if (pmpriv->media_connected != MTRUE) { - pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; - ret = MLAN_STATUS_FAILURE; - goto exit; + sec = (mlan_ds_sec_cfg *) pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, + "Cannot set esupplicant mode configuration while connected.\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.rsn_mode || + (sec->param.esupp_mode.rsn_mode & RSN_TYPE_VALID_BITS) + != sec->param.esupp_mode.rsn_mode) { + PRINTM(MERROR, "Invalid RSN mode\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.act_paircipher || + (sec->param.esupp_mode.act_paircipher & EMBED_CIPHER_VALID_BITS) + != sec->param.esupp_mode.act_paircipher) { + PRINTM(MERROR, "Invalid pairwise cipher\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.act_groupcipher || + (sec->param.esupp_mode.act_groupcipher & EMBED_CIPHER_VALID_BITS) + != sec->param.esupp_mode.act_groupcipher) { + PRINTM(MERROR, "Invalid group cipher\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } else { + cmd_action = HostCmd_ACT_GEN_GET_CURRENT; } /* Send request to firmware */ - ret = wlan_prepare_cmd(pmpriv, - HostCmd_CMD_SUPPLICANT_PROFILE, - HostCmd_ACT_GEN_GET_CURRENT, - 0, (t_void *) pioctl_req, MNULL); + if (sec) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SUPPLICANT_PROFILE, + cmd_action, + 0, + (t_void *) pioctl_req, &sec->param.esupp_mode); + } else { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SUPPLICANT_PROFILE, + cmd_action, 0, (t_void *) pioctl_req, MNULL); + } if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; @@ -4137,6 +4187,8 @@ wlan_misc_ioctl_region(IN pmlan_adapter pmadapter, LEAVE(); return MLAN_STATUS_FAILURE; } + pmadapter->cfp_code_bg = misc->param.region_code; + pmadapter->cfp_code_a = misc->param.region_code; if (wlan_set_regiontable(pmpriv, (t_u8) pmadapter->region_code, pmadapter->config_bands | pmadapter-> adhoc_start_band)) { @@ -4827,6 +4879,158 @@ wlan_misc_ioctl_ipaddr_cfg(IN pmlan_adapter pmadapter, } /** + * @brief CFP code configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_cfp_code_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *) pioctl_req->pbuf; + mlan_ds_misc_cfp_code *cfp_code = MNULL; + t_u32 region_bg = 0; + t_u32 region_a = 0; + int i; + + ENTER(); + + cfp_code = &misc->param.cfp_code; + if (pioctl_req->action == MLAN_ACT_SET) { + /* Save the values in MLAN */ + if (!cfp_code->cfp_code_bg) + cfp_code->cfp_code_bg = pmadapter->cfp_code_bg; + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (cfp_code->cfp_code_bg == region_code_index[i]) { + region_bg = cfp_code->cfp_code_bg; + break; + } + } + if (!region_bg) { + for (i = 0; i < MRVDRV_MAX_CFP_CODE_BG; i++) { + /* Use the CFP code to search for the index */ + if (cfp_code->cfp_code_bg == cfp_code_index_bg[i]) + break; + } + if (i >= MRVDRV_MAX_CFP_CODE_BG) { + PRINTM(MERROR, "CFP Code not identified for BG\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (!cfp_code->cfp_code_a) + cfp_code->cfp_code_a = pmadapter->cfp_code_a; + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (cfp_code->cfp_code_a == region_code_index[i]) { + region_a = cfp_code->cfp_code_a; + break; + } + } + if (!region_a) { + for (i = 0; i < MRVDRV_MAX_CFP_CODE_A; i++) { + /* Use the CFP code to search for the index */ + if (cfp_code->cfp_code_a == cfp_code_index_a[i]) + break; + } + if (i >= MRVDRV_MAX_CFP_CODE_A) { + PRINTM(MERROR, "CFP Code not identified for A\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + pmadapter->cfp_code_bg = (t_u8) cfp_code->cfp_code_bg; + pmadapter->cfp_code_a = (t_u8) cfp_code->cfp_code_a; + if (region_bg && region_a && (region_bg == region_a)) + pmadapter->region_code = pmadapter->cfp_code_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, (t_u8) pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { + /* GET operation */ + cfp_code->cfp_code_bg = pmadapter->cfp_code_bg; + cfp_code->cfp_code_a = pmadapter->cfp_code_a; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief This function sets up country code and downloads CMD to FW + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_ioctl_country_code(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req * pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_country_code *country_code = MNULL; + mlan_ds_misc_cfg *cfg_misc = MNULL; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + cfg_misc = (mlan_ds_misc_cfg *) pioctl_req->pbuf; + country_code = &cfg_misc->param.country_code; + + if (pioctl_req->action == MLAN_ACT_SET) { + /* Update region code and table based on country code */ + if (wlan_misc_country_2_cfp_table_code(pmadapter, + country_code->country_code, + &cfp_bg, &cfp_a)) { + PRINTM(MERROR, "Country code not found!\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_bg && cfp_a && (cfp_bg == cfp_a)) + pmadapter->region_code = cfp_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + memcpy(pmadapter, pmadapter->country_code, + country_code->country_code, COUNTRY_CODE_LEN); + } else { + /* GET operation */ + memcpy(pmadapter, country_code->country_code, + pmadapter->country_code, COUNTRY_CODE_LEN); + } + + done: + LEAVE(); + return ret; +} + +/** * @brief Miscellaneous configuration handler * * @param pmadapter A pointer to mlan_adapter structure @@ -4906,12 +5110,21 @@ wlan_misc_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) case MLAN_OID_MISC_IP_ADDR: status = wlan_misc_ioctl_ipaddr_cfg(pmadapter, pioctl_req); break; + case MLAN_OID_MISC_CFP_CODE: + status = wlan_misc_ioctl_cfp_code_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_COUNTRY_CODE: + status = wlan_misc_ioctl_country_code(pmadapter, pioctl_req); + break; case MLAN_OID_MISC_THERMAL: status = wlan_misc_ioctl_thermal(pmadapter, pioctl_req); break; case MLAN_OID_MISC_SUBSCRIBE_EVENT: status = wlan_misc_ioctl_subscribe_evt(pmadapter, pioctl_req); break; + case MLAN_OID_MISC_OTP_USER_DATA: + status = wlan_misc_otp_user_data(pmadapter, pioctl_req); + break; default: if (pioctl_req) pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; @@ -5070,13 +5283,31 @@ wlan_scan_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) pioctl_req->data_read_written = sizeof(mlan_scan_resp) + MLAN_SUB_COMMAND_SIZE; } else { - pscan->param.scan_resp.pscan_table = - (t_u8 *) pmadapter->pscan_table; - pscan->param.scan_resp.num_in_scan_table = - pmadapter->num_in_scan_table; - pscan->param.scan_resp.age_in_secs = pmadapter->age_in_secs; - pioctl_req->data_read_written = sizeof(mlan_scan_resp) + - MLAN_SUB_COMMAND_SIZE; + if (pmadapter->bgscan_reported) { + pmadapter->bgscan_reported = MFALSE; + /* Clear the previous scan result */ + memset(pmadapter, pmadapter->pscan_table, 0x00, + sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST); + pmadapter->num_in_scan_table = 0; + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + status = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, + 0, (t_void *) pioctl_req, MNULL); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, + "wlan_scan_ioctl: return MLAN_STATUS_PENDING\n"); + status = MLAN_STATUS_PENDING; + } + } else { + pscan->param.scan_resp.pscan_table = + (t_u8 *) pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = pmadapter->age_in_secs; + pioctl_req->data_read_written = sizeof(mlan_scan_resp) + + MLAN_SUB_COMMAND_SIZE; + } } } @@ -5198,6 +5429,7 @@ wlan_ops_sta_ioctl(t_void * adapter, pmlan_ioctl_req pioctl_req) mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); + switch (pioctl_req->req_id) { case MLAN_IOCTL_SCAN: status = wlan_scan_ioctl(pmadapter, pioctl_req); diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c index 2ad9a810afcf..5e5c68e2a108 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c @@ -2,7 +2,7 @@ * * @brief This file contains the handling of RX in MLAN * module. - * + * * Copyright (C) 2008-2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International @@ -59,6 +59,7 @@ typedef struct /******************************************************** Global functions ********************************************************/ + /** * @brief This function processes received packet and forwards it * to kernel/upper layer @@ -174,6 +175,7 @@ wlan_process_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, prx_pd->priority); + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, pmbuf); if (ret == MLAN_STATUS_FAILURE) { pmbuf->status_code = MLAN_ERROR_PKT_INVALID; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c index 06c1a4de064e..2258853a4089 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c @@ -166,7 +166,9 @@ wlan_send_null_packet(pmlan_private priv, t_u8 flags) pmlan_buffer pmbuf = MNULL; t_u8 *ptr; mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 t_u32 sec, usec; +#endif ENTER(); @@ -228,8 +230,7 @@ wlan_send_null_packet(pmlan_private priv, t_u8 flags) break; } - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); PRINTM(MDATA, "%lu.%06lu : Null data => FW\n", sec, usec); DBG_HEXDUMP(MDAT_D, "Null data", ptr, sizeof(TxPD) + INTF_HEADER_LEN); done: diff --git a/drivers/net/wireless/sd8797/mlan/mlan_txrx.c b/drivers/net/wireless/sd8797/mlan/mlan_txrx.c index e15dd14f0443..93dea9e284f8 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_txrx.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_txrx.c @@ -63,7 +63,9 @@ wlan_handle_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); RxPD *prx_pd; +#ifdef DEBUG_LEVEL1 t_u32 sec, usec; +#endif ENTER(); @@ -75,8 +77,7 @@ wlan_handle_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) if (!priv) priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); pmbuf->bss_index = priv->bss_index; - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); PRINTM_NETINTF(MDATA, priv); PRINTM(MDATA, "%lu.%06lu : Data <= FW\n", sec, usec); ret = priv->ops.process_rx_packet(pmadapter, pmbuf); @@ -101,7 +102,9 @@ wlan_process_tx(pmlan_private priv, pmlan_buffer pmbuf, mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_adapter pmadapter = priv->adapter; t_u8 *head_ptr = MNULL; +#ifdef DEBUG_LEVEL1 t_u32 sec, usec; +#endif #ifdef STA_SUPPORT TxPD *plocal_tx_pd = MNULL; #endif @@ -153,8 +156,7 @@ wlan_process_tx(pmlan_private priv, pmlan_buffer pmbuf, } if ((ret == MLAN_STATUS_SUCCESS) || (ret == MLAN_STATUS_PENDING)) { - pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, - &usec); + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); PRINTM_NETINTF(MDATA, priv); PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); } diff --git a/drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c b/drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c index ed79dcfaaf68..7d7d5510433c 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c @@ -3,7 +3,7 @@ * @brief This file contains the handling of AP mode command and event * * Copyright (C) 2009-2011, Marvell International Ltd. - * + * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in diff --git a/drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c b/drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c index 5081d9e4aabc..f71a222f4f48 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c @@ -3,7 +3,7 @@ * @brief This file contains the handling of AP mode ioctls * * Copyright (C) 2009-2011, Marvell International Ltd. - * + * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in @@ -230,8 +230,8 @@ wlan_uap_bss_ioctl_reset(IN pmlan_adapter pmadapter, memset(pmadapter, &pmpriv->mgmt_ie[i], 0, sizeof(custom_ie)); } pmpriv->add_ba_param.timeout = MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; - pmpriv->add_ba_param.tx_win_size = MLAN_AMPDU_DEF_TXWINSIZE; - pmpriv->add_ba_param.rx_win_size = MLAN_AMPDU_DEF_RXWINSIZE; + pmpriv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; + pmpriv->add_ba_param.rx_win_size = MLAN_UAP_AMPDU_DEF_RXWINSIZE; for (i = 0; i < MAX_NUM_TID; i++) { pmpriv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; pmpriv->aggr_prio_tbl[i].amsdu = BA_STREAM_NOT_ALLOWED; diff --git a/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c b/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c index 0abd1ecd1c2a..590def325f27 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c @@ -3,7 +3,7 @@ * @brief This file contains AP mode transmit and receive functions * * Copyright (C) 2009-2011, Marvell International Ltd. - * + * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in diff --git a/drivers/net/wireless/sd8797/mlan/mlan_wmm.c b/drivers/net/wireless/sd8797/mlan/mlan_wmm.c index d2c3227aca87..4b72c9193e27 100644 --- a/drivers/net/wireless/sd8797/mlan/mlan_wmm.c +++ b/drivers/net/wireless/sd8797/mlan/mlan_wmm.c @@ -946,8 +946,6 @@ wlan_dequeue_tx_packet(pmlan_adapter pmadapter) t_u8 ra[MLAN_MAC_ADDR_LENGTH]; int tid_del = 0; int tid = 0; - t_u8 avail_ports = 0; - int i; ENTER(); @@ -975,24 +973,6 @@ wlan_dequeue_tx_packet(pmlan_adapter pmadapter) if (tid >= MAX_NUM_TID) tid = wlan_wmm_downgrade_tid(priv, tid); - for (i = 1; i < pmadapter->mp_end_port; i++) { - if ((pmadapter->mp_wr_bitmap >> i) & 1) - avail_ports++; - } - - PRINTM(MINFO, - "mp_wr_bitmap=0x%08x avail_ports=%d tid=%d tx_eligibility[%d]=%d\n", - pmadapter->mp_wr_bitmap, avail_ports, - tid, tid, pmadapter->tx_eligibility[tid]); - - if (avail_ports < pmadapter->tx_eligibility[tid]) { - pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, - priv->wmm.ra_list_spinlock); - pmadapter->data_sent = MTRUE; - LEAVE(); - return MLAN_STATUS_FAILURE; - } - if (wlan_is_ptr_processed(priv, ptr)) { wlan_send_processed_packet(priv, ptr, ptrindex); LEAVE(); @@ -1407,8 +1387,22 @@ wlan_wmm_init(pmlan_adapter pmadapter) = priv->aggr_prio_tbl[7].ampdu_user = BA_STREAM_NOT_ALLOWED; priv->add_ba_param.timeout = MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; - priv->add_ba_param.tx_win_size = MLAN_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = MLAN_AMPDU_DEF_RXWINSIZE; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + priv->add_ba_param.tx_win_size = MLAN_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_STA_AMPDU_DEF_RXWINSIZE; + } +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP +#ifdef WIFI_DIRECT_SUPPORT + || priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) { + priv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_UAP_AMPDU_DEF_RXWINSIZE; + } +#endif priv->add_ba_param.tx_amsdu = MTRUE; priv->add_ba_param.rx_amsdu = MTRUE; memset(priv->adapter, priv->rx_seq, 0xff, sizeof(priv->rx_seq)); @@ -1885,8 +1879,7 @@ wlan_wmm_process_association_req(pmlan_private priv, if ((priv->wmm_required || (pHTCap && (pHTCap->ieee_hdr.element_id == HT_CAPABILITY) - && (priv->adapter->config_bands & BAND_GN - || priv->adapter->config_bands & BAND_AN)) + && (priv->config_bands & BAND_GN || priv->config_bands & BAND_AN)) ) && pWmmIE->vend_hdr.element_id == WMM_IE) { pwmm_tlv = (MrvlIEtypes_WmmParamSet_t *) * ppAssocBuf; @@ -2015,14 +2008,12 @@ wlan_wmm_select_queue(mlan_private * pmpriv, t_u8 tid) * @param priv Pointer to the mlan_private driver data struct * @param ra_list_head ra list header * @param tid tid - * @param tx_pause tx_pause flag * * @return N/A */ static INLINE t_u8 wlan_del_tx_pkts_in_ralist(pmlan_private priv, - mlan_list_head * ra_list_head, - int tid, t_u8 tx_pause) + mlan_list_head * ra_list_head, int tid) { raListTbl *ra_list = MNULL; pmlan_adapter pmadapter = priv->adapter; @@ -2034,15 +2025,14 @@ wlan_del_tx_pkts_in_ralist(pmlan_private priv, MNULL, MNULL); while (ra_list && ra_list != (raListTbl *) ra_list_head) { if (ra_list->total_pkts && (ra_list->tx_pause || - (!tx_pause && - ra_list->total_pkts > RX_LOW_THRESHOLD))) { + (ra_list->total_pkts > RX_LOW_THRESHOLD))) { if ((pmbuf = (pmlan_buffer) util_dequeue_list(pmadapter->pmoal_handle, &ra_list->buf_head, MNULL, MNULL))) { PRINTM(MDATA, "Drop pkts: tid=%d tx_pause=%d pkts=%d brd_pkts=%d %02x:%02x:%02x:%02x:%02x:%02x\n", - tid, tx_pause, ra_list->total_pkts, + tid, ra_list->tx_pause, ra_list->total_pkts, pmadapter->pending_bridge_pkts, ra_list->ra[0], ra_list->ra[1], ra_list->ra[2], ra_list->ra[3], ra_list->ra[4], ra_list->ra[5]); @@ -2077,17 +2067,14 @@ wlan_drop_tx_pkts(pmlan_private priv) { int j; static int i = 0; - t_u8 tx_pause = 0; pmlan_adapter pmadapter = priv->adapter; pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); - if (wlan_get_tx_pause_station_entry(priv)) - tx_pause = 1; for (j = 0; j < MAX_NUM_TID; j++, i++) { if (i == MAX_NUM_TID) i = 0; if (wlan_del_tx_pkts_in_ralist - (priv, &priv->wmm.tid_tbl_ptr[i].ra_list, i, tx_pause)) { + (priv, &priv->wmm.tid_tbl_ptr[i].ra_list, i)) { i++; break; } diff --git a/drivers/net/wireless/sd8797/mlinux/mlan.h b/drivers/net/wireless/sd8797/mlinux/mlan.h new file mode 100644 index 000000000000..a341e1547099 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/mlan.h @@ -0,0 +1,35 @@ +/** @file mlan.h + * + * @brief This file declares all APIs that will be called from MOAL module. + * It also defines the data structures used for APIs between MLAN and MOAL. + * + * Copyright (C) 2008-2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/13/2008: initial version + 11/07/2008: split mlan.h into mlan_decl.h & mlan_ioctl.h +******************************************************/ + +#ifndef _MLAN_H_ +#define _MLAN_H_ + +#include "mlan_decl.h" +#include "mlan_ioctl.h" +#include "mlan_ieee.h" + +#endif /* !_MLAN_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/mlan_decl.h b/drivers/net/wireless/sd8797/mlinux/mlan_decl.h new file mode 100644 index 000000000000..41cf2bf5f2f1 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/mlan_decl.h @@ -0,0 +1,893 @@ +/** @file mlan_decl.h + * + * @brief This file declares the generic data structures and APIs. + * + * Copyright (C) 2008-2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_DECL_H_ +#define _MLAN_DECL_H_ + +/** MLAN release version */ +#define MLAN_RELEASE_VERSION "311" + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef char t_s8; +/** Unsigned char (1-byte) */ +typedef unsigned char t_u8; +/** Signed short (2-bytes) */ +typedef short t_s16; +/** Unsigned short (2-bytes) */ +typedef unsigned short t_u16; +/** Signed long (4-bytes) */ +typedef int t_s32; +/** Unsigned long (4-bytes) */ +typedef unsigned int t_u32; +/** Signed long long 8-bytes) */ +typedef long long t_s64; +/** Unsigned long long 8-bytes) */ +typedef unsigned long long t_u64; +/** Void pointer (4-bytes) */ +typedef void t_void; +/** Size type */ +typedef t_u32 t_size; +/** Boolean type */ +typedef t_u8 t_bool; + +#ifdef MLAN_64BIT +/** Pointer type (64-bit) */ +typedef t_u64 t_ptr; +/** Signed value (64-bit) */ +typedef t_s64 t_sval; +#else +/** Pointer type (32-bit) */ +typedef t_u32 t_ptr; +/** Signed value (32-bit) */ +typedef t_s32 t_sval; +#endif + +/** Constants below */ + +#ifdef __GNUC__ +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END __attribute__ ((packed)) +#else /* !__GNUC__ */ +#ifdef PRAGMA_PACK +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END +#else /* !PRAGMA_PACK */ +/** Structure packing begins */ +#define MLAN_PACK_START __packed +/** Structure packing end */ +#define MLAN_PACK_END +#endif /* PRAGMA_PACK */ +#endif /* __GNUC__ */ + +#ifndef INLINE +#ifdef __GNUC__ +/** inline directive */ +#define INLINE inline +#else +/** inline directive */ +#define INLINE __inline +#endif +#endif + +/** MLAN TRUE */ +#define MTRUE (1) +/** MLAN FALSE */ +#define MFALSE (0) + +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** Return the byte offset of a field in the given structure */ +#define MLAN_FIELD_OFFSET(type, field) ((t_u32)(t_ptr)&(((type *)0)->field)) +/** Return aligned offset */ +#define OFFSET_ALIGN_ADDR(p, a) (t_u32)(ALIGN_ADDR(p, a) - (t_ptr)p) + +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (16) + +/** NET IP alignment */ +#define MLAN_NET_IP_ALIGN 0 + +/** DMA alignment */ +#define DMA_ALIGNMENT 64 +/** max size of TxPD */ +#define MAX_TXPD_SIZE 32 + +/** Minimum data header length */ +#define MLAN_MIN_DATA_HEADER_LEN (DMA_ALIGNMENT+MAX_TXPD_SIZE) + +/** rx data header length */ +#define MLAN_RX_HEADER_LEN MLAN_MIN_DATA_HEADER_LEN + +/** This is current limit on Maximum Tx AMPDU allowed */ +#define MLAN_MAX_TX_BASTREAM_SUPPORTED 2 +/** This is current limit on Maximum Rx AMPDU allowed */ +#define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 + +#ifdef STA_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_STA_AMPDU_DEF_TXWINSIZE 16 +/** Default Win size attached during ADDBA response */ +#define MLAN_STA_AMPDU_DEF_RXWINSIZE 32 +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_UAP_AMPDU_DEF_TXWINSIZE 32 +/** Default Win size attached during ADDBA response */ +#define MLAN_UAP_AMPDU_DEF_RXWINSIZE 16 +#endif /* UAP_SUPPORT */ +/** Block ack timeout value */ +#define MLAN_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff +/** Maximum Tx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_TXWINSIZE 0x3ff +/** Maximum Rx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_RXWINSIZE 0x3ff + +/** Rate index for HR/DSSS 0 */ +#define MLAN_RATE_INDEX_HRDSSS0 0 +/** Rate index for HR/DSSS 3 */ +#define MLAN_RATE_INDEX_HRDSSS3 3 +/** Rate index for OFDM 0 */ +#define MLAN_RATE_INDEX_OFDM0 4 +/** Rate index for OFDM 7 */ +#define MLAN_RATE_INDEX_OFDM7 11 +/** Rate index for MCS 0 */ +#define MLAN_RATE_INDEX_MCS0 12 +/** Rate index for MCS 7 */ +#define MLAN_RATE_INDEX_MCS7 19 +/** Rate index for MCS 9 */ +#define MLAN_RATE_INDEX_MCS9 21 +/** Rate index for MCS15 */ +#define MLAN_RATE_INDEX_MCS15 27 +/** Rate index for MCS 32 */ +#define MLAN_RATE_INDEX_MCS32 44 +/** Rate index for MCS 127 */ +#define MLAN_RATE_INDEX_MCS127 139 + +/** Rate bitmap for OFDM 0 */ +#define MLAN_RATE_BITMAP_OFDM0 16 +/** Rate bitmap for OFDM 7 */ +#define MLAN_RATE_BITMAP_OFDM7 23 +/** Rate bitmap for MCS 0 */ +#define MLAN_RATE_BITMAP_MCS0 32 +/** Rate bitmap for MCS 127 */ +#define MLAN_RATE_BITMAP_MCS127 159 + +/** Size of rx data buffer */ +#define MLAN_RX_DATA_BUF_SIZE (4 * 1024) +/** Size of rx command buffer */ +#define MLAN_RX_CMD_BUF_SIZE (2 * 1024) + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** MLAN 802.11 MAC Address */ +typedef t_u8 mlan_802_11_mac_addr[MLAN_MAC_ADDR_LENGTH]; + +/** MLAN Maximum SSID Length */ +#define MLAN_MAX_SSID_LENGTH (32) + +/** RTS/FRAG related defines */ +/** Minimum RTS value */ +#define MLAN_RTS_MIN_VALUE (0) +/** Maximum RTS value */ +#define MLAN_RTS_MAX_VALUE (2347) +/** Minimum FRAG value */ +#define MLAN_FRAG_MIN_VALUE (256) +/** Maximum FRAG value */ +#define MLAN_FRAG_MAX_VALUE (2346) + +/** Minimum tx retry count */ +#define MLAN_TX_RETRY_MIN (0) +/** Maximum tx retry count */ +#define MLAN_TX_RETRY_MAX (14) + +/** define SDIO block size for data Tx/Rx */ +/* We support up to 480-byte block size due to FW buffer limitation. */ +#define MLAN_SDIO_BLOCK_SIZE 256 + +/** define SDIO block size for firmware download */ +#define MLAN_SDIO_BLOCK_SIZE_FW_DNLD MLAN_SDIO_BLOCK_SIZE + +/** define allocated buffer size */ +#define ALLOC_BUF_SIZE (4 * 1024) + +/** SDIO IO Port mask */ +#define MLAN_SDIO_IO_PORT_MASK 0xfffff +/** SDIO Block/Byte mode mask */ +#define MLAN_SDIO_BYTE_MODE_MASK 0x80000000 + +/** Max retry number of IO write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/** IN parameter */ +#define IN +/** OUT parameter */ +#define OUT + +/** BIT value */ +#define MBIT(x) (((t_u32)1) << (x)) + +/** Buffer flag for requeued packet */ +#define MLAN_BUF_FLAG_REQUEUED_PKT MBIT(0) +/** Buffer flag for transmit buf from moal */ +#define MLAN_BUF_FLAG_MOAL_TX_BUF MBIT(1) +/** Buffer flag for malloc mlan_buffer */ +#define MLAN_BUF_FLAG_MALLOC_BUF MBIT(2) + +/** Buffer flag for bridge packet */ +#define MLAN_BUF_FLAG_BRIDGE_BUF MBIT(3) + +#ifdef DEBUG_LEVEL1 +/** Debug level bit definition */ +#define MMSG MBIT(0) +#define MFATAL MBIT(1) +#define MERROR MBIT(2) +#define MDATA MBIT(3) +#define MCMND MBIT(4) +#define MEVENT MBIT(5) +#define MINTR MBIT(6) +#define MIOCTL MBIT(7) + +#define MDAT_D MBIT(16) +#define MCMD_D MBIT(17) +#define MEVT_D MBIT(18) +#define MFW_D MBIT(19) +#define MIF_D MBIT(20) + +#define MENTRY MBIT(28) +#define MWARN MBIT(29) +#define MINFO MBIT(30) +#define MHEX_DUMP MBIT(31) +#endif /* DEBUG_LEVEL1 */ + +/** Memory allocation type: DMA */ +#define MLAN_MEM_DMA MBIT(0) + +/** Default memory allocation flag */ +#define MLAN_MEM_DEF 0 + +/** mlan_status */ +typedef enum _mlan_status +{ + MLAN_STATUS_FAILURE = 0xffffffff, + MLAN_STATUS_SUCCESS = 0, + MLAN_STATUS_PENDING, + MLAN_STATUS_RESOURCE, +} mlan_status; + +/** mlan_error_code */ +typedef enum _mlan_error_code +{ + /** No error */ + MLAN_ERROR_NO_ERROR = 0, + /** Firmware/device errors below (MSB=0) */ + MLAN_ERROR_FW_NOT_READY = 0x00000001, + MLAN_ERROR_FW_BUSY, + MLAN_ERROR_FW_CMDRESP, + MLAN_ERROR_DATA_TX_FAIL, + MLAN_ERROR_DATA_RX_FAIL, + /** Driver errors below (MSB=1) */ + MLAN_ERROR_PKT_SIZE_INVALID = 0x80000001, + MLAN_ERROR_PKT_TIMEOUT, + MLAN_ERROR_PKT_INVALID, + MLAN_ERROR_CMD_INVALID, + MLAN_ERROR_CMD_TIMEOUT, + MLAN_ERROR_CMD_DNLD_FAIL, + MLAN_ERROR_CMD_CANCEL, + MLAN_ERROR_CMD_RESP_FAIL, + MLAN_ERROR_CMD_ASSOC_FAIL, + MLAN_ERROR_CMD_SCAN_FAIL, + MLAN_ERROR_IOCTL_INVALID, + MLAN_ERROR_IOCTL_FAIL, + MLAN_ERROR_EVENT_UNKNOWN, + MLAN_ERROR_INVALID_PARAMETER, + MLAN_ERROR_NO_MEM, + /** More to add */ +} mlan_error_code; + +/** mlan_buf_type */ +typedef enum _mlan_buf_type +{ + MLAN_BUF_TYPE_CMD = 1, + MLAN_BUF_TYPE_DATA, + MLAN_BUF_TYPE_EVENT, + MLAN_BUF_TYPE_RAW_DATA, +} mlan_buf_type; + +/** MLAN BSS type */ +typedef enum _mlan_bss_type +{ + MLAN_BSS_TYPE_STA = 0, + MLAN_BSS_TYPE_UAP = 1, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_BSS_TYPE_WIFIDIRECT = 2, +#endif + MLAN_BSS_TYPE_ANY = 0xff, +} mlan_bss_type; + +/** MLAN BSS role */ +typedef enum _mlan_bss_role +{ + MLAN_BSS_ROLE_STA = 0, + MLAN_BSS_ROLE_UAP = 1, + MLAN_BSS_ROLE_ANY = 0xff, +} mlan_bss_role; + +/** BSS role bit mask */ +#define BSS_ROLE_BIT_MASK MBIT(0) + +/** Get BSS role */ +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) + +/** mlan_data_frame_type */ +typedef enum _mlan_data_frame_type +{ + MLAN_DATA_FRAME_TYPE_ETH_II = 0, + MLAN_DATA_FRAME_TYPE_802_11, +} mlan_data_frame_type; + +/** mlan_event_id */ +typedef enum _mlan_event_id +{ + /* Event generated by firmware (MSB=0) */ + MLAN_EVENT_ID_FW_UNKNOWN = 0x00000001, + MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED, + MLAN_EVENT_ID_FW_ADHOC_LINK_LOST, + MLAN_EVENT_ID_FW_DISCONNECTED, + MLAN_EVENT_ID_FW_MIC_ERR_UNI, + MLAN_EVENT_ID_FW_MIC_ERR_MUL, + MLAN_EVENT_ID_FW_BCN_RSSI_LOW, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH, + MLAN_EVENT_ID_FW_BCN_SNR_LOW, + MLAN_EVENT_ID_FW_BCN_SNR_HIGH, + MLAN_EVENT_ID_FW_MAX_FAIL, + MLAN_EVENT_ID_FW_DATA_RSSI_LOW, + MLAN_EVENT_ID_FW_DATA_RSSI_HIGH, + MLAN_EVENT_ID_FW_DATA_SNR_LOW, + MLAN_EVENT_ID_FW_DATA_SNR_HIGH, + MLAN_EVENT_ID_FW_LINK_QUALITY, + MLAN_EVENT_ID_FW_PORT_RELEASE, + MLAN_EVENT_ID_FW_PRE_BCN_LOST, + MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE, + MLAN_EVENT_ID_FW_HS_WAKEUP, + MLAN_EVENT_ID_FW_BG_SCAN, + MLAN_EVENT_ID_FW_WEP_ICV_ERR, + MLAN_EVENT_ID_FW_STOP_TX, + MLAN_EVENT_ID_FW_START_TX, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY, + MLAN_EVENT_ID_FW_BW_CHANGED, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED, +#endif +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_UAP_FW_BSS_START, + MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE, + MLAN_EVENT_ID_UAP_FW_BSS_IDLE, + MLAN_EVENT_ID_UAP_FW_STA_CONNECT, + MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT, +#endif + + /* Event generated by MLAN driver (MSB=1) */ + MLAN_EVENT_ID_DRV_CONNECTED = 0x80000001, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MLAN_EVENT_ID_DRV_HS_ACTIVATED, + MLAN_EVENT_ID_DRV_HS_DEACTIVATED, + MLAN_EVENT_ID_DRV_MGMT_FRAME, + MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM, + MLAN_EVENT_ID_DRV_PASSTHRU, + MLAN_EVENT_ID_DRV_SCAN_REPORT, + MLAN_EVENT_ID_DRV_MEAS_REPORT, + MLAN_EVENT_ID_DRV_REPORT_STRING, + MLAN_EVENT_ID_DRV_DBG_DUMP, +} mlan_event_id; + +/** Data Structures */ +/** mlan_image data structure */ +typedef struct _mlan_fw_image +{ + /** Helper image buffer pointer */ + t_u8 *phelper_buf; + /** Helper image length */ + t_u32 helper_len; + /** Firmware image buffer pointer */ + t_u8 *pfw_buf; + /** Firmware image length */ + t_u32 fw_len; +} mlan_fw_image, *pmlan_fw_image; + +/** Custom data structure */ +typedef struct _mlan_init_param +{ + /** Cal data buffer pointer */ + t_u8 *pcal_data_buf; + /** Cal data length */ + t_u32 cal_data_len; + /** Other custom data */ +} mlan_init_param, *pmlan_init_param; + +/** mlan_event data structure */ +typedef struct _mlan_event +{ + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Event ID */ + mlan_event_id event_id; + /** Event length */ + t_u32 event_len; + /** Event buffer */ + t_u8 event_buf[1]; +} mlan_event, *pmlan_event; + +/** mlan_event_scan_result data structure */ +typedef MLAN_PACK_START struct _mlan_event_scan_result +{ + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** More event available or not */ + t_u8 more_event; + /** Reserved */ + t_u8 reserved[3]; + /** Size of the response buffer */ + t_u16 buf_size; + /** Number of BSS in scan response */ + t_u8 num_of_set; +} MLAN_PACK_END mlan_event_scan_result, *pmlan_event_scan_result; + +/** mlan_ioctl_req data structure */ +typedef struct _mlan_ioctl_req +{ + /** Status code from firmware/driver */ + t_u32 status_code; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Request id */ + t_u32 req_id; + /** Action: set or get */ + t_u32 action; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Length of buffer */ + t_u32 buf_len; + /** Length of the data read/written in buffer */ + t_u32 data_read_written; + /** Length of buffer needed */ + t_u32 buf_len_needed; + /** Reserved for MOAL module */ + t_ptr reserved_1; +} mlan_ioctl_req, *pmlan_ioctl_req; + +/** mlan_buffer data structure */ +typedef struct _mlan_buffer +{ + /** Pointer to previous mlan_buffer */ + struct _mlan_buffer *pprev; + /** Pointer to next mlan_buffer */ + struct _mlan_buffer *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** Flags for this buffer */ + t_u32 flags; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Buffer descriptor, e.g. skb in Linux */ + t_void *pdesc; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Offset to data */ + t_u32 data_offset; + /** Data length */ + t_u32 data_len; + /** Buffer type: data, cmd, event etc. */ + mlan_buf_type buf_type; + + /** Fields below are valid for data packet only */ + /** QoS priority */ + t_u32 priority; + /** Time stamp when packet is received (seconds) */ + t_u32 in_ts_sec; + /** Time stamp when packet is received (micro seconds) */ + t_u32 in_ts_usec; + /** Time stamp when packet is processed (seconds) */ + t_u32 out_ts_sec; + /** Time stamp when packet is processed (micro seconds) */ + t_u32 out_ts_usec; + + /** Fields below are valid for MLAN module only */ + /** Pointer to parent mlan_buffer */ + struct _mlan_buffer *pparent; + /** Use count for this buffer */ + t_u32 use_count; +} mlan_buffer, *pmlan_buffer; + +/** mlan_bss_attr data structure */ +typedef struct _mlan_bss_attr +{ + /** BSS type */ + t_u32 bss_type; + /** Data frame type: Ethernet II, 802.11, etc. */ + t_u32 frame_type; + /** The BSS is active (non-0) or not (0). */ + t_u32 active; + /** BSS Priority */ + t_u32 bss_priority; + /** BSS number */ + t_u32 bss_num; +} mlan_bss_attr, *pmlan_bss_attr; + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** Type enumeration for the command result */ +typedef MLAN_PACK_START enum _mlan_cmd_result_e +{ + MLAN_CMD_RESULT_SUCCESS = 0, + MLAN_CMD_RESULT_FAILURE = 1, + MLAN_CMD_RESULT_TIMEOUT = 2, + MLAN_CMD_RESULT_INVALID_DATA = 3 +} MLAN_PACK_END mlan_cmd_result_e; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _mlan_wmm_ac_e +{ + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} MLAN_PACK_END mlan_wmm_ac_e; + +/** Type enumeration for the action field in the Queue Config command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_config_action_e +{ + MLAN_WMM_QUEUE_CONFIG_ACTION_GET = 0, + MLAN_WMM_QUEUE_CONFIG_ACTION_SET = 1, + MLAN_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, + MLAN_WMM_QUEUE_CONFIG_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_config_action_e; + +/** Type enumeration for the action field in the queue stats command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_stats_action_e +{ + MLAN_WMM_STATS_ACTION_START = 0, + MLAN_WMM_STATS_ACTION_STOP = 1, + MLAN_WMM_STATS_ACTION_GET_CLR = 2, + MLAN_WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ + MLAN_WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ + MLAN_WMM_STATS_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_stats_action_e; + +/** + * @brief IOCTL structure for a Traffic stream status. + * + */ +typedef MLAN_PACK_START struct +{ + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Upstream(0), Downlink(1), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END wlan_ioctl_wmm_ts_status_t, +/** Type definition of mlan_ds_wmm_ts_status for MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status, *pmlan_ds_wmm_ts_status; + +/** Max Ie length */ +#define MAX_IE_SIZE 256 + +/** custom IE */ +typedef MLAN_PACK_START struct _custom_ie +{ + /** IE Index */ + t_u16 ie_index; + /** Mgmt Subtype Mask */ + t_u16 mgmt_subtype_mask; + /** IE Length */ + t_u16 ie_length; + /** IE buffer */ + t_u8 ie_buffer[MAX_IE_SIZE]; +} MLAN_PACK_END custom_ie; + +/** Max IE index to FW */ +#define MAX_MGMT_IE_INDEX_TO_FW 4 +/** Max IE index per BSS */ +#define MAX_MGMT_IE_INDEX 16 + +/** custom IE info */ +typedef MLAN_PACK_START struct _custom_ie_info +{ + /** size of buffer */ + t_u16 buf_size; + /** no of buffers of buf_size */ + t_u16 buf_count; +} MLAN_PACK_END custom_ie_info; + +/** TLV buffer : Max Mgmt IE */ +typedef MLAN_PACK_START struct _tlvbuf_max_mgmt_ie +{ + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** No of tuples */ + t_u16 count; + /** custom IE info tuples */ + custom_ie_info info[MAX_MGMT_IE_INDEX]; +} MLAN_PACK_END tlvbuf_max_mgmt_ie; + +/** TLV buffer : custom IE */ +typedef MLAN_PACK_START struct _tlvbuf_custom_ie +{ + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** IE data */ + custom_ie ie_data_list[MAX_MGMT_IE_INDEX_TO_FW]; + /** Max mgmt IE TLV */ + tlvbuf_max_mgmt_ie max_mgmt_ie; +} MLAN_PACK_END mlan_ds_misc_custom_ie; + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +/** mlan_callbacks data structure */ +typedef struct _mlan_callbacks +{ + /** moal_get_fw_data */ + mlan_status(*moal_get_fw_data) (IN t_void * pmoal_handle, + IN t_u32 offset, + IN t_u32 len, OUT t_u8 * pbuf); + /** moal_init_fw_complete */ + mlan_status(*moal_init_fw_complete) (IN t_void * pmoal_handle, + IN mlan_status status); + /** moal_shutdown_fw_complete */ + mlan_status(*moal_shutdown_fw_complete) (IN t_void * pmoal_handle, + IN mlan_status status); + /** moal_send_packet_complete */ + mlan_status(*moal_send_packet_complete) (IN t_void * pmoal_handle, + IN pmlan_buffer pmbuf, + IN mlan_status status); + /** moal_recv_complete */ + mlan_status(*moal_recv_complete) (IN t_void * pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, IN mlan_status status); + /** moal_recv_packet */ + mlan_status(*moal_recv_packet) (IN t_void * pmoal_handle, + IN pmlan_buffer pmbuf); + /** moal_recv_event */ + mlan_status(*moal_recv_event) (IN t_void * pmoal_handle, + IN pmlan_event pmevent); + /** moal_ioctl_complete */ + mlan_status(*moal_ioctl_complete) (IN t_void * pmoal_handle, + IN pmlan_ioctl_req pioctl_req, + IN mlan_status status); + /** moal_alloc_mlan_buffer */ + mlan_status(*moal_alloc_mlan_buffer) (IN t_void * pmoal_handle, + IN t_u32 size, + OUT pmlan_buffer * pmbuf); + /** moal_free_mlan_buffer */ + mlan_status(*moal_free_mlan_buffer) (IN t_void * pmoal_handle, + IN pmlan_buffer pmbuf); + /** moal_write_reg */ + mlan_status(*moal_write_reg) (IN t_void * pmoal_handle, + IN t_u32 reg, IN t_u32 data); + /** moal_read_reg */ + mlan_status(*moal_read_reg) (IN t_void * pmoal_handle, + IN t_u32 reg, OUT t_u32 * data); + /** moal_write_data_sync */ + mlan_status(*moal_write_data_sync) (IN t_void * pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); + /** moal_read_data_sync */ + mlan_status(*moal_read_data_sync) (IN t_void * pmoal_handle, + IN OUT pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); + /** moal_malloc */ + mlan_status(*moal_malloc) (IN t_void * pmoal_handle, + IN t_u32 size, IN t_u32 flag, OUT t_u8 ** ppbuf); + /** moal_mfree */ + mlan_status(*moal_mfree) (IN t_void * pmoal_handle, IN t_u8 * pbuf); + /** moal_memset */ + t_void *(*moal_memset) (IN t_void * pmoal_handle, + IN t_void * pmem, IN t_u8 byte, IN t_u32 num); + /** moal_memcpy */ + t_void *(*moal_memcpy) (IN t_void * pmoal_handle, + IN t_void * pdest, + IN const t_void * psrc, IN t_u32 num); + /** moal_memmove */ + t_void *(*moal_memmove) (IN t_void * pmoal_handle, + IN t_void * pdest, + IN const t_void * psrc, IN t_u32 num); + /** moal_memcmp */ + t_s32(*moal_memcmp) (IN t_void * pmoal_handle, + IN const t_void * pmem1, + IN const t_void * pmem2, IN t_u32 num); + /** moal_udelay */ + t_void(*moal_udelay) (IN t_void * pmoal_handle, IN t_u32 udelay); + /** moal_get_system_time */ + mlan_status(*moal_get_system_time) (IN t_void * pmoal_handle, + OUT t_u32 * psec, OUT t_u32 * pusec); + /** moal_init_timer*/ + mlan_status(*moal_init_timer) (IN t_void * pmoal_handle, + OUT t_void ** pptimer, + IN t_void(*callback) (t_void * pcontext), + IN t_void * pcontext); + /** moal_free_timer */ + mlan_status(*moal_free_timer) (IN t_void * pmoal_handle, + IN t_void * ptimer); + /** moal_start_timer*/ + mlan_status(*moal_start_timer) (IN t_void * pmoal_handle, + IN t_void * ptimer, + IN t_u8 periodic, IN t_u32 msec); + /** moal_stop_timer*/ + mlan_status(*moal_stop_timer) (IN t_void * pmoal_handle, + IN t_void * ptimer); + /** moal_init_lock */ + mlan_status(*moal_init_lock) (IN t_void * pmoal_handle, + OUT t_void ** pplock); + /** moal_free_lock */ + mlan_status(*moal_free_lock) (IN t_void * pmoal_handle, + IN t_void * plock); + /** moal_spin_lock */ + mlan_status(*moal_spin_lock) (IN t_void * pmoal_handle, + IN t_void * plock); + /** moal_spin_unlock */ + mlan_status(*moal_spin_unlock) (IN t_void * pmoal_handle, + IN t_void * plock); + /** moal_print */ + t_void(*moal_print) (IN t_void * pmoal_handle, + IN t_u32 level, IN t_s8 * pformat, IN ...); + /** moal_print_netintf */ + t_void(*moal_print_netintf) (IN t_void * pmoal_handle, + IN t_u32 bss_index, IN t_u32 level); + /** moal_assert */ + t_void(*moal_assert) (IN t_void * pmoal_handle, IN t_u32 cond); +} mlan_callbacks, *pmlan_callbacks; + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 + +/** Parameter unchanged, use MLAN default setting */ +#define MLAN_INIT_PARA_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define MLAN_INIT_PARA_ENABLED 1 +/** Parameter disabled, override MLAN default setting */ +#define MLAN_INIT_PARA_DISABLED 2 + +/** mlan_device data structure */ +typedef struct _mlan_device +{ + /** MOAL Handle */ + t_void *pmoal_handle; + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; + /** Callbacks */ + mlan_callbacks callbacks; +#ifdef MFG_CMD_SUPPORT + /** MFG mode */ + t_u32 mfg_mode; +#endif + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; +#if defined(STA_SUPPORT) + /** 802.11d configuration */ + t_u32 cfg_11d; +#endif +} mlan_device, *pmlan_device; + +/** MLAN API function prototype */ +#define MLAN_API + +/** Registration */ +MLAN_API mlan_status mlan_register(IN pmlan_device pmdevice, + OUT t_void ** ppmlan_adapter); + +/** Un-registration */ +MLAN_API mlan_status mlan_unregister(IN t_void * pmlan_adapter); + +/** Firmware Downloading */ +MLAN_API mlan_status mlan_dnld_fw(IN t_void * pmlan_adapter, + IN pmlan_fw_image pmfw); + +/** Custom data pass API */ +MLAN_API mlan_status mlan_set_init_param(IN t_void * pmlan_adapter, + IN pmlan_init_param pparam); + +/** Firmware Initialization */ +MLAN_API mlan_status mlan_init_fw(IN t_void * pmlan_adapter); + +/** Firmware Shutdown */ +MLAN_API mlan_status mlan_shutdown_fw(IN t_void * pmlan_adapter); + +/** Main Process */ +MLAN_API mlan_status mlan_main_process(IN t_void * pmlan_adapter); + +/** Packet Transmission */ +MLAN_API mlan_status mlan_send_packet(IN t_void * pmlan_adapter, + IN pmlan_buffer pmbuf); + +/** Packet Reception complete callback */ +MLAN_API mlan_status mlan_recv_packet_complete(IN t_void * pmlan_adapter, + IN pmlan_buffer pmbuf, + IN mlan_status status); + +/** interrupt handler */ +MLAN_API t_void mlan_interrupt(IN t_void * pmlan_adapter); + +/** mlan ioctl */ +MLAN_API mlan_status mlan_ioctl(IN t_void * pmlan_adapter, + IN pmlan_ioctl_req pioctl_req); +/** mlan select wmm queue */ +MLAN_API t_u8 mlan_select_wmm_queue(IN t_void * pmlan_adapter, + IN t_u8 bss_num, IN t_u8 tid); +#endif /* !_MLAN_DECL_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/mlan_ieee.h b/drivers/net/wireless/sd8797/mlinux/mlan_ieee.h new file mode 100644 index 000000000000..2431e06f48fa --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/mlan_ieee.h @@ -0,0 +1,1322 @@ +/** @file mlan_ieee.h + * + * @brief This file contains IEEE information element related + * definitions used in MLAN and MOAL module. + * + * Copyright (C) 2008-2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/03/2008: initial version +******************************************************/ + +#ifndef _MLAN_IEEE_H_ +#define _MLAN_IEEE_H_ + +/** FIX IES size in beacon buffer */ +#define WLAN_802_11_FIXED_IE_SIZE 12 +/** WLAN supported rates */ +#define WLAN_SUPPORTED_RATES 14 + +/** WLAN supported rates extension*/ +#define WLAN_SUPPORTED_RATES_EXT 32 + +/** Enumeration definition*/ +/** WLAN_802_11_NETWORK_TYPE */ +typedef enum _WLAN_802_11_NETWORK_TYPE +{ + Wlan802_11FH, + Wlan802_11DS, + /* Defined as upper bound */ + Wlan802_11NetworkTypeMax +} WLAN_802_11_NETWORK_TYPE; + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +#ifdef BIG_ENDIAN_SUPPORT +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x3000 +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc) & 0xF000) >> 12) +#else +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x000C +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc) & 0x00F0) >> 4) +#endif + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** IEEE Type definitions */ +typedef MLAN_PACK_START enum _IEEEtypes_ElementId_e +{ + SSID = 0, + SUPPORTED_RATES = 1, + + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + + IBSS_PARAM_SET = 6, + +#ifdef STA_SUPPORT + COUNTRY_INFO = 7, +#endif /* STA_SUPPORT */ + + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + SUPPORTED_CHANNELS = 36, + CHANNEL_SWITCH_ANN = 37, + QUIET = 40, + IBSS_DFS = 41, + HT_CAPABILITY = 45, + HT_OPERATION = 61, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + EXT_CAPABILITY = 127, + + ERP_INFO = 42, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + + WPS_IE = VENDOR_SPECIFIC_221, + + WPA_IE = VENDOR_SPECIFIC_221, + RSN_IE = 48, + VS_IE = VENDOR_SPECIFIC_221, + WAPI_IE = 68, +} MLAN_PACK_END IEEEtypes_ElementId_e; + +/** IEEE IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_Header_t +{ + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; +} MLAN_PACK_END IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** Vendor specific IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorHeader_t +{ + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** OUI subtype */ + t_u8 oui_subtype; + /** Version */ + t_u8 version; +} MLAN_PACK_END IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t; + +/** Vendor specific IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorSpecific_t +{ + /** Vendor specific IE header */ + IEEEtypes_VendorHeader_t vend_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)]; +} +MLAN_PACK_END IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t; + +/** IEEE IE */ +typedef MLAN_PACK_START struct _IEEEtypes_Generic_t +{ + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)]; +} +MLAN_PACK_END IEEEtypes_Generic_t, *pIEEEtypes_Generic_t; + +/** Capability information mask */ +#define CAPINFO_MASK (~(MBIT(15) | MBIT(14) | \ + MBIT(12) | MBIT(11) | MBIT(9))) + +/** Capability Bit Map*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t +{ + t_u8 rsrvd1:2; + t_u8 dsss_ofdm:1; + t_u8 rsvrd2:2; + t_u8 short_slot_time:1; + t_u8 rsrvd3:1; + t_u8 spectrum_mgmt:1; + t_u8 chan_agility:1; + t_u8 pbcc:1; + t_u8 short_preamble:1; + t_u8 privacy:1; + t_u8 cf_poll_rqst:1; + t_u8 cf_pollable:1; + t_u8 ibss:1; + t_u8 ess:1; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t +{ + /** Capability Bit Map : ESS */ + t_u8 ess:1; + /** Capability Bit Map : IBSS */ + t_u8 ibss:1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable:1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst:1; + /** Capability Bit Map : privacy */ + t_u8 privacy:1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble:1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc:1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility:1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3:1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time:1; + /** Capability Bit Map : APSD */ + t_u8 Apsd:1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2:1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1:2; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#endif /* BIG_ENDIAN_SUPPORT */ + +/** IEEEtypes_CfParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_CfParamSet_t +{ + /** CF peremeter : Element ID */ + t_u8 element_id; + /** CF peremeter : Length */ + t_u8 len; + /** CF peremeter : Count */ + t_u8 cfp_cnt; + /** CF peremeter : Period */ + t_u8 cfp_period; + /** CF peremeter : Maximum duration */ + t_u16 cfp_max_duration; + /** CF peremeter : Remaining duration */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END IEEEtypes_CfParamSet_t, *pIEEEtypes_CfParamSet_t; + +/** IEEEtypes_IbssParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_IbssParamSet_t +{ + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ATIM window value in milliseconds */ + t_u16 atim_window; +} MLAN_PACK_END IEEEtypes_IbssParamSet_t, *pIEEEtypes_IbssParamSet_t; + +/** IEEEtypes_SsParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_SsParamSet_t +{ + /** SS parameter : CF parameter set */ + IEEEtypes_CfParamSet_t cf_param_set; + /** SS parameter : IBSS parameter set */ + IEEEtypes_IbssParamSet_t ibss_param_set; +} MLAN_PACK_END IEEEtypes_SsParamSet_t, *pIEEEtypes_SsParamSet_t; + +/** IEEEtypes_FhParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_FhParamSet_t +{ + /** FH parameter : Element ID */ + t_u8 element_id; + /** FH parameter : Length */ + t_u8 len; + /** FH parameter : Dwell time in milliseconds */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END IEEEtypes_FhParamSet_t, *pIEEEtypes_FhParamSet_t; + +/** IEEEtypes_DsParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_DsParamSet_t +{ + /** DS parameter : Element ID */ + t_u8 element_id; + /** DS parameter : Length */ + t_u8 len; + /** DS parameter : Current channel */ + t_u8 current_chan; +} MLAN_PACK_END IEEEtypes_DsParamSet_t, *pIEEEtypes_DsParamSet_t; + +/** IEEEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_PhyParamSet_t +{ + /** FH parameter set */ + IEEEtypes_FhParamSet_t fh_param_set; + /** DS parameter set */ + IEEEtypes_DsParamSet_t ds_param_set; +} MLAN_PACK_END IEEEtypes_PhyParamSet_t, *pIEEEtypes_PhyParamSet_t; + +/** IEEEtypes_ERPInfo_t */ +typedef MLAN_PACK_START struct _IEEEtypes_ERPInfo_t +{ + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ERP flags */ + t_u8 erp_flags; +} MLAN_PACK_END IEEEtypes_ERPInfo_t, *pIEEEtypes_ERPInfo_t; + +/** IEEEtypes_AId_t */ +typedef t_u16 IEEEtypes_AId_t; + +/** IEEEtypes_StatusCode_t */ +typedef t_u16 IEEEtypes_StatusCode_t; + +/** IEEEtypes_AssocRsp_t */ +typedef MLAN_PACK_START struct _IEEEtypes_AssocRsp_t +{ + /** Capability information */ + IEEEtypes_CapInfo_t capability; + /** Association response status code */ + IEEEtypes_StatusCode_t status_code; + /** Association ID */ + IEEEtypes_AId_t a_id; + /** IE data buffer */ + t_u8 ie_buffer[1]; +} MLAN_PACK_END IEEEtypes_AssocRsp_t, *pIEEEtypes_AssocRsp_t; + +/** 802.11 supported rates */ +typedef t_u8 WLAN_802_11_RATES[WLAN_SUPPORTED_RATES]; + +/** cipher TKIP */ +#define WPA_CIPHER_TKIP 2 +/** cipher AES */ +#define WPA_CIPHER_AES_CCM 4 +/** AKM: 8021x */ +#define RSN_AKM_8021X 1 +/** AKM: PSK */ +#define RSN_AKM_PSK 2 + +/** wpa_suite_t */ +typedef MLAN_PACK_START struct _wpa_suite_t +{ + /** OUI */ + t_u8 oui[3]; + /** tyep */ + t_u8 type; +} MLAN_PACK_END wpa_suite, wpa_suite_mcast_t; + +/** wpa_suite_ucast_t */ +typedef MLAN_PACK_START struct +{ + /* count */ + t_u16 count; + /** wpa_suite list */ + wpa_suite list[1]; +} MLAN_PACK_END wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; + +/** IEEEtypes_Rsn_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Rsn_t +{ + /** Rsn : Element ID */ + t_u8 element_id; + /** Rsn : Length */ + t_u8 len; + /** Rsn : version */ + t_u16 version; + /** Rsn : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Rsn : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Rsn_t, *pIEEEtypes_Rsn_t; + +/** IEEEtypes_Wpa_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Wpa_t +{ + /** Wpa : Element ID */ + t_u8 element_id; + /** Wpa : Length */ + t_u8 len; + /** Wpa : oui */ + t_u8 oui[4]; + /** version */ + t_u16 version; + /** Wpa : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Wpa : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Wpa_t, *pIEEEtypes_Wpa_t; + +/** Maximum number of AC QOS queues available in the driver/firmware */ +#define MAX_AC_QUEUES 4 + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmQosInfo_t +{ +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmQosInfo_t, *pIEEEtypes_WmmQosInfo_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAciAifsn_t +{ +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmEcw_t +{ +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAcParameters_t +{ + IEEEtypes_WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + IEEEtypes_WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t; + +/** Data structure of WMM Info IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmInfo_t +{ + + /** + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + +} MLAN_PACK_END IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t; + +/** Data structure of WMM parameter IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmParameter_t +{ + /** + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + /** Reserved */ + t_u8 reserved; + + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t; + +/** Enumerator for TSPEC direction */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_Direction_e +{ + + TSPEC_DIR_UPLINK = 0, + TSPEC_DIR_DOWNLINK = 1, + /* 2 is a reserved value */ + TSPEC_DIR_BIDIRECT = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_Direction_e; + +/** Enumerator for TSPEC PSB */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_PSB_e +{ + + TSPEC_PSB_LEGACY = 0, + TSPEC_PSB_TRIG = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_PSB_e; + +/** Enumerator for TSPEC Ack Policy */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e +{ + + TSPEC_ACKPOLICY_NORMAL = 0, + TSPEC_ACKPOLICY_NOACK = 1, + /* 2 is reserved */ + TSPEC_ACKPOLICY_BLOCKACK = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e; + +/** Enumerator for TSPEC Trafffice type */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e +{ + + TSPEC_TRAFFIC_APERIODIC = 0, + TSPEC_TRAFFIC_PERIODIC = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e; + +/** Data structure of WMM TSPEC information */ +typedef MLAN_PACK_START struct +{ +#ifdef BIG_ENDIAN_SUPPORT + t_u8 Reserved17_23:7; // ! Reserved + t_u8 Schedule:1; + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + t_u8 UserPri:3; // ! 802.1d User Priority + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior:1; // ! + // Legacy/Trigg + t_u8 Aggregation:1; // ! Reserved + t_u8 AccessPolicy2:1; // ! + t_u8 AccessPolicy1:1; // ! + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + t_u8 TID:4; // ! Unique identifier + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; +#else + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; + t_u8 TID:4; // ! Unique identifier + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + t_u8 AccessPolicy1:1; // ! + t_u8 AccessPolicy2:1; // ! + t_u8 Aggregation:1; // ! Reserved + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior:1; // ! + // Legacy/Trigg + t_u8 UserPri:3; // ! 802.1d User Priority + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + t_u8 Schedule:1; + t_u8 Reserved17_23:7; // ! Reserved +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_t; + +/** Data structure of WMM TSPEC Nominal Size */ +typedef MLAN_PACK_START struct +{ +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Fixed:1; // ! 1: Fixed size given in Size, 0: Var, size + // is nominal + t_u16 Size:15; // ! Nominal size in octets +#else + t_u16 Size:15; // ! Nominal size in octets + t_u16 Fixed:1; // ! 1: Fixed size given in Size, 0: Var, size + // is nominal +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_NomMSDUSize_t; + +/** Data structure of WMM TSPEC SBWA */ +typedef MLAN_PACK_START struct +{ +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Whole:3; // ! Whole portion + t_u16 Fractional:13; // ! Fractional portion +#else + t_u16 Fractional:13; // ! Fractional portion + t_u16 Whole:3; // ! Whole portion +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_SBWA; + +/** Data structure of WMM TSPEC Body */ +typedef MLAN_PACK_START struct +{ + + IEEEtypes_WMM_TSPEC_TS_Info_t TSInfo; + IEEEtypes_WMM_TSPEC_NomMSDUSize_t NomMSDUSize; + t_u16 MaximumMSDUSize; + t_u32 MinServiceInterval; + t_u32 MaxServiceInterval; + t_u32 InactivityInterval; + t_u32 SuspensionInterval; + t_u32 ServiceStartTime; + t_u32 MinimumDataRate; + t_u32 MeanDataRate; + t_u32 PeakDataRate; + t_u32 MaxBurstSize; + t_u32 DelayBound; + t_u32 MinPHYRate; + IEEEtypes_WMM_TSPEC_SBWA SurplusBWAllowance; + t_u16 MediumTime; +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_Body_t; + +/** Data structure of WMM TSPEC all elements */ +typedef MLAN_PACK_START struct +{ + t_u8 ElementId; + t_u8 Len; + t_u8 OuiType[4]; /* 00:50:f2:02 */ + t_u8 OuiSubType; /* 01 */ + t_u8 Version; + + IEEEtypes_WMM_TSPEC_Body_t TspecBody; + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_t; + +/** WMM Action Category values */ +typedef MLAN_PACK_START enum _IEEEtypes_ActionCategory_e +{ + + IEEE_MGMT_ACTION_CATEGORY_SPECTRUM_MGMT = 0, + IEEE_MGMT_ACTION_CATEGORY_QOS = 1, + IEEE_MGMT_ACTION_CATEGORY_DLS = 2, + IEEE_MGMT_ACTION_CATEGORY_BLOCK_ACK = 3, + IEEE_MGMT_ACTION_CATEGORY_PUBLIC = 4, + IEEE_MGMT_ACTION_CATEGORY_RADIO_RSRC = 5, + IEEE_MGMT_ACTION_CATEGORY_FAST_BSS_TRANS = 6, + IEEE_MGMT_ACTION_CATEGORY_HT = 7, + + IEEE_MGMT_ACTION_CATEGORY_WNM = 10, + IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM = 11, + + IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC = 17 +} MLAN_PACK_END IEEEtypes_ActionCategory_e; + +/** WMM TSPEC operations */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_Tspec_Action_e +{ + + TSPEC_ACTION_CODE_ADDTS_REQ = 0, + TSPEC_ACTION_CODE_ADDTS_RSP = 1, + TSPEC_ACTION_CODE_DELTS = 2, + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_e; + +/** WMM TSPEC Category Action Base */ +typedef MLAN_PACK_START struct +{ + + IEEEtypes_ActionCategory_e category; + IEEEtypes_WMM_Tspec_Action_e action; + t_u8 dialogToken; + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_Base_Tspec_t; + +/** WMM TSPEC AddTS request structure */ +typedef MLAN_PACK_START struct +{ + + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + t_u8 statusCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + t_u8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsReq_t; + +/** WMM TSPEC AddTS response structure */ +typedef MLAN_PACK_START struct +{ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + t_u8 statusCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + t_u8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsRsp_t; + +/** WMM TSPEC DelTS structure */ +typedef MLAN_PACK_START struct +{ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + t_u8 reasonCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + +} MLAN_PACK_END IEEEtypes_Action_WMM_DelTs_t; + +/** union of WMM TSPEC structures */ +typedef MLAN_PACK_START union +{ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + + IEEEtypes_Action_WMM_AddTsReq_t addTsReq; + IEEEtypes_Action_WMM_AddTsRsp_t addTsRsp; + IEEEtypes_Action_WMM_DelTs_t delTs; + +} MLAN_PACK_END IEEEtypes_Action_WMMAC_t; + +/** union of WMM TSPEC & Action category */ +typedef MLAN_PACK_START union +{ + IEEEtypes_ActionCategory_e category; + + IEEEtypes_Action_WMMAC_t wmmAc; + +} MLAN_PACK_END IEEEtypes_ActionFrame_t; + +/** Data structure for subband set */ +typedef MLAN_PACK_START struct _IEEEtypes_SubbandSet_t +{ + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power in dBm */ + t_u8 max_tx_pwr; +} MLAN_PACK_END IEEEtypes_SubbandSet_t, *pIEEEtypes_SubbandSet_t; + +#ifdef STA_SUPPORT +/** Data structure for Country IE */ +typedef MLAN_PACK_START struct _IEEEtypes_CountryInfoSet_t +{ + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[1]; +} MLAN_PACK_END IEEEtypes_CountryInfoSet_t, *pIEEEtypes_CountryInfoSet_t; + +/** Data structure for Country IE full set */ +typedef MLAN_PACK_START struct _IEEEtypes_CountryInfoFullSet_t +{ + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} MLAN_PACK_END IEEEtypes_CountryInfoFullSet_t, + *pIEEEtypes_CountryInfoFullSet_t; + +#endif /* STA_SUPPORT */ + +/** HT Capabilities Data */ +typedef struct MLAN_PACK_START _HTCap_t +{ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; +} MLAN_PACK_END HTCap_t, *pHTCap_t; + +/** HT Information Data */ +typedef struct MLAN_PACK_START _HTInfo_t +{ + /** Primary channel */ + t_u8 pri_chan; + /** Field 2 */ + t_u8 field2; + /** Field 3 */ + t_u16 field3; + /** Field 4 */ + t_u16 field4; + /** Bitmap indicating MCSs supported by all HT STAs in the BSS */ + t_u8 basic_mcs_set[16]; +} MLAN_PACK_END HTInfo_t, *pHTInfo_t; + +/** 20/40 BSS Coexistence Data */ +typedef struct MLAN_PACK_START _BSSCo2040_t +{ + /** 20/40 BSS Coexistence value */ + t_u8 bss_co_2040_value; +} MLAN_PACK_END BSSCo2040_t, *pBSSCo2040_t; + +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t +{ + /** Extended Capabilities value */ + t_u8 ext_cap_value; +} MLAN_PACK_END ExtCap_t, *pExtCap_t; + +/** Overlapping BSS Scan Parameters Data */ +typedef struct MLAN_PACK_START _OverlapBSSScanParam_t +{ + /** OBSS Scan Passive Dwell in milliseconds */ + t_u16 obss_scan_passive_dwell; + /** OBSS Scan Active Dwell in milliseconds */ + t_u16 obss_scan_active_dwell; + /** BSS Channel Width Trigger Scan Interval in seconds */ + t_u16 bss_chan_width_trigger_scan_int; + /** OBSS Scan Passive Total Per Channel */ + t_u16 obss_scan_passive_total; + /** OBSS Scan Active Total Per Channel */ + t_u16 obss_scan_active_total; + /** BSS Width Channel Transition Delay Factor */ + t_u16 bss_width_chan_trans_delay; + /** OBSS Scan Activity Threshold */ + t_u16 obss_scan_active_threshold; +} MLAN_PACK_END OBSSScanParam_t, *pOBSSScanParam_t; + +/** HT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTCap_t +{ + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTCap struct */ + HTCap_t ht_cap; +} MLAN_PACK_END IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t; + +/** HT Information IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTInfo_t +{ + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t; + +/** 20/40 BSS Coexistence IE */ +typedef MLAN_PACK_START struct _IEEEtypes_2040BSSCo_t +{ + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t; + +/** Extended Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtCap_t +{ + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t; + +/** Overlapping BSS Scan Parameters IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OverlapBSSScanParam_t +{ + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END IEEEtypes_OverlapBSSScanParam_t, + *pIEEEtypes_OverlapBSSScanParam_t; + +/** Maximum number of subbands in the IEEEtypes_SupportedChannels_t structure */ +#define WLAN_11H_MAX_SUBBANDS 5 + +/** Maximum number of DFS channels configured in IEEEtypes_IBSS_DFS_t */ +#define WLAN_11H_MAX_IBSS_DFS_CHANNELS 25 + +/** IEEE Power Constraint element (7.3.2.15) */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 32 */ + t_u8 len; /**< Element length after id and len */ + t_u8 local_constraint; /**< Local power constraint applied to 11d chan info */ +} MLAN_PACK_END IEEEtypes_PowerConstraint_t; + +/** IEEE Power Capability element (7.3.2.16) */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 33 */ + t_u8 len; /**< Element length after id and len */ + t_s8 min_tx_power_capability; /**< Minimum Transmit power (dBm) */ + t_s8 max_tx_power_capability; /**< Maximum Transmit power (dBm) */ +} MLAN_PACK_END IEEEtypes_PowerCapability_t; + +/** IEEE TPC Report element (7.3.2.18) */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 35 */ + t_u8 len; /**< Element length after id and len */ + t_s8 tx_power; /**< Max power used to transmit the TPC Report frame (dBm) */ + t_s8 link_margin; /**< Link margin when TPC Request received (dB) */ +} MLAN_PACK_END IEEEtypes_TPCReport_t; + +/* IEEE Supported Channel sub-band description (7.3.2.19) */ +/** + * Sub-band description used in the supported channels element. + */ +typedef MLAN_PACK_START struct +{ + t_u8 start_chan; /**< Starting channel in the subband */ + t_u8 num_chans; /**< Number of channels in the subband */ + +} MLAN_PACK_END IEEEtypes_SupportChan_Subband_t; + +/* IEEE Supported Channel element (7.3.2.19) */ +/** + * Sent in association requests. Details the sub-bands and number + * of channels supported in each subband + */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 36 */ + t_u8 len; /**< Element length after id and len */ + + /** Configured sub-bands information in the element */ + IEEEtypes_SupportChan_Subband_t subband[WLAN_11H_MAX_SUBBANDS]; + +} MLAN_PACK_END IEEEtypes_SupportedChannels_t; + +/* IEEE Channel Switch Announcement Element (7.3.2.20) */ +/** + * Provided in beacons and probe responses. Used to advertise when + * and to which channel it is changing to. Only starting STAs in + * an IBSS and APs are allowed to originate a chan switch element. + */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 37 */ + t_u8 len; /**< Element length after id and len */ + t_u8 chan_switch_mode; /**< STA should not transmit any frames if 1 */ + t_u8 new_channel_num; /**< Channel # that AP/IBSS is moving to */ + t_u8 chan_switch_count; /**< # of TBTTs before channel switch */ + +} MLAN_PACK_END IEEEtypes_ChanSwitchAnn_t; + +/* IEEE Quiet Period Element (7.3.2.23) */ +/** + * Provided in beacons and probe responses. Indicates times during + * which the STA should not be transmitting data. Only starting STAs in + * an IBSS and APs are allowed to originate a quiet element. + */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 40 */ + t_u8 len; /**< Element length after id and len */ + t_u8 quiet_count; /**< Number of TBTTs until beacon with the quiet period */ + t_u8 quiet_period; /**< Regular quiet period, # of TBTTS between periods */ + t_u16 quiet_duration; /**< Duration of the quiet period in TUs */ + t_u16 quiet_offset; /**< Offset in TUs from the TBTT for the quiet period */ + +} MLAN_PACK_END IEEEtypes_Quiet_t; + +/** +*** @brief Map octet of the basic measurement report (7.3.2.22.1) +**/ +typedef MLAN_PACK_START struct +{ +#ifdef BIG_ENDIAN_SUPPORT + t_u8 rsvd5_7:3; /**< Reserved */ + t_u8 unmeasured:1; /**< Channel is unmeasured */ + t_u8 radar:1; /**< Radar detected on channel */ + t_u8 unidentified_sig:1; /**< Unidentified signal found on channel */ + t_u8 ofdm_preamble:1; /**< OFDM preamble detected on channel */ + t_u8 bss:1; /**< At least one valid MPDU received on channel */ +#else + t_u8 bss:1; /**< At least one valid MPDU received on channel */ + t_u8 ofdm_preamble:1; /**< OFDM preamble detected on channel */ + t_u8 unidentified_sig:1; /**< Unidentified signal found on channel */ + t_u8 radar:1; /**< Radar detected on channel */ + t_u8 unmeasured:1; /**< Channel is unmeasured */ + t_u8 rsvd5_7:3; /**< Reserved */ +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptBasicMap_t; + +/* IEEE DFS Channel Map field (7.3.2.24) */ +/** + * Used to list supported channels and provide a octet "map" field which + * contains a basic measurement report for that channel in the + * IEEEtypes_IBSS_DFS_t element + */ +typedef MLAN_PACK_START struct +{ + t_u8 channel_number; /**< Channel number */ + MeasRptBasicMap_t rpt_map; /**< Basic measurement report for the channel */ + +} MLAN_PACK_END IEEEtypes_ChannelMap_t; + +/* IEEE IBSS DFS Element (7.3.2.24) */ +/** + * IBSS DFS element included in ad hoc beacons and probe responses. + * Provides information regarding the IBSS DFS Owner as well as the + * originating STAs supported channels and basic measurement results. + */ +typedef MLAN_PACK_START struct +{ + t_u8 element_id; /**< IEEE Element ID = 41 */ + t_u8 len; /**< Element length after id and len */ + t_u8 dfs_owner[MLAN_MAC_ADDR_LENGTH]; /**< DFS Owner STA Address */ + t_u8 dfs_recovery_interval; /**< DFS Recovery time in TBTTs */ + + /** Variable length map field, one Map entry for each supported channel */ + IEEEtypes_ChannelMap_t channel_map[WLAN_11H_MAX_IBSS_DFS_CHANNELS]; + +} MLAN_PACK_END IEEEtypes_IBSS_DFS_t; + +/* 802.11h BSS information kept for each BSSID received in scan results */ +/** + * IEEE BSS information needed from scan results for later processing in + * join commands + */ +typedef struct +{ + t_u8 sensed_11h; /**< Capability bit set or 11h IE found in this BSS */ + + IEEEtypes_PowerConstraint_t power_constraint; /**< Power Constraint IE */ + IEEEtypes_PowerCapability_t power_capability; /**< Power Capability IE */ + IEEEtypes_TPCReport_t tpc_report; /**< TPC Report IE */ + IEEEtypes_ChanSwitchAnn_t chan_switch_ann; /**< Channel Switch Announcement IE */ + IEEEtypes_Quiet_t quiet; /**< Quiet IE */ + IEEEtypes_IBSS_DFS_t ibss_dfs; /**< IBSS DFS Element IE */ + +} wlan_11h_bss_info_t; + +#ifdef STA_SUPPORT +/** Macro for maximum size of scan response buffer */ +#define MAX_SCAN_RSP_BUF (16 * 1024) + +/** Maximum number of channels that can be sent in user scan config */ +#define WLAN_USER_SCAN_CHAN_MAX 50 + +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 + +/** Scan all the channels in specified band */ +#define BAND_SPECIFIED 0x80 + +/** + * IOCTL SSID List sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Used to specify SSID specific filters as well as SSID pattern matching + * filters for scan result processing in firmware. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_ssid +{ + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; +} MLAN_PACK_END wlan_user_scan_ssid; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_chan +{ + /** Channel Number to scan */ + t_u8 chan_number; + /** Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 radio_type; + /** Scan type: Active = 1, Passive = 2 */ + t_u8 scan_type; + /** Reserved */ + t_u8 reserved; + /** Scan duration in milliseconds; if 0 default used */ + t_u32 scan_time; +} MLAN_PACK_END wlan_user_scan_chan; + +/** + * Input structure to configure an immediate scan cmd to firmware + * + * Specifies a number of parameters to be used in general for the scan + * as well as a channel list (wlan_user_scan_chan) for each scan period + * desired. + */ +typedef MLAN_PACK_START struct +{ + /** + * Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + t_u8 keep_previous_scan; + /** + * BSS mode to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + /** + * Configure the number of probe requests for active chan scans + */ + t_u8 num_probes; + /** + * @brief Reserved + */ + t_u8 reserved; + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + /** + * SSID filter list used in the to limit the scan results + */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** + * Variable number (fixed maximum) of channels to scan up + */ + wlan_user_scan_chan chan_list[WLAN_USER_SCAN_CHAN_MAX]; +} MLAN_PACK_END wlan_user_scan_cfg; + +/** Default scan interval in millisecond*/ +#define DEFAULT_BGSCAN_INTERVAL 30000 + +/** action get all, except pps/uapsd config */ +#define BG_SCAN_ACT_GET 0x0000 +/** action set all, except pps/uapsd config */ +#define BG_SCAN_ACT_SET 0x0001 +/** action get pps/uapsd config */ +#define BG_SCAN_ACT_GET_PPS_UAPSD 0x0100 +/** action set pps/uapsd config */ +#define BG_SCAN_ACT_SET_PPS_UAPSD 0x0101 +/** action set all */ +#define BG_SCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define BG_SCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define BG_SCAN_SSID_RSSI_MATCH 0x0004 +/** Maximum number of channels that can be sent in bg scan config */ +#define WLAN_BG_SCAN_CHAN_MAX 32 + +/** + * Input structure to configure bs scan cmd to firmware + */ +typedef MLAN_PACK_START struct +{ + /** action */ + t_u16 action; + /** enable/disable */ + t_u8 enable; + /** BSS type: + * MLAN_SCAN_MODE_BSS (infrastructure) + * MLAN_SCAN_MODE_IBSS (adhoc) + * MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_type; + /** number of channel scanned during each scan */ + t_u8 chan_per_scan; + /** interval between consecutive scan */ + t_u32 scan_interval; + /** bit 0: ssid match bit 1: ssid match and SNR exceeded + * bit 2: ssid match and RSSI exceeded + * bit 31: wait for all channel scan to complete to report scan result + */ + t_u32 report_condition; + /* Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + /** RSSI threshold */ + t_u8 rssi_threshold; + /** SNR threshold */ + t_u8 snr_threshold; + /** repeat count */ + t_u16 repeat_count; + /** SSID filter list used in the to limit the scan results */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** Variable number (fixed maximum) of channels to scan up */ + wlan_user_scan_chan chan_list[WLAN_BG_SCAN_CHAN_MAX]; +} MLAN_PACK_END wlan_bgscan_cfg; +#endif /* STA_SUPPORT */ + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +/** BSSDescriptor_t + * Structure used to store information for beacon/probe response + */ +typedef struct _BSSDescriptor_t +{ + /** MAC address */ + mlan_802_11_mac_addr mac_address; + + /** SSID */ + mlan_802_11_ssid ssid; + + /** WEP encryption requirement */ + t_u32 privacy; + + /** Receive signal strength in dBm */ + t_s32 rssi; + + /** Channel */ + t_u32 channel; + + /** Freq */ + t_u32 freq; + + /** Beacon period */ + t_u16 beacon_period; + + /** ATIM window */ + t_u32 atim_window; + + /** ERP flags */ + t_u8 erp_flags; + + /** Type of network in use */ + WLAN_802_11_NETWORK_TYPE network_type_use; + + /** Network infrastructure mode */ + t_u32 bss_mode; + + /** Network supported rates */ + WLAN_802_11_RATES supported_rates; + + /** Supported data rates */ + t_u8 data_rates[WLAN_SUPPORTED_RATES]; + + /** Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + t_u16 bss_band; + + /** TSF timestamp from the current firmware TSF */ + t_u64 network_tsf; + + /** TSF value included in the beacon/probe response */ + t_u8 time_stamp[8]; + + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + + /** Capability information */ + IEEEtypes_CapInfo_t cap_info; + + /** WMM IE */ + IEEEtypes_WmmParameter_t wmm_ie; + + /** 802.11h BSS information */ + wlan_11h_bss_info_t wlan_11h_bss_info; + + /** Indicate disabling 11n when associate with AP */ + t_u8 disable_11n; + /** 802.11n BSS information */ + /** HT Capabilities IE */ + IEEEtypes_HTCap_t *pht_cap; + /** HT Capabilities Offset */ + t_u16 ht_cap_offset; + /** HT Information IE */ + IEEEtypes_HTInfo_t *pht_info; + /** HT Information Offset */ + t_u16 ht_info_offset; + /** 20/40 BSS Coexistence IE */ + IEEEtypes_2040BSSCo_t *pbss_co_2040; + /** 20/40 BSS Coexistence Offset */ + t_u16 bss_co_2040_offset; + /** Extended Capabilities IE */ + IEEEtypes_ExtCap_t *pext_cap; + /** Extended Capabilities Offset */ + t_u16 ext_cap_offset; + /** Overlapping BSS Scan Parameters IE */ + IEEEtypes_OverlapBSSScanParam_t *poverlap_bss_scan_param; + /** Overlapping BSS Scan Parameters Offset */ + t_u16 overlap_bss_offset; + +#ifdef STA_SUPPORT + /** Country information set */ + IEEEtypes_CountryInfoFullSet_t country_info; +#endif /* STA_SUPPORT */ + + /** WPA IE */ + IEEEtypes_VendorSpecific_t *pwpa_ie; + /** WPA IE offset in the beacon buffer */ + t_u16 wpa_offset; + /** RSN IE */ + IEEEtypes_Generic_t *prsn_ie; + /** RSN IE offset in the beacon buffer */ + t_u16 rsn_offset; +#ifdef STA_SUPPORT + /** WAPI IE */ + IEEEtypes_Generic_t *pwapi_ie; + /** WAPI IE offset in the beacon buffer */ + t_u16 wapi_offset; +#endif + + /** Pointer to the returned scan response */ + t_u8 *pbeacon_buf; + /** Length of the stored scan response */ + t_u32 beacon_buf_size; + /** Max allocated size for updated scan response */ + t_u32 beacon_buf_size_max; + +} BSSDescriptor_t, *pBSSDescriptor_t; + +#endif /* !_MLAN_IEEE_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/mlan_ioctl.h b/drivers/net/wireless/sd8797/mlinux/mlan_ioctl.h new file mode 100644 index 000000000000..964de9197ffe --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/mlan_ioctl.h @@ -0,0 +1,2981 @@ +/** @file mlan_ioctl.h + * + * @brief This file declares the IOCTL data structures and APIs. + * + * Copyright (C) 2008-2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_IOCTL_H_ +#define _MLAN_IOCTL_H_ + +/** Enumeration for IOCTL request ID */ +enum _mlan_ioctl_req_id +{ + /* Scan Group */ + MLAN_IOCTL_SCAN = 0x00010000, + MLAN_OID_SCAN_NORMAL, + MLAN_OID_SCAN_SPECIFIC_SSID, + MLAN_OID_SCAN_USER_CONFIG, + MLAN_OID_SCAN_CONFIG, + MLAN_OID_SCAN_GET_CURRENT_BSS, + MLAN_OID_SCAN_CANCEL, + MLAN_OID_SCAN_TABLE_FLUSH, + MLAN_OID_SCAN_BGSCAN_CONFIG, + /* BSS Configuration Group */ + MLAN_IOCTL_BSS = 0x00020000, + MLAN_OID_BSS_START, + MLAN_OID_BSS_STOP, + MLAN_OID_BSS_MODE, + MLAN_OID_BSS_CHANNEL, + MLAN_OID_BSS_CHANNEL_LIST, + MLAN_OID_BSS_MAC_ADDR, + MLAN_OID_BSS_MULTICAST_LIST, + MLAN_OID_BSS_FIND_BSS, + MLAN_OID_IBSS_BCN_INTERVAL, + MLAN_OID_IBSS_ATIM_WINDOW, + MLAN_OID_IBSS_CHANNEL, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_BSS_CONFIG, + MLAN_OID_UAP_DEAUTH_STA, + MLAN_OID_UAP_BSS_RESET, +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + MLAN_OID_BSS_ROLE, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_WIFI_DIRECT_MODE, +#endif + + /* Radio Configuration Group */ + MLAN_IOCTL_RADIO_CFG = 0x00030000, + MLAN_OID_RADIO_CTRL, + MLAN_OID_BAND_CFG, + MLAN_OID_ANT_CFG, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_REMAIN_CHAN_CFG, +#endif + + /* SNMP MIB Group */ + MLAN_IOCTL_SNMP_MIB = 0x00040000, + MLAN_OID_SNMP_MIB_RTS_THRESHOLD, + MLAN_OID_SNMP_MIB_FRAG_THRESHOLD, + MLAN_OID_SNMP_MIB_RETRY_COUNT, +#if defined(UAP_SUPPORT) + MLAN_OID_SNMP_MIB_DOT11D, + MLAN_OID_SNMP_MIB_DOT11H, +#endif + MLAN_OID_SNMP_MIB_DTIM_PERIOD, + + /* Status Information Group */ + MLAN_IOCTL_GET_INFO = 0x00050000, + MLAN_OID_GET_STATS, + MLAN_OID_GET_SIGNAL, + MLAN_OID_GET_FW_INFO, + MLAN_OID_GET_VER_EXT, + MLAN_OID_GET_BSS_INFO, + MLAN_OID_GET_DEBUG_INFO, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_STA_LIST, +#endif + + /* Security Configuration Group */ + MLAN_IOCTL_SEC_CFG = 0x00060000, + MLAN_OID_SEC_CFG_AUTH_MODE, + MLAN_OID_SEC_CFG_ENCRYPT_MODE, + MLAN_OID_SEC_CFG_WPA_ENABLED, + MLAN_OID_SEC_CFG_ENCRYPT_KEY, + MLAN_OID_SEC_CFG_PASSPHRASE, + MLAN_OID_SEC_CFG_EWPA_ENABLED, + MLAN_OID_SEC_CFG_ESUPP_MODE, + MLAN_OID_SEC_CFG_WAPI_ENABLED, + MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED, + + /* Rate Group */ + MLAN_IOCTL_RATE = 0x00070000, + MLAN_OID_RATE_CFG, + MLAN_OID_GET_DATA_RATE, + MLAN_OID_SUPPORTED_RATES, + + /* Power Configuration Group */ + MLAN_IOCTL_POWER_CFG = 0x00080000, + MLAN_OID_POWER_CFG, + MLAN_OID_POWER_CFG_EXT, + + /* Power Management Configuration Group */ + MLAN_IOCTL_PM_CFG = 0x00090000, + MLAN_OID_PM_CFG_IEEE_PS, + MLAN_OID_PM_CFG_HS_CFG, + MLAN_OID_PM_CFG_INACTIVITY_TO, + MLAN_OID_PM_CFG_DEEP_SLEEP, + MLAN_OID_PM_CFG_SLEEP_PD, + MLAN_OID_PM_CFG_PS_CFG, + MLAN_OID_PM_CFG_SLEEP_PARAMS, +#ifdef UAP_SUPPORT + MLAN_OID_PM_CFG_PS_MODE, +#endif /* UAP_SUPPORT */ + MLAN_OID_PM_INFO, + + /* WMM Configuration Group */ + MLAN_IOCTL_WMM_CFG = 0x000A0000, + MLAN_OID_WMM_CFG_ENABLE, + MLAN_OID_WMM_CFG_QOS, + MLAN_OID_WMM_CFG_ADDTS, + MLAN_OID_WMM_CFG_DELTS, + MLAN_OID_WMM_CFG_QUEUE_CONFIG, + MLAN_OID_WMM_CFG_QUEUE_STATS, + MLAN_OID_WMM_CFG_QUEUE_STATUS, + MLAN_OID_WMM_CFG_TS_STATUS, + + /* WPS Configuration Group */ + MLAN_IOCTL_WPS_CFG = 0x000B0000, + MLAN_OID_WPS_CFG_SESSION, + + /* 802.11n Configuration Group */ + MLAN_IOCTL_11N_CFG = 0x000C0000, + MLAN_OID_11N_CFG_TX, + MLAN_OID_11N_HTCAP_CFG, + MLAN_OID_11N_CFG_ADDBA_REJECT, + MLAN_OID_11N_CFG_AGGR_PRIO_TBL, + MLAN_OID_11N_CFG_ADDBA_PARAM, + MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE, + MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL, + MLAN_OID_11N_CFG_SUPPORTED_MCS_SET, + MLAN_OID_11N_CFG_TX_BF_CAP, + MLAN_OID_11N_CFG_TX_BF_CFG, + MLAN_OID_11N_CFG_STREAM_CFG, + + /* 802.11d Configuration Group */ + MLAN_IOCTL_11D_CFG = 0x000D0000, +#ifdef STA_SUPPORT + MLAN_OID_11D_CFG_ENABLE, + MLAN_OID_11D_CLR_CHAN_TABLE, +#endif /* STA_SUPPORT */ + MLAN_OID_11D_DOMAIN_INFO, + + /* Register Memory Access Group */ + MLAN_IOCTL_REG_MEM = 0x000E0000, + MLAN_OID_REG_RW, + MLAN_OID_EEPROM_RD, + MLAN_OID_MEM_RW, + + /* Multi-Radio Configuration Group */ + MLAN_IOCTL_MFR_CFG = 0x00100000, + + /* 802.11h Configuration Group */ + MLAN_IOCTL_11H_CFG = 0x00110000, + MLAN_OID_11H_CHANNEL_CHECK, + MLAN_OID_11H_LOCAL_POWER_CONSTRAINT, +#if defined(DFS_TESTING_SUPPORT) + MLAN_OID_11H_DFS_TESTING, +#endif + + /* Miscellaneous Configuration Group */ + MLAN_IOCTL_MISC_CFG = 0x00200000, + MLAN_OID_MISC_GEN_IE, + MLAN_OID_MISC_REGION, + MLAN_OID_MISC_WARM_RESET, +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + MLAN_OID_MISC_SDIO_MPA_CTRL, +#endif + MLAN_OID_MISC_HOST_CMD, + MLAN_OID_MISC_SYS_CLOCK, + MLAN_OID_MISC_SOFT_RESET, + MLAN_OID_MISC_WWS, + MLAN_OID_MISC_INIT_SHUTDOWN, + MLAN_OID_MISC_CUSTOM_IE, + MLAN_OID_MISC_TX_DATAPAUSE, + MLAN_OID_MISC_IP_ADDR, + MLAN_OID_MISC_MAC_CONTROL, + MLAN_OID_MISC_MEF_CFG, + MLAN_OID_MISC_CFP_CODE, + MLAN_OID_MISC_COUNTRY_CODE, + MLAN_OID_MISC_THERMAL, + MLAN_OID_MISC_RX_MGMT_IND, + MLAN_OID_MISC_SUBSCRIBE_EVENT, +#ifdef DEBUG_LEVEL1 + MLAN_OID_MISC_DRVDBG, +#endif + MLAN_OID_MISC_OTP_USER_DATA, +}; + +/** Sub command size */ +#define MLAN_SUB_COMMAND_SIZE 4 + +/** Enumeration for the action of IOCTL request */ +enum _mlan_act_ioctl +{ + MLAN_ACT_SET = 1, + MLAN_ACT_GET, + MLAN_ACT_CANCEL +}; + +/** Enumeration for generic enable/disable */ +enum _mlan_act_generic +{ + MLAN_ACT_DISABLE = 0, + MLAN_ACT_ENABLE = 1 +}; + +/** Enumeration for scan mode */ +enum _mlan_scan_mode +{ + MLAN_SCAN_MODE_UNCHANGED = 0, + MLAN_SCAN_MODE_BSS, + MLAN_SCAN_MODE_IBSS, + MLAN_SCAN_MODE_ANY +}; + +/** Enumeration for scan type */ +enum _mlan_scan_type +{ + MLAN_SCAN_TYPE_UNCHANGED = 0, + MLAN_SCAN_TYPE_ACTIVE, + MLAN_SCAN_TYPE_PASSIVE +}; + +/** Max number of supported rates */ +#define MLAN_SUPPORTED_RATES 32 + +/** RSSI scan */ +#define SCAN_RSSI(RSSI) (0x100 - ((t_u8)(RSSI))) + +/** Max passive scan time for each channel in milliseconds */ +#define MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME 2000 + +/** Max active scan time for each channel in milliseconds */ +#define MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME 500 + +/** Maximum number of probes to send on each channel */ +#define MAX_PROBES 4 + +/** Default number of probes to send on each channel */ +#define DEFAULT_PROBES 4 + +/** + * @brief Sub-structure passed in wlan_ioctl_get_scan_table_entry for each BSS + * + * Fixed field information returned for the scan response in the IOCTL + * response. + */ +typedef struct _wlan_get_scan_table_fixed +{ + /** BSSID of this network */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Channel this beacon/probe response was detected */ + t_u8 channel; + /** RSSI for the received packet */ + t_u8 rssi; + /** TSF value in microseconds from the firmware at packet reception */ + t_u64 network_tsf; +} wlan_get_scan_table_fixed; + +/** mlan_802_11_ssid data structure */ +typedef struct _mlan_802_11_ssid +{ + /** SSID Length */ + t_u32 ssid_len; + /** SSID information field */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; +} mlan_802_11_ssid, *pmlan_802_11_ssid; + +/** + * Sructure to retrieve the scan table + */ +typedef struct +{ + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures. + * Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} wlan_ioctl_get_scan_table_info; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_scan_table_entry +{ + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field_length + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[0]; */ +} wlan_ioctl_get_scan_table_entry; + +/** Type definition of mlan_scan_time_params */ +typedef struct _mlan_scan_time_params +{ + /** Scan channel time for specific scan in milliseconds */ + t_u32 specific_scan_time; + /** Scan channel time for active scan in milliseconds */ + t_u32 active_scan_time; + /** Scan channel time for passive scan in milliseconds */ + t_u32 passive_scan_time; +} mlan_scan_time_params, *pmlan_scan_time_params; + +/** Type definition of mlan_user_scan */ +typedef struct _mlan_user_scan +{ + /** Length of scan_cfg_buf */ + t_u32 scan_cfg_len; + /** Buffer of scan config */ + t_u8 scan_cfg_buf[1]; +} mlan_user_scan, *pmlan_user_scan; + +/** Type definition of mlan_scan_req */ +typedef struct _mlan_scan_req +{ + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan type */ + t_u32 scan_type; + /** SSID */ + mlan_802_11_ssid scan_ssid; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; +} mlan_scan_req, *pmlan_scan_req; + +/** Type defnition of mlan_scan_resp */ +typedef struct _mlan_scan_resp +{ + /** Number of scan result */ + t_u32 num_in_scan_table; + /** Scan table */ + t_u8 *pscan_table; + /* Age in seconds */ + t_u32 age_in_secs; +} mlan_scan_resp, *pmlan_scan_resp; + +/** Type definition of mlan_scan_cfg */ +typedef struct _mlan_scan_cfg +{ + /** Scan type */ + t_u32 scan_type; + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan probe */ + t_u32 scan_probe; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Extended Scan */ + t_u32 ext_scan; +} mlan_scan_cfg, *pmlan_scan_cfg; + +/** Type defnition of mlan_ds_scan for MLAN_IOCTL_SCAN */ +typedef struct _mlan_ds_scan +{ + /** Sub-command */ + t_u32 sub_command; + /** Scan request/response */ + union + { + /** Scan request */ + mlan_scan_req scan_req; + /** Scan response */ + mlan_scan_resp scan_resp; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; + /** Scan config parameters */ + mlan_scan_cfg scan_cfg; + } param; +} mlan_ds_scan, *pmlan_ds_scan; + +/*-----------------------------------------------------------------*/ +/** BSS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for BSS mode */ +enum _mlan_bss_mode +{ + MLAN_BSS_MODE_INFRA = 1, + MLAN_BSS_MODE_IBSS, + MLAN_BSS_MODE_AUTO +}; + +/** Maximum key length */ +#define MLAN_MAX_KEY_LENGTH 32 + +/** max Wmm AC queues */ +#define MAX_AC_QUEUES 4 + +/** Maximum atim window in milliseconds */ +#define MLAN_MAX_ATIM_WINDOW 50 + +/** Minimum beacon interval */ +#define MLAN_MIN_BEACON_INTERVAL 20 +/** Maximum beacon interval */ +#define MLAN_MAX_BEACON_INTERVAL 1000 +/** Default beacon interval */ +#define MLAN_BEACON_INTERVAL 100 + +/** Receive all packets */ +#define MLAN_PROMISC_MODE 1 +/** Receive multicast packets in multicast list */ +#define MLAN_MULTICAST_MODE 2 +/** Receive all multicast packets */ +#define MLAN_ALL_MULTI_MODE 4 + +/** Maximum size of multicast list */ +#define MLAN_MAX_MULTICAST_LIST_SIZE 32 + +/** mlan_multicast_list data structure for MLAN_OID_BSS_MULTICAST_LIST */ +typedef struct _mlan_multicast_list +{ + /** Multicast mode */ + t_u32 mode; + /** Number of multicast addresses in the list */ + t_u32 num_multicast_addr; + /** Multicast address list */ + mlan_802_11_mac_addr mac_list[MLAN_MAX_MULTICAST_LIST_SIZE]; +} mlan_multicast_list, *pmlan_multicast_list; + +/** Max channel */ +#define MLAN_MAX_CHANNEL 165 + +/** Maximum number of channels in table */ +#define MLAN_MAX_CHANNEL_NUM 128 + +/** Channel/frequence for MLAN_OID_BSS_CHANNEL */ +typedef struct _chan_freq +{ + /** Channel Number */ + t_u32 channel; + /** Frequency of this Channel */ + t_u32 freq; +} chan_freq; + +/** mlan_chan_list data structure for MLAN_OID_BSS_CHANNEL_LIST */ +typedef struct _mlan_chan_list +{ + /** Number of channel */ + t_u32 num_of_chan; + /** Channel-Frequency table */ + chan_freq cf[MLAN_MAX_CHANNEL_NUM]; +} mlan_chan_list; + +/** mlan_ssid_bssid data structure for MLAN_OID_BSS_START and MLAN_OID_BSS_FIND_BSS */ +typedef struct _mlan_ssid_bssid +{ + /** SSID */ + mlan_802_11_ssid ssid; + /** BSSID */ + mlan_802_11_mac_addr bssid; + /** index in BSSID list, start from 1 */ + t_u32 idx; +} mlan_ssid_bssid; + +#ifdef UAP_SUPPORT +/** Maximum packet forward control value */ +#define MAX_PKT_FWD_CTRL 15 +/** Maximum BEACON period */ +#define MAX_BEACON_PERIOD 4000 +/** Minimum BEACON period */ +#define MIN_BEACON_PERIOD 50 +/** Maximum DTIM period */ +#define MAX_DTIM_PERIOD 100 +/** Minimum DTIM period */ +#define MIN_DTIM_PERIOD 1 +/** Maximum TX Power Limit */ +#define MAX_TX_POWER 20 +/** Minimum TX Power Limit */ +#define MIN_TX_POWER 0 +/** MAX station count */ +#define MAX_STA_COUNT 10 +/** Maximum RTS threshold */ +#define MAX_RTS_THRESHOLD 2347 +/** Maximum fragmentation threshold */ +#define MAX_FRAG_THRESHOLD 2346 +/** Minimum fragmentation threshold */ +#define MIN_FRAG_THRESHOLD 256 +/** data rate 54 M */ +#define DATA_RATE_54M 108 +/** antenna A */ +#define ANTENNA_MODE_A 0 +/** antenna B */ +#define ANTENNA_MODE_B 1 +/** transmit antenna */ +#define TX_ANTENNA 1 +/** receive antenna */ +#define RX_ANTENNA 0 +/** Maximum stage out time */ +#define MAX_STAGE_OUT_TIME 864000 +/** Minimum stage out time */ +#define MIN_STAGE_OUT_TIME 300 +/** Maximum Retry Limit */ +#define MAX_RETRY_LIMIT 14 + +/** Maximum group key timer in seconds */ +#define MAX_GRP_TIMER 86400 + +/** Maximum value of 4 byte configuration */ +#define MAX_VALID_DWORD 0x7FFFFFFF /* (1 << 31) - 1 */ + +/** Band config ACS mode */ +#define BAND_CONFIG_ACS_MODE 0x40 +/** Band config manual */ +#define BAND_CONFIG_MANUAL 0x00 + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 + +/** auto data rate */ +#define DATA_RATE_AUTO 0 + +/**filter mode: disable */ +#define MAC_FILTER_MODE_DISABLE 0 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_ALLOW_MAC 1 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_BLOCK_MAC 2 +/** Maximum mac filter num */ +#define MAX_MAC_FILTER_NUM 16 + +/* Bitmap for protocol to use */ +/** No security */ +#define PROTOCOL_NO_SECURITY 0x01 +/** Static WEP */ +#define PROTOCOL_STATIC_WEP 0x02 +/** WPA */ +#define PROTOCOL_WPA 0x08 +/** WPA2 */ +#define PROTOCOL_WPA2 0x20 +/** WP2 Mixed */ +#define PROTOCOL_WPA2_MIXED 0x28 +/** EAP */ +#define PROTOCOL_EAP 0x40 +/** WAPI */ +#define PROTOCOL_WAPI 0x80 + +/** Key_mgmt_psk */ +#define KEY_MGMT_NONE 0x04 +/** Key_mgmt_none */ +#define KEY_MGMT_PSK 0x02 +/** Key_mgmt_eap */ +#define KEY_MGMT_EAP 0x01 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c + +/** Channel List Entry */ +typedef struct _channel_list +{ + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + t_u8 band_config_type; +} scan_chan_list; + +/** mac_filter data structure */ +typedef struct _mac_filter +{ + /** mac filter mode */ + t_u16 filter_mode; + /** mac adress count */ + t_u16 mac_count; + /** mac address list */ + mlan_802_11_mac_addr mac_list[MAX_MAC_FILTER_NUM]; +} mac_filter; + +/** wpa parameter */ +typedef struct _wpa_param +{ + /** Pairwise cipher WPA */ + t_u8 pairwise_cipher_wpa; + /** Pairwise cipher WPA2 */ + t_u8 pairwise_cipher_wpa2; + /** group cipher */ + t_u8 group_cipher; + /** RSN replay protection */ + t_u8 rsn_protection; + /** passphrase length */ + t_u32 length; + /** passphrase */ + t_u8 passphrase[64]; + /**group key rekey time in seconds */ + t_u32 gk_rekey_time; +} wpa_param; + +/** wep key */ +typedef struct _wep_key +{ + /** key index 0-3 */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** length */ + t_u16 length; + /** key data */ + t_u8 key[26]; +} wep_key; + +/** wep param */ +typedef struct _wep_param +{ + /** key 0 */ + wep_key key0; + /** key 1 */ + wep_key key1; + /** key 2 */ + wep_key key2; + /** key 3 */ + wep_key key3; +} wep_param; + +/** Data structure of WMM QoS information */ +typedef struct _wmm_qos_info_t +{ +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_qos_info_t, *pwmm_qos_info_t; + +/** Data structure of WMM ECW */ +typedef struct _wmm_ecw_t +{ +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_ecw_t, *pwmm_ecw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _wmm_aci_aifsn_t +{ +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_aci_aifsn_t, *pwmm_aci_aifsn_t; + +/** Data structure of WMM AC parameters */ +typedef struct _wmm_ac_parameters_t +{ + wmm_aci_aifsn_t aci_aifsn; /**< AciAifSn */ + wmm_ecw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} wmm_ac_parameters_t, *pwmm_ac_parameters_t; + +/** Data structure of WMM parameter IE */ +typedef struct _wmm_parameter_t +{ + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +} wmm_parameter_t, *pwmm_parameter_t; + +/** mlan_bss_param + * Note: For each entry you must enter an invalid value + * in the MOAL function woal_set_sys_config_invalid_data(). + * Otherwise for a valid data an unwanted TLV will be + * added to that command. + */ +typedef struct _mlan_uap_bss_param +{ + /** AP mac addr */ + mlan_802_11_mac_addr mac_addr; + /** SSID */ + mlan_802_11_ssid ssid; + /** Broadcast ssid control */ + t_u8 bcast_ssid_ctl; + /** Radio control: on/off */ + t_u8 radio_ctl; + /** dtim period */ + t_u8 dtim_period; + /** beacon period */ + t_u16 beacon_period; + /** rates */ + t_u8 rates[MAX_DATA_RATES]; + /** Tx data rate */ + t_u16 tx_data_rate; + /** multicast/broadcast data rate */ + t_u16 mcbc_data_rate; + /** Tx power level in dBm */ + t_u8 tx_power_level; + /** Tx antenna */ + t_u8 tx_antenna; + /** Rx antenna */ + t_u8 rx_antenna; + /** packet forward control */ + t_u8 pkt_forward_ctl; + /** max station count */ + t_u16 max_sta_count; + /** mac filter */ + mac_filter filter; + /** station ageout timer in unit of 100ms */ + t_u32 sta_ageout_timer; + /** PS station ageout timer in unit of 100ms */ + t_u32 ps_sta_ageout_timer; + /** RTS threshold */ + t_u16 rts_threshold; + /** fragmentation threshold */ + t_u16 frag_threshold; + /** retry_limit */ + t_u16 retry_limit; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; + /** pairwise handshake retries */ + t_u32 pwk_retries; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; + /** groupwise handshake retries */ + t_u32 gwk_retries; + /** preamble type */ + t_u8 preamble_type; + /** band cfg */ + t_u8 band_cfg; + /** channel */ + t_u8 channel; + /** auth mode */ + t_u16 auth_mode; + /** encryption protocol */ + t_u16 protocol; + /** key managment type */ + t_u16 key_mgmt; + /** wep param */ + wep_param wep_cfg; + /** wpa param */ + wpa_param wpa_cfg; + /** Mgmt IE passthru mask */ + t_u32 mgmt_ie_passthru_mask; + /* + * 11n HT Cap HTCap_t ht_cap + */ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Enable 2040 Coex */ + t_u8 enable_2040coex; + /** key management operation */ + t_u16 key_mgmt_operation; + /** BSS status */ + t_u16 bss_status; +#ifdef WIFI_DIRECT_SUPPORT + /* pre shared key */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +#endif /* WIFI_DIRECT_SUPPORT */ + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; + /** Wmm parameters */ + wmm_parameter_t wmm_para; +} mlan_uap_bss_param; + +/** mlan_deauth_param */ +typedef struct _mlan_deauth_param +{ + /** STA mac addr */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** deauth reason */ + t_u16 reason_code; +} mlan_deauth_param; +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** mode: disable wifi direct */ +#define WIFI_DIRECT_MODE_DISABLE 0 +/** mode: listen */ +#define WIFI_DIRECT_MODE_LISTEN 1 +/** mode: GO */ +#define WIFI_DIRECT_MODE_GO 2 +/** mode: client */ +#define WIFI_DIRECT_MODE_CLIENT 3 +/** mode: find */ +#define WIFI_DIRECT_MODE_FIND 4 +/** mode: stop find */ +#define WIFI_DIRECT_MODE_STOP_FIND 5 +#endif + +/** Type definition of mlan_ds_bss for MLAN_IOCTL_BSS */ +typedef struct _mlan_ds_bss +{ + /** Sub-command */ + t_u32 sub_command; + /** BSS parameter */ + union + { + /** SSID-BSSID for MLAN_OID_BSS_START */ + mlan_ssid_bssid ssid_bssid; + /** BSSID for MLAN_OID_BSS_STOP */ + mlan_802_11_mac_addr bssid; + /** BSS mode for MLAN_OID_BSS_MODE */ + t_u32 bss_mode; + /** BSS channel/frequency for MLAN_OID_BSS_CHANNEL */ + chan_freq bss_chan; + /** BSS channel list for MLAN_OID_BSS_CHANNEL_LIST */ + mlan_chan_list chanlist; + /** MAC address for MLAN_OID_BSS_MAC_ADDR */ + mlan_802_11_mac_addr mac_addr; + /** Multicast list for MLAN_OID_BSS_MULTICAST_LIST */ + mlan_multicast_list multicast_list; + /** Beacon interval for MLAN_OID_IBSS_BCN_INTERVAL */ + t_u32 bcn_interval; + /** ATIM window for MLAN_OID_IBSS_ATIM_WINDOW */ + t_u32 atim_window; +#ifdef UAP_SUPPORT + /** BSS param for AP mode */ + mlan_uap_bss_param bss_config; + /** deauth param for MLAN_OID_UAP_DEAUTH_STA */ + mlan_deauth_param deauth_param; +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + /** BSS role */ + t_u8 bss_role; +#endif +#ifdef WIFI_DIRECT_SUPPORT + t_u16 wfd_mode; +#endif + } param; +} mlan_ds_bss, *pmlan_ds_bss; + +/*-----------------------------------------------------------------*/ +/** Radio Control Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for band */ +enum _mlan_band_def +{ + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, +}; + +/** NO secondary channel */ +#define NO_SEC_CHANNEL 0 +/** secondary channel is above primary channel */ +#define SEC_CHANNEL_ABOVE 1 +/** secondary channel is below primary channel */ +#define SEC_CHANNEL_BELOW 3 +/** Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 + +/** Type definition of mlan_ds_band_cfg for MLAN_OID_BAND_CFG */ +typedef struct _mlan_ds_band_cfg +{ + /** Infra band */ + t_u32 config_bands; + /** Ad-hoc start band */ + t_u32 adhoc_start_band; + /** Ad-hoc start channel */ + t_u32 adhoc_channel; + /** Ad-hoc channel bandwidth */ + t_u32 sec_chan_offset; + /** fw supported band */ + t_u32 fw_bands; +} mlan_ds_band_cfg; + +/** Type definition of mlan_ds_ant_cfg for MLAN_OID_ANT_CFG */ +typedef struct _mlan_ds_ant_cfg +{ + /** Tx antenna mode */ + t_u32 tx_antenna; + /** Rx antenna mode */ + t_u32 rx_antenna; +} mlan_ds_ant_cfg, *pmlan_ds_ant_cfg; + +#ifdef WIFI_DIRECT_SUPPORT +/** Type definition of mlan_ds_remain_chan for MLAN_OID_REMAIN_CHAN_CFG */ +typedef struct _mlan_ds_remain_chan +{ + /** remove flag */ + t_u16 remove; + /** status */ + t_u8 status; + /** Band cfg */ + t_u8 bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} mlan_ds_remain_chan, *pmlan_ds_remain_chan; +#endif + +/** Type definition of mlan_ds_radio_cfg for MLAN_IOCTL_RADIO_CFG */ +typedef struct _mlan_ds_radio_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** Radio control parameter */ + union + { + /** Radio on/off for MLAN_OID_RADIO_CTRL */ + t_u32 radio_on_off; + /** Band info for MLAN_OID_BAND_CFG */ + mlan_ds_band_cfg band_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg ant_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + t_u32 antenna; +#ifdef WIFI_DIRECT_SUPPORT + /** remain on channel for MLAN_OID_REMAIN_CHAN_CFG */ + mlan_ds_remain_chan remain_chan; +#endif + } param; +} mlan_ds_radio_cfg, *pmlan_ds_radio_cfg; + +/*-----------------------------------------------------------------*/ +/** SNMP MIB Group */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_snmp_mib for MLAN_IOCTL_SNMP_MIB */ +typedef struct _mlan_ds_snmp_mib +{ + /** Sub-command */ + t_u32 sub_command; + /** SNMP MIB parameter */ + union + { + /** RTS threshold for MLAN_OID_SNMP_MIB_RTS_THRESHOLD */ + t_u32 rts_threshold; + /** Fragment threshold for MLAN_OID_SNMP_MIB_FRAG_THRESHOLD */ + t_u32 frag_threshold; + /** Retry count for MLAN_OID_SNMP_MIB_RETRY_COUNT */ + t_u32 retry_count; +#if defined(UAP_SUPPORT) + /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ + t_u32 oid_value; +#endif + /** DTIM period for MLAN_OID_SNMP_MIB_DTIM_PERIOD */ + t_u32 dtim_period; + } param; +} mlan_ds_snmp_mib, *pmlan_ds_snmp_mib; + +/*-----------------------------------------------------------------*/ +/** Status Information Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for ad-hoc status */ +enum _mlan_adhoc_status +{ + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED, ADHOC_STARTING +}; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_stats +{ + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; +} mlan_ds_get_stats, *pmlan_ds_get_stats; + +/** Type definition of mlan_ds_uap_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_uap_stats +{ + /** tkip mic failures */ + t_u32 tkip_mic_failures; + /** ccmp decrypt errors */ + t_u32 ccmp_decrypt_errors; + /** wep undecryptable count */ + t_u32 wep_undecryptable_count; + /** wep icv error count */ + t_u32 wep_icv_error_count; + /** decrypt failure count */ + t_u32 decrypt_failure_count; + /** dot11 multicast tx count */ + t_u32 mcast_tx_count; + /** dot11 failed count */ + t_u32 failed_count; + /** dot11 retry count */ + t_u32 retry_count; + /** dot11 multi retry count */ + t_u32 multi_retry_count; + /** dot11 frame duplicate count */ + t_u32 frame_dup_count; + /** dot11 rts success count */ + t_u32 rts_success_count; + /** dot11 rts failure count */ + t_u32 rts_failure_count; + /** dot11 ack failure count */ + t_u32 ack_failure_count; + /** dot11 rx ragment count */ + t_u32 rx_fragment_count; + /** dot11 mcast rx frame count */ + t_u32 mcast_rx_frame_count; + /** dot11 fcs error count */ + t_u32 fcs_error_count; + /** dot11 tx frame count */ + t_u32 tx_frame_count; + /** dot11 rsna tkip cm invoked */ + t_u32 rsna_tkip_cm_invoked; + /** dot11 rsna 4way handshake failures */ + t_u32 rsna_4way_hshk_failures; +} mlan_ds_uap_stats, *pmlan_ds_uap_stats; + +/** Mask of last beacon RSSI */ +#define BCN_RSSI_LAST_MASK 0x00000001 +/** Mask of average beacon RSSI */ +#define BCN_RSSI_AVG_MASK 0x00000002 +/** Mask of last data RSSI */ +#define DATA_RSSI_LAST_MASK 0x00000004 +/** Mask of average data RSSI */ +#define DATA_RSSI_AVG_MASK 0x00000008 +/** Mask of last beacon SNR */ +#define BCN_SNR_LAST_MASK 0x00000010 +/** Mask of average beacon SNR */ +#define BCN_SNR_AVG_MASK 0x00000020 +/** Mask of last data SNR */ +#define DATA_SNR_LAST_MASK 0x00000040 +/** Mask of average data SNR */ +#define DATA_SNR_AVG_MASK 0x00000080 +/** Mask of last beacon NF */ +#define BCN_NF_LAST_MASK 0x00000100 +/** Mask of average beacon NF */ +#define BCN_NF_AVG_MASK 0x00000200 +/** Mask of last data NF */ +#define DATA_NF_LAST_MASK 0x00000400 +/** Mask of average data NF */ +#define DATA_NF_AVG_MASK 0x00000800 +/** Mask of all RSSI_INFO */ +#define ALL_RSSI_INFO_MASK 0x00000fff + +/** Type definition of mlan_ds_get_signal for MLAN_OID_GET_SIGNAL */ +typedef struct _mlan_ds_get_signal +{ + /** Selector of get operation */ + /* + * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, + * Bit2: Last Data RSSI, Bit3: Average Data RSSI, + * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, + * Bit6: Last Data SNR, Bit7: Average Data SNR, + * Bit8: Last Beacon NF, Bit9: Average Beacon NF, + * Bit10: Last Data NF, Bit11: Average Data NF + */ + t_u16 selector; + + /** RSSI */ + /** RSSI of last beacon */ + t_s16 bcn_rssi_last; + /** RSSI of beacon average */ + t_s16 bcn_rssi_avg; + /** RSSI of last data packet */ + t_s16 data_rssi_last; + /** RSSI of data packet average */ + t_s16 data_rssi_avg; + + /** SNR */ + /** SNR of last beacon */ + t_s16 bcn_snr_last; + /** SNR of beacon average */ + t_s16 bcn_snr_avg; + /** SNR of last data packet */ + t_s16 data_snr_last; + /** SNR of data packet average */ + t_s16 data_snr_avg; + + /** NF */ + /** NF of last beacon */ + t_s16 bcn_nf_last; + /** NF of beacon average */ + t_s16 bcn_nf_avg; + /** NF of last data packet */ + t_s16 data_nf_last; + /** NF of data packet average */ + t_s16 data_nf_avg; +} mlan_ds_get_signal, *pmlan_ds_get_signal; + +/** mlan_fw_info data structure for MLAN_OID_GET_FW_INFO */ +typedef struct _mlan_fw_info +{ + /** Firmware version */ + t_u32 fw_ver; + /** MAC address */ + mlan_802_11_mac_addr mac_addr; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** fw supported band */ + t_u8 fw_bands; +} mlan_fw_info, *pmlan_fw_info; + +/** Version string buffer length */ +#define MLAN_MAX_VER_STR_LEN 128 + +/** mlan_ver_ext data structure for MLAN_OID_GET_VER_EXT */ +typedef struct _mlan_ver_ext +{ + /** Selected version string */ + t_u32 version_str_sel; + /** Version string */ + char version_str[MLAN_MAX_VER_STR_LEN]; +} mlan_ver_ext, *pmlan_ver_ext; + +/** mlan_bss_info data structure for MLAN_OID_GET_BSS_INFO */ +typedef struct _mlan_bss_info +{ + /** BSS mode */ + t_u32 bss_mode; + /** SSID */ + mlan_802_11_ssid ssid; + /** Table index */ + t_u32 scan_table_idx; + /** Channel */ + t_u32 bss_chan; + /** Band */ + t_u8 bss_band; + /** Region code */ + t_u32 region_code; + /** Connection status */ + t_u32 media_connected; + /** Radio on */ + t_u32 radio_on; + /** Max power level in dBm */ + t_u32 max_power_level; + /** Min power level in dBm */ + t_u32 min_power_level; + /** Adhoc state */ + t_u32 adhoc_state; + /** NF of last beacon */ + t_s32 bcn_nf_last; + /** wep status */ + t_u32 wep_status; + /** Host Sleep configured flag */ + t_u32 is_hs_configured; + /** Deep Sleep flag */ + t_u32 is_deep_sleep; + /** BSSID */ + mlan_802_11_mac_addr bssid; +#ifdef STA_SUPPORT + /** Capability Info */ + t_u16 capability_info; + /** Beacon Interval */ + t_u16 beacon_interval; + /** Listen Interval */ + t_u16 listen_interval; + /** Association Id */ + t_u16 assoc_id; + /** AP/Peer supported rates */ + t_u8 peer_supp_rates[MLAN_SUPPORTED_RATES]; +#endif /* STA_SUPPORT */ +} mlan_bss_info, *pmlan_bss_info; + +/** MAXIMUM number of TID */ +#define MAX_NUM_TID 8 + +/** Max RX Win size */ +#define MAX_RX_WINSIZE 64 + +/** rx_reorder_tbl */ +typedef struct +{ + /** TID */ + t_u16 tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + t_u32 start_win; + /** Window size */ + t_u32 win_size; + /** amsdu flag */ + t_u8 amsdu; + /** buffer status */ + t_u32 buffer[MAX_RX_WINSIZE]; +} rx_reorder_tbl; + +/** tx_ba_stream_tbl */ +typedef struct +{ + /** TID */ + t_u16 tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** amsdu flag */ + t_u8 amsdu; +} tx_ba_stream_tbl; + +/** Debug command number */ +#define DBG_CMD_NUM 5 + +/** mlan_debug_info data structure for MLAN_OID_GET_DEBUG_INFO */ +typedef struct _mlan_debug_info +{ + /* WMM AC_BK count */ + t_u32 wmm_ac_bk; + /* WMM AC_BE count */ + t_u32 wmm_ac_be; + /* WMM AC_VI count */ + t_u32 wmm_ac_vi; + /* WMM AC_VO count */ + t_u32 wmm_ac_vo; + /** Corresponds to max_tx_buf_size member of mlan_adapter*/ + t_u32 max_tx_buf_size; + /** Corresponds to tx_buf_size member of mlan_adapter*/ + t_u32 tx_buf_size; + /** Corresponds to curr_tx_buf_size member of mlan_adapter*/ + t_u32 curr_tx_buf_size; + /** Tx table num */ + t_u32 tx_tbl_num; + /** Tx ba stream table */ + tx_ba_stream_tbl tx_tbl[MLAN_MAX_TX_BASTREAM_SUPPORTED]; + /** Rx table num */ + t_u32 rx_tbl_num; + /** Rx reorder table*/ + rx_reorder_tbl rx_tbl[MLAN_MAX_RX_BASTREAM_SUPPORTED]; + /** Corresponds to ps_mode member of mlan_adapter */ + t_u16 ps_mode; + /** Corresponds to ps_state member of mlan_adapter */ + t_u32 ps_state; +#ifdef STA_SUPPORT + /** Corresponds to is_deep_sleep member of mlan_adapter */ + t_u8 is_deep_sleep; +#endif /** STA_SUPPORT */ + /** Corresponds to pm_wakeup_card_req member of mlan_adapter */ + t_u8 pm_wakeup_card_req; + /** Corresponds to pm_wakeup_fw_try member of mlan_adapter */ + t_u32 pm_wakeup_fw_try; + /** Corresponds to is_hs_configured member of mlan_adapter */ + t_u8 is_hs_configured; + /** Corresponds to hs_activated member of mlan_adapter */ + t_u8 hs_activated; + /** Corresponds to pps_uapsd_mode member of mlan_adapter */ + t_u16 pps_uapsd_mode; + /** Corresponds to sleep_period.period member of mlan_adapter */ + t_u16 sleep_pd; + /** Corresponds to wmm_qosinfo member of mlan_private */ + t_u8 qos_cfg; + /** Corresponds to tx_lock_flag member of mlan_adapter */ + t_u8 tx_lock_flag; + /** Corresponds to port_open member of mlan_private */ + t_u8 port_open; + /** Corresponds to scan_processing member of mlan_adapter */ + t_u32 scan_processing; + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of Tx timeouts */ + t_u32 num_tx_timeout; + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + + /** Corresponds to data_sent member of mlan_adapter */ + t_u8 data_sent; + /** Corresponds to cmd_sent member of mlan_adapter */ + t_u8 cmd_sent; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; + /** Corresponds to cmdresp_received member of mlan_adapter */ + t_u8 cmd_resp_received; + /** Corresponds to event_received member of mlan_adapter */ + t_u8 event_received; + /** pendig tx pkts */ + t_u32 tx_pkts_queued; +#ifdef UAP_SUPPORT + /** pending bridge pkts */ + t_u16 num_bridge_pkts; + /** dropped pkts */ + t_u32 num_drop_pkts; +#endif +} mlan_debug_info, *pmlan_debug_info; + +#ifdef UAP_SUPPORT +/** Maximum number of clients supported by AP */ +#define MAX_NUM_CLIENTS MAX_STA_COUNT + +/** station info */ +typedef struct _sta_info +{ + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power mfg status */ + t_u8 power_mfg_status; + /** RSSI */ + t_s8 rssi; +} sta_info; + +/** mlan_ds_sta_list structure for MLAN_OID_UAP_STA_LIST */ +typedef struct _mlan_ds_sta_list +{ + /** station count */ + t_u16 sta_count; + /** station list */ + sta_info info[MAX_NUM_CLIENTS]; +} mlan_ds_sta_list, *pmlan_ds_sta_list; +#endif + +/** Type definition of mlan_ds_get_info for MLAN_IOCTL_GET_INFO */ +typedef struct _mlan_ds_get_info +{ + /** Sub-command */ + t_u32 sub_command; + + /** Status information parameter */ + union + { + /** Signal information for MLAN_OID_GET_SIGNAL */ + mlan_ds_get_signal signal; + /** Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_get_stats stats; + /** Firmware information for MLAN_OID_GET_FW_INFO */ + mlan_fw_info fw_info; + /** Extended version information for MLAN_OID_GET_VER_EXT */ + mlan_ver_ext ver_ext; + /** BSS information for MLAN_OID_GET_BSS_INFO */ + mlan_bss_info bss_info; + /** Debug information for MLAN_OID_GET_DEBUG_INFO */ + mlan_debug_info debug_info; +#ifdef UAP_SUPPORT + /** UAP Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_uap_stats ustats; + /** UAP station list for MLAN_OID_UAP_STA_LIST */ + mlan_ds_sta_list sta_list; +#endif + } param; +} mlan_ds_get_info, *pmlan_ds_get_info; + +/*-----------------------------------------------------------------*/ +/** Security Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for authentication mode */ +enum _mlan_auth_mode +{ + MLAN_AUTH_MODE_OPEN = 0x00, + MLAN_AUTH_MODE_SHARED = 0x01, + MLAN_AUTH_MODE_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +/** Enumeration for encryption mode */ +enum _mlan_encryption_mode +{ + MLAN_ENCRYPTION_MODE_NONE = 0, + MLAN_ENCRYPTION_MODE_WEP40 = 1, + MLAN_ENCRYPTION_MODE_TKIP = 2, + MLAN_ENCRYPTION_MODE_CCMP = 3, + MLAN_ENCRYPTION_MODE_WEP104 = 4, +}; + +/** Enumeration for PSK */ +enum _mlan_psk_type +{ + MLAN_PSK_PASSPHRASE = 1, + MLAN_PSK_PMK, + MLAN_PSK_CLEAR, + MLAN_PSK_QUERY, +}; + +/** The bit to indicate the key is for unicast */ +#define MLAN_KEY_INDEX_UNICAST 0x40000000 +/** The key index to indicate default key */ +#define MLAN_KEY_INDEX_DEFAULT 0x000000ff +/** Maximum key length */ +// #define MLAN_MAX_KEY_LENGTH 32 +/** Minimum passphrase length */ +#define MLAN_MIN_PASSPHRASE_LENGTH 8 +/** Maximum passphrase length */ +#define MLAN_MAX_PASSPHRASE_LENGTH 63 +/** PMK length */ +#define MLAN_PMK_HEXSTR_LENGTH 64 +/* A few details needed for WEP (Wireless Equivalent Privacy) */ +/** 104 bits */ +#define MAX_WEP_KEY_SIZE 13 +/** 40 bits RC4 - WEP */ +#define MIN_WEP_KEY_SIZE 5 +/** packet number size */ +#define PN_SIZE 16 +/** max seq size of wpa/wpa2 key */ +#define SEQ_MAX_SIZE 8 + +/** key flag for tx_seq */ +#define KEY_FLAG_TX_SEQ_VALID 0x00000001 +/** key flag for rx_seq */ +#define KEY_FLAG_RX_SEQ_VALID 0x00000002 +/** key flag for group key */ +#define KEY_FLAG_GROUP_KEY 0x00000004 +/** key flag for tx and rx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** key flag for remove key */ +#define KEY_FLAG_REMOVE_KEY 0x80000000 + +/** Type definition of mlan_ds_encrypt_key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ +typedef struct _mlan_ds_encrypt_key +{ + /** Key disabled, all other fields will be ignore when this flag set to MTRUE */ + t_u32 key_disable; + /** key removed flag, when this flag is set to MTRUE, only key_index will be check */ + t_u32 key_remove; + /** Key index, used as current tx key index when is_current_wep_key is set to MTRUE */ + t_u32 key_index; + /** Current Tx key flag */ + t_u32 is_current_wep_key; + /** Key length */ + t_u32 key_len; + /** Key */ + t_u8 key_material[MLAN_MAX_KEY_LENGTH]; + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** wapi key flag */ + t_u32 is_wapi_key; + /** Initial packet number */ + t_u8 pn[PN_SIZE]; + /** key flags */ + t_u32 key_flags; +} mlan_ds_encrypt_key, *pmlan_ds_encrypt_key; + +/** Type definition of mlan_passphrase_t */ +typedef struct _mlan_passphrase_t +{ + /** Length of passphrase */ + t_u32 passphrase_len; + /** Passphrase */ + t_u8 passphrase[MLAN_MAX_PASSPHRASE_LENGTH]; +} mlan_passphrase_t; + +/** Type defnition of mlan_pmk_t */ +typedef struct _mlan_pmk_t +{ + /** PMK */ + t_u8 pmk[MLAN_MAX_KEY_LENGTH]; +} mlan_pmk_t; + +/** Embedded supplicant RSN type: No RSN */ +#define RSN_TYPE_NO_RSN MBIT(0) +/** Embedded supplicant RSN type: WPA */ +#define RSN_TYPE_WPA MBIT(3) +/** Embedded supplicant RSN type: WPA-NONE */ +#define RSN_TYPE_WPANONE MBIT(4) +/** Embedded supplicant RSN type: WPA2 */ +#define RSN_TYPE_WPA2 MBIT(5) +/** Embedded supplicant RSN type: RFU */ +#define RSN_TYPE_VALID_BITS (RSN_TYPE_NO_RSN | RSN_TYPE_WPA | RSN_TYPE_WPANONE | RSN_TYPE_WPA2) + +/** Embedded supplicant cipher type: TKIP */ +#define EMBED_CIPHER_TKIP MBIT(2) +/** Embedded supplicant cipher type: AES */ +#define EMBED_CIPHER_AES MBIT(3) +/** Embedded supplicant cipher type: RFU */ +#define EMBED_CIPHER_VALID_BITS (EMBED_CIPHER_TKIP | EMBED_CIPHER_AES) + +/** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ +typedef struct _mlan_ds_passphrase +{ + /** SSID may be used */ + mlan_802_11_ssid ssid; + /** BSSID may be used */ + mlan_802_11_mac_addr bssid; + /** Flag for passphrase or pmk used */ + t_u16 psk_type; + /** Passphrase or PMK */ + union + { + /** Passphrase */ + mlan_passphrase_t passphrase; + /** PMK */ + mlan_pmk_t pmk; + } psk; +} mlan_ds_passphrase, *pmlan_ds_passphrase; + +/** Type definition of mlan_ds_esupp_mode for MLAN_OID_SEC_CFG_ESUPP_MODE */ +typedef struct _mlan_ds_ewpa_mode +{ + /** RSN mode */ + t_u32 rsn_mode; + /** Active pairwise cipher */ + t_u32 act_paircipher; + /** Active pairwise cipher */ + t_u32 act_groupcipher; +} mlan_ds_esupp_mode, *pmlan_ds_esupp_mode; + +/** Type definition of mlan_ds_sec_cfg for MLAN_IOCTL_SEC_CFG */ +typedef struct _mlan_ds_sec_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** Security configuration parameter */ + union + { + /** Authentication mode for MLAN_OID_SEC_CFG_AUTH_MODE */ + t_u32 auth_mode; + /** Encryption mode for MLAN_OID_SEC_CFG_ENCRYPT_MODE */ + t_u32 encrypt_mode; + /** WPA enabled flag for MLAN_OID_SEC_CFG_WPA_ENABLED */ + t_u32 wpa_enabled; + /** WAPI enabled flag for MLAN_OID_SEC_CFG_WAPI_ENABLED */ + t_u32 wapi_enabled; + /** Port Control enabled flag for MLAN_OID_SEC_CFG_PORT_CTRL */ + t_u32 port_ctrl_enabled; + /** Encryption key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ + mlan_ds_encrypt_key encrypt_key; + /** Passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ + mlan_ds_passphrase passphrase; + /** Embedded supplicant WPA enabled flag for MLAN_OID_SEC_CFG_EWPA_ENABLED */ + t_u32 ewpa_enabled; + /** Embedded supplicant mode for MLAN_OID_SEC_CFG_ESUPP_MODE */ + mlan_ds_esupp_mode esupp_mode; + } param; +} mlan_ds_sec_cfg, *pmlan_ds_sec_cfg; + +/*-----------------------------------------------------------------*/ +/** Rate Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for rate type */ +enum _mlan_rate_type +{ + MLAN_RATE_INDEX, + MLAN_RATE_VALUE +}; + +/** Enumeration for rate format */ +enum _mlan_rate_format +{ + MLAN_RATE_FORMAT_LG = 0, + MLAN_RATE_FORMAT_HT, + MLAN_RATE_FORMAT_AUTO = 0xFF, +}; +/** Max bitmap rates size */ +#define MAX_BITMAP_RATES_SIZE 10 + +/** Type definition of mlan_rate_cfg_t for MLAN_OID_RATE_CFG */ +typedef struct _mlan_rate_cfg_t +{ + /** Fixed rate: 0, auto rate: 1 */ + t_u32 is_rate_auto; + /** Rate type. 0: index; 1: valude */ + t_u32 rate_type; + /** Rate/MCS index or rate value if fixed rate */ + t_u32 rate; + /** Rate Bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; +} mlan_rate_cfg_t; + +/** HT channel bandwidth */ +typedef enum _mlan_ht_bw +{ + MLAN_HT_BW20, + MLAN_HT_BW40, +} mlan_ht_bw; + +/** HT guard interval */ +typedef enum _mlan_ht_gi +{ + MLAN_HT_LGI, + MLAN_HT_SGI, +} mlan_ht_gi; + +/** Band and BSS mode */ +typedef struct _mlan_band_data_rate +{ + /** Band configuration */ + t_u8 config_bands; + /** BSS mode (Infra or IBSS) */ + t_u8 bss_mode; +} mlan_band_data_rate; + +/** Type definition of mlan_data_rate for MLAN_OID_GET_DATA_RATE */ +typedef struct _mlan_data_rate +{ + /** Tx data rate */ + t_u32 tx_data_rate; + /** Rx data rate */ + t_u32 rx_data_rate; + + /** Tx channel bandwidth */ + t_u32 tx_ht_bw; + /** Tx guard interval */ + t_u32 tx_ht_gi; + /** Rx channel bandwidth */ + t_u32 rx_ht_bw; + /** Rx guard interval */ + t_u32 rx_ht_gi; +} mlan_data_rate; + +/** Type definition of mlan_ds_rate for MLAN_IOCTL_RATE */ +typedef struct _mlan_ds_rate +{ + /** Sub-command */ + t_u32 sub_command; + /** Rate configuration parameter */ + union + { + /** Rate configuration for MLAN_OID_RATE_CFG */ + mlan_rate_cfg_t rate_cfg; + /** Data rate for MLAN_OID_GET_DATA_RATE */ + mlan_data_rate data_rate; + /** Supported rates for MLAN_OID_SUPPORTED_RATES */ + t_u8 rates[MLAN_SUPPORTED_RATES]; + /** Band/BSS mode for getting supported rates */ + mlan_band_data_rate rate_band_cfg; + } param; +} mlan_ds_rate, *pmlan_ds_rate; + +/*-----------------------------------------------------------------*/ +/** Power Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** Type definition of mlan_power_cfg_t for MLAN_OID_POWER_CFG */ +typedef struct _mlan_power_cfg_t +{ + /** Is power auto */ + t_u32 is_power_auto; + /** Power level in dBm */ + t_u32 power_level; +} mlan_power_cfg_t; + +/** max power table size */ +#define MAX_POWER_TABLE_SIZE 128 + +/** The HT BW40 bit in Tx rate index */ +#define TX_RATE_HT_BW40_BIT MBIT(7) + +/** Type definition of mlan_power_cfg_ext for MLAN_OID_POWER_CFG_EXT */ +typedef struct _mlan_power_cfg_ext +{ + /** Length of power_data */ + t_u32 len; + /** Buffer of power configuration data */ + t_u32 power_data[MAX_POWER_TABLE_SIZE]; +} mlan_power_cfg_ext; + +/** Type definition of mlan_ds_power_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_power_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** Power configuration parameter */ + union + { + /** Power configuration for MLAN_OID_POWER_CFG */ + mlan_power_cfg_t power_cfg; + /** Extended power configuration for MLAN_OID_POWER_CFG_EXT */ + mlan_power_cfg_ext power_ext; + } param; +} mlan_ds_power_cfg, *pmlan_ds_power_cfg; + +/*-----------------------------------------------------------------*/ +/** Power Management Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Host sleep config conditions : Cancel */ +#define HOST_SLEEP_CFG_CANCEL 0xffffffff + +/** Host sleep config condition: broadcast data */ +#define HOST_SLEEP_COND_BROADCAST_DATA MBIT(0) +/** Host sleep config condition: unicast data */ +#define HOST_SLEEP_COND_UNICAST_DATA MBIT(1) +/** Host sleep config condition: mac event */ +#define HOST_SLEEP_COND_MAC_EVENT MBIT(2) +/** Host sleep config condition: multicast data */ +#define HOST_SLEEP_COND_MULTICAST_DATA MBIT(3) + +/** Host sleep config conditions: Default */ +#define HOST_SLEEP_DEF_COND (HOST_SLEEP_COND_BROADCAST_DATA | HOST_SLEEP_COND_UNICAST_DATA | HOST_SLEEP_COND_MAC_EVENT) +/** Host sleep config GPIO : Default */ +#define HOST_SLEEP_DEF_GPIO 0xff +/** Host sleep config gap : Default */ +#define HOST_SLEEP_DEF_GAP 200 + +/** Type definition of mlan_ds_hs_cfg for MLAN_OID_PM_CFG_HS_CFG */ +typedef struct _mlan_ds_hs_cfg +{ + /** MTRUE to invoke the HostCmd, MFALSE otherwise */ + t_u32 is_invoke_hostcmd; + /** Host sleep config condition */ + /** Bit0: broadcast data + * Bit1: unicast data + * Bit2: mac event + * Bit3: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u32 gpio; + /** Gap in milliseconds or or 0xff for special setting when GPIO is used to wakeup host */ + t_u32 gap; +} mlan_ds_hs_cfg, *pmlan_ds_hs_cfg; + +/** Enable deep sleep mode */ +#define DEEP_SLEEP_ON 1 +/** Disable deep sleep mode */ +#define DEEP_SLEEP_OFF 0 + +/** Default idle time in milliseconds for auto deep sleep */ +#define DEEP_SLEEP_IDLE_TIME 100 + +typedef struct _mlan_ds_auto_ds +{ + /** auto ds mode, 0 - disable, 1 - enable */ + t_u16 auto_ds; + /** auto ds idle time in milliseconds */ + t_u16 idletime; +} mlan_ds_auto_ds; + +/** Type definition of mlan_ds_inactivity_to for MLAN_OID_PM_CFG_INACTIVITY_TO */ +typedef struct _mlan_ds_inactivity_to +{ + /** Timeout unit in microsecond, 0 means 1000us (1ms) */ + t_u32 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u32 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u32 mcast_timeout; + /** Timeout for additional Rx traffic after Null PM1 packet exchange */ + t_u32 ps_entry_timeout; +} mlan_ds_inactivity_to, *pmlan_ds_inactivity_to; + +/** Minimum sleep period in milliseconds */ +#define MIN_SLEEP_PERIOD 10 +/** Maximum sleep period in milliseconds */ +#define MAX_SLEEP_PERIOD 60 +/** Special setting for UPSD certification tests */ +#define SLEEP_PERIOD_RESERVED_FF 0xFF + +/** PS null interval disable */ +#define PS_NULL_DISABLE (-1) + +/** Local listen interval disable */ +#define MRVDRV_LISTEN_INTERVAL_DISABLE (-1) +/** Minimum listen interval */ +#define MRVDRV_MIN_LISTEN_INTERVAL 0 + +/** Minimum multiple DTIM */ +#define MRVDRV_MIN_MULTIPLE_DTIM 0 +/** Maximum multiple DTIM */ +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +/** Ignore multiple DTIM */ +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +/** Match listen interval to closest DTIM */ +#define MRVDRV_MATCH_CLOSEST_DTIM 0xfffd + +/** Minimum adhoc awake period */ +#define MIN_ADHOC_AWAKE_PD 0 +/** Maximum adhoc awake period */ +#define MAX_ADHOC_AWAKE_PD 31 +/** Special adhoc awake period */ +#define SPECIAL_ADHOC_AWAKE_PD 255 + +/** Minimum beacon miss timeout in milliseconds */ +#define MIN_BCN_MISS_TO 0 +/** Maximum beacon miss timeout in milliseconds */ +#define MAX_BCN_MISS_TO 50 +/** Disable beacon miss timeout */ +#define DISABLE_BCN_MISS_TO 65535 + +/** Minimum delay to PS in milliseconds */ +#define MIN_DELAY_TO_PS 0 +/** Maximum delay to PS in milliseconds */ +#define MAX_DELAY_TO_PS 65535 +/** Delay to PS unchanged */ +#define DELAY_TO_PS_UNCHANGED (-1) +/** Default delay to PS in milliseconds */ +#define DELAY_TO_PS_DEFAULT 1000 + +/** PS mode: Unchanged */ +#define PS_MODE_UNCHANGED 0 +/** PS mode: Auto */ +#define PS_MODE_AUTO 1 +/** PS mode: Poll */ +#define PS_MODE_POLL 2 +/** PS mode: Null */ +#define PS_MODE_NULL 3 + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_ps_cfg +{ + /** PS null interval in seconds */ + t_u32 ps_null_interval; + /** Multiple DTIM interval */ + t_u32 multiple_dtim_interval; + /** Listen interval */ + t_u32 listen_interval; + /** Adhoc awake period */ + t_u32 adhoc_awake_period; + /** Beacon miss timeout in milliseconds */ + t_u32 bcn_miss_timeout; + /** Delay to PS in milliseconds */ + t_s32 delay_to_ps; + /** PS mode */ + t_u32 ps_mode; +} mlan_ds_ps_cfg, *pmlan_ds_ps_cfg; + +/** Type definition of mlan_ds_sleep_params for MLAN_OID_PM_CFG_SLEEP_PARAMS */ +typedef struct _mlan_ds_sleep_params +{ + /** Error */ + t_u32 error; + /** Offset in microseconds */ + t_u32 offset; + /** Stable time in microseconds */ + t_u32 stable_time; + /** Calibration control */ + t_u32 cal_control; + /** External sleep clock */ + t_u32 ext_sleep_clk; + /** Reserved */ + t_u32 reserved; +} mlan_ds_sleep_params, *pmlan_ds_sleep_params; + +/** sleep_param */ +typedef struct _ps_sleep_param +{ + /** control bitmap */ + t_u32 ctrl_bitmap; + /** minimum sleep period (micro second) */ + t_u32 min_sleep; + /** maximum sleep period (micro second) */ + t_u32 max_sleep; +} ps_sleep_param; + +/** inactivity sleep_param */ +typedef struct _inact_sleep_param +{ + /** inactivity timeout (micro second) */ + t_u32 inactivity_to; + /** miniumu awake period (micro second) */ + t_u32 min_awake; + /** maximum awake period (micro second) */ + t_u32 max_awake; +} inact_sleep_param; + +/** flag for ps mode */ +#define PS_FLAG_PS_MODE 1 +/** flag for sleep param */ +#define PS_FLAG_SLEEP_PARAM 2 +/** flag for inactivity sleep param */ +#define PS_FLAG_INACT_SLEEP_PARAM 4 + +/** Disable power mode */ +#define PS_MODE_DISABLE 0 +/** Enable periodic dtim ps */ +#define PS_MODE_PERIODIC_DTIM 1 +/** Enable inactivity ps */ +#define PS_MODE_INACTIVITY 2 + +/** mlan_ds_ps_mgmt */ +typedef struct _mlan_ds_ps_mgmt +{ + /** flags for valid field */ + t_u16 flags; + /** power mode */ + t_u16 ps_mode; + /** sleep param */ + ps_sleep_param sleep_param; + /** inactivity sleep param */ + inact_sleep_param inact_param; +} mlan_ds_ps_mgmt; + +/** mlan_ds_ps_info */ +typedef struct _mlan_ds_ps_info +{ + /** suspend allowed flag */ + t_u32 is_suspend_allowed; +} mlan_ds_ps_info; + +/** Type definition of mlan_ds_pm_cfg for MLAN_IOCTL_PM_CFG */ +typedef struct _mlan_ds_pm_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** Power management parameter */ + union + { + /** Power saving mode for MLAN_OID_PM_CFG_IEEE_PS */ + t_u32 ps_mode; + /** Host Sleep configuration for MLAN_OID_PM_CFG_HS_CFG */ + mlan_ds_hs_cfg hs_cfg; + /** Deep sleep mode for MLAN_OID_PM_CFG_DEEP_SLEEP */ + mlan_ds_auto_ds auto_deep_sleep; + /** Inactivity timeout for MLAN_OID_PM_CFG_INACTIVITY_TO */ + mlan_ds_inactivity_to inactivity_to; + /** Sleep period for MLAN_OID_PM_CFG_SLEEP_PD */ + t_u32 sleep_period; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_CFG */ + mlan_ds_ps_cfg ps_cfg; + /** PS configuration parameters for MLAN_OID_PM_CFG_SLEEP_PARAMS */ + mlan_ds_sleep_params sleep_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_MODE */ + mlan_ds_ps_mgmt ps_mgmt; + /** power info for MLAN_OID_PM_INFO */ + mlan_ds_ps_info ps_info; + } param; +} mlan_ds_pm_cfg, *pmlan_ds_pm_cfg; + +/*-----------------------------------------------------------------*/ +/** WMM Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** WMM TSpec size */ +#define MLAN_WMM_TSPEC_SIZE 63 +/** WMM Add TS extra IE bytes */ +#define MLAN_WMM_ADDTS_EXTRA_IE_BYTES 256 +/** WMM statistics for packets hist bins */ +#define MLAN_WMM_STATS_PKTS_HIST_BINS 7 +/** Maximum number of AC QOS queues available */ +#define MLAN_WMM_MAX_AC_QUEUES 4 + +/** + * @brief IOCTL structure to send an ADDTS request and retrieve the response. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an ADDTS management frame with an appropriate TSPEC IE as well + * as any additional IEs appended in the ADDTS Action frame. + * + * @sa woal_wmm_addts_req_ioctl + */ +typedef struct +{ + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 ieee_status_code; /**< IEEE status code */ + + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE /**< TSPEC to send in the ADDTS */ + + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; /**< Extra IE buf*/ +} wlan_ioctl_wmm_addts_req_t; + +/** + * @brief IOCTL structure to send a DELTS request. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an DELTS management frame with an appropriate TSPEC IE. + * + * @sa woal_wmm_delts_req_ioctl + */ +typedef struct +{ + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + t_u8 ieee_reason_code; /**< IEEE reason code sent, unused for WMM */ + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; /**< TSPEC to send in the DELTS */ +} wlan_ioctl_wmm_delts_req_t; + +/** + * @brief IOCTL structure to configure a specific AC Queue's parameters + * + * IOCTL structure from the application layer relayed to firmware to + * get, set, or default the WMM AC queue parameters. + * + * - msdu_lifetime_expiry is ignored if set to 0 on a set command + * + * @sa woal_wmm_queue_config_ioctl + */ +typedef struct +{ + mlan_wmm_queue_config_action_e action; /**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + t_u16 msdu_lifetime_expiry; /**< lifetime expiry in TUs */ + t_u8 supported_rates[10]; /**< Not supported yet */ +} wlan_ioctl_wmm_queue_config_t; + +/** + * @brief IOCTL structure to start, stop, and get statistics for a WMM AC + * + * IOCTL structure from the application layer relayed to firmware to + * start or stop statistical collection for a given AC. Also used to + * retrieve and clear the collected stats on a given AC. + * + * @sa woal_wmm_queue_stats_ioctl + */ +typedef struct +{ + /** Action of Queue Config : Start, Stop, or Get */ + mlan_wmm_queue_stats_action_e action; + /** User Priority */ + t_u8 user_priority; + /** Number of successful packets transmitted */ + t_u16 pkt_count; + /** Packets lost; not included in pkt_count */ + t_u16 pkt_loss; + /** Average Queue delay in microseconds */ + t_u32 avg_queue_delay; + /** Average Transmission delay in microseconds */ + t_u32 avg_tx_delay; + /** Calculated used time in units of 32 microseconds */ + t_u16 used_time; + /** Calculated policed time in units of 32 microseconds */ + t_u16 policed_time; + /** Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[MLAN_WMM_STATS_PKTS_HIST_BINS]; +} wlan_ioctl_wmm_queue_stats_t, +/** Type definition of mlan_ds_wmm_queue_stats for MLAN_OID_WMM_CFG_QUEUE_STATS */ + mlan_ds_wmm_queue_stats, *pmlan_ds_wmm_queue_stats; + +/** + * @brief IOCTL sub structure for a specific WMM AC Status + */ +typedef struct +{ + /** WMM Acm */ + t_u8 wmm_acm; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Disabled flag */ + t_u8 disabled; +} wlan_ioctl_wmm_queue_status_ac_t; + +/** + * @brief IOCTL structure to retrieve the WMM AC Queue status + * + * IOCTL structure from the application layer to retrieve: + * - ACM bit setting for the AC + * - Firmware status (flow required, flow created, flow disabled) + * + * @sa woal_wmm_queue_status_ioctl + */ +typedef struct +{ + /** WMM AC queue status */ + wlan_ioctl_wmm_queue_status_ac_t ac_status[MLAN_WMM_MAX_AC_QUEUES]; +} wlan_ioctl_wmm_queue_status_t, +/** Type definition of mlan_ds_wmm_queue_status for MLAN_OID_WMM_CFG_QUEUE_STATUS */ + mlan_ds_wmm_queue_status, *pmlan_ds_wmm_queue_status; + +/** Type definition of mlan_ds_wmm_addts for MLAN_OID_WMM_CFG_ADDTS */ +typedef struct _mlan_ds_wmm_addts +{ + /** Result of ADDTS request */ + mlan_cmd_result_e result; + /** Timeout value in milliseconds */ + t_u32 timeout; + /** IEEE status code */ + t_u32 status_code; + /** Dialog token */ + t_u8 dialog_tok; + /** TSPEC data length */ + t_u8 ie_data_len; + /** TSPEC to send in the ADDTS + buffering for any extra IEs */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; +} mlan_ds_wmm_addts, *pmlan_ds_wmm_addts; + +/** Type definition of mlan_ds_wmm_delts for MLAN_OID_WMM_CFG_DELTS */ +typedef struct _mlan_ds_wmm_delts +{ + /** Result of DELTS request */ + mlan_cmd_result_e result; + /** IEEE status code */ + t_u32 status_code; + /** TSPEC data length */ + t_u8 ie_data_len; + /** TSPEC to send in the DELTS */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; +} mlan_ds_wmm_delts, *pmlan_ds_wmm_delts; + +/** Type definition of mlan_ds_wmm_queue_config for MLAN_OID_WMM_CFG_QUEUE_CONFIG */ +typedef struct _mlan_ds_wmm_queue_config +{ + /** Action of Queue Config : Set, Get, or Default */ + mlan_wmm_queue_config_action_e action; + /** WMM Access Category: WMM_AC_BK(0) to WMM_AC_VO(3) */ + mlan_wmm_ac_e access_category; + /** Lifetime expiry in TUs */ + t_u16 msdu_lifetime_expiry; + /** Reserve for future use */ + t_u8 reserved[10]; +} mlan_ds_wmm_queue_config, *pmlan_ds_wmm_queue_config; + +/** Type definition of mlan_ds_wmm_cfg for MLAN_IOCTL_WMM_CFG */ +typedef struct _mlan_ds_wmm_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** WMM configuration parameter */ + union + { + /** WMM enable for MLAN_OID_WMM_CFG_ENABLE */ + t_u32 wmm_enable; + /** QoS configuration for MLAN_OID_WMM_CFG_QOS */ + t_u8 qos_cfg; + /** WMM add TS for MLAN_OID_WMM_CFG_ADDTS */ + mlan_ds_wmm_addts addts; + /** WMM delete TS for MLAN_OID_WMM_CFG_DELTS */ + mlan_ds_wmm_delts delts; + /** WMM queue configuration for MLAN_OID_WMM_CFG_QUEUE_CONFIG */ + mlan_ds_wmm_queue_config q_cfg; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATS */ + mlan_ds_wmm_queue_stats q_stats; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATUS */ + mlan_ds_wmm_queue_status q_status; + /** WMM TS status for MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status ts_status; + } param; +} mlan_ds_wmm_cfg, *pmlan_ds_wmm_cfg; + +/*-----------------------------------------------------------------*/ +/** WPS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for WPS session */ +enum _mlan_wps_status +{ + MLAN_WPS_CFG_SESSION_START = 1, + MLAN_WPS_CFG_SESSION_END = 0 +}; + +/** Type definition of mlan_ds_wps_cfg for MLAN_IOCTL_WPS_CFG */ +typedef struct _mlan_ds_wps_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** WPS configuration parameter */ + union + { + /** WPS session for MLAN_OID_WPS_CFG_SESSION */ + t_u32 wps_session; + } param; +} mlan_ds_wps_cfg, *pmlan_ds_wps_cfg; + +/*-----------------------------------------------------------------*/ +/** 802.11n Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum MCS */ +#define NUM_MCS_FIELD 16 + +/** Supported stream modes */ +#define HT_STREAM_MODE_1X1 0x11 +#define HT_STREAM_MODE_2X2 0x22 + +/* Both 2.4G and 5G band selected */ +#define BAND_SELECT_BOTH 0 +/* Band 2.4G selected */ +#define BAND_SELECT_BG 1 +/* Band 5G selected */ +#define BAND_SELECT_A 2 + +/** Type definition of mlan_ds_11n_htcap_cfg for MLAN_OID_11N_HTCAP_CFG */ +typedef struct _mlan_ds_11n_htcap_cfg +{ + /** HT Capability information */ + t_u32 htcap; + /** Band selection */ + t_u32 misc_cfg; + /** Hardware HT cap information required */ + t_u32 hw_cap_req; +} mlan_ds_11n_htcap_cfg, *pmlan_ds_11n_htcap_cfg; + +/** Type definition of mlan_ds_11n_addba_param for MLAN_OID_11N_CFG_ADDBA_PARAM */ +typedef struct _mlan_ds_11n_addba_param +{ + /** Timeout */ + t_u32 timeout; + /** Buffer size for ADDBA request */ + t_u32 txwinsize; + /** Buffer size for ADDBA response */ + t_u32 rxwinsize; + /** amsdu for ADDBA request */ + t_u8 txamsdu; + /** amsdu for ADDBA response */ + t_u8 rxamsdu; +} mlan_ds_11n_addba_param, *pmlan_ds_11n_addba_param; + +/** Type definition of mlan_ds_11n_tx_cfg for MLAN_OID_11N_CFG_TX */ +typedef struct _mlan_ds_11n_tx_cfg +{ + /** HTTxCap */ + t_u16 httxcap; + /** HTTxInfo */ + t_u16 httxinfo; + /** Band selection */ + t_u32 misc_cfg; +} mlan_ds_11n_tx_cfg, *pmlan_ds_11n_tx_cfg; + +/** BF Global Configuration */ +#define BF_GLOBAL_CONFIGURATION 0x00 +/** Performs NDP sounding for PEER specified */ +#define TRIGGER_SOUNDING_FOR_PEER 0x01 +/** TX BF interval for channel sounding */ +#define SET_GET_BF_PERIODICITY 0x02 +/** Tell FW not to perform any sounding for peer */ +#define TX_BF_FOR_PEER_ENBL 0x03 +/** TX BF SNR threshold for peer */ +#define SET_SNR_THR_PEER 0x04 + +/* Maximum number of peer MAC and status/SNR tuples */ +#define MAX_PEER_MAC_TUPLES 10 + +/** Any new subcommand structure should be declare here */ + +/** bf global cfg args */ +typedef struct _mlan_bf_global_cfg_args +{ + /** Global enable/disable bf */ + t_u8 bf_enbl; + /** Global enable/disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; + /** SNR Threshold */ + t_u8 snr_threshold; + /** Sounding interval in milliseconds */ + t_u16 sounding_interval; + /** BF mode */ + t_u8 bf_mode; + /** Reserved */ + t_u8 reserved; +} mlan_bf_global_cfg_args; + +/** trigger sounding args */ +typedef struct _mlan_trigger_sound_args +{ + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Status */ + t_u8 status; +} mlan_trigger_sound_args; + +/** bf periodicity args */ +typedef struct _mlan_bf_periodicity_args +{ + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Current Tx BF Interval in milliseconds */ + t_u16 interval; + /** Status */ + t_u8 status; +} mlan_bf_periodicity_args; + +/** tx bf peer args */ +typedef struct _mlan_tx_bf_peer_args +{ + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Reserved */ + t_u16 reserved; + /** Enable/Disable Beamforming */ + t_u8 bf_enbl; + /** Enable/Disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; +} mlan_tx_bf_peer_args; + +/** SNR threshold args */ +typedef struct _mlan_snr_thr_args +{ + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** SNR for peer */ + t_u8 snr; +} mlan_snr_thr_args; + +/** Type definition of mlan_ds_11n_tx_bf_cfg for MLAN_OID_11N_CFG_TX_BF_CFG */ +typedef struct _mlan_ds_11n_tx_bf_cfg +{ + /** BF Action */ + t_u16 bf_action; + /** Action */ + t_u16 action; + /** Number of peers */ + t_u32 no_of_peers; + union + { + mlan_bf_global_cfg_args bf_global_cfg; + mlan_trigger_sound_args bf_sound[MAX_PEER_MAC_TUPLES]; + mlan_bf_periodicity_args bf_periodicity[MAX_PEER_MAC_TUPLES]; + mlan_tx_bf_peer_args tx_bf_peer[MAX_PEER_MAC_TUPLES]; + mlan_snr_thr_args bf_snr[MAX_PEER_MAC_TUPLES]; + } body; +} mlan_ds_11n_tx_bf_cfg, *pmlan_ds_11n_tx_bf_cfg; + +/** Type definition of mlan_ds_11n_amsdu_aggr_ctrl for + * MLAN_OID_11N_AMSDU_AGGR_CTRL*/ +typedef struct _mlan_ds_11n_amsdu_aggr_ctrl +{ + /** Enable/Disable */ + t_u16 enable; + /** Current AMSDU size valid */ + t_u16 curr_buf_size; +} mlan_ds_11n_amsdu_aggr_ctrl, *pmlan_ds_11n_amsdu_aggr_ctrl; + +/** Type definition of mlan_ds_11n_aggr_prio_tbl for MLAN_OID_11N_CFG_AGGR_PRIO_TBL */ +typedef struct _mlan_ds_11n_aggr_prio_tbl +{ + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} mlan_ds_11n_aggr_prio_tbl, *pmlan_ds_11n_aggr_prio_tbl; + +/** Type definition of mlan_ds_11n_cfg for MLAN_IOCTL_11N_CFG */ +typedef struct _mlan_ds_11n_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union + { + /** Tx param for 11n for MLAN_OID_11N_CFG_TX */ + mlan_ds_11n_tx_cfg tx_cfg; + /** Aggr priority table for MLAN_OID_11N_CFG_AGGR_PRIO_TBL */ + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + /** Add BA param for MLAN_OID_11N_CFG_ADDBA_PARAM */ + mlan_ds_11n_addba_param addba_param; + /** Add BA Reject paramters for MLAN_OID_11N_CFG_ADDBA_REJECT */ + t_u8 addba_reject[MAX_NUM_TID]; + /** Tx buf size for MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE */ + t_u32 tx_buf_size; + /** HT cap info configuration for MLAN_OID_11N_HTCAP_CFG */ + mlan_ds_11n_htcap_cfg htcap_cfg; + /** Tx param for 11n for MLAN_OID_11N_AMSDU_AGGR_CTRL */ + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_FIELD]; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Transmit Beamforming configuration */ + mlan_ds_11n_tx_bf_cfg tx_bf; + /** HT stream configuration */ + t_u32 stream_cfg; + } param; +} mlan_ds_11n_cfg, *pmlan_ds_11n_cfg; + +/** Country code length */ +#define COUNTRY_CODE_LEN 3 + +/*-----------------------------------------------------------------*/ +/** 802.11d Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum subbands for 11d */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#ifdef STA_SUPPORT +/** Data structure for subband set */ +typedef struct _mlan_ds_subband_set_t +{ + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power in dBm */ + t_u8 max_tx_pwr; +} mlan_ds_subband_set_t; + +/** Domain regulatory information */ +typedef struct _mlan_ds_11d_domain_info +{ + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Band that channels in sub_band belong to */ + t_u8 band; + /** No. of subband in below */ + t_u8 no_of_sub_band; + /** Subband data to send/last sent */ + mlan_ds_subband_set_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} mlan_ds_11d_domain_info; +#endif + +/** Type definition of mlan_ds_11d_cfg for MLAN_IOCTL_11D_CFG */ +typedef struct _mlan_ds_11d_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** 802.11d configuration parameter */ + union + { +#ifdef STA_SUPPORT + /** Enable for MLAN_OID_11D_CFG_ENABLE */ + t_u32 enable_11d; + /** Domain info for MLAN_OID_11D_DOMAIN_INFO */ + mlan_ds_11d_domain_info domain_info; +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + /** tlv data for MLAN_OID_11D_DOMAIN_INFO */ + t_u8 domain_tlv[MAX_IE_SIZE]; +#endif /* UAP_SUPPORT */ + } param; +} mlan_ds_11d_cfg, *pmlan_ds_11d_cfg; + +/*-----------------------------------------------------------------*/ +/** Register Memory Access Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for register type */ +enum _mlan_reg_type +{ + MLAN_REG_MAC = 1, + MLAN_REG_BBP, + MLAN_REG_RF, + MLAN_REG_CAU = 5, +}; + +/** Type definition of mlan_ds_reg_rw for MLAN_OID_REG_RW */ +typedef struct _mlan_ds_reg_rw +{ + /** Register type */ + t_u32 type; + /** Offset */ + t_u32 offset; + /** Value */ + t_u32 value; +} mlan_ds_reg_rw; + +/** Maximum EEPROM data */ +#define MAX_EEPROM_DATA 256 + +/** Type definition of mlan_ds_read_eeprom for MLAN_OID_EEPROM_RD */ +typedef struct _mlan_ds_read_eeprom +{ + /** Multiples of 4 */ + t_u16 offset; + /** Number of bytes */ + t_u16 byte_count; + /** Value */ + t_u8 value[MAX_EEPROM_DATA]; +} mlan_ds_read_eeprom; + +/** Type definition of mlan_ds_mem_rw for MLAN_OID_MEM_RW */ +typedef struct _mlan_ds_mem_rw +{ + /** Address */ + t_u32 addr; + /** Value */ + t_u32 value; +} mlan_ds_mem_rw; + +/** Type definition of mlan_ds_reg_mem for MLAN_IOCTL_REG_MEM */ +typedef struct _mlan_ds_reg_mem +{ + /** Sub-command */ + t_u32 sub_command; + /** Register memory access parameter */ + union + { + /** Register access for MLAN_OID_REG_RW */ + mlan_ds_reg_rw reg_rw; + /** EEPROM access for MLAN_OID_EEPROM_RD */ + mlan_ds_read_eeprom rd_eeprom; + /** Memory access for MLAN_OID_MEM_RW */ + mlan_ds_mem_rw mem_rw; + } param; +} mlan_ds_reg_mem, *pmlan_ds_reg_mem; + +/*-----------------------------------------------------------------*/ +/** Multi-Radio Configuration Group */ +/*-----------------------------------------------------------------*/ + +/*-----------------------------------------------------------------*/ +/** 802.11h Configuration Group */ +/*-----------------------------------------------------------------*/ +#if defined(DFS_TESTING_SUPPORT) +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_DFS_TESTING */ +typedef struct _mlan_ds_11h_dfs_testing +{ + /** User-configured CAC period in milliseconds, 0 to use default */ + t_u16 usr_cac_period_msec; + /** User-configured NOP period in seconds, 0 to use default */ + t_u16 usr_nop_period_sec; + /** User-configured skip channel change, 0 to disable */ + t_u8 usr_no_chan_change; + /** User-configured fixed channel to change to, 0 to use random channel */ + t_u8 usr_fixed_new_chan; +} mlan_ds_11h_dfs_testing, *pmlan_ds_11h_dfs_testing; +#endif + +/** Type definition of mlan_ds_11h_cfg for MLAN_IOCTL_11H_CFG */ +typedef struct _mlan_ds_11h_cfg +{ + /** Sub-command */ + t_u32 sub_command; + union + { + /** Local power constraint for MLAN_OID_11H_LOCAL_POWER_CONSTRAINT */ + t_s8 usr_local_power_constraint; +#if defined(DFS_TESTING_SUPPORT) + /** User-configuation for MLAN_OID_11H_DFS_TESTING */ + mlan_ds_11h_dfs_testing dfs_testing; +#endif + } param; +} mlan_ds_11h_cfg, *pmlan_ds_11h_cfg; + +/*-----------------------------------------------------------------*/ +/** Miscellaneous Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** CMD buffer size */ +#define MLAN_SIZE_OF_CMD_BUFFER 2048 + +/** LDO Internal */ +#define LDO_INTERNAL 0 +/** LDO External */ +#define LDO_EXTERNAL 1 + +/** Enumeration for IE type */ +enum _mlan_ie_type +{ + MLAN_IE_TYPE_GEN_IE = 0, +#ifdef STA_SUPPORT + MLAN_IE_TYPE_ARP_FILTER, +#endif /* STA_SUPPORT */ +}; + +/** Type definition of mlan_ds_misc_gen_ie for MLAN_OID_MISC_GEN_IE */ +typedef struct _mlan_ds_misc_gen_ie +{ + /** IE type */ + t_u32 type; + /** IE length */ + t_u32 len; + /** IE buffer */ + t_u8 ie_data[MAX_IE_SIZE]; +} mlan_ds_misc_gen_ie; + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** Type definition of mlan_ds_misc_sdio_mpa_ctrl for MLAN_OID_MISC_SDIO_MPA_CTRL */ +typedef struct _mlan_ds_misc_sdio_mpa_ctrl +{ + /** SDIO MP-A TX enable/disable */ + t_u16 tx_enable; + /** SDIO MP-A RX enable/disable */ + t_u16 rx_enable; + /** SDIO MP-A TX buf size */ + t_u16 tx_buf_size; + /** SDIO MP-A RX buf size */ + t_u16 rx_buf_size; + /** SDIO MP-A TX Max Ports */ + t_u16 tx_max_ports; + /** SDIO MP-A RX Max Ports */ + t_u16 rx_max_ports; +} mlan_ds_misc_sdio_mpa_ctrl; +#endif + +/** Type definition of mlan_ds_misc_cmd for MLAN_OID_MISC_HOST_CMD */ +typedef struct _mlan_ds_misc_cmd +{ + /** Command length */ + t_u32 len; + /** Command buffer */ + t_u8 cmd[MLAN_SIZE_OF_CMD_BUFFER]; +} mlan_ds_misc_cmd; + +/** Maximum number of system clocks */ +#define MLAN_MAX_CLK_NUM 16 + +/** Clock type : Configurable */ +#define MLAN_CLK_CONFIGURABLE 0 +/** Clock type : Supported */ +#define MLAN_CLK_SUPPORTED 1 + +/** Type definition of mlan_ds_misc_sys_clock for MLAN_OID_MISC_SYS_CLOCK */ +typedef struct _mlan_ds_misc_sys_clock +{ + /** Current system clock */ + t_u16 cur_sys_clk; + /** Clock type */ + t_u16 sys_clk_type; + /** Number of clocks */ + t_u16 sys_clk_num; + /** System clocks */ + t_u16 sys_clk[MLAN_MAX_CLK_NUM]; +} mlan_ds_misc_sys_clock; + +/** Enumeration for function init/shutdown */ +enum _mlan_func_cmd +{ + MLAN_FUNC_INIT = 1, + MLAN_FUNC_SHUTDOWN, +}; + +/** Type definition of mlan_ds_misc_tx_datapause for MLAN_OID_MISC_TX_DATAPAUSE */ +typedef struct _mlan_ds_misc_tx_datapause +{ + /** Tx data pause flag */ + t_u16 tx_pause; + /** Max number of Tx buffers for all PS clients */ + t_u16 tx_buf_cnt; +} mlan_ds_misc_tx_datapause; + +/** IP address length */ +#define IPADDR_LEN (16) +/** Max number of ip */ +#define MAX_IPADDR (4) +/** IP address type - IPv4*/ +#define IPADDR_TYPE_IPV4 (1) +/** IP operation remove */ +#define MLAN_IPADDR_OP_IP_REMOVE (0) +/** IP operation ARP filter */ +#define MLAN_IPADDR_OP_ARP_FILTER MBIT(0) +/** IP operation ARP response */ +#define MLAN_IPADDR_OP_AUTO_ARP_RESP MBIT(1) + +/** Type definition of mlan_ds_misc_ipaddr_cfg for MLAN_OID_MISC_IP_ADDR */ +typedef struct _mlan_ds_misc_ipaddr_cfg +{ + /** Operation code */ + t_u32 op_code; + /** IP address type */ + t_u32 ip_addr_type; + /** Number of IP */ + t_u32 ip_addr_num; + /** IP address */ + t_u8 ip_addr[MAX_IPADDR][IPADDR_LEN]; +} mlan_ds_misc_ipaddr_cfg; + +/* MEF configuration disable */ +#define MEF_CFG_DISABLE 0 +/* MEF configuration Rx filter enable */ +#define MEF_CFG_RX_FILTER_ENABLE 1 +/* MEF configuration auto ARP response */ +#define MEF_CFG_AUTO_ARP_RESP 2 +/* MEF configuration host command */ +#define MEF_CFG_HOSTCMD 0xFFFF + +/** Type definition of mlan_ds_misc_mef_cfg for MLAN_OID_MISC_MEF_CFG */ +typedef struct _mlan_ds_misc_mef_cfg +{ + /** Sub-ID for operation */ + t_u32 sub_id; + /** Parameter according to sub-ID */ + union + { + /** MEF command buffer for MEF_CFG_HOSTCMD */ + mlan_ds_misc_cmd cmd_buf; + } param; +} mlan_ds_misc_mef_cfg; + +/** Type definition of mlan_ds_misc_cfp_code for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_misc_cfp_code +{ + /** CFP table code for 2.4GHz */ + t_u32 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u32 cfp_code_a; +} mlan_ds_misc_cfp_code; + +/** Type definition of mlan_ds_misc_country_code for MLAN_OID_MISC_COUNTRY_CODE */ +typedef struct _mlan_ds_misc_country_code +{ + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; +} mlan_ds_misc_country_code; + +/** BITMAP for subscribe event rssi low */ +#define SUBSCRIBE_EVT_RSSI_LOW MBIT(0) +/** BITMAP for subscribe event snr low */ +#define SUBSCRIBE_EVT_SNR_LOW MBIT(1) +/** BITMAP for subscribe event max fail */ +#define SUBSCRIBE_EVT_MAX_FAIL MBIT(2) +/** BITMAP for subscribe event beacon missed */ +#define SUBSCRIBE_EVT_BEACON_MISSED MBIT(3) +/** BITMAP for subscribe event rssi high */ +#define SUBSCRIBE_EVT_RSSI_HIGH MBIT(4) +/** BITMAP for subscribe event snr high */ +#define SUBSCRIBE_EVT_SNR_HIGH MBIT(5) +/** BITMAP for subscribe event data rssi low */ +#define SUBSCRIBE_EVT_DATA_RSSI_LOW MBIT(6) +/** BITMAP for subscribe event data snr low */ +#define SUBSCRIBE_EVT_DATA_SNR_LOW MBIT(7) +/** BITMAP for subscribe event data rssi high */ +#define SUBSCRIBE_EVT_DATA_RSSI_HIGH MBIT(8) +/** BITMAP for subscribe event data snr high */ +#define SUBSCRIBE_EVT_DATA_SNR_HIGH MBIT(9) +/** BITMAP for subscribe event link quality */ +#define SUBSCRIBE_EVT_LINK_QUALITY MBIT(10) +/** BITMAP for subscribe event pre_beacon_lost */ +#define SUBSCRIBE_EVT_PRE_BEACON_LOST MBIT(11) +/** default PRE_BEACON_MISS_COUNT */ +#define DEFAULT_PRE_BEACON_MISS 30 + +/** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_subscribe_evt +{ + /** bitmap for subscribe event */ + t_u16 evt_bitmap; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 low_rssi; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 low_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 low_snr; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 low_snr_freq; + /** Failure count threshold */ + t_u8 failure_count; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 failure_count_freq; + /** num of missed beacons */ + t_u8 beacon_miss; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 beacon_miss_freq; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 high_rssi; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 high_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 high_snr; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 high_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_low_rssi; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 data_low_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_low_snr; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 data_low_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_high_rssi; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 data_high_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_high_snr; + /** 0--report once, 1--report everytime happen, N -- report only happend > N consecutive times */ + t_u8 data_high_snr_freq; + /* Link SNR threshold (dB) */ + t_u16 link_snr; + /* Link SNR frequency */ + t_u16 link_snr_freq; + /* Second minimum rate value as per the rate table below */ + t_u16 link_rate; + /* Second minimum rate frequency */ + t_u16 link_rate_freq; + /* Tx latency value (us) */ + t_u16 link_tx_latency; + /* Tx latency frequency */ + t_u16 link_tx_lantency_freq; + /* Number of pre missed beacons */ + t_u8 pre_beacon_miss; +} mlan_ds_subscribe_evt; + +/** Max OTP user data length */ +#define MAX_OTP_USER_DATA_LEN 252 + +/** Type definition of mlan_ds_misc_otp_user_data for MLAN_OID_MISC_OTP_USER_DATA */ +typedef struct _mlan_ds_misc_otp_user_data +{ + /** Reserved */ + t_u16 reserved; + /** OTP user data length */ + t_u16 user_data_length; + /** User data buffer */ + t_u8 user_data[MAX_OTP_USER_DATA_LEN]; +} mlan_ds_misc_otp_user_data; + +/** Type definition of mlan_ds_misc_cfg for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** Miscellaneous configuration parameter */ + union + { + /** Generic IE for MLAN_OID_MISC_GEN_IE */ + mlan_ds_misc_gen_ie gen_ie; + /** Region code for MLAN_OID_MISC_REGION */ + t_u32 region_code; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + /** SDIO MP-A Ctrl command for MLAN_OID_MISC_SDIO_MPA_CTRL */ + mlan_ds_misc_sdio_mpa_ctrl mpa_ctrl; +#endif + /** Hostcmd for MLAN_OID_MISC_HOST_CMD */ + mlan_ds_misc_cmd hostcmd; + /** System clock for MLAN_OID_MISC_SYS_CLOCK */ + mlan_ds_misc_sys_clock sys_clock; + /** WWS set/get for MLAN_OID_MISC_WWS */ + t_u32 wws_cfg; + /** Function init/shutdown for MLAN_OID_MISC_INIT_SHUTDOWN */ + t_u32 func_init_shutdown; + /** Custom IE for MLAN_OID_MISC_CUSTOM_IE */ + mlan_ds_misc_custom_ie cust_ie; + /** Tx data pause for MLAN_OID_MISC_TX_DATAPAUSE */ + mlan_ds_misc_tx_datapause tx_datapause; + /** IP address configuration */ + mlan_ds_misc_ipaddr_cfg ipaddr_cfg; + /** MAC control for MLAN_OID_MISC_MAC_CONTROL */ + t_u32 mac_ctrl; + /** MEF configuration for MLAN_OID_MISC_MEF_CFG */ + mlan_ds_misc_mef_cfg mef_cfg; + /** CFP code for MLAN_OID_MISC_CFP_CODE */ + mlan_ds_misc_cfp_code cfp_code; + /** Country code for MLAN_OID_MISC_COUNTRY_CODE */ + mlan_ds_misc_country_code country_code; + /** Thermal reading for MLAN_OID_MISC_THERMAL */ + t_u32 thermal; + /** Mgmt subtype mask for MLAN_OID_MISC_RX_MGMT_IND */ + t_u32 mgmt_subtype_mask; + /** subscribe event for MLAN_OID_MISC_SUBSCRIBE_EVENT */ + mlan_ds_subscribe_evt subscribe_event; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + mlan_ds_misc_otp_user_data otp_user_data; + } param; +} mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; + +#endif /* !_MLAN_IOCTL_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c index 1b04db6b4da0..6b2a01f5c218 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c @@ -164,6 +164,19 @@ woal_get_wiphy_priv(struct wiphy *wiphy) } /** + * @brief Get the private structure from net device + * + * @param wiphy A pointer to net_device structure + * + * @return Pointer to moal_private + */ +void * +woal_get_netdev_priv(struct net_device *dev) +{ + return (void *) netdev_priv(dev); +} + +/** * @brief Set/Enable encryption key * * @param priv A pointer to moal_private structure @@ -270,8 +283,12 @@ woal_cfg80211_set_key(moal_private * priv, t_u8 is_enable_wep, } } } else { - sec->param.encrypt_key.key_remove = MTRUE; - sec->param.encrypt_key.key_index = key_index; + if (key_index == KEY_INDEX_CLEAR_ALL) + sec->param.encrypt_key.key_disable = MTRUE; + else { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + } sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; if (addr) memcpy(sec->param.encrypt_key.mac_addr, addr, ETH_ALEN); @@ -343,96 +360,30 @@ static int woal_cfg80211_bss_role_cfg(moal_private * priv, t_u16 action, t_u8 * bss_role) { int ret = 0; - mlan_ds_bss *bss = NULL; - mlan_ioctl_req *req = NULL; - struct net_device *dev = priv->netdev; ENTER(); - req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); - if (req == NULL) { - ret = -ENOMEM; - goto done; - } - bss = (mlan_ds_bss *) req->pbuf; - bss->sub_command = MLAN_OID_BSS_ROLE; - req->req_id = MLAN_IOCTL_BSS; - req->action = action; - bss->param.bss_role = *bss_role; - - if (req->action == MLAN_ACT_SET) { + if (action == MLAN_ACT_SET) { /* Reset interface */ woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); } - if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(priv, action, MOAL_IOCTL_WAIT, bss_role)) { ret = -EFAULT; goto done; } - if (req->action == MLAN_ACT_GET) { - *bss_role = bss->param.bss_role; - } else { - /* Update moal_private */ - priv->bss_role = *bss_role; - if (priv->bss_type == MLAN_BSS_TYPE_UAP) - priv->bss_type = MLAN_BSS_TYPE_STA; - else if (priv->bss_type == MLAN_BSS_TYPE_STA) - priv->bss_type = MLAN_BSS_TYPE_UAP; - + if (action == MLAN_ACT_SET) { /* Initialize private structures */ woal_init_priv(priv, MOAL_IOCTL_WAIT); - if (*bss_role == MLAN_BSS_ROLE_UAP) { - /* Switch: STA -> uAP */ - /* Setup the OS Interface to our functions */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) - dev->do_ioctl = woal_uap_do_ioctl; - dev->set_multicast_list = woal_uap_set_multicast_list; -#else - dev->netdev_ops = &woal_uap_netdev_ops; -#endif -#ifdef WIRELESS_EXT -#ifdef UAP_WEXT - if (IS_UAP_WEXT(cfg80211_wext)) { -#if WIRELESS_EXT < 21 - dev->get_wireless_stats = woal_get_uap_wireless_stats; -#endif - dev->wireless_handlers = - (struct iw_handler_def *) &woal_uap_handler_def; - init_waitqueue_head(&priv->w_stats_wait_q); - } -#endif /* UAP_WEXT */ -#endif /* WIRELESS_EXT */ - } else if (*bss_role == MLAN_BSS_ROLE_STA) { - /* Switch: uAP -> STA */ - /* Setup the OS Interface to our functions */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) - dev->do_ioctl = woal_do_ioctl; - dev->set_multicast_list = woal_set_multicast_list; -#else - dev->netdev_ops = &woal_netdev_ops; -#endif -#ifdef WIRELESS_EXT -#ifdef STA_WEXT - if (IS_STA_WEXT(cfg80211_wext)) { -#if WIRELESS_EXT < 21 - dev->get_wireless_stats = woal_get_wireless_stats; -#endif - dev->wireless_handlers = - (struct iw_handler_def *) &woal_handler_def; - init_waitqueue_head(&priv->w_stats_wait_q); - } -#endif /* STA_WEXT */ -#endif - } /* Enable interfaces */ - netif_device_attach(dev); - woal_start_queue(dev); + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); } done: - if (req) - kfree(req); LEAVE(); return ret; } @@ -478,8 +429,7 @@ woal_cfg80211_init_p2p_client(moal_private * priv) wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, - MLAN_ACT_SET, &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -487,8 +437,7 @@ woal_cfg80211_init_p2p_client(moal_private * priv) /* first, init wifi direct to listen mode */ wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, - &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -496,8 +445,7 @@ woal_cfg80211_init_p2p_client(moal_private * priv) /* second, init wifi direct client */ wifi_direct_mode = WIFI_DIRECT_MODE_CLIENT; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, - MLAN_ACT_SET, &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -531,8 +479,7 @@ woal_cfg80211_init_p2p_go(moal_private * priv) wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, - MLAN_ACT_SET, &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -540,8 +487,7 @@ woal_cfg80211_init_p2p_go(moal_private * priv) /* first, init wifi direct to listen mode */ wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, - &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -549,8 +495,7 @@ woal_cfg80211_init_p2p_go(moal_private * priv) /* second, init wifi direct to GO mode */ wifi_direct_mode = WIFI_DIRECT_MODE_GO; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, - MLAN_ACT_SET, &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -603,8 +548,7 @@ woal_cfg80211_deinit_p2p(moal_private * priv) /* cancel previous remain on channel */ if (priv->phandle->remain_on_channel) { if (woal_cfg80211_remain_on_channel_cfg - (priv->wdev->wiphy, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, - 0, 0)) { + (priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { PRINTM(MERROR, "Fail to cancel remain on channel\n"); ret = -EFAULT; goto done; @@ -640,8 +584,7 @@ woal_cfg80211_deinit_p2p(moal_private * priv) wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; if (MLAN_STATUS_SUCCESS != - woal_cfg80211_wifi_direct_mode_cfg(priv, - MLAN_ACT_SET, &wifi_direct_mode)) { + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { ret = -EFAULT; goto done; } @@ -670,7 +613,7 @@ woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, struct vif_params *params) { int ret = 0; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); mlan_ds_bss *bss = NULL; mlan_ioctl_req *req = NULL; @@ -687,7 +630,7 @@ woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, if (priv->phandle->remain_on_channel) { t_u8 channel_status; if (woal_cfg80211_remain_on_channel_cfg - (wiphy, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { + (priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { PRINTM(MERROR, "Fail to cancel remain on channel\n"); ret = -EFAULT; goto done; @@ -908,7 +851,7 @@ woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, #endif const t_u8 * mac_addr, struct key_params *params) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(netdev); ENTER(); @@ -945,7 +888,7 @@ woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, #endif const t_u8 * mac_addr) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(netdev); ENTER(); @@ -981,7 +924,7 @@ woal_cfg80211_set_default_key(struct wiphy *wiphy, ) { int ret = 0; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(netdev); mlan_bss_info bss_info; ENTER(); @@ -1019,9 +962,15 @@ woal_cfg80211_set_channel(struct wiphy *wiphy, enum nl80211_channel_type channel_type) { int ret = 0; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; ENTER(); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) || defined(COMPAT_WIRELESS) + if (dev) + priv = woal_get_netdev_priv(dev); + else +#endif + priv = (moal_private *) woal_get_wiphy_priv(wiphy); #ifdef STA_CFG80211 #ifdef STA_SUPPORT if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { @@ -1031,7 +980,7 @@ woal_cfg80211_set_channel(struct wiphy *wiphy, LEAVE(); return -EINVAL; } - ret = woal_set_rf_channel(wiphy, chan, channel_type); + ret = woal_set_rf_channel(priv, chan, channel_type); } #endif #endif @@ -1055,7 +1004,7 @@ woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, struct net_device *dev, u16 frame_type, bool reg) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); mlan_status status = MLAN_STATUS_SUCCESS; t_u32 mgmt_subtype_mask = 0x0; static t_u32 last_mgmt_subtype_mask = 0x0; @@ -1112,7 +1061,7 @@ woal_cfg80211_mgmt_tx(struct wiphy *wiphy, #endif u64 * cookie) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; pmlan_buffer pmbuf = NULL; mlan_status status = MLAN_STATUS_SUCCESS; @@ -1151,7 +1100,7 @@ woal_cfg80211_mgmt_tx(struct wiphy *wiphy, /** cancel previous remain on channel */ if (priv->phandle->remain_on_channel) { if (woal_cfg80211_remain_on_channel_cfg - (wiphy, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { + (priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { PRINTM(MERROR, "Fail to cancel remain on channel\n"); ret = -EFAULT; goto done; @@ -1171,13 +1120,13 @@ woal_cfg80211_mgmt_tx(struct wiphy *wiphy, duration = MGMT_TX_DEFAULT_WAIT_TIME; if (channel_type_valid) ret = - woal_cfg80211_remain_on_channel_cfg(wiphy, MOAL_IOCTL_WAIT, + woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE, &channel_status, chan, channel_type, duration); else ret = - woal_cfg80211_remain_on_channel_cfg(wiphy, MOAL_IOCTL_WAIT, + woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE, &channel_status, chan, 0, duration); if (ret) { @@ -1261,3 +1210,478 @@ woal_cfg80211_mgmt_tx(struct wiphy *wiphy, LEAVE(); return ret; } + +/** + * @brief Look up specific IE in a buf + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param id Element id to lookup + * + * @return Pointer of the specific IE -- success, NULL -- fail + */ +const t_u8 * +woal_parse_ie_tlv(const t_u8 * ie, int len, t_u8 id) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + + /* IE format: | u8 | id | | u8 | len | | var | data | */ + while (left_len >= 2) { + length = *(pos + 1); + if ((*pos == id) && (length + 2) <= left_len) + return pos; + pos += (length + 2); + left_len -= (length + 2); + } + + return NULL; +} + +/** + * @brief This function returns priv + * based on mgmt ie index + * + * @param handle A pointer to moal_handle + * @param index mgmt ie index + * + * @return Pointer to moal_private + */ +static moal_private * +woal_get_priv_by_mgmt_index(moal_handle * handle, t_u16 index) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (handle->priv[i]->probereq_index == index) + return (handle->priv[i]); + } + } + return NULL; +} + +/** + * @brief Add custom ie to mgmt frames. + * + * @param priv A pointer to moal private structure + * @param beacon_ies_data Beacon ie + * @param beacon_index The index for beacon when auto index + * @param proberesp_ies_data Probe resp ie + * @param proberesp_index The index for probe resp when auto index + * @param assocresp_ies_data Assoc resp ie + * @param assocresp_index The index for assoc resp when auto index + * @param probereq_ies_data Probe req ie + * @param probereq_index The index for probe req when auto index * + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_custom_ie(moal_private * priv, + custom_ie * beacon_ies_data, t_u16 * beacon_index, + custom_ie * proberesp_ies_data, t_u16 * proberesp_index, + custom_ie * assocresp_ies_data, t_u16 * assocresp_index, + custom_ie * probereq_ies_data, t_u16 * probereq_index) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + t_u8 *pos = NULL; + t_u16 len = 0; + int ret = 0; + + ENTER(); + + if (!(custom_ie = kmalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + + memset(custom_ie, 0x00, sizeof(mlan_ds_misc_custom_ie)); + custom_ie->type = TLV_TYPE_MGMT_IE; + + pos = (t_u8 *) custom_ie->ie_data_list; + if (beacon_ies_data) { + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + memcpy(pos, beacon_ies_data, len); + pos += len; + custom_ie->len += len; + } + + if (proberesp_ies_data) { + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + memcpy(pos, proberesp_ies_data, len); + pos += len; + custom_ie->len += len; + } + + if (assocresp_ies_data) { + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + memcpy(pos, assocresp_ies_data, len); + custom_ie->len += len; + } + + if (probereq_ies_data) { + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + memcpy(pos, probereq_ies_data, len); + pos += len; + custom_ie->len += len; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *) ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + /* get the assigned index */ + pos = (t_u8 *) (&misc->param.cust_ie.ie_data_list[0].ie_index); + if (beacon_ies_data && beacon_ies_data->ie_length + && beacon_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save beacon ie index after auto-indexing */ + *beacon_index = misc->param.cust_ie.ie_data_list[0].ie_index; + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + pos += len; + } + + if (proberesp_ies_data && proberesp_ies_data->ie_length + && proberesp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *proberesp_index = *((t_u16 *) pos); + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + pos += len; + } + + if (assocresp_ies_data && assocresp_ies_data->ie_length + && assocresp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save assoc resp ie index after auto-indexing */ + *assocresp_index = *((t_u16 *) pos); + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + pos += len; + } + if (probereq_ies_data && probereq_ies_data->ie_length + && probereq_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *probereq_index = *((t_u16 *) pos); + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + pos += len; + } + + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) + ret = -EFAULT; + + done: + if (ioctl_req) + kfree(ioctl_req); + if (custom_ie) + kfree(custom_ie); + LEAVE(); + return ret; +} + +/** + * @brief Filter specific IE in ie buf + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * + * @return out IE length + */ +static t_u16 +woal_filter_beacon_ies(const t_u8 * ie, int len, t_u8 * ie_out) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + + /* ERP_INFO and RSN IE will be fileter out */ + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + switch (id) { + case WLAN_EID_ERP_INFO: + case RSN_IE: + break; + default: + memcpy(ie_out + out_len, pos, length + 2); + out_len += length + 2; + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + return out_len; +} + +/** + * @brief config AP or GO for mgmt frame ies. + * + * @param priv A pointer to moal private structure + * @param beacon_ies A pointer to beacon ies + * @param beacon_ies_len Beacon ies length + * @param proberesp_ies A pointer to probe resp ies + * @param proberesp_ies_len Probe resp ies length + * @param assocresp_ies A pointer to probe resp ies + * @param assocresp_ies_len Assoc resp ies length + * @param probereq_ies A pointer to probe req ies + * @param probereq_ies_len Probe req ies length * + * @param mask Mgmt frame mask + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_mgmt_frame_ie(moal_private * priv, + const t_u8 * beacon_ies, size_t beacon_ies_len, + const t_u8 * proberesp_ies, + size_t proberesp_ies_len, + const t_u8 * assocresp_ies, + size_t assocresp_ies_len, const t_u8 * probereq_ies, + size_t probereq_ies_len, t_u16 mask) +{ + int ret = 0; + t_u8 *pos = NULL; + custom_ie *beacon_ies_data = NULL; + custom_ie *proberesp_ies_data = NULL; + custom_ie *assocresp_ies_data = NULL; + custom_ie *probereq_ies_data = NULL; + + /* static variables for mgmt frame ie auto-indexing */ + static t_u16 beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + static t_u16 proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + static t_u16 assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + static t_u16 probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + moal_private *pmpriv = NULL; + static t_u16 rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + const t_u8 *rsn_ie; + + ENTER(); + + if (mask & MGMT_MASK_BEACON) { + if (!(beacon_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + if (beacon_ies && beacon_ies_len) { + rsn_ie = + woal_parse_ie_tlv((t_u8 *) beacon_ies, (int) beacon_ies_len, + RSN_IE); + if (rsn_ie) { + beacon_ies_data->ie_index = rsn_index; + beacon_ies_data->mgmt_subtype_mask = + MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP; + beacon_ies_data->ie_length = rsn_ie[1] + 2; + memcpy(beacon_ies_data->ie_buffer, rsn_ie, rsn_ie[1] + 2); + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index, + NULL, &proberesp_index, NULL, + &assocresp_index, NULL, + &probereq_index)) { + ret = -EFAULT; + goto done; + } + } + } else { + /* clear rsn_ie */ + if (rsn_index <= MAX_MGMT_IE_INDEX) { + beacon_ies_data->ie_index = rsn_index; + beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index, + NULL, &proberesp_index, NULL, + &assocresp_index, NULL, + &probereq_index)) { + ret = -EFAULT; + goto done; + } + } + } + } + + if (mask & MGMT_MASK_PROBE_RESP) { + if (!(proberesp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + } + + if (mask & MGMT_MASK_ASSOC_RESP) { + if (!(assocresp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + } + if (mask & MGMT_MASK_PROBE_REQ) { + if (!(probereq_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + } + + if (beacon_ies_data) { + memset(beacon_ies_data, 0x00, sizeof(custom_ie)); + if (beacon_ies && beacon_ies_len) { + /* set the beacon ies */ + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON; + beacon_ies_data->mgmt_subtype_mask |= MGMT_MASK_ASSOC_RESP; + beacon_ies_data->ie_length = woal_filter_beacon_ies(beacon_ies, + beacon_ies_len, + beacon_ies_data-> + ie_buffer); + } else { + /* clear the beacon ies */ + if (beacon_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, "Invalid beacon index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (proberesp_ies_data) { + memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response ies */ + // proberesp_ies_data->ie_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_RESP; + proberesp_ies_data->ie_length = proberesp_ies_len; + pos = proberesp_ies_data->ie_buffer; + memcpy(pos, proberesp_ies, proberesp_ies_len); + } else { + /* clear the probe response ies */ + if (proberesp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, "Invalid probe resp index for mgmt frame ie.\n"); + goto done; + } + + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + if (assocresp_ies_data) { + memset(assocresp_ies_data, 0x00, sizeof(custom_ie)); + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response ies */ + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = MGMT_MASK_ASSOC_RESP; + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + memcpy(pos, assocresp_ies, assocresp_ies_len); + } else { + /* clear the assoc response ies */ + if (assocresp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, "Invalid assoc resp index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (probereq_ies_data) { + memset(probereq_ies_data, 0x00, sizeof(custom_ie)); + if ((probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) && + (priv->probereq_index != probereq_index)) { + pmpriv = woal_get_priv_by_mgmt_index(priv->phandle, probereq_index); + if (pmpriv) { + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + probereq_ies_data->ie_length = 0; + probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + pmpriv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(pmpriv, NULL, &beacon_index, + NULL, &proberesp_index, + NULL, &assocresp_index, + probereq_ies_data, + &probereq_index)) { + ret = -EFAULT; + goto done; + } + memset(probereq_ies_data, 0x00, sizeof(custom_ie)); + } + } + if (probereq_ies && probereq_ies_len) { + /* set the probe req ies */ + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_REQ; + probereq_ies_data->ie_length = probereq_ies_len; + pos = probereq_ies_data->ie_buffer; + memcpy(pos, probereq_ies, probereq_ies_len); + } else { + /* clear the probe req ies */ + if (probereq_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, "Invalid probe resp index for mgmt frame ie.\n"); + goto done; + } + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; + probereq_ies_data->ie_length = 0; + probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, &beacon_index, + proberesp_ies_data, &proberesp_index, + assocresp_ies_data, &assocresp_index, + probereq_ies_data, &probereq_index)) { + ret = -EFAULT; + goto done; + } + if (probereq_ies_data) + priv->probereq_index = probereq_index; + + done: + if (beacon_ies_data) + kfree(beacon_ies_data); + if (proberesp_ies_data) + kfree(proberesp_ies_data); + if (assocresp_ies_data) + kfree(assocresp_ies_data); + + LEAVE(); + + return ret; +} diff --git a/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h index e1683ed08a89..f58fc18829fa 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h +++ b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h @@ -24,8 +24,11 @@ #include "moal_main.h" +/* Clear all key indexes */ +#define KEY_INDEX_CLEAR_ALL (0x0000000F) + /** RTS/FRAG disabled value */ -#define MLAN_FRAG_RTS_DISABLED (0xFFFFFFFF) +#define MLAN_FRAG_RTS_DISABLED (0xFFFFFFFF) #ifndef WLAN_CIPHER_SUITE_WAPI #define WLAN_CIPHER_SUITE_WAPI 0x00000020 @@ -55,6 +58,9 @@ static const void *const mrvl_wiphy_privid = &mrvl_wiphy_privid; /* Get the private structure from wiphy */ void *woal_get_wiphy_priv(struct wiphy *wiphy); +/* Get the private structure from net device */ +void *woal_get_netdev_priv(struct net_device *dev); + int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, @@ -78,7 +84,7 @@ int woal_cfg80211_del_key(struct wiphy *wiphy, #ifdef STA_CFG80211 #ifdef STA_SUPPORT -int woal_set_rf_channel(struct wiphy *wiphy, +int woal_set_rf_channel(moal_private * priv, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); mlan_status woal_inform_bss_from_scan_result(moal_private * priv, @@ -149,7 +155,7 @@ int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev); #if defined(WIFI_DIRECT_SUPPORT) /** Define kernel version for wifi direct */ #if !defined(COMPAT_WIRELESS) -#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(3,0,0) +#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(2,6,39) #else #define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(2,6,33) #endif /* COMPAT_WIRELESS */ @@ -173,7 +179,7 @@ int woal_cfg80211_init_p2p_go(moal_private * priv); int woal_cfg80211_deinit_p2p(moal_private * priv); -int woal_cfg80211_remain_on_channel_cfg(struct wiphy *wiphy, +int woal_cfg80211_remain_on_channel_cfg(moal_private * priv, t_u8 wait_option, t_u8 remove, t_u8 * status, struct ieee80211_channel *chan, @@ -184,6 +190,8 @@ int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, #endif /* KERNEL_VERSION */ #endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ +const t_u8 *woal_parse_ie_tlv(const t_u8 * ie, int len, t_u8 id); + int woal_cfg80211_mgmt_frame_ie(moal_private * priv, const t_u8 * beacon_ies, size_t beacon_ies_len, const t_u8 * proberesp_ies, diff --git a/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c index 2282216f7d55..31040a29ceec 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c @@ -50,10 +50,29 @@ Change log: #define PRIV_CMD_BANDCFG "bandcfg" /** Private command: Host cmd */ #define PRIV_CMD_HOSTCMD "hostcmd" +/** Private command: Custom IE config*/ +#define PRIV_CMD_CUSTOMIE "customie" /** Private command: HT Tx Cfg */ #define PRIV_CMD_HTTXCFG "httxcfg" #define PRIV_CMD_DATARATE "getdatarate" #define PRIV_CMD_TXRATECFG "txratecfg" +#define PRIV_CMD_ESUPPMODE "esuppmode" +#define PRIV_CMD_PASSPHRASE "passphrase" +#define PRIV_CMD_DEAUTH "deauth" +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#define PRIV_CMD_BSSROLE "bssrole" +#endif +#endif +#ifdef STA_SUPPORT +#define PRIV_CMD_SETUSERSCAN "setuserscan" +#endif +#define PRIV_CMD_DEEPSLEEP "deepsleep" +#define PRIV_CMD_IPADDR "ipaddr" +#define PRIV_CMD_WPSSESSION "wpssession" +#define PRIV_CMD_OTPUSERDATA "otpuserdata" +#define PRIV_CMD_COUNTRYCODE "countrycode" +#define PRIV_CMD_TCPACKENH "tcpackenh" /** Bands supported in Infra mode */ static t_u8 SupportedInfraBand[] = { @@ -76,6 +95,17 @@ static t_u8 SupportedAdhocBand[] = { /******************************************************** Global Variables ********************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) +#ifdef UAP_SUPPORT +/** Network device handlers for uAP */ +extern const struct net_device_ops woal_uap_netdev_ops; +#endif +#ifdef STA_SUPPORT +/** Network device handlers for STA */ +extern const struct net_device_ops woal_netdev_ops; +#endif +#endif +extern int cfg80211_wext; /******************************************************** Local Functions @@ -85,7 +115,7 @@ static t_u8 SupportedAdhocBand[] = { * * @param pos Pointer to the arguments string * @param data Pointer to the arguments buffer - * @param len Pointer to the number of arguments extracted + * @param user_data_len Pointer to the number of arguments extracted * * @return MLAN_STATUS_SUCCESS * @@ -246,7 +276,6 @@ woal_get_priv_driver_version(moal_private * priv, t_u8 * respbuf, return ret; } -#ifdef WIFI_DIRECT_SUPPORT /** * @brief Hostcmd interface from application * @@ -296,8 +325,12 @@ woal_priv_hostcmd(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) ret = -EFAULT; goto error; } - sprintf(respbuf, "OK"); - ret = 3; + memcpy(data_ptr + sizeof(buf_len), misc_cfg->param.hostcmd.cmd, + misc_cfg->param.hostcmd.len); + ret = + misc_cfg->param.hostcmd.len + sizeof(buf_len) + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_HOSTCMD); + memcpy(data_ptr, (t_u8 *) & ret, sizeof(t_u32)); error: if (req) @@ -306,7 +339,65 @@ woal_priv_hostcmd(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) LEAVE(); return ret; } -#endif + +/** + * @brief Custom IE setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_customie(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 *data_ptr; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + + ENTER(); + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_CUSTOMIE)); + + custom_ie = (mlan_ds_misc_custom_ie *) data_ptr; + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *) ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + if ((custom_ie->len == 0) || + (custom_ie->len == sizeof(custom_ie->ie_data_list[0].ie_index))) + ioctl_req->action = MLAN_ACT_GET; + else + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + custom_ie = (mlan_ds_misc_custom_ie *) data_ptr; + memcpy(custom_ie, &misc->param.cust_ie, sizeof(mlan_ds_misc_custom_ie)); + ret = sizeof(mlan_ds_misc_custom_ie); + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) { + /* send a separate error code to indicate error from driver */ + ret = EFAULT; + } + done: + if (ioctl_req) { + kfree(ioctl_req); + } + LEAVE(); + return ret; +} /** * @brief Set/Get Band and Adhoc-band setting @@ -728,6 +819,824 @@ woal_setget_priv_txratecfg(moal_private * priv, t_u8 * respbuf, } /** + * @brief Set/Get esupplicant mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_esuppmode(moal_private * priv, t_u8 * respbuf, + t_u32 respbuflen) +{ + t_u32 data[3]; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + woal_esuppmode_cfg *esupp_mode = NULL; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ESUPPMODE))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *) data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_ESUPPMODE), data, &user_data_len); + } + + if (user_data_len >= 4 || user_data_len == 1 || user_data_len == 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *) req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE; + + if (user_data_len == 0) { + /* Get operation */ + req->action = MLAN_ACT_GET; + } else { + /* Set operation */ + req->action = MLAN_ACT_SET; + /* RSN mode */ + sec->param.esupp_mode.rsn_mode = data[0]; + /* Pairwise cipher */ + sec->param.esupp_mode.act_paircipher = (data[1] & 0xFF); + /* Group cipher */ + sec->param.esupp_mode.act_groupcipher = (data[2] & 0xFF); + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + esupp_mode = (woal_esuppmode_cfg *) respbuf; + esupp_mode->rsn_mode = (t_u16) ((sec->param.esupp_mode.rsn_mode) & 0xFFFF); + esupp_mode->pairwise_cipher = + (t_u8) ((sec->param.esupp_mode.act_paircipher) & 0xFF); + esupp_mode->group_cipher = + (t_u8) ((sec->param.esupp_mode.act_groupcipher) & 0xFF); + + ret = sizeof(woal_esuppmode_cfg); + done: + if (req) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get esupplicant passphrase configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_passphrase(moal_private * priv, t_u8 * respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0, action = -1, i = 0; + char *begin, *end, *opt; + t_u16 len = 0; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + t_u8 *mac = NULL; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_PASSPHRASE))) { + PRINTM(MERROR, "No arguments provided\n"); + ret = -EINVAL; + goto done; + } + + /* Parse the buf to get the cmd_action */ + begin = respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_PASSPHRASE); + end = woal_strsep(&begin, ';', '/'); + if (end) + action = woal_atox(end); + if (action < 0 || action > 2 || end[1] != '\0') { + PRINTM(MERROR, "Invalid action argument %s\n", end); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *) req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + if (action == 0) + req->action = MLAN_ACT_GET; + else + req->action = MLAN_ACT_SET; + + while (begin) { + end = woal_strsep(&begin, ';', '/'); + opt = woal_strsep(&end, '=', '/'); + if (!opt || !end || !end[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + break; + } else if (!strnicmp(opt, "ssid", strlen(opt))) { + if (strlen(end) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, "SSID length exceeds max length\n"); + ret = -EFAULT; + break; + } + sec->param.passphrase.ssid.ssid_len = strlen(end); + strncpy((char *) sec->param.passphrase.ssid.ssid, end, strlen(end)); + PRINTM(MINFO, "ssid=%s, len=%d\n", sec->param.passphrase.ssid.ssid, + (int) sec->param.passphrase.ssid.ssid_len); + } else if (!strnicmp(opt, "bssid", strlen(opt))) { + woal_mac2u8((t_u8 *) & sec->param.passphrase.bssid, end); + } else if (!strnicmp(opt, "psk", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PMK length\n"); + ret = -EINVAL; + break; + } + woal_ascii2hex((t_u8 *) (sec->param.passphrase.psk.pmk.pmk), end, + MLAN_PMK_HEXSTR_LENGTH / 2); + sec->param.passphrase.psk_type = MLAN_PSK_PMK; + } else if (!strnicmp(opt, "passphrase", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) { + PRINTM(MERROR, "Invalid length for passphrase\n"); + ret = -EINVAL; + break; + } + sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE; + strncpy(sec->param.passphrase.psk.passphrase.passphrase, end, + sizeof(sec->param.passphrase.psk.passphrase.passphrase)); + sec->param.passphrase.psk.passphrase.passphrase_len = strlen(end); + PRINTM(MINFO, "passphrase=%s, len=%d\n", + sec->param.passphrase.psk.passphrase.passphrase, + (int) sec->param.passphrase.psk.passphrase.passphrase_len); + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + break; + } + } + if (ret) + goto done; + + if (action == 2) + sec->param.passphrase.psk_type = MLAN_PSK_CLEAR; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + if (sec->param.passphrase.ssid.ssid_len) { + len += sprintf(respbuf + len, "ssid:"); + memcpy(respbuf + len, sec->param.passphrase.ssid.ssid, + sec->param.passphrase.ssid.ssid_len); + len += sec->param.passphrase.ssid.ssid_len; + len += sprintf(respbuf + len, " "); + } + if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) { + mac = (t_u8 *) & sec->param.passphrase.bssid; + len += sprintf(respbuf + len, "bssid:"); + for (i = 0; i < ETH_ALEN - 1; ++i) + len += sprintf(respbuf + len, "%02x:", mac[i]); + len += sprintf(respbuf + len, "%02x ", mac[i]); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { + len += sprintf(respbuf + len, "psk:"); + for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) + len += + sprintf(respbuf + len, "%02x", + sec->param.passphrase.psk.pmk.pmk[i]); + len += sprintf(respbuf + len, "\n"); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) { + len += + sprintf(respbuf + len, "passphrase:%s \n", + sec->param.passphrase.psk.passphrase.passphrase); + } + + ret = len; + done: + if (req) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Deauthenticate + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_deauth(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + int ret = 0; + t_u8 mac[ETH_ALEN]; + + ENTER(); + + if (strlen(respbuf) > (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEAUTH))) { + /* Deauth mentioned BSSID */ + woal_mac2u8(mac, + respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEAUTH)); + if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, mac)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL)) + ret = -EFAULT; + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Set/Get BSS role + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_bssrole(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + t_u8 action = MLAN_ACT_GET; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_BSSROLE))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *) data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_BSSROLE), data, &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + + if (user_data_len == 0) { + action = MLAN_ACT_GET; + } else { + if ((data[0] != MLAN_BSS_ROLE_STA && + data[0] != MLAN_BSS_ROLE_UAP) || + priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MWARN, "Invalid BSS role\n"); + ret = -EINVAL; + goto error; + } + if (data[0] == GET_BSS_ROLE(priv)) { + PRINTM(MWARN, "Already BSS is in desired role\n"); + goto done; + } + action = MLAN_ACT_SET; + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv, + action, MOAL_IOCTL_WAIT, + (t_u8 *) data)) { + ret = -EFAULT; + goto error; + } + + if (user_data_len) { + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + + done: + memset(respbuf, 0, respbuflen); + respbuf[0] = (t_u8) data[0]; + ret = 1; + + error: + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +#ifdef STA_SUPPORT +/** + * @brief Set user scan + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setuserscan(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + wlan_user_scan_cfg scan_cfg; + int ret = 0; + + ENTER(); + + /* Create the scan_cfg structure */ + memset(&scan_cfg, 0, sizeof(scan_cfg)); + + /* We expect the scan_cfg structure to be passed in respbuf */ + memcpy((char *) &scan_cfg, + respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_SETUSERSCAN), + sizeof(wlan_user_scan_cfg)); + + /* Call for scan */ + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg)) + ret = -EFAULT; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get deep sleep mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setgetdeepsleep(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + t_u32 data[2]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEEPSLEEP))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *) data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_DEEPSLEEP), data, &user_data_len); + } + + if (user_data_len >= 3) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { + ret = -EFAULT; + goto done; + } + sprintf(respbuf, "%d %d", data[0], data[1]); + ret = strlen(respbuf) + 1; + } else { + if (data[0] == DEEP_SLEEP_OFF) { + PRINTM(MINFO, "Exit Deep Sleep Mode\n"); + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, 0); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + } else if (data[0] == DEEP_SLEEP_ON) { + PRINTM(MINFO, "Enter Deep Sleep Mode\n"); + if (user_data_len != 2) + data[1] = 0; + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, data[1]); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + } else { + PRINTM(MERROR, "Unknown option = %u\n", data[0]); + ret = -EINVAL; + goto done; + } + ret = sprintf(respbuf, "OK\n") + 1; + } + + done: + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get IP address configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setgetipaddr(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, op_code = 0, data_length = 0, header = 0; + + ENTER(); + + header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_IPADDR); + data_length = strlen(respbuf) - header; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *) req->pbuf; + + if (data_length < 1) { /* GET */ + req->action = MLAN_ACT_GET; + } else { + /* Make sure we have the operation argument */ + if (data_length > 2 && respbuf[header + 1] != ';') { + PRINTM(MERROR, "No operation argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } else { + respbuf[header + 1] = '\0'; + } + req->action = MLAN_ACT_SET; + + /* Only one IP is supported in current firmware */ + memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); + in4_pton(&respbuf[header + 2], + MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), + misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); + misc->param.ipaddr_cfg.ip_addr_num = 1; + misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; + + if (woal_atoi(&op_code, &respbuf[header]) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + misc->param.ipaddr_cfg.op_code = (t_u32) op_code; + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_IP_ADDR; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + snprintf(respbuf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d", + misc->param.ipaddr_cfg.op_code, + misc->param.ipaddr_cfg.ip_addr[0][0], + misc->param.ipaddr_cfg.ip_addr[0][1], + misc->param.ipaddr_cfg.ip_addr[0][2], + misc->param.ipaddr_cfg.ip_addr[0][3]); + ret = IPADDR_MAX_BUF + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WPS session configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setwpssession(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wps_cfg *pwps = NULL; + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_WPSSESSION))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *) data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_WPSSESSION), data, &user_data_len); + } + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pwps = (mlan_ds_wps_cfg *) req->pbuf; + + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + + if (data[0] == 1) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + ret = sprintf(respbuf, "OK\n") + 1; + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get OTP user data + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_otpuserdata(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + int data[1]; + int user_data_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_otp_user_data *otp = NULL; + int ret = 0; + + ENTER(); + + memset((char *) data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_OTPUSERDATA), data, &user_data_len); + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + req->action = MLAN_ACT_GET; + req->req_id = MLAN_IOCTL_MISC_CFG; + + misc = (mlan_ds_misc_cfg *) req->pbuf; + misc->sub_command = MLAN_OID_MISC_OTP_USER_DATA; + misc->param.otp_user_data.user_data_length = data[0]; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + otp = (mlan_ds_misc_otp_user_data *) req->pbuf; + + if (req->action == MLAN_ACT_GET) { + ret = MIN(otp->user_data_length, data[0]); + memcpy(respbuf, otp->user_data, ret); + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set / Get country code + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_set_get_countrycode(moal_private * priv, t_u8 * respbuf, + t_u32 respbuflen) +{ + int ret = 0; + // char data[COUNTRY_CODE_LEN] = {0, 0, 0}; + int header = 0, data_length = 0; // wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_ds_misc_country_code *country_code = NULL; + + ENTER(); + + header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COUNTRYCODE); + data_length = strlen(respbuf) - header; + + if (data_length > COUNTRY_CODE_LEN) { + PRINTM(MERROR, "Invalid argument!\n"); + ret = -EINVAL; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *) req->pbuf; + country_code = &pcfg_misc->param.country_code; + pcfg_misc->sub_command = MLAN_OID_MISC_COUNTRY_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (data_length <= 1) { + req->action = MLAN_ACT_GET; + } else { + memset(country_code->country_code, 0, COUNTRY_CODE_LEN); + memcpy(country_code->country_code, respbuf + header, COUNTRY_CODE_LEN); + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + if (woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + ret = data_length = COUNTRY_CODE_LEN; + memset(respbuf + header, 0, COUNTRY_CODE_LEN); + memcpy(respbuf, country_code->country_code, COUNTRY_CODE_LEN); + } + + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TCP Ack enhancement configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setgettcpackenh(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + struct list_head *link = NULL; + struct tcp_sess *tcp_sess = NULL; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TCPACKENH))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *) data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_TCPACKENH), data, &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + /* get operation */ + respbuf[0] = priv->enable_tcp_ack_enh; + } else { + /* set operation */ + if (data[0] == MTRUE) { + PRINTM(MINFO, "Enabling TCP Ack enhancement\n"); + priv->enable_tcp_ack_enh = MTRUE; + } else if (data[0] == MFALSE) { + PRINTM(MINFO, "Disabling TCP Ack enhancement\n"); + priv->enable_tcp_ack_enh = MFALSE; + /* release the tcp sessions if any */ + while (!list_empty(&priv->tcp_sess_queue)) { + link = priv->tcp_sess_queue.next; + tcp_sess = list_entry(link, struct tcp_sess, link); + PRINTM(MINFO, + "Disable TCP ACK Enh: release a tcp session in dl. (src: ipaddr 0x%08x, port: %d. dst: ipaddr 0x%08x, port: %d\n", + tcp_sess->src_ip_addr, tcp_sess->src_tcp_port, + tcp_sess->dst_ip_addr, tcp_sess->dst_tcp_port); + list_del(link); + kfree(tcp_sess); + } + } else { + PRINTM(MERROR, "Unknown option = %u\n", data[0]); + ret = -EINVAL; + goto done; + } + respbuf[0] = priv->enable_tcp_ack_enh; + } + ret = 1; + + done: + LEAVE(); + return ret; + +} + +/** * @brief Set priv command for Android * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure @@ -788,7 +1697,6 @@ woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) /* Set/Get band configuration */ len = woal_setget_priv_bandcfg(priv, buf, priv_cmd.total_len); goto handled; -#ifdef WIFI_DIRECT_SUPPORT } else if (strnicmp (buf + strlen(CMD_MARVELL), PRIV_CMD_HOSTCMD, @@ -796,7 +1704,6 @@ woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) /* hostcmd configuration */ len = woal_priv_hostcmd(priv, buf, priv_cmd.total_len); goto handled; -#endif } else if (strnicmp (buf + strlen(CMD_MARVELL), PRIV_CMD_HTTXCFG, @@ -818,13 +1725,110 @@ woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) /* Set/Get tx rate cfg */ len = woal_setget_priv_txratecfg(priv, buf, priv_cmd.total_len); goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CUSTOMIE, + strlen(PRIV_CMD_CUSTOMIE)) == 0) { + /* Custom IE configuration */ + len = woal_priv_customie(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ESUPPMODE, + strlen(PRIV_CMD_ESUPPMODE)) == 0) { + /* Esupplicant mode configuration */ + len = woal_setget_priv_esuppmode(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PASSPHRASE, + strlen(PRIV_CMD_PASSPHRASE)) == 0) { + /* Esupplicant passphrase configuration */ + len = woal_setget_priv_passphrase(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DEAUTH, + strlen(PRIV_CMD_DEAUTH)) == 0) { + /* Deauth */ + len = woal_priv_deauth(priv, buf, priv_cmd.total_len); + goto handled; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BSSROLE, + strlen(PRIV_CMD_BSSROLE)) == 0) { + /* BSS Role */ + len = woal_priv_bssrole(priv, buf, priv_cmd.total_len); + goto handled; +#endif +#endif +#ifdef STA_SUPPORT + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SETUSERSCAN, + strlen(PRIV_CMD_SETUSERSCAN)) == 0) { + /* Set user scan */ + len = woal_priv_setuserscan(priv, buf, priv_cmd.total_len); + goto handled; +#endif + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DEEPSLEEP, + strlen(PRIV_CMD_DEEPSLEEP)) == 0) { + /* Deep sleep */ + len = woal_priv_setgetdeepsleep(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_IPADDR, + strlen(PRIV_CMD_IPADDR)) == 0) { + /* IP address */ + len = woal_priv_setgetipaddr(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_WPSSESSION, + strlen(PRIV_CMD_WPSSESSION)) == 0) { + /* WPS Session */ + len = woal_priv_setwpssession(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_OTPUSERDATA, + strlen(PRIV_CMD_OTPUSERDATA)) == 0) { + /* OTP user data */ + len = woal_priv_otpuserdata(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_COUNTRYCODE, + strlen(PRIV_CMD_COUNTRYCODE)) == 0) { + /* OTP user data */ + len = woal_priv_set_get_countrycode(priv, buf, priv_cmd.total_len); + goto handled; + } else + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TCPACKENH, + strlen(PRIV_CMD_TCPACKENH)) == 0) { + /* OTP user data */ + len = woal_priv_setgettcpackenh(priv, buf, priv_cmd.total_len); + goto handled; } else { /* Fall through, after stripping off the custom header */ buf += strlen(CMD_MARVELL); } } #ifdef STA_SUPPORT - if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { + if (strncmp(buf, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == 0) { + pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_rssi_low_threshold(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { @@ -983,12 +1987,14 @@ woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) priv->bg_scan_reported = MFALSE; len = sprintf(buf, "OK\n") + 1; } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { - if (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { - ret = -EFAULT; - goto done; + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; } - priv->bg_scan_start = MFALSE; - priv->bg_scan_reported = MFALSE; len = sprintf(buf, "OK\n") + 1; } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == 0) { #ifdef MEF_CFG_RX_FILTER @@ -1004,7 +2010,19 @@ woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) } #endif len = sprintf(buf, "OK\n") + 1; - } else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { + } +#ifdef STA_CFG80211 + else if (strncmp(buf, "GET_RSSI_STATUS", strlen("GET_RSSI_STATUS")) == 0) { + if (priv->rssi_status == MLAN_EVENT_ID_FW_BCN_RSSI_LOW) + len = sprintf(buf, "EVENT=BEACON_RSSI_LOW\n"); + else if (priv->rssi_status == MLAN_EVENT_ID_FW_PRE_BCN_LOST) + len = sprintf(buf, "EVENT=PRE_BEACON_LOST\n"); + else + len = sprintf(buf, "OK\n") + 1; + priv->rssi_status = 0; + } +#endif + else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { pdata = buf + strlen("RXFILTER-ADD") + 1; if (MLAN_STATUS_SUCCESS != woal_add_rxfilter(priv, pdata)) { ret = -EFAULT; @@ -1071,10 +2089,20 @@ woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) if (len > 0) { priv_cmd.used_len = len; - if (copy_to_user(priv_cmd.buf, buf, len)) { + if (priv_cmd.used_len < priv_cmd.total_len) + memset(priv_cmd.buf + priv_cmd.used_len, 0, + priv_cmd.total_len - priv_cmd.used_len); + if (copy_to_user(priv_cmd.buf, buf, priv_cmd.used_len)) { PRINTM(MERROR, "%s: failed to copy data to user buffer\n", __FUNCTION__); ret = -EFAULT; + goto done; + } + if (copy_to_user + (req->ifr_data, &priv_cmd, sizeof(android_wifi_priv_cmd))) { + PRINTM(MERROR, "%s: failed to copy command header to user buffer\n", + __FUNCTION__); + ret = -EFAULT; } } else { ret = len; diff --git a/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h index a1481e88ff21..a68991694a9d 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h +++ b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h @@ -44,12 +44,23 @@ Change log: /** Private command ID to get BSS type */ #define WOAL_GET_BSS_TYPE (SIOCDEVPRIVATE + 15) -int woal_do_ioctl(struct net_device *dev, struct ifreq *req, int i); +int woal_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd); +/* + * For android private commands, fixed value of ioctl is used. + * Internally commands are differentiated using strings. + * + * application needs to specify "total_len" of data for copy_from_user + * kernel updates "used_len" during copy_to_user + */ +/** Private command structure from app */ typedef struct _android_wifi_priv_cmd { + /** Buffer pointer */ char *buf; + /** buffer updated by driver */ int used_len; + /** buffer sent by application */ int total_len; } android_wifi_priv_cmd; @@ -62,6 +73,16 @@ typedef struct woal_priv_tx_rate_cfg t_u32 rate_index; } woal_tx_rate_cfg; +typedef struct woal_priv_esuppmode_cfg +{ + /* RSN mode */ + t_u16 rsn_mode; + /* Pairwise cipher */ + t_u8 pairwise_cipher; + /* Group cipher */ + t_u8 group_cipher; +} woal_esuppmode_cfg; + mlan_status woal_set_ap_wps_p2p_ie(moal_private * priv, t_u8 * ie, size_t len); int woal_android_priv_cmd(struct net_device *dev, struct ifreq *req); diff --git a/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c b/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c index efe6e7ddcea1..94fe7e11669e 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c @@ -2,7 +2,7 @@ * * @brief This file contains ioctl function to MLAN * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -792,7 +792,7 @@ woal_set_get_retry(moal_private * priv, t_u32 action, #ifdef STA_CFG80211 /* If set is invoked from other than iw i.e iwconfig, wiphy retry count should be updated as well */ - if (IS_STA_CFG80211(cfg80211_wext) && + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy && (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && (action == MLAN_ACT_SET)) { priv->wdev->wiphy->retry_long = (t_u8) * value; priv->wdev->wiphy->retry_short = (t_u8) * value; @@ -854,7 +854,7 @@ woal_set_get_rts(moal_private * priv, t_u32 action, #ifdef STA_CFG80211 /* If set is invoked from other than iw i.e iwconfig, wiphy RTS threshold should be updated as well */ - if (IS_STA_CFG80211(cfg80211_wext) && + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy && (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && (action == MLAN_ACT_SET)) priv->wdev->wiphy->rts_threshold = *value; #endif @@ -914,7 +914,7 @@ woal_set_get_frag(moal_private * priv, t_u32 action, #ifdef STA_CFG80211 /* If set is invoked from other than iw i.e iwconfig, wiphy fragment threshold should be updated as well */ - if (IS_STA_CFG80211(cfg80211_wext) && + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy && (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && (action == MLAN_ACT_SET)) priv->wdev->wiphy->frag_threshold = *value; #endif @@ -1096,7 +1096,7 @@ woal_set_get_power_mgmt(moal_private * priv, #ifdef STA_CFG80211 /* If set is invoked from other than iw i.e iwconfig, wiphy IEEE power save mode should be updated */ - if (IS_STA_CFG80211(cfg80211_wext) && + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && (action == MLAN_ACT_SET)) { if (*disabled) priv->wdev->ps = MFALSE; @@ -1780,20 +1780,22 @@ woal_get_bss_type(struct net_device *dev, struct ifreq *req) #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) #if defined(STA_WEXT) || defined(UAP_WEXT) /** - * @brief Set/Get BSS role + * @brief Swithces BSS role of WFD interface * - * @param priv A pointer to moal_private structure - * @param wrq A pointer to iwreq structure + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT) + * @param bss_role A pointer to bss role * * @return 0 --success, otherwise fail */ -int -woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq) +mlan_status +woal_bss_role_cfg(moal_private * priv, t_u8 action, + t_u8 wait_option, t_u8 * bss_role) { int ret = 0; mlan_ds_bss *bss = NULL; mlan_ioctl_req *req = NULL; - int bss_role = 0; struct net_device *dev = priv->netdev; ENTER(); @@ -1812,55 +1814,26 @@ woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq) bss = (mlan_ds_bss *) req->pbuf; bss->sub_command = MLAN_OID_BSS_ROLE; req->req_id = MLAN_IOCTL_BSS; - if (wrq->u.data.length) { - if (copy_from_user(&bss_role, wrq->u.data.pointer, sizeof(int))) { - PRINTM(MERROR, "Copy from user failed\n"); - ret = -EFAULT; - goto done; - } - if (bss_role != MLAN_BSS_ROLE_STA && bss_role != MLAN_BSS_ROLE_UAP) { - PRINTM(MWARN, "Invalid BSS role\n"); - ret = -EINVAL; - goto done; - } - if (bss_role == GET_BSS_ROLE(priv)) { - PRINTM(MWARN, "Already BSS is in desired role\n"); - ret = -EINVAL; - goto done; - } - req->action = MLAN_ACT_SET; - bss->param.bss_role = (t_u8) bss_role; - } else { - req->action = MLAN_ACT_GET; - } - - if (req->action == MLAN_ACT_SET) { - /* Reset interface */ - woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + req->action = action; + if (action == MLAN_ACT_SET) { + bss->param.bss_role = *bss_role; } - if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) { ret = -EFAULT; goto done; } - if (!wrq->u.data.length) { - bss_role = (int) bss->param.bss_role; - if (copy_to_user(wrq->u.data.pointer, &bss_role, sizeof(int))) { - ret = -EFAULT; - goto done; - } - wrq->u.data.length = 1; + + if (action == MLAN_ACT_GET) { + *bss_role = bss->param.bss_role; } else { /* Update moal_private */ - priv->bss_role = bss_role; + priv->bss_role = *bss_role; if (priv->bss_type == MLAN_BSS_TYPE_UAP) priv->bss_type = MLAN_BSS_TYPE_STA; else if (priv->bss_type == MLAN_BSS_TYPE_STA) priv->bss_type = MLAN_BSS_TYPE_UAP; - /* Initialize private structures */ - woal_init_priv(priv, MOAL_IOCTL_WAIT); - - if (bss_role == MLAN_BSS_ROLE_UAP) { + if (*bss_role == MLAN_BSS_ROLE_UAP) { /* Switch: STA -> uAP */ /* Setup the OS Interface to our functions */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) @@ -1881,7 +1854,7 @@ woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq) init_waitqueue_head(&priv->w_stats_wait_q); } #endif /* UAP_WEXT */ - } else if (bss_role == MLAN_BSS_ROLE_STA) { + } else if (*bss_role == MLAN_BSS_ROLE_STA) { /* Switch: uAP -> STA */ /* Setup the OS Interface to our functions */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) @@ -1903,9 +1876,6 @@ woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq) } #endif /* STA_WEXT */ } - /* Enable interfaces */ - netif_device_attach(dev); - woal_start_queue(dev); } done: @@ -1914,8 +1884,75 @@ woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq) LEAVE(); return ret; } -#endif -#endif + +/** + * @brief Set/Get BSS role + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +int +woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq) +{ + int ret = 0; + int bss_role = 0; + t_u8 action = MLAN_ACT_GET; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&bss_role, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((bss_role != MLAN_BSS_ROLE_STA && + bss_role != MLAN_BSS_ROLE_UAP) || + (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT)) { + PRINTM(MWARN, "Invalid BSS role\n"); + ret = -EINVAL; + goto done; + } + if (bss_role == GET_BSS_ROLE(priv)) { + PRINTM(MWARN, "Already BSS is in desired role\n"); + ret = -EINVAL; + goto done; + } + action = MLAN_ACT_SET; + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv, + action, MOAL_IOCTL_WAIT, + (t_u8 *) & bss_role)) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + if (copy_to_user(wrq->u.data.pointer, &bss_role, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } else { + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + + done: + LEAVE(); + return ret; +} +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ #endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ /** @@ -1993,6 +2030,7 @@ woal_cancel_hs(moal_private * priv, t_u8 wait_option) return ret; } +#if defined(SDIO_SUSPEND_RESUME) /** @brief This function enables the host sleep * * @param priv A Pointer to the moal_private structure @@ -2074,6 +2112,7 @@ woal_enable_hs(moal_private * priv) LEAVE(); return hs_actived; } +#endif /** * @brief This function send soft_reset command to firmware @@ -2525,6 +2564,48 @@ woal_get_pm_info(moal_private * priv, mlan_ds_ps_info * pm_info) } /** + * @brief Get Deep Sleep + * + * @param priv Pointer to the moal_private driver data struct + * @param data Pointer to return deep_sleep setting + * + * @return 0 --success, otherwise fail + */ +int +woal_get_deep_sleep(moal_private * priv, t_u32 * data) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *) req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + req->req_id = MLAN_IOCTL_PM_CFG; + + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + *data = pm->param.auto_deep_sleep.auto_ds; + *(data + 1) = pm->param.auto_deep_sleep.idletime; + + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/** * @brief Set Deep Sleep * * @param priv Pointer to the moal_private driver data struct @@ -2665,8 +2746,7 @@ woal_11h_channel_check_ioctl(moal_private * priv) * @return MLAN_STATUS_SUCCESS -- success, otherwise fail */ mlan_status -woal_cfg80211_wifi_direct_mode_cfg(moal_private * priv, t_u16 action, - t_u16 * mode) +woal_wifi_direct_mode_cfg(moal_private * priv, t_u16 action, t_u16 * mode) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ioctl_req *req = NULL; @@ -3640,6 +3720,13 @@ woal_set_bg_scan(moal_private * priv, char *buf, int length) ptr += 2; buf_left -= 2; break; + case WEXT_BGSCAN_REPEAT_SECTION: + priv->scan_cfg.repeat_count = (t_u16) ptr[1]; + PRINTM(MIOCTL, "BG scan: repeat_count=%d\n", + (int) priv->scan_cfg.repeat_count); + ptr += 2; + buf_left -= 2; + break; case WEXT_BGSCAN_INTERVAL_SECTION: priv->scan_cfg.scan_interval = (ptr[2] << 8 | ptr[1]) * 1000; PRINTM(MIOCTL, "BG scan: scan_interval=%d\n", @@ -3728,7 +3815,7 @@ woal_reconfig_bgscan(moal_handle * handle) * @brief set rssi low threshold * * @param priv A pointer to moal_private structure - * @param scan_type MLAN_SCAN_TYPE_ACTIVE/MLAN_SCAN_TYPE_PASSIVE + * @param rssi A pointer to low rssi * * @return MLAN_STATUS_SUCCESS -- success, otherwise fail */ @@ -3754,10 +3841,16 @@ woal_set_rssi_low_threshold(moal_private * priv, char *rssi) misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT; req->action = MLAN_ACT_SET; misc->param.subscribe_event.evt_bitmap = SUBSCRIBE_EVT_RSSI_LOW; + misc->param.subscribe_event.evt_bitmap |= SUBSCRIBE_EVT_PRE_BEACON_LOST; + misc->param.subscribe_event.pre_beacon_miss = DEFAULT_PRE_BEACON_MISS; + if (MLAN_STATUS_SUCCESS != woal_atoi(&low_rssi, rssi)) { ret = -EFAULT; goto done; } +#ifdef STA_CFG80211 + priv->mrvl_rssi_low = low_rssi; +#endif misc->param.subscribe_event.low_rssi = low_rssi; misc->param.subscribe_event.low_rssi_freq = 0; if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { @@ -3771,6 +3864,69 @@ woal_set_rssi_low_threshold(moal_private * priv, char *rssi) return ret; } +#ifdef STA_CFG80211 +/** + * @brief set rssi low threshold + * + * @param priv A pointer to moal_private structure + * @param event_id event id. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_rssi_threshold(moal_private * priv, t_u32 event_id) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + if (priv->media_connected == MFALSE) + goto done; + if (priv->mrvl_rssi_low) + goto done; + if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_LOW) { + if (priv->last_rssi_low < 100) + priv->last_rssi_low += priv->cqm_rssi_hyst; + priv->last_rssi_high = abs(priv->cqm_rssi_thold); + } else if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_HIGH) { + priv->last_rssi_low = abs(priv->cqm_rssi_thold); + if (priv->last_rssi_high > priv->cqm_rssi_hyst) + priv->last_rssi_high -= priv->cqm_rssi_hyst; + } else { + priv->last_rssi_low = abs(priv->cqm_rssi_thold); + priv->last_rssi_high = abs(priv->cqm_rssi_thold); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *) req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT; + req->action = MLAN_ACT_SET; + misc->param.subscribe_event.evt_bitmap = + SUBSCRIBE_EVT_RSSI_LOW | SUBSCRIBE_EVT_RSSI_HIGH; + misc->param.subscribe_event.low_rssi_freq = 0; + misc->param.subscribe_event.low_rssi = priv->last_rssi_low; + misc->param.subscribe_event.high_rssi_freq = 0; + misc->param.subscribe_event.high_rssi = priv->last_rssi_high; + PRINTM(MIOCTL, "rssi_low=%d, rssi_high=%d\n", (int) priv->last_rssi_low, + (int) priv->last_rssi_high); + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} +#endif + /** * @brief Get power mode * diff --git a/drivers/net/wireless/sd8797/mlinux/moal_main.c b/drivers/net/wireless/sd8797/mlinux/moal_main.c index eac248b70129..d5bed42fa73d 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_main.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_main.c @@ -47,6 +47,11 @@ Change log: #include <linux/wlan_plat.h> #include "moal_eth_ioctl.h" +#include <linux/if_ether.h> +#include <linux/in.h> +#include <linux/tcp.h> +#include <net/tcp.h> + /******************************************************** Local Variables ********************************************************/ @@ -205,7 +210,7 @@ int woal_close(struct net_device *dev); int woal_set_mac_address(struct net_device *dev, void *addr); void woal_tx_timeout(struct net_device *dev); struct net_device_stats *woal_get_stats(struct net_device *dev); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb); #endif @@ -404,7 +409,7 @@ woal_init_sw(moal_handle * handle) /* PnP and power profile */ handle->surprise_removed = MFALSE; init_waitqueue_head(&handle->init_wait_q); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) spin_lock_init(&handle->queue_lock); #endif @@ -1215,9 +1220,7 @@ const struct net_device_ops woal_netdev_ops = { #else .ndo_set_multicast_list = woal_set_multicast_list, #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) .ndo_select_queue = woal_select_queue, -#endif }; #endif @@ -1292,9 +1295,7 @@ const struct net_device_ops woal_uap_netdev_ops = { #else .ndo_set_multicast_list = woal_uap_set_multicast_list, #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) .ndo_select_queue = woal_select_queue, -#endif }; #endif @@ -1373,7 +1374,7 @@ woal_add_interface(moal_handle * handle, t_u8 bss_index, t_u8 bss_type) moal_private *priv = NULL; ENTER(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) #define MAX_WMM_QUEUE 4 /* Allocate an Ethernet device */ if (!(dev = alloc_etherdev_mq(sizeof(moal_private), MAX_WMM_QUEUE))) { @@ -1420,11 +1421,8 @@ woal_add_interface(moal_handle * handle, t_u8 bss_index, t_u8 bss_type) else if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) priv->bss_role = MLAN_BSS_ROLE_STA; #endif -#if defined(STA_CFG80211) || defined(UAP_CFG80211) - priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; -#endif -#ifdef STA_SUPPORT -#endif + + INIT_LIST_HEAD(&priv->tcp_sess_queue); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) SET_MODULE_OWNER(dev); @@ -1448,7 +1446,7 @@ woal_add_interface(moal_handle * handle, t_u8 bss_index, t_u8 bss_type) if ((priv->bss_role == MLAN_BSS_ROLE_STA) && IS_STA_CFG80211(cfg80211_wext)) { if (bss_type == MLAN_BSS_TYPE_STA #if defined(WIFI_DIRECT_SUPPORT) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION || bss_type == MLAN_BSS_TYPE_WIFIDIRECT #endif #endif @@ -1883,7 +1881,14 @@ woal_close(struct net_device *dev) moal_private *priv = (moal_private *) netdev_priv(dev); ENTER(); - +#ifdef STA_SUPPORT +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext) && priv->scan_request) { + cfg80211_scan_done(priv->scan_request, MTRUE); + priv->scan_request = NULL; + } +#endif +#endif woal_stop_queue(priv->netdev); MODULE_PUT; @@ -2066,7 +2071,7 @@ woal_get_stats(struct net_device *dev) return &priv->stats; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) /** * @brief This function handles wmm queue select * @@ -2109,6 +2114,233 @@ woal_select_queue(struct net_device * dev, struct sk_buff * skb) #endif /** + * @brief This function gets tcp session from the tcp session queue + * + * @param priv A pointer to moal_private structure + * @param src_ip IP address of the device + * @param src_port TCP port of the device + * @param dst_ip IP address of the client + * @param src_port TCP port of the client + * + * @return A pointer to the tcp session data structure, if found. + * Otherwise, null + */ +struct tcp_sess * +woal_get_tcp_sess(moal_private * priv, + t_u32 src_ip, t_u16 src_port, t_u32 dst_ip, t_u16 dst_port) +{ + struct tcp_sess *tcp_sess = NULL; + ENTER(); + + list_for_each_entry(tcp_sess, &priv->tcp_sess_queue, link) { + if ((tcp_sess->src_ip_addr == src_ip) && + (tcp_sess->src_tcp_port == src_port) && + (tcp_sess->dst_ip_addr == dst_ip) && + (tcp_sess->dst_tcp_port == dst_port)) { + LEAVE(); + return tcp_sess; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function checks received tcp packet for FIN + * and release the tcp session if received + * + * @param priv A pointer to moal_private structure + * @param skb A pointer to sk_buff structure + * + * @return None + */ +void +woal_check_tcp_fin(moal_private * priv, struct sk_buff *skb) +{ + struct ethhdr *ethh = NULL; + struct iphdr *iph = NULL; + struct tcphdr *tcph = NULL; + struct tcp_sess *tcp_sess = NULL; + + ENTER(); + + ethh = eth_hdr(skb); + if (ntohs(ethh->h_proto) == ETH_P_IP) { + iph = (struct iphdr *) ((t_u8 *) ethh + sizeof(struct ethhdr)); + if (iph->protocol == IPPROTO_TCP) { + tcph = (struct tcphdr *) ((t_u8 *) iph + iph->ihl * 4); + if (tcph->fin) { + tcp_sess = woal_get_tcp_sess(priv, iph->daddr, + tcph->dest, iph->saddr, + tcph->source); + if (tcp_sess != NULL) { + PRINTM(MINFO, + "TX: release a tcp session in dl. (src: ipaddr 0x%08x, port: %d. dst: ipaddr 0x%08x, port: %d\n", + iph->daddr, tcph->dest, iph->saddr, tcph->source); + /* remove the tcp session from the queue */ + list_del(&tcp_sess->link); + kfree(tcp_sess); + } else { + PRINTM(MINFO, "Tx: released TCP session is not found.\n "); + } + } + } + } + LEAVE(); +} + +/** + * @brief This function process tcp ack packets + * + * @param priv A pointer to moal_private structure + * @param pmbuf A pointer to the mlan buffer associated with the skb + * + * @return 1, if a tcp ack packet has been dropped. Otherwise, 0. + */ +int +woal_process_tcp_ack(moal_private * priv, mlan_buffer * pmbuf) +{ + int ret = 0; + struct ethhdr *ethh = NULL; + struct iphdr *iph = NULL; + struct tcphdr *tcph = NULL; + struct tcp_sess *tcp_sess = NULL; + struct sk_buff *skb = NULL; + t_u32 ack_seq = 0; + t_u32 len = 0; + t_u8 opt = 0; + t_u8 opt_len = 0; + t_u8 *pos = NULL; + t_u32 win_size = 0; + +#define TCP_ACK_DELAY 3 +#define TCP_ACK_INIT_WARMING_UP 1000 /* initial */ +#define TCP_ACK_RECV_WARMING_UP 1000 /* recovery */ + + ENTER(); + + /** check the tcp packet */ + ethh = (struct ethhdr *) (pmbuf->pbuf + pmbuf->data_offset); + if (ntohs(ethh->h_proto) != ETH_P_IP) { + return 0; + } + iph = (struct iphdr *) ((t_u8 *) ethh + sizeof(struct ethhdr)); + if (iph->protocol != IPPROTO_TCP) { + return 0; + } + tcph = (struct tcphdr *) ((t_u8 *) iph + iph->ihl * 4); + + if (tcph->syn & tcph->ack) { + /* respond to a TCP request. create a tcp session */ + if (!(tcp_sess = kmalloc(sizeof(struct tcp_sess), GFP_ATOMIC))) { + PRINTM(MERROR, "Fail to allocate tcp_sess.\n"); + return 0; + } + memset(tcp_sess, 0, sizeof(struct tcp_sess)); + tcp_sess->src_ip_addr = iph->saddr; /* my ip addr */ + tcp_sess->dst_ip_addr = iph->daddr; + tcp_sess->src_tcp_port = tcph->source; + tcp_sess->dst_tcp_port = tcph->dest; + tcp_sess->start_cnt = TCP_ACK_INIT_WARMING_UP; + + INIT_LIST_HEAD(&tcp_sess->link); + list_add_tail(&tcp_sess->link, &priv->tcp_sess_queue); + PRINTM(MINFO, + "create a tcp session. (src: ipaddr 0x%08x, port: %d. dst: ipaddr 0x%08x, port: %d\n", + iph->saddr, tcph->source, iph->daddr, tcph->dest); + + /* parse tcp options for the window scale */ + len = (tcph->doff * 4) - sizeof(struct tcphdr); + pos = (t_u8 *) (tcph + 1); + while (len > 0) { + opt = *pos++; + switch (opt) { + case TCPOPT_EOL: + len = 0; + continue; + case TCPOPT_NOP: + len--; + continue; + case TCPOPT_WINDOW: + opt_len = *pos++; + if (opt_len == TCPOLEN_WINDOW) { + tcp_sess->rx_win_scale = *pos; + tcp_sess->rx_win_opt = 1; + if (tcp_sess->rx_win_scale > 14) + tcp_sess->rx_win_scale = 14; + PRINTM(MINFO, "TCP Window Scale: %d \n", + tcp_sess->rx_win_scale); + } + break; + default: + opt_len = *pos++; + } + len -= opt_len; + pos += opt_len - 2; + } + } else if (tcph->ack && !(tcph->syn) && + (ntohs(iph->tot_len) == (iph->ihl * 4 + tcph->doff * 4))) { + /** it is an ack packet, not a piggyback ack */ + tcp_sess = woal_get_tcp_sess(priv, iph->saddr, tcph->source, + iph->daddr, tcph->dest); + if (tcp_sess == NULL) { + return 0; + } + /** do not drop the ack packets initially */ + if (tcp_sess->start_cnt) { + tcp_sess->start_cnt--; + return 0; + } + + /* check the window size. */ + win_size = ntohs(tcph->window); + if (tcp_sess->rx_win_opt) { + win_size <<= tcp_sess->rx_win_scale; + /* Note: it may depend on the rtd */ + if ((win_size > 1500 * (TCP_ACK_DELAY + 5)) && + (tcp_sess->ack_cnt < TCP_ACK_DELAY)) { + /* if windiow is big enough, drop the ack packet */ + if (tcp_sess->ack_seq != ntohl(tcph->ack_seq)) { + ack_seq = tcp_sess->ack_seq; + tcp_sess->ack_seq = ntohl(tcph->ack_seq); + tcp_sess->ack_cnt++; + skb = (struct sk_buff *) pmbuf->pdesc; + if (skb) + dev_kfree_skb_any(skb); + ret = 1; + } else { + /* the ack packet is retransmitted. thus, stop dropping + the ack packets */ + tcp_sess->start_cnt = TCP_ACK_RECV_WARMING_UP; + PRINTM(MINFO, "Recover the TCP session.\n "); + } + } else { + /* send the current ack pacekt */ + ack_seq = tcp_sess->ack_seq; + tcp_sess->ack_seq = ntohl(tcph->ack_seq); + tcp_sess->ack_cnt = 0; + ret = 0; + } + } + } else if (tcph->fin) { + tcp_sess = woal_get_tcp_sess(priv, iph->saddr, tcph->source, + iph->daddr, tcph->dest); + if (tcp_sess != NULL) { + /* remove the tcp session from the queue */ + PRINTM(MINFO, + "Rx: release a tcp session in ul. (src: ipaddr 0x%08x, port: %d. dst: ipaddr 0x%08x, port: %d\n", + iph->saddr, tcph->source, iph->daddr, tcph->dest); + list_del(&tcp_sess->link); + kfree(tcp_sess); + } else { + PRINTM(MINFO, "released TCP session is not found.\n "); + } + } + LEAVE(); + return ret; +} + +/** * @brief This function handles packet transmission * * @param skb A pointer to sk_buff structure @@ -2165,6 +2397,13 @@ woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) memset((t_u8 *) pmbuf, 0, sizeof(mlan_buffer)); pmbuf->bss_index = priv->bss_index; woal_fill_mlan_buffer(priv, pmbuf, skb); + if (priv->enable_tcp_ack_enh == MTRUE) { + if (woal_process_tcp_ack(priv, pmbuf)) { + /* the ack packet has been dropped */ + goto done; + } + } + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); switch (status) { case MLAN_STATUS_PENDING: @@ -2394,6 +2633,8 @@ woal_set_multicast_list(struct net_device *dev) void woal_init_priv(moal_private * priv, t_u8 wait_option) { + struct list_head *link = NULL; + struct tcp_sess *tcp_sess = NULL; ENTER(); #ifdef STA_SUPPORT if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { @@ -2414,7 +2655,7 @@ woal_init_priv(moal_private * priv, t_u8 wait_option) if (IS_STA_CFG80211(cfg80211_wext)) { if (priv->bss_type == MLAN_BSS_TYPE_STA #if defined(WIFI_DIRECT_SUPPORT) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION || priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT #endif #endif @@ -2434,6 +2675,25 @@ woal_init_priv(moal_private * priv, t_u8 wait_option) } #endif priv->media_connected = MFALSE; + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +#endif +#ifdef STA_SUPPORT +#endif + + priv->enable_tcp_ack_enh = MTRUE; + while (!list_empty(&priv->tcp_sess_queue)) { + link = priv->tcp_sess_queue.next; + tcp_sess = list_entry(link, struct tcp_sess, link); + PRINTM(MINFO, + "warm reset: release a tcp session in dl. (src: ipaddr 0x%08x, port: %d. dst: ipaddr 0x%08x, port: %d\n", + tcp_sess->src_ip_addr, tcp_sess->src_tcp_port, + tcp_sess->dst_ip_addr, tcp_sess->dst_tcp_port); + list_del(link); + kfree(tcp_sess); + } + woal_request_get_fw_info(priv, wait_option, NULL); LEAVE(); @@ -2805,8 +3065,9 @@ woal_reassociation_thread(void *data) for (i = 0; i < handle->priv_num && (priv = handle->priv[i]); i++) { - if (priv->reassoc_required == MFALSE) + if (priv->reassoc_required == MFALSE) { continue; + } memset(&bss_info, 0x00, sizeof(bss_info)); @@ -2892,7 +3153,6 @@ woal_reassociation_thread(void *data) mlan_ioctl_req *req = NULL; reassoc_timer_req = MFALSE; - if (priv->rate_index != AUTO_RATE) { req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); @@ -2928,11 +3188,13 @@ woal_reassociation_thread(void *data) break; if (reassoc_timer_req == MTRUE) { - PRINTM(MEVENT, - "Reassoc: No AP found or assoc failed. " - "Restarting re-assoc Timer: %d\n", (int) timer_val); handle->is_reassoc_timer_set = MTRUE; - woal_mod_timer(&handle->reassoc_timer, timer_val); + { + PRINTM(MEVENT, + "Reassoc: No AP found or assoc failed. " + "Restarting re-assoc Timer: %d\n", (int) timer_val); + woal_mod_timer(&handle->reassoc_timer, timer_val); + } } } woal_deactivate_thread(pmoal_thread); @@ -3083,7 +3345,7 @@ woal_moal_debug_info(moal_private * priv, moal_handle * handle, u8 flag) { moal_handle *phandle = NULL; char buf[MLAN_MAX_VER_STR_LEN]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) int i = 0; #endif @@ -3122,7 +3384,7 @@ woal_moal_debug_info(moal_private * priv, moal_handle * handle, u8 flag) MFALSE) ? "Disconnected" : "Connected")); PRINTM(MERROR, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) for (i = 0; i < (priv->netdev->num_tx_queues); i++) { PRINTM(MERROR, "tx queue %d: %s\n", i, ((netif_tx_queue_stopped @@ -3599,11 +3861,11 @@ woal_init_module(void) /* Init mutex */ MOAL_INIT_SEMAPHORE(&AddRemoveCardSem); + wifi_add_dev(); + /* Register with bus */ ret = woal_bus_register(); - wifi_add_dev(); - LEAVE(); return ret; } @@ -3645,7 +3907,8 @@ woal_cleanup_module(void) if (handle->priv[i]->media_connected == MTRUE) woal_disconnect(handle->priv[i], MOAL_CMD_WAIT, NULL); #ifdef STA_CFG80211 - if (handle->priv[i]->scan_request) { + if (IS_STA_CFG80211(cfg80211_wext) && + handle->priv[i]->scan_request) { cfg80211_scan_done(handle->priv[i]->scan_request, MTRUE); handle->priv[i]->scan_request = NULL; } @@ -3681,13 +3944,13 @@ woal_cleanup_module(void) MOAL_CMD_WAIT); } - wifi_del_dev(); - exit: MOAL_REL_SEMAPHORE(&AddRemoveCardSem); exit_sem_err: /* Unregister from bus */ woal_bus_unregister(); + wifi_del_dev(); + LEAVE(); } @@ -3749,7 +4012,7 @@ module_param(init_cfg, charp, 0); MODULE_PARM_DESC(init_cfg, "Init config file name"); module_param(cal_data_cfg, charp, 0); MODULE_PARM_DESC(cal_data_cfg, "Calibration data file name"); -module_param(minicard_pwrup, int, 0); +module_param(minicard_pwrup, int, 1); MODULE_PARM_DESC(minicard_pwrup, "1: Driver load clears PDn/Rst, unload sets (default); 0: Don't do this."); module_param(cfg80211_wext, int, 0); diff --git a/drivers/net/wireless/sd8797/mlinux/moal_main.h b/drivers/net/wireless/sd8797/mlinux/moal_main.h index cd1fdd8244c1..32426a149779 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_main.h +++ b/drivers/net/wireless/sd8797/mlinux/moal_main.h @@ -2,7 +2,7 @@ * * @brief This file contains wlan driver specific defines etc. * - * Copyright (C) 2008-2011, Marvell International Ltd. + * Copyright (C) 2008-2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 @@ -49,8 +49,9 @@ Change log: #include <linux/ctype.h> #include <linux/proc_fs.h> #include <linux/vmalloc.h> -#include <linux/ptrace.h> -#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/string.h> +#include <linux/list.h> #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) #include <linux/config.h> @@ -654,6 +655,24 @@ struct debug_data_priv /** IP address operation: Remove */ #define IPADDR_OP_REMOVE 0 +struct tcp_sess +{ + struct list_head link; + /** tcp session info */ + t_u32 src_ip_addr; + t_u32 dst_ip_addr; + t_u16 src_tcp_port; + t_u16 dst_tcp_port; + /** tcp window info */ + t_u8 rx_win_opt; + t_u32 rx_win_scale; + /** warming up counter */ + t_u32 start_cnt; + /** tx ack packet info */ + t_u32 ack_seq; + t_u32 ack_cnt; +}; + /** Private structure for MOAL */ struct _moal_private { @@ -715,6 +734,18 @@ struct _moal_private t_u8 cfg_bssid[ETH_ALEN]; /** Disconnect request from CFG80211 */ bool cfg_disconnect; + /** rssi_threshold */ + s32 cqm_rssi_thold; + /** rssi hysteresis */ + u32 cqm_rssi_hyst; + /** last rssi_low */ + u8 last_rssi_low; + /** last rssi_high */ + u8 last_rssi_high; + /** mrvl rssi threshold */ + u8 mrvl_rssi_low; + /** rssi status */ + u32 rssi_status; #endif /* STA_SUPPORT */ #endif /* STA_CFG80211 */ /** IOCTL wait queue */ @@ -779,6 +810,11 @@ struct _moal_private /** MLAN debug info */ struct debug_data_priv items_priv; #endif + + /** tcp session queue */ + struct list_head tcp_sess_queue; + /** TCP Ack enhance flag */ + t_u8 enable_tcp_ack_enh; }; /** Handle data structure for MOAL */ @@ -818,6 +854,7 @@ struct _moal_handle t_u16 init_wait_q_woken; /** Init wait queue */ wait_queue_head_t init_wait_q __ATTRIB_ALIGN__; +#if defined(SDIO_SUSPEND_RESUME) /** Device suspend flag */ BOOLEAN is_suspended; #ifdef SDIO_SUSPEND_RESUME @@ -830,6 +867,7 @@ struct _moal_handle t_u16 hs_activate_wait_q_woken; /** Host Sleep activated event wait queue */ wait_queue_head_t hs_activate_wait_q __ATTRIB_ALIGN__; +#endif /** Card pointer */ t_void *card; /** Rx pending in MLAN */ @@ -844,12 +882,14 @@ struct _moal_handle t_u32 lock_count; /** mlan buffer alloc count */ t_u32 mbufalloc_count; +#if defined(SDIO_SUSPEND_RESUME) /** hs skip count */ t_u32 hs_skip_count; /** hs force count */ t_u32 hs_force_count; /** suspend_fail flag */ BOOLEAN suspend_fail; +#endif #ifdef REASSOCIATION /** Re-association thread */ moal_thread reassoc_thread; @@ -923,7 +963,7 @@ struct _moal_handle t_u8 cmd52_reg; /** cmd52 value */ t_u8 cmd52_val; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) /** spinlock to stop_queue/wake_queue*/ spinlock_t queue_lock; #endif @@ -939,7 +979,7 @@ struct _moal_handle static inline void woal_set_trans_start(struct net_device *dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) unsigned int i; for (i = 0; i < dev->num_tx_queues; i++) { netdev_get_tx_queue(dev, i)->trans_start = jiffies; @@ -958,7 +998,7 @@ woal_set_trans_start(struct net_device *dev) static inline void woal_start_queue(struct net_device *dev) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) netif_start_queue(dev); #else netif_tx_start_all_queues(dev); @@ -975,7 +1015,7 @@ woal_start_queue(struct net_device *dev) static inline void woal_stop_queue(struct net_device *dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) unsigned long flags; moal_private *priv = (moal_private *) netdev_priv(dev); spin_lock_irqsave(&priv->phandle->queue_lock, flags); @@ -1000,7 +1040,7 @@ woal_stop_queue(struct net_device *dev) static inline void woal_wake_queue(struct net_device *dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) unsigned long flags; moal_private *priv = (moal_private *) netdev_priv(dev); spin_lock_irqsave(&priv->phandle->queue_lock, flags); @@ -1217,6 +1257,8 @@ typedef struct _HostCmd_DS_802_11_CFG_DATA #define WEXT_BGSCAN_RSSI_SECTION 'R' /** BGSCAN SCAN INTERVAL SECTION */ #define WEXT_BGSCAN_INTERVAL_SECTION 'T' +/** BGSCAN REPEAT SECTION */ +#define WEXT_BGSCAN_REPEAT_SECTION 'E' /** band AUTO */ #define WIFI_FREQUENCY_BAND_AUTO 0 @@ -1316,11 +1358,15 @@ mlan_status woal_set_get_hs_params(moal_private * priv, t_u16 action, t_u8 wait_option, mlan_ds_hs_cfg * hscfg); /** Cancel Host Sleep configuration */ mlan_status woal_cancel_hs(moal_private * priv, t_u8 wait_option); +#if defined(SDIO_SUSPEND_RESUME) /** Enable Host Sleep configuration */ int woal_enable_hs(moal_private * priv); /** hs active timeout 2 second */ #define HS_ACTIVE_TIMEOUT (2 * HZ) +#endif +/** get deep sleep */ +int woal_get_deep_sleep(moal_private * priv, t_u32 * data); /** set deep sleep */ int woal_set_deep_sleep(moal_private * priv, t_u8 wait_option, BOOLEAN bdeep_sleep, t_u16 idletime); @@ -1431,6 +1477,8 @@ int woal_host_command(moal_private * priv, struct iwreq *wrq); #if defined(WIFI_DIRECT_SUPPORT) #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) #if defined(STA_WEXT) || defined(UAP_WEXT) +mlan_status woal_bss_role_cfg(moal_private * priv, t_u8 action, + t_u8 wait_option, t_u8 * bss_role); int woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq); #endif #endif @@ -1443,8 +1491,8 @@ int woal_hostcmd_ioctl(struct net_device *dev, struct ifreq *req); #if defined(WIFI_DIRECT_SUPPORT) mlan_status woal_set_remain_channel_ioctl(moal_private * priv, t_u8 wait_option, mlan_ds_remain_chan * pchan); -mlan_status woal_cfg80211_wifi_direct_mode_cfg(moal_private * priv, - t_u16 action, t_u16 * mode); +mlan_status woal_wifi_direct_mode_cfg(moal_private * priv, t_u16 action, + t_u16 * mode); #endif /* WIFI_DIRECT_SUPPORT */ #ifdef CONFIG_PROC_FS @@ -1502,9 +1550,15 @@ mlan_status woal_remove_rxfilter(moal_private * priv, char *rxfilter); mlan_status woal_set_qos_cfg(moal_private * priv, char *qos_cfg); int woal_set_sleeppd(moal_private * priv, char *psleeppd); mlan_status woal_set_rssi_low_threshold(moal_private * priv, char *rssi); +mlan_status woal_set_rssi_threshold(moal_private * priv, t_u32 event_id); mlan_status woal_set_bg_scan(moal_private * priv, char *buf, int length); mlan_status woal_stop_bg_scan(moal_private * priv); void woal_reconfig_bgscan(moal_handle * handle); #endif +struct tcp_sess *woal_get_tcp_sess(moal_private * priv, + t_u32 src_ip, t_u16 src_port, + t_u32 dst_ip, t_u16 dst_port); +void woal_check_tcp_fin(moal_private * priv, struct sk_buff *skb); + #endif /* _MOAL_MAIN_H */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_priv.c b/drivers/net/wireless/sd8797/mlinux/moal_priv.c index e3e4b844e11b..9a5173ddfb3f 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_priv.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_priv.c @@ -101,6 +101,13 @@ woal_warm_reset(moal_private * priv) moal_handle *handle = priv->phandle; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *misc = NULL; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ ENTER(); @@ -110,8 +117,24 @@ woal_warm_reset(moal_private * priv) ret = woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); /* Initialize private structures */ - for (intf_num = 0; intf_num < handle->priv_num; intf_num++) + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if (handle->priv[intf_num]->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + &bss_role)) { + ret = -EFAULT; + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + } /* Restart the firmware */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); @@ -334,48 +357,6 @@ woal_get_signal(moal_private * priv, struct iwreq *wrq) } /** - * @brief Get Deep Sleep - * - * @param priv Pointer to the moal_private driver data struct - * @param deep_sleep Pointer to return deep_sleep setting - * - * @return 0 --success, otherwise fail - */ -static int -woal_get_deep_sleep(moal_private * priv, t_u32 * data) -{ - int ret = 0; - mlan_ioctl_req *req = NULL; - mlan_ds_pm_cfg *pm = NULL; - - ENTER(); - - req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); - if (req == NULL) { - LEAVE(); - return -ENOMEM; - } - pm = (mlan_ds_pm_cfg *) req->pbuf; - pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; - req->req_id = MLAN_IOCTL_PM_CFG; - - req->action = MLAN_ACT_GET; - if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { - ret = -EFAULT; - goto done; - } - *data = pm->param.auto_deep_sleep.auto_ds; - *(data + 1) = pm->param.auto_deep_sleep.idletime; - - done: - if (req) - kfree(req); - - LEAVE(); - return ret; -} - -/** * @brief Get/Set DeepSleep mode * * @param priv Pointer to the moal_private driver data struct @@ -3692,7 +3673,6 @@ static int woal_set_get_ip_addr(moal_private * priv, struct iwreq *wrq) { char buf[IPADDR_MAX_BUF]; - struct iwreq *wreq = (struct iwreq *) wrq; mlan_ioctl_req *ioctl_req = NULL; mlan_ds_misc_cfg *misc = NULL; int ret = 0, op_code = 0, data_length = wrq->u.data.length; @@ -3710,8 +3690,8 @@ woal_set_get_ip_addr(moal_private * priv, struct iwreq *wrq) if (data_length <= 1) { /* GET */ ioctl_req->action = MLAN_ACT_GET; } else { - if (copy_from_user(buf, wreq->u.data.pointer, - MIN(IPADDR_MAX_BUF - 1, wreq->u.data.length))) { + if (copy_from_user(buf, wrq->u.data.pointer, + MIN(IPADDR_MAX_BUF - 1, wrq->u.data.length))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; @@ -3727,7 +3707,7 @@ woal_set_get_ip_addr(moal_private * priv, struct iwreq *wrq) ioctl_req->action = MLAN_ACT_SET; /* only one IP is supported in current firmware */ memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); - in4_pton(&buf[2], MIN((IPADDR_MAX_BUF - 3), (wreq->u.data.length - 2)), + in4_pton(&buf[2], MIN((IPADDR_MAX_BUF - 3), (wrq->u.data.length - 2)), misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); /* only one IP is supported in current firmware */ misc->param.ipaddr_cfg.ip_addr_num = 1; @@ -4525,6 +4505,34 @@ woal_get_scan_table_ioctl(moal_private * priv, struct iwreq *wrq) } /** + * @brief Set user scan ext -- Async mode, without wait + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_set_user_scan_ext_ioctl(moal_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_user_scan_cfg scan_req; + ENTER(); + memset(&scan_req, 0x00, sizeof(scan_req)); + if (copy_from_user + (&scan_req, wrq->u.data.pointer, + MIN(wrq->u.data.length, sizeof(scan_req)))) { + PRINTM(MINFO, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_req)) + ret = -EFAULT; + LEAVE(); + return ret; +} + +/** * @brief Set user scan * * @param priv A pointer to moal_private structure @@ -5912,6 +5920,86 @@ woal_mgmt_frame_passthru_ctrl(moal_private * priv, struct iwreq *wrq) } /** + * @brief Set/Get CFP table codes + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_cfp_code(moal_private * priv, struct iwreq *wrq) +{ + int ret = 0; + int data[2]; + int data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_cfp_code *cfp_code = NULL; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of argument!\n"); + ret = -EINVAL; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc_cfg = (mlan_ds_misc_cfg *) req->pbuf; + cfp_code = &misc_cfg->param.cfp_code; + misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!data_length) { + req->action = MLAN_ACT_GET; + } else { + if (copy_from_user + (data, wrq->u.data.pointer, sizeof(int) * data_length)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + cfp_code->cfp_code_bg = data[0]; + if (data_length == 2) + cfp_code->cfp_code_a = data[1]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + if (woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!data_length) { + data[0] = cfp_code->cfp_code_bg; + data[1] = cfp_code->cfp_code_a; + data_length = 2; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int) * data_length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = data_length; + } + + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/** * @brief Set/Get Tx/Rx antenna * * @param priv A pointer to moal_private structure @@ -6183,6 +6271,9 @@ woal_wext_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) case WOAL_MGMT_FRAME_CTRL: ret = woal_mgmt_frame_passthru_ctrl(priv, wrq); break; + case WOAL_CFP_CODE: + ret = woal_cfp_code(priv, wrq); + break; case WOAL_SET_GET_TX_RX_ANT: ret = woal_set_get_tx_rx_ant(priv, wrq); break; @@ -6296,6 +6387,9 @@ woal_wext_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) case WOAL_GET_SCAN_TABLE: ret = woal_get_scan_table_ioctl(priv, wrq); break; + case WOAL_SET_USER_SCAN_EXT: + ret = woal_set_user_scan_ext_ioctl(priv, wrq); + break; case WOAL_WMM_ADDTS: ret = woal_wmm_addts_req_ioctl(priv, wrq); break; diff --git a/drivers/net/wireless/sd8797/mlinux/moal_priv.h b/drivers/net/wireless/sd8797/mlinux/moal_priv.h index de491f6d6a29..1c24f30502f6 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_priv.h +++ b/drivers/net/wireless/sd8797/mlinux/moal_priv.h @@ -115,6 +115,8 @@ Change log: /** Private command ID to set/get dfs testing settings */ #define WOAL_DFS_TESTING 33 #endif +/** Private command ID to set/get CFP table codes */ +#define WOAL_CFP_CODE 34 /** Private command ID to set/get tx/rx antenna */ #define WOAL_SET_GET_TX_RX_ANT 35 /** Private command ID to set/get management frame passthru mask */ @@ -226,6 +228,8 @@ Change log: #define WOAL_SET_USER_SCAN 3 /** Private command ID for getscantable */ #define WOAL_GET_SCAN_TABLE 4 +/** Private command ID for setuserscanext: async without wait */ +#define WOAL_SET_USER_SCAN_EXT 5 /** Private command ID to request ADDTS */ #define WOAL_WMM_ADDTS 7 @@ -526,6 +530,11 @@ static const struct iw_priv_args woal_private_args[] = { IW_PRIV_TYPE_INT | 16, "mgmtframectrl"}, { + WOAL_CFP_CODE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "cfpcode"}, + { WOAL_SET_GET_TX_RX_ANT, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, @@ -656,6 +665,11 @@ static const struct iw_priv_args woal_private_args[] = { IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "getscantable"}, { + WOAL_SET_USER_SCAN_EXT, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "setuserscanext"}, + { WOAL_WMM_ADDTS, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, diff --git a/drivers/net/wireless/sd8797/mlinux/moal_proc.c b/drivers/net/wireless/sd8797/mlinux/moal_proc.c index e98803c2e3fb..fb7c63a29cab 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_proc.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_proc.c @@ -95,9 +95,9 @@ woal_info_proc_read(char *page, char **start, off_t offset, int mc_count = netdev_mc_count(netdev); #endif /* < 2.6.35 */ #else -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) int i = 0; -#endif /* >= 2.6.34 */ +#endif /* >= 2.6.29 */ #endif #ifdef UAP_SUPPORT mlan_ds_uap_stats ustats; @@ -199,7 +199,7 @@ woal_info_proc_read(char *page, char **start, off_t offset, p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) for (i = 0; i < netdev->num_tx_queues; i++) { p += sprintf(p, "tx queue %d: %s\n", i, ((netif_tx_queue_stopped(netdev_get_tx_queue(netdev, 0))) ? diff --git a/drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c b/drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c index 96e3e971936f..f09f33e6435d 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c @@ -315,6 +315,12 @@ woal_sdio_suspend(struct device *dev) } handle = cardp->handle; + if (handle->is_suspended == MTRUE) { + PRINTM(MWARN, "Device already suspended\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + handle->suspend_fail = MFALSE; memset(&pm_info, 0, sizeof(pm_info)); if (MLAN_STATUS_SUCCESS == diff --git a/drivers/net/wireless/sd8797/mlinux/moal_shim.c b/drivers/net/wireless/sd8797/mlinux/moal_shim.c index e33a6e38067a..25cfdc549f3d 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_shim.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_shim.c @@ -745,6 +745,11 @@ moal_recv_packet(IN t_void * pmoal_handle, IN pmlan_buffer pmbuf) skb->dev = priv->netdev; skb->protocol = eth_type_trans(skb, priv->netdev); skb->ip_summed = CHECKSUM_NONE; + + if (priv->enable_tcp_ack_enh == MTRUE) { + woal_check_tcp_fin(priv, skb); + } + priv->stats.rx_bytes += skb->len; priv->stats.rx_packets++; if (in_interrupt()) @@ -950,6 +955,13 @@ moal_recv_event(IN t_void * pmoal_handle, IN pmlan_event pmevent) #endif } #endif /* STA_WEXT */ +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_PAIRWISE, -1, NULL, + GFP_KERNEL); + } +#endif break; case MLAN_EVENT_ID_FW_MIC_ERR_MUL: #ifdef STA_WEXT @@ -961,18 +973,48 @@ moal_recv_event(IN t_void * pmoal_handle, IN pmlan_event pmevent) #endif } #endif /* STA_WEXT */ +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_GROUP, -1, NULL, + GFP_KERNEL); + } +#endif break; case MLAN_EVENT_ID_FW_BCN_RSSI_LOW: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_BEACON_RSSI_LOW); #endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + priv->rssi_status = MLAN_EVENT_ID_FW_BCN_RSSI_LOW; +#endif + woal_set_rssi_threshold(priv, MLAN_EVENT_ID_FW_BCN_RSSI_LOW); + } +#endif break; case MLAN_EVENT_ID_FW_BCN_RSSI_HIGH: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_BEACON_RSSI_HIGH); #endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (!priv->mrvl_rssi_low) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); +#endif + woal_set_rssi_threshold(priv, MLAN_EVENT_ID_FW_BCN_RSSI_HIGH); + } + } +#endif break; case MLAN_EVENT_ID_FW_BCN_SNR_LOW: #ifdef STA_WEXT @@ -1033,6 +1075,22 @@ moal_recv_event(IN t_void * pmoal_handle, IN pmlan_event pmevent) if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_PRE_BEACON_LOST); #endif +#ifdef STA_CFG80211 +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) + if (IS_STA_CFG80211(cfg80211_wext)) { + struct cfg80211_bss *bss = NULL; + bss = + cfg80211_get_bss(priv->wdev->wiphy, NULL, priv->cfg_bssid, NULL, + 0, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (bss) + cfg80211_unlink_bss(priv->wdev->wiphy, bss); + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + priv->rssi_status = MLAN_EVENT_ID_FW_PRE_BCN_LOST; + } +#endif +#endif break; case MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE: #ifdef STA_WEXT @@ -1069,11 +1127,22 @@ moal_recv_event(IN t_void * pmoal_handle, IN pmlan_event pmevent) priv->bg_scan_reported = MTRUE; #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { - woal_send_iwevcustom_event(priv, CUS_EVT_BG_SCAN); memset(&wrqu, 0, sizeof(union iwreq_data)); wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); } #endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + woal_inform_bss_from_scan_result(priv, NULL); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) + if (priv->mrvl_rssi_low) { + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); + } +#endif + } +#endif break; case MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN: #ifdef STA_WEXT @@ -1285,10 +1354,15 @@ moal_recv_event(IN t_void * pmoal_handle, IN pmlan_event pmevent) #endif /* UAP_WEXT */ break; case MLAN_EVENT_ID_DRV_MGMT_FRAME: +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); + } +#endif /* UAP_WEXT */ #ifdef WIFI_DIRECT_SUPPORT #if defined(STA_CFG80211) || defined(UAP_CFG80211) if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION if (priv->netdev && priv->netdev->ieee80211_ptr->wiphy->mgmt_stypes) { /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ #define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) @@ -1310,11 +1384,6 @@ moal_recv_event(IN t_void * pmoal_handle, IN pmlan_event pmevent) } #endif /* STA_CFG80211 || UAP_CFG80211 */ #endif /* WIFI_DIRECT_SUPPORT */ -#ifdef UAP_WEXT - if (IS_UAP_WEXT(cfg80211_wext)) { - woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); - } -#endif /* UAP_WEXT */ break; #endif /* UAP_SUPPORT */ case MLAN_EVENT_ID_DRV_PASSTHRU: diff --git a/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c index d6768a3bd647..8a60be2298c8 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c @@ -44,6 +44,11 @@ static int woal_cfg80211_dump_station(struct wiphy *wiphy, static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) +static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); +#endif static int woal_cfg80211_set_tx_power(struct wiphy *wiphy, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && !defined(COMPAT_WIRELESS) @@ -98,6 +103,9 @@ static struct cfg80211_ops woal_cfg80211_sta_ops = { .set_default_key = woal_cfg80211_set_default_key, .set_power_mgmt = woal_cfg80211_set_power_mgmt, .set_tx_power = woal_cfg80211_set_tx_power, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) + .set_cqm_rssi_config = woal_cfg80211_set_cqm_rssi_config, +#endif }; #if defined(WIFI_DIRECT_SUPPORT) @@ -518,14 +526,13 @@ woal_cfg80211_assoc_ies_cfg(moal_private * priv, t_u8 * ie, int ie_len) /** * @brief Send domain info command to FW * - * @param wiphy A pointer to wiphy structure + * @param priv A pointer to moal_private structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status -woal_send_domain_info_cmd_fw(struct wiphy *wiphy) +woal_send_domain_info_cmd_fw(moal_private * priv) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); mlan_status ret = MLAN_STATUS_SUCCESS; enum ieee80211_band band; struct ieee80211_supported_band *sband = NULL; @@ -540,6 +547,12 @@ woal_send_domain_info_cmd_fw(struct wiphy *wiphy) ENTER(); + if (!priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "No wdev or wiphy in priv\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); if (req == NULL) { @@ -556,7 +569,7 @@ woal_send_domain_info_cmd_fw(struct wiphy *wiphy) goto done; } band = woal_band_cfg_to_ieee_band(radio_cfg->param.band_cfg.config_bands); - if (!wiphy->bands[band]) { + if (!priv->wdev->wiphy->bands[band]) { PRINTM(MERROR, "11D: setting domain info in FW failed"); ret = MLAN_STATUS_FAILURE; goto done; @@ -580,7 +593,7 @@ woal_send_domain_info_cmd_fw(struct wiphy *wiphy) cfg_11d->param.domain_info.country_code[2] = ' '; cfg_11d->param.domain_info.band = band; - sband = wiphy->bands[band]; + sband = priv->wdev->wiphy->bands[band]; for (i = 0; (i < sband->n_channels) && (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D); i++) { channel = &sband->channels[i]; @@ -641,18 +654,17 @@ woal_send_domain_info_cmd_fw(struct wiphy *wiphy) * @brief Request the driver to change the channel and * change domain info according to that channel * - * @param wiphy A pointer to wiphy structure + * @param priv A pointer to moal_private structure * @param chan A pointer to ieee80211_channel structure * @param channel_type Channel type of nl80211_channel_type * * @return 0 -- success, otherwise fail */ int -woal_set_rf_channel(struct wiphy *wiphy, +woal_set_rf_channel(moal_private * priv, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); int ret = 0; t_u32 mode, config_bands = 0; mlan_ioctl_req *req1 = NULL, *req2 = NULL; @@ -692,7 +704,7 @@ woal_set_rf_channel(struct wiphy *wiphy, ret = -EFAULT; goto done; } - woal_send_domain_info_cmd_fw(wiphy); + woal_send_domain_info_cmd_fw(priv); } PRINTM(MINFO, "Setting band %d, channel bandwidth %d and mode = %d\n", @@ -864,17 +876,15 @@ woal_inform_bss_from_scan_result(moal_private * priv, mlan_802_11_ssid * ssid) struct ieee80211_channel *chan; mlan_scan_resp scan_resp; BSSDescriptor_t *scan_table; - t_u8 *ie, *tmp, *ie_buf; - __le32 ie_len; t_u64 ts = 0; - t_u8 *cap; - t_u8 *beacon; - int beacon_size, i, j; - IEEEtypes_ElementId_e element_id; - t_u8 element_len; + u16 cap_info = 0; + int i = 0; struct cfg80211_bss *pub = NULL; - ENTER(); + if (!priv->wdev || !priv->wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } memset(&scan_resp, 0, sizeof(scan_resp)); if (MLAN_STATUS_SUCCESS != woal_get_scan_table(priv, @@ -885,14 +895,6 @@ woal_inform_bss_from_scan_result(moal_private * priv, mlan_802_11_ssid * ssid) } if (scan_resp.num_in_scan_table) { -#define MAX_IE_BUF 2048 - ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL); - if (ie_buf == NULL) { - PRINTM(MERROR, "%s: failed to allocate ie_buf\n", __func__); - ret = MLAN_STATUS_FAILURE; - goto done; - } - scan_table = (BSSDescriptor_t *) scan_resp.pscan_table; for (i = 0; i < scan_resp.num_in_scan_table; i++) { if (ssid) { @@ -905,105 +907,31 @@ woal_inform_bss_from_scan_result(moal_private * priv, mlan_802_11_ssid * ssid) (int) scan_table[i].channel); continue; } - memset(ie_buf, 0, MAX_IE_BUF); - ie_buf[0] = WLAN_EID_SSID; - ie_buf[1] = scan_table[i].ssid.ssid_len; - memcpy(&ie_buf[sizeof(IEEEtypes_Header_t)], - scan_table[i].ssid.ssid, ie_buf[1]); - - ie = ie_buf + ie_buf[1] + sizeof(IEEEtypes_Header_t); - ie_len = ie_buf[1] + sizeof(IEEEtypes_Header_t); - - ie[0] = WLAN_EID_SUPP_RATES; - - for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { - if (!scan_table[i].supported_rates[j]) - break; - else { - ie[j + sizeof(IEEEtypes_Header_t)] = - scan_table[i].supported_rates[j]; - } - } - - ie[1] = j; - ie_len += ie[1] + sizeof(IEEEtypes_Header_t); - - beacon = scan_table[i].pbeacon_buf; - beacon_size = scan_table[i].beacon_buf_size; - - /* Skip time stamp, beacon interval and capability */ - - if (beacon) { - beacon += sizeof(scan_table[i].beacon_period) - + sizeof(scan_table[i].time_stamp) + - +sizeof(scan_table[i].cap_info); - - beacon_size -= sizeof(scan_table[i].beacon_period) - + sizeof(scan_table[i].time_stamp) - + sizeof(scan_table[i].cap_info); - } - - while (beacon_size >= sizeof(IEEEtypes_Header_t)) { - ie = ie_buf + ie_len; - element_id = *beacon; - element_len = *(beacon + 1); - if (beacon_size < (int) element_len + - sizeof(IEEEtypes_Header_t)) { - PRINTM(MERROR, - "Get scan: Error in processing IE, " - "bytes left < IE length\n"); - break; - } - switch (element_id) { - case WLAN_EID_FH_PARAMS: - case WLAN_EID_DS_PARAMS: - case WLAN_EID_CF_PARAMS: - case WLAN_EID_IBSS_PARAMS: - case WLAN_EID_COUNTRY: - case WLAN_EID_PWR_CONSTRAINT: - case WLAN_EID_PWR_CAPABILITY: - case WLAN_EID_TPC_REQUEST: - case WLAN_EID_TPC_REPORT: - case WLAN_EID_CHANNEL_SWITCH: - case WLAN_EID_QUIET: - case WLAN_EID_IBSS_DFS: - case WLAN_EID_SUPPORTED_CHANNELS: - case WLAN_EID_ERP_INFO: - case WLAN_EID_EXT_SUPP_RATES: - case WLAN_EID_HT_CAPABILITY: - case WLAN_EID_HT_INFORMATION: - case EXT_CAPABILITY: // TODO: Replace when kernel macro - // available - case WLAN_EID_RSN: - case WAPI_IE: // TODO: Replace when kernel macro available - case WLAN_EID_VENDOR_SPECIFIC: - ie[0] = element_id; - ie[1] = element_len; - tmp = (t_u8 *) beacon; - memcpy(&ie[sizeof(IEEEtypes_Header_t)], - tmp + sizeof(IEEEtypes_Header_t), element_len); - ie_len += ie[1] + sizeof(IEEEtypes_Header_t); - break; - default: - break; - } - beacon += element_len + sizeof(IEEEtypes_Header_t); - beacon_size -= (element_len + sizeof(IEEEtypes_Header_t)); - } chan = ieee80211_get_channel(priv->wdev->wiphy, scan_table[i].freq); - cap = (t_u8 *) & scan_table[i].cap_info; + if (!chan) { + PRINTM(MERROR, + "Fail to get chan with freq: channel=%d freq=%d\n", + (int) scan_table[i].channel, (int) scan_table[i].freq); + continue; + } + memcpy(&ts, scan_table[i].time_stamp, sizeof(ts)); + memcpy(&cap_info, &scan_table[i].cap_info, sizeof(cap_info)); pub = cfg80211_inform_bss(priv->wdev->wiphy, chan, scan_table[i].mac_address, - ts, (__le16) (*cap), - scan_table[i].beacon_period, ie_buf, - ie_len, + ts, cap_info, + scan_table[i].beacon_period, + scan_table[i].pbeacon_buf + + WLAN_802_11_FIXED_IE_SIZE, + scan_table[i].beacon_buf_size - + WLAN_802_11_FIXED_IE_SIZE, -RSSI_DBM_TO_MDM(scan_table[i].rssi), GFP_KERNEL); - pub->len_information_elements = pub->len_beacon_ies; + if (pub) { + pub->len_information_elements = pub->len_beacon_ies; + cfg80211_put_bss(pub); + } } - kfree(ie_buf); } - done: LEAVE(); return ret; @@ -1044,6 +972,7 @@ woal_cfg80211_inform_ibss_bss(moal_private * priv, mlan_ds_get_signal signal; t_u8 ie_buf[MLAN_MAX_SSID_LENGTH + sizeof(IEEEtypes_Header_t)]; int ie_len = 0; + struct cfg80211_bss *bss = NULL; ENTER(); @@ -1068,11 +997,12 @@ woal_cfg80211_inform_ibss_bss(moal_private * priv, goto done; } - cfg80211_inform_bss(priv->wdev->wiphy, chan, - bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, - beacon_interval, ie_buf, ie_len, signal.bcn_rssi_avg, - GFP_KERNEL); - + bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + beacon_interval, ie_buf, ie_len, + signal.bcn_rssi_avg, GFP_KERNEL); + if (bss) + cfg80211_put_bss(bss); done: LEAVE(); return ret; @@ -1081,15 +1011,14 @@ woal_cfg80211_inform_ibss_bss(moal_private * priv, /** * @brief Request the driver for (re)association * - * @param wiphy A pointer to wiphy structure + * @param priv A pointer to moal_private structure * @param sme A pointer to connect parameters * * @return 0 -- success, otherwise fail */ static int -woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) +woal_cfg80211_assoc(moal_private * priv, void *sme) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); struct cfg80211_ibss_params *ibss_param = NULL; struct cfg80211_connect_params *conn_param = NULL; mlan_802_11_ssid req_ssid; @@ -1153,7 +1082,7 @@ woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) goto done; } - if (channel) { + if (mode == MLAN_BSS_MODE_IBSS && channel) { /* Get the secondary channel offset */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); if (req == NULL) { @@ -1170,7 +1099,7 @@ woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) ret = -EFAULT; goto done; } - if (MLAN_STATUS_SUCCESS != woal_set_rf_channel(wiphy, + if (MLAN_STATUS_SUCCESS != woal_set_rf_channel(priv, channel, woal_channel_to_nl80211_channel_type (radio_cfg->param. @@ -1188,8 +1117,9 @@ woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) } if (MLAN_STATUS_SUCCESS != - woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, 0, NULL, 1)) { - /* Disable keys */ + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, + KEY_INDEX_CLEAR_ALL, NULL, 1)) { + /* Disable keys and clear all previous security settings */ ret = -EFAULT; goto done; } @@ -1386,6 +1316,58 @@ woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) return ret; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) +/** + * @brief Set/Get DTIM period + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value DTIM period + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +static mlan_status +woal_set_get_dtim_period(moal_private * priv, + t_u32 action, t_u8 wait_option, t_u8 * value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *) req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_DTIM_PERIOD; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) { + mib->param.dtim_period = *value; + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) { + *value = (t_u8) mib->param.dtim_period; + } + + done: + if (req && (ret != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return ret; +} +#endif + /** * @brief Request the driver to dump the station information * @@ -1397,10 +1379,14 @@ woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) static mlan_status woal_cfg80211_dump_station_info(moal_private * priv, struct station_info *sinfo) { + mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ds_get_signal signal; mlan_ioctl_req *req = NULL; mlan_ds_rate *rate = NULL; - mlan_status ret = MLAN_STATUS_SUCCESS; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) + mlan_bss_info bss_info; + t_u8 dtim_period = 0; +#endif ENTER(); sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | @@ -1443,6 +1429,28 @@ woal_cfg80211_dump_station_info(moal_private * priv, struct station_info *sinfo) sinfo->tx_packets = priv->stats.tx_packets; sinfo->signal = signal.bcn_rssi_avg; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) + /* Update BSS information */ + sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->bss_param.flags = 0; + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret) + goto done; + if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.beacon_interval = bss_info.beacon_interval; + /* Get DTIM period */ + ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, &dtim_period); + if (ret) { + PRINTM(MERROR, "Get DTIM period failed\n"); + goto done; + } + sinfo->bss_param.dtim_period = dtim_period; +#endif + done: if (req) kfree(req); @@ -1472,6 +1480,12 @@ woal_cfg80211_reg_notifier(struct wiphy *wiphy, ENTER(); + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __FUNCTION__); + LEAVE(); + return -EINVAL; + } + PRINTM(MINFO, "cfg80211 regulatory domain callback " "%c%c\n", request->alpha2[0], request->alpha2[1]); @@ -1496,7 +1510,7 @@ woal_cfg80211_reg_notifier(struct wiphy *wiphy, break; } - if (MLAN_STATUS_SUCCESS != woal_send_domain_info_cmd_fw(wiphy)) + if (MLAN_STATUS_SUCCESS != woal_send_domain_info_cmd_fw(priv)) ret = -EFAULT; LEAVE(); @@ -1519,7 +1533,7 @@ static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); wlan_user_scan_cfg scan_req; struct ieee80211_channel *chan; int ret = 0, i; @@ -1604,10 +1618,11 @@ static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; ENTER(); + PRINTM(MINFO, "Received association request on %s\n", dev->name); if (priv->wdev->iftype != NL80211_IFTYPE_STATION @@ -1645,7 +1660,7 @@ woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); - ret = woal_cfg80211_assoc(wiphy, (void *) sme); + ret = woal_cfg80211_assoc(priv, (void *) sme); if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); @@ -1680,7 +1695,7 @@ static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, t_u16 reason_code) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); ENTER(); PRINTM(MINFO, "Received disassociation request on %s\n", dev->name); @@ -1729,7 +1744,7 @@ woal_cfg80211_get_station(struct wiphy *wiphy, t_u8 * mac, struct station_info *sinfo) { mlan_status ret = MLAN_STATUS_SUCCESS; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); ENTER(); @@ -1778,7 +1793,7 @@ woal_cfg80211_dump_station(struct wiphy *wiphy, t_u8 * mac, struct station_info *sinfo) { mlan_status ret = MLAN_STATUS_SUCCESS; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); ENTER(); @@ -1814,7 +1829,7 @@ static int woal_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; ENTER(); @@ -1826,7 +1841,7 @@ woal_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, return -EINVAL; } - ret = woal_cfg80211_assoc(wiphy, (void *) params); + ret = woal_cfg80211_assoc(priv, (void *) params); if (!ret) { cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); @@ -1852,7 +1867,7 @@ woal_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, static int woal_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); ENTER(); @@ -1898,7 +1913,7 @@ woal_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { int ret = 0, disabled; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); ENTER(); @@ -1940,6 +1955,12 @@ woal_cfg80211_set_tx_power(struct wiphy *wiphy, ENTER(); + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __FUNCTION__); + LEAVE(); + return -EFAULT; + } + if (type) { power_cfg.is_power_auto = 0; power_cfg.power_level = dbm; @@ -1954,6 +1975,33 @@ woal_cfg80211_set_tx_power(struct wiphy *wiphy, return ret; } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) || defined(COMPAT_WIRELESS) +/** + * CFG802.11 operation handler for connection quality monitoring. + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param rssi_thold rssi threshold + * @param rssi_hyst rssi hysteresis + */ +static int +woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); + ENTER(); + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + PRINTM(MIOCTL, "rssi_thold=%d rssi_hyst=%d\n", + (int) rssi_thold, (int) rssi_hyst); + woal_set_rssi_threshold(priv, 0); + LEAVE(); + return 0; +} +#endif + /** * @brief Sets up the CFG802.11 specific HT capability fields * with default values @@ -2010,7 +2058,7 @@ woal_cfg80211_setup_sta_ht_cap(struct ieee80211_sta_ht_cap *ht_info, /** * @brief remain on channel config * - * @param wiphy A pointer to wiphy structure + * @param priv A pointer to moal_private structure * @param wait_option Wait option * @param cancel cancel remain on channel flag * @param status A pointer to status, success, in process or reject @@ -2021,7 +2069,7 @@ woal_cfg80211_setup_sta_ht_cap(struct ieee80211_sta_ht_cap *ht_info, * @return 0 -- success, otherwise fail */ int -woal_cfg80211_remain_on_channel_cfg(struct wiphy *wiphy, +woal_cfg80211_remain_on_channel_cfg(moal_private * priv, t_u8 wait_option, t_u8 remove, t_u8 * status, struct ieee80211_channel *chan, @@ -2029,8 +2077,6 @@ woal_cfg80211_remain_on_channel_cfg(struct wiphy *wiphy, t_u32 duration) { mlan_ds_remain_chan chan_cfg; - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); - int ret = 0; ENTER(); @@ -2042,10 +2088,6 @@ woal_cfg80211_remain_on_channel_cfg(struct wiphy *wiphy, chan_cfg.bandcfg = 0; else if (chan->band == IEEE80211_BAND_5GHZ) chan_cfg.bandcfg = 1; -/** secondary channel is below */ -#define SEC_CHAN_BELOW 0x30 -/** secondary channel is above */ -#define SEC_CHAN_ABOVE 0x10 switch (channel_type) { case NL80211_CHAN_HT40MINUS: chan_cfg.bandcfg |= SEC_CHANNEL_BELOW; @@ -2084,14 +2126,14 @@ static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, struct net_device *dev, u64 cookie) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; t_u8 status = 1; ENTER(); if (priv->phandle->remain_on_channel) { - if (woal_cfg80211_remain_on_channel_cfg(priv->wdev->wiphy, + if (woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { PRINTM(MERROR, "Fail to cancel remain on channel\n"); @@ -2133,7 +2175,7 @@ woal_cfg80211_remain_on_channel(struct wiphy *wiphy, enum nl80211_channel_type channel_type, unsigned int duration, u64 * cookie) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; t_u8 status = 1; @@ -2146,8 +2188,9 @@ woal_cfg80211_remain_on_channel(struct wiphy *wiphy, } /** cancel previous remain on channel */ if (priv->phandle->remain_on_channel) { - if (woal_cfg80211_remain_on_channel_cfg - (wiphy, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { + if (woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, MTRUE, &status, + NULL, 0, 0)) { PRINTM(MERROR, "Fail to cancel remain on channel\n"); ret = -EFAULT; goto done; @@ -2156,7 +2199,7 @@ woal_cfg80211_remain_on_channel(struct wiphy *wiphy, priv->phandle->remain_on_channel = MFALSE; } if (MLAN_STATUS_SUCCESS != - woal_cfg80211_remain_on_channel_cfg(wiphy, MOAL_IOCTL_WAIT, + woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE, &status, chan, channel_type, (t_u32) duration)) { ret = -EFAULT; @@ -2195,14 +2238,15 @@ static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, u64 cookie) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; t_u8 status = 1; ENTER(); PRINTM(MIOCTL, "Cancel remain on Channel: cookie = %#llx\n", cookie); - if (woal_cfg80211_remain_on_channel_cfg - (wiphy, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { + if (woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, MTRUE, &status, + NULL, 0, 0)) { PRINTM(MERROR, "Fail to cancel remain on channel\n"); ret = -EFAULT; goto done; diff --git a/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c index 7f7528216282..18d18328d0ed 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c @@ -124,44 +124,6 @@ static struct cfg80211_ops woal_cfg80211_uap_ops = { /******************************************************** Global Functions ********************************************************/ -/** - * @brief Filter specific IE in ie buf - * - * @param ie Pointer to IEs - * @param len Total length of ie - * @param ie_out Pointer to out IE buf - * - * @return out IE length - */ -static t_u16 -woal_filter_beacon_ies(const t_u8 * ie, int len, t_u8 * ie_out) -{ - int left_len = len; - const t_u8 *pos = ie; - int length; - t_u8 id = 0; - t_u16 out_len = 0; - - /* ERP_INFO and RSN IE will be fileter out */ - while (left_len >= 2) { - length = *(pos + 1); - id = *pos; - if ((length + 2) > left_len) - break; - switch (id) { - case ERP_INFO: - case RSN_IE: - break; - default: - memcpy(ie_out + out_len, pos, length + 2); - out_len += length + 2; - break; - } - pos += (length + 2); - left_len -= (length + 2); - } - return out_len; -} #if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS) /** @@ -364,442 +326,6 @@ woal_find_wpa_ies(t_u8 * ie, int len, mlan_uap_bss_param * sys_config) #endif /** - * @brief Look up specific IE in a buf - * - * @param ie Pointer to IEs - * @param len Total length of ie - * @param id Element id to lookup - * - * @return Pointer of the specific IE -- success, NULL -- fail - */ -static const t_u8 * -woal_parse_ie_tlv(const t_u8 * ie, int len, t_u8 id) -{ - int left_len = len; - const t_u8 *pos = ie; - int length; - - /* IE format: | u8 | id | | u8 | len | | var | data | */ - while (left_len >= 2) { - length = *(pos + 1); - if ((*pos == id) && (length + 2) <= left_len) - return pos; - pos += (length + 2); - left_len -= (length + 2); - } - - return NULL; -} - -/** - * @brief Add custom ie to mgmt frames. - * - * @param priv A pointer to moal private structure - * @param beacon_ies_data Beacon ie - * @param beacon_index The index for beacon when auto index - * @param proberesp_ies_data Probe resp ie - * @param proberesp_index The index for probe resp when auto index - * @param assocresp_ies_data Assoc resp ie - * @param assocresp_index The index for assoc resp when auto index - * @param probereq_ies_data Probe req ie - * @param probereq_index The index for probe req when auto index * - * - * @return 0 -- success, otherwise fail - */ -static int -woal_cfg80211_custom_ie(moal_private * priv, - custom_ie * beacon_ies_data, t_u16 * beacon_index, - custom_ie * proberesp_ies_data, t_u16 * proberesp_index, - custom_ie * assocresp_ies_data, t_u16 * assocresp_index, - custom_ie * probereq_ies_data, t_u16 * probereq_index) -{ - mlan_ioctl_req *ioctl_req = NULL; - mlan_ds_misc_cfg *misc = NULL; - mlan_ds_misc_custom_ie *custom_ie = NULL; - t_u8 *pos = NULL; - t_u16 len = 0; - int ret = 0; - - ENTER(); - - if (!(custom_ie = kmalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL))) { - ret = -ENOMEM; - goto done; - } - - memset(custom_ie, 0x00, sizeof(mlan_ds_misc_custom_ie)); - custom_ie->type = TLV_TYPE_MGMT_IE; - - pos = (t_u8 *) custom_ie->ie_data_list; - if (beacon_ies_data) { - len = sizeof(*beacon_ies_data) - MAX_IE_SIZE - + beacon_ies_data->ie_length; - memcpy(pos, beacon_ies_data, len); - pos += len; - custom_ie->len += len; - } - - if (proberesp_ies_data) { - len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE - + proberesp_ies_data->ie_length; - memcpy(pos, proberesp_ies_data, len); - pos += len; - custom_ie->len += len; - } - - if (assocresp_ies_data) { - len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE - + assocresp_ies_data->ie_length; - memcpy(pos, assocresp_ies_data, len); - custom_ie->len += len; - } - - if (probereq_ies_data) { - len = sizeof(*probereq_ies_data) - MAX_IE_SIZE - + probereq_ies_data->ie_length; - memcpy(pos, probereq_ies_data, len); - pos += len; - custom_ie->len += len; - } - - ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); - if (ioctl_req == NULL) { - ret = -ENOMEM; - goto done; - } - - misc = (mlan_ds_misc_cfg *) ioctl_req->pbuf; - misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; - ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; - ioctl_req->action = MLAN_ACT_SET; - - memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); - - if (MLAN_STATUS_SUCCESS != - woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { - ret = -EFAULT; - goto done; - } - - /* get the assigned index */ - pos = (t_u8 *) (&misc->param.cust_ie.ie_data_list[0].ie_index); - if (beacon_ies_data && beacon_ies_data->ie_length - && beacon_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { - /* save beacon ie index after auto-indexing */ - *beacon_index = misc->param.cust_ie.ie_data_list[0].ie_index; - len = sizeof(*beacon_ies_data) - MAX_IE_SIZE - + beacon_ies_data->ie_length; - pos += len; - } - - if (proberesp_ies_data && proberesp_ies_data->ie_length - && proberesp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { - /* save probe resp ie index after auto-indexing */ - *proberesp_index = *((t_u16 *) pos); - len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE - + proberesp_ies_data->ie_length; - pos += len; - } - - if (assocresp_ies_data && assocresp_ies_data->ie_length - && assocresp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { - /* save assoc resp ie index after auto-indexing */ - *assocresp_index = *((t_u16 *) pos); - len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE - + assocresp_ies_data->ie_length; - pos += len; - } - if (probereq_ies_data && probereq_ies_data->ie_length - && probereq_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { - /* save probe resp ie index after auto-indexing */ - *probereq_index = *((t_u16 *) pos); - len = sizeof(*probereq_ies_data) - MAX_IE_SIZE - + probereq_ies_data->ie_length; - pos += len; - } - - if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) - ret = -EFAULT; - - done: - if (ioctl_req) - kfree(ioctl_req); - if (custom_ie) - kfree(custom_ie); - LEAVE(); - return ret; -} - -/** - * @brief This function returns priv - * based on mgmt ie index - * - * @param handle A pointer to moal_handle - * @param index mgmt ie index - * - * @return Pointer to moal_private - */ -static moal_private * -woal_get_priv_by_mgmt_index(moal_handle * handle, t_u16 index) -{ - int i; - - for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { - if (handle->priv[i]) { - if (handle->priv[i]->probereq_index == index) - return (handle->priv[i]); - } - } - return NULL; -} - -/** - * @brief config AP or GO for mgmt frame ies. - * - * @param priv A pointer to moal private structure - * @param beacon_ies A pointer to beacon ies - * @param beacon_ies_len Beacon ies length - * @param proberesp_ies A pointer to probe resp ies - * @param proberesp_ies_len Probe resp ies length - * @param assocresp_ies A pointer to probe resp ies - * @param assocresp_ies_len Assoc resp ies length - * @param probereq_ies A pointer to probe req ies - * @param probereq_ies_len Probe req ies length * - * @param mask Mgmt frame mask - * - * @return 0 -- success, otherwise fail - */ -int -woal_cfg80211_mgmt_frame_ie(moal_private * priv, - const t_u8 * beacon_ies, size_t beacon_ies_len, - const t_u8 * proberesp_ies, - size_t proberesp_ies_len, - const t_u8 * assocresp_ies, - size_t assocresp_ies_len, const t_u8 * probereq_ies, - size_t probereq_ies_len, t_u16 mask) -{ - int ret = 0; - t_u8 *pos = NULL; - custom_ie *beacon_ies_data = NULL; - custom_ie *proberesp_ies_data = NULL; - custom_ie *assocresp_ies_data = NULL; - custom_ie *probereq_ies_data = NULL; - - /* static variables for mgmt frame ie auto-indexing */ - static t_u16 beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - static t_u16 proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - static t_u16 assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - static t_u16 probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - moal_private *pmpriv = NULL; - static t_u16 rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - const t_u8 *rsn_ie; - - ENTER(); - - if (mask & MGMT_MASK_BEACON) { - if (!(beacon_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { - ret = -ENOMEM; - goto done; - } - if (beacon_ies && beacon_ies_len) { - rsn_ie = - woal_parse_ie_tlv((t_u8 *) beacon_ies, (int) beacon_ies_len, - RSN_IE); - if (rsn_ie) { - beacon_ies_data->ie_index = rsn_index; - beacon_ies_data->mgmt_subtype_mask = - MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | - MGMT_MASK_ASSOC_RESP; - beacon_ies_data->ie_length = rsn_ie[1] + 2; - memcpy(beacon_ies_data->ie_buffer, rsn_ie, rsn_ie[1] + 2); - if (MLAN_STATUS_SUCCESS != - woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index, - NULL, &proberesp_index, NULL, - &assocresp_index, NULL, - &probereq_index)) { - ret = -EFAULT; - goto done; - } - } - } else { - /* clear rsn_ie */ - if (rsn_index <= MAX_MGMT_IE_INDEX) { - beacon_ies_data->ie_index = rsn_index; - beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; - beacon_ies_data->ie_length = 0; - rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - if (MLAN_STATUS_SUCCESS != - woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index, - NULL, &proberesp_index, NULL, - &assocresp_index, NULL, - &probereq_index)) { - ret = -EFAULT; - goto done; - } - } - } - } - - if (mask & MGMT_MASK_PROBE_RESP) { - if (!(proberesp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { - ret = -ENOMEM; - goto done; - } - } - - if (mask & MGMT_MASK_ASSOC_RESP) { - if (!(assocresp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { - ret = -ENOMEM; - goto done; - } - } - if (mask & MGMT_MASK_PROBE_REQ) { - if (!(probereq_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { - ret = -ENOMEM; - goto done; - } - } - - if (beacon_ies_data) { - memset(beacon_ies_data, 0x00, sizeof(custom_ie)); - if (beacon_ies && beacon_ies_len) { - /* set the beacon ies */ - beacon_ies_data->ie_index = beacon_index; - beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON; - beacon_ies_data->mgmt_subtype_mask |= MGMT_MASK_ASSOC_RESP; - beacon_ies_data->ie_length = woal_filter_beacon_ies(beacon_ies, - beacon_ies_len, - beacon_ies_data-> - ie_buffer); - } else { - /* clear the beacon ies */ - if (beacon_index > MAX_MGMT_IE_INDEX) { - PRINTM(MERROR, "Invalid beacon index for mgmt frame ie.\n"); - goto done; - } - - beacon_ies_data->ie_index = beacon_index; - beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; - beacon_ies_data->ie_length = 0; - beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - } - } - - if (proberesp_ies_data) { - memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); - if (proberesp_ies && proberesp_ies_len) { - /* set the probe response ies */ - // proberesp_ies_data->ie_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - proberesp_ies_data->ie_index = proberesp_index; - proberesp_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_RESP; - proberesp_ies_data->ie_length = proberesp_ies_len; - pos = proberesp_ies_data->ie_buffer; - memcpy(pos, proberesp_ies, proberesp_ies_len); - } else { - /* clear the probe response ies */ - if (proberesp_index > MAX_MGMT_IE_INDEX) { - PRINTM(MERROR, "Invalid probe resp index for mgmt frame ie.\n"); - goto done; - } - - proberesp_ies_data->ie_index = proberesp_index; - proberesp_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; - proberesp_ies_data->ie_length = 0; - proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - } - } - if (assocresp_ies_data) { - memset(assocresp_ies_data, 0x00, sizeof(custom_ie)); - if (assocresp_ies && assocresp_ies_len) { - /* set the assoc response ies */ - assocresp_ies_data->ie_index = assocresp_index; - assocresp_ies_data->mgmt_subtype_mask = MGMT_MASK_ASSOC_RESP; - assocresp_ies_data->ie_length = assocresp_ies_len; - pos = assocresp_ies_data->ie_buffer; - memcpy(pos, assocresp_ies, assocresp_ies_len); - } else { - /* clear the assoc response ies */ - if (assocresp_index > MAX_MGMT_IE_INDEX) { - PRINTM(MERROR, "Invalid assoc resp index for mgmt frame ie.\n"); - goto done; - } - - assocresp_ies_data->ie_index = assocresp_index; - assocresp_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; - assocresp_ies_data->ie_length = 0; - assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - } - } - - if (probereq_ies_data) { - memset(probereq_ies_data, 0x00, sizeof(custom_ie)); - if ((probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) && - (priv->probereq_index != probereq_index)) { - pmpriv = woal_get_priv_by_mgmt_index(priv->phandle, probereq_index); - if (pmpriv) { - probereq_ies_data->ie_index = probereq_index; - probereq_ies_data->mgmt_subtype_mask = - MLAN_CUSTOM_IE_DELETE_MASK; - probereq_ies_data->ie_length = 0; - probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - pmpriv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - if (MLAN_STATUS_SUCCESS != - woal_cfg80211_custom_ie(pmpriv, NULL, &beacon_index, - NULL, &proberesp_index, - NULL, &assocresp_index, - probereq_ies_data, - &probereq_index)) { - ret = -EFAULT; - goto done; - } - memset(probereq_ies_data, 0x00, sizeof(custom_ie)); - } - } - if (probereq_ies && probereq_ies_len) { - /* set the probe req ies */ - probereq_ies_data->ie_index = probereq_index; - probereq_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_REQ; - probereq_ies_data->ie_length = probereq_ies_len; - pos = probereq_ies_data->ie_buffer; - memcpy(pos, probereq_ies, probereq_ies_len); - } else { - /* clear the probe req ies */ - if (probereq_index > MAX_MGMT_IE_INDEX) { - PRINTM(MERROR, "Invalid probe resp index for mgmt frame ie.\n"); - goto done; - } - probereq_ies_data->ie_index = probereq_index; - probereq_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; - probereq_ies_data->ie_length = 0; - probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; - } - } - - if (MLAN_STATUS_SUCCESS != - woal_cfg80211_custom_ie(priv, beacon_ies_data, &beacon_index, - proberesp_ies_data, &proberesp_index, - assocresp_ies_data, &assocresp_index, - probereq_ies_data, &probereq_index)) { - ret = -EFAULT; - goto done; - } - if (probereq_ies_data) - priv->probereq_index = probereq_index; - - done: - if (beacon_ies_data) - kfree(beacon_ies_data); - if (proberesp_ies_data) - kfree(proberesp_ies_data); - if (assocresp_ies_data) - kfree(assocresp_ies_data); - - LEAVE(); - - return ret; -} - -/** * @brief initialize AP or GO bss config * * @param priv A pointer to moal private structure @@ -1330,7 +856,7 @@ woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *params) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; ENTER(); @@ -1402,7 +928,7 @@ woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *params) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; ENTER(); @@ -1471,7 +997,7 @@ woal_cfg80211_set_beacon(struct wiphy *wiphy, int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = 0; ENTER(); @@ -1521,7 +1047,7 @@ int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, u8 * mac, struct station_info *stainfo) { - moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + moal_private *priv = (moal_private *) woal_get_netdev_priv(dev); int ret = -EFAULT; int i = 0; mlan_ds_get_info *info = NULL; diff --git a/drivers/net/wireless/sd8797/mlinux/moal_wext.c b/drivers/net/wireless/sd8797/mlinux/moal_wext.c index 8595892090fb..a967c939f009 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_wext.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_wext.c @@ -2124,12 +2124,14 @@ woal_set_priv(struct net_device *dev, struct iw_request_info *info, priv->bg_scan_reported = MFALSE; len = sprintf(buf, "OK\n") + 1; } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { - if (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { - ret = -EFAULT; - goto done; + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; } - priv->bg_scan_start = MFALSE; - priv->bg_scan_reported = MFALSE; len = sprintf(buf, "OK\n") + 1; } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == 0) { #ifdef MEF_CFG_RX_FILTER @@ -2326,13 +2328,13 @@ woal_set_essid(struct net_device *dev, struct iw_request_info *info, * Check if we asked for `any' or 'particular' */ if (!dwrq->flags) { - +#ifdef REASSOCIATION if (!req_ssid.ssid_len) { memset(&priv->prev_ssid_bssid.ssid, 0x00, sizeof(mlan_802_11_ssid)); memset(&priv->prev_ssid_bssid.bssid, 0x00, MLAN_MAC_ADDR_LENGTH); goto setessid_ret; } - +#endif /* Do normal SSID scanning */ if (MLAN_STATUS_SUCCESS != woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { @@ -2351,6 +2353,7 @@ woal_set_essid(struct net_device *dev, struct iw_request_info *info, PRINTM(MINFO, "Requested new SSID = %s\n", (char *) req_ssid.ssid); memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); + if (dwrq->flags != 0xFFFF) { if (MLAN_STATUS_SUCCESS != woal_find_essid(priv, &ssid_bssid)) { /* Do specific SSID scanning */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_wext.h b/drivers/net/wireless/sd8797/mlinux/moal_wext.h index 91f918e1b130..dfafff866b96 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_wext.h +++ b/drivers/net/wireless/sd8797/mlinux/moal_wext.h @@ -45,7 +45,6 @@ Change log: #define CUS_EVT_BEACON_SNR_HIGH "EVENT=BEACON_SNR_HIGH" /** Custom event : Max fail */ #define CUS_EVT_MAX_FAIL "EVENT=MAX_FAIL" -#define CUS_EVT_BG_SCAN "EVENT=BG_SCAN_REPORT" /** Custom event : Data RSSI low */ #define CUS_EVT_DATA_RSSI_LOW "EVENT=DATA_RSSI_LOW" /** Custom event : Data SNR low */ diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index dc29348264c6..c3706246a3b7 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -55,6 +55,31 @@ config PCIEASPM_DEBUG This enables PCI Express ASPM debug support. It will add per-device interface to control ASPM. +choice + prompt "Default ASPM policy" + default PCIEASPM_DEFAULT + depends on PCIEASPM + +config PCIEASPM_DEFAULT + bool "BIOS default" + depends on PCIEASPM + help + Use the BIOS defaults for PCI Express ASPM. + +config PCIEASPM_POWERSAVE + bool "Powersave" + depends on PCIEASPM + help + Enable PCI Express ASPM L0s and L1 where possible, even if the + BIOS did not. + +config PCIEASPM_PERFORMANCE + bool "Performance" + depends on PCIEASPM + help + Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them. +endchoice + config PCIE_PME def_bool y depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index cbfbab18be91..d65b56893506 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -76,7 +76,15 @@ static LIST_HEAD(link_list); #define POLICY_DEFAULT 0 /* BIOS default setting */ #define POLICY_PERFORMANCE 1 /* high performance */ #define POLICY_POWERSAVE 2 /* high power saving */ + +#ifdef CONFIG_PCIEASPM_PERFORMANCE +static int aspm_policy = POLICY_PERFORMANCE; +#elif defined CONFIG_PCIEASPM_POWERSAVE +static int aspm_policy = POLICY_POWERSAVE; +#else static int aspm_policy; +#endif + static const char *policy_str[] = { [POLICY_DEFAULT] = "default", [POLICY_PERFORMANCE] = "performance", diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index e392f4dc77de..857deb8faa0a 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -885,6 +885,9 @@ static int bq27x00_battery_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct bq27x00_device_info *di = platform_get_drvdata(pdev); + cancel_delayed_work_sync(&di->work); + cancel_delayed_work_sync(&di->external_power_changed_work); + if (di->chip == BQ27510) { ret = bq27x00_write(di, BQ27510_CNTL, BQ27510_CNTL_SET_SLEEP, false); @@ -920,6 +923,9 @@ static int bq27x00_battery_resume(struct device *dev) return ret; } } + + schedule_delayed_work(&di->work, HZ); + return 0; } diff --git a/drivers/power/smb349-charger.c b/drivers/power/smb349-charger.c index 6164905efb70..134b8154a2b9 100644 --- a/drivers/power/smb349-charger.c +++ b/drivers/power/smb349-charger.c @@ -3,7 +3,7 @@ * * Battery charger driver for smb349 from summit microelectronics * - * Copyright (c) 2012, NVIDIA Corporation. + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +36,9 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/usb/otg.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> #define SMB349_CHARGE 0x00 #define SMB349_CHRG_CRNTS 0x01 @@ -170,17 +173,28 @@ static int smb349_configure_otg(struct i2c_client *client, int enable) } if (enable) { - /* Configure PGOOD to be active low */ - ret = smb349_read(client, SMB349_SYSOK_USB3); + /* Configure PGOOD to be active low if no 5V on VBUS */ + ret = smb349_read(client, SMB349_STS_REG_C); if (ret < 0) { dev_err(&client->dev, "%s: err %d\n", __func__, ret); goto error; } - ret = smb349_write(client, SMB349_SYSOK_USB3, (ret & (~(1)))); - if (ret < 0) { - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - goto error; + if (!(ret & 0x01)) { + ret = smb349_read(client, SMB349_SYSOK_USB3); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", + __func__, ret); + goto error; + } + + ret = smb349_write(client, SMB349_SYSOK_USB3, + (ret & (~(1)))); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", + __func__, ret); + goto error; + } } /* Enable OTG */ @@ -275,7 +289,7 @@ error: int update_charger_status(void) { struct i2c_client *client; - int ret, val; + int val; if (!charger) return -ENODEV; @@ -304,8 +318,6 @@ int update_charger_status(void) return 0; val_error: return val; -ret_error: - return ret; } EXPORT_SYMBOL_GPL(update_charger_status); @@ -338,39 +350,53 @@ int smb349_battery_online(void) return 1; } -static void smb349_otg_status(enum usb_otg_state to, enum usb_otg_state from, void *data) +static int smb349_enable_otg(struct regulator_dev *otg_rdev) { struct i2c_client *client = charger->client; int ret; - if ((from == OTG_STATE_A_SUSPEND) && (to == OTG_STATE_A_HOST)) { + /* configure charger */ + ret = smb349_configure_charger(client, 0); + if (ret < 0) + goto error; + /* ENABLE OTG */ + ret = smb349_configure_otg(client, 1); + if (ret < 0) + goto error; - /* configure charger */ - ret = smb349_configure_charger(client, 0); - if (ret < 0) - dev_err(&client->dev, "%s() error in configuring" - "otg..\n", __func__); + charger->is_otg_enabled = 1; + return 0; +error: + dev_err(&client->dev, "%s() error in enabling" + "otg..\n", __func__); + return ret; +} - /* ENABLE OTG */ - ret = smb349_configure_otg(client, 1); - if (ret < 0) - dev_err(&client->dev, "%s() error in configuring" - "otg..\n", __func__); +static int smb349_disable_otg(struct regulator_dev *otg_rdev) +{ + struct i2c_client *client = charger->client; + int ret; - } else if ((from == OTG_STATE_A_HOST) && (to == OTG_STATE_A_SUSPEND)) { + /* Disable OTG */ + ret = smb349_configure_otg(client, 0); + if (ret < 0) + goto error; + /* configure charger */ + ret = smb349_configure_charger(client, 1); + if (ret < 0) + goto error; - /* Disable OTG */ - ret = smb349_configure_otg(client, 0); - if (ret < 0) - dev_err(&client->dev, "%s() error in configuring" - "otg..\n", __func__); + charger->is_otg_enabled = 0; + return 0; +error: + dev_err(&client->dev, "%s() error in disabling" + "otg..\n", __func__); + return ret; +} - /* configure charger */ - ret = smb349_configure_charger(client, 1); - if (ret < 0) - dev_err(&client->dev, "%s() error in configuring" - "otg..\n", __func__); - } +static int smb349_is_otg_enabled(struct regulator_dev *otg_rdev) +{ + return charger->is_otg_enabled; } static int smb349_enable_charging(struct regulator_dev *rdev, @@ -381,15 +407,11 @@ static int smb349_enable_charging(struct regulator_dev *rdev, if (!max_uA) { charger->state = stopped; - /* Disable charger */ - ret = smb349_configure_charger(client, 0); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring" - "charger..\n", __func__); - return ret; - } charger->chrg_type = NONE; } else { + /* Wait for SMB349 to reload OTP setting and detect type*/ + msleep(500); + ret = smb349_read(client, SMB349_STS_REG_D); if (ret < 0) { dev_err(&client->dev, "%s(): Failed in reading register" @@ -414,13 +436,103 @@ static int smb349_enable_charging(struct regulator_dev *rdev, if (charger->charger_cb) charger->charger_cb(charger->state, charger->chrg_type, charger->charger_cb_data); -return 0; + return 0; } static struct regulator_ops smb349_tegra_regulator_ops = { .set_current_limit = smb349_enable_charging, }; +static struct regulator_ops smb349_tegra_otg_regulator_ops = { + .enable = smb349_enable_otg, + .disable = smb349_disable_otg, + .is_enabled = smb349_is_otg_enabled, +}; + +#if defined(CONFIG_DEBUG_FS) +static struct dentry *smb349_dentry_regs; + +static int smb349_dump_regs(struct i2c_client *client, u8 *addrs, int num_addrs, + char *buf, ssize_t *len) +{ + ssize_t count = *len; + int ret = 0; + int i; + + if (count >= PAGE_SIZE - 1) + return -ERANGE; + + for (i = 0; i < num_addrs; i++) { + count += sprintf(buf + count, "0x%02x: ", addrs[i]); + if (count >= PAGE_SIZE - 1) + return -ERANGE; + + ret = smb349_read(client, addrs[i]); + if (ret < 0) + count += sprintf(buf + count, "<read fail: %d\n", ret); + else + count += sprintf(buf + count, "0x%02x\n", ret); + + if (count >= PAGE_SIZE - 1) + return -ERANGE; + } + *len = count; + + return 0; +} + +static ssize_t smb349_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static u8 regs[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x10, 0x11, 0x12, 0x30, 0x31, 0x33, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F +}; +static ssize_t smb349_debugfs_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + struct i2c_client *client = file->private_data; + char *buf; + size_t len = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += sprintf(buf + len, "SMB349 Registers\n"); + smb349_dump_regs(client, regs, ARRAY_SIZE(regs), buf, &len); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; +} + +static const struct file_operations smb349_debugfs_fops = { + .open = smb349_debugfs_open, + .read = smb349_debugfs_read, +}; + +static void smb349_debugfs_init(struct i2c_client *client) +{ + smb349_dentry_regs = debugfs_create_file(client->name, + 0444, 0, client, + &smb349_debugfs_fops); +} + +static void smb349_debugfs_exit(struct i2c_client *client) +{ + debugfs_remove(smb349_dentry_regs); +} +#else +static void smb349_debugfs_init(struct i2c_client *client){} +static void smb349_debugfs_exit(struct i2c_client *client){} +#endif /* CONFIG_DEBUG_FS */ + static int __devinit smb349_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -438,6 +550,11 @@ static int __devinit smb349_probe(struct i2c_client *client, charger->client = client; charger->dev = &client->dev; pdata = client->dev.platform_data; + if(!pdata) { + ret = -ENXIO; + goto error; + } + i2c_set_clientdata(client, charger); /* Check battery presence */ @@ -448,11 +565,12 @@ static int __devinit smb349_probe(struct i2c_client *client, goto regulator_error; } + charger->is_otg_enabled = 0; + charger->reg_desc.name = "vbus_charger"; charger->reg_desc.ops = &smb349_tegra_regulator_ops; charger->reg_desc.type = REGULATOR_CURRENT; charger->reg_desc.id = pdata->regulator_id; - charger->reg_desc.type = REGULATOR_CURRENT; charger->reg_desc.owner = THIS_MODULE; charger->reg_init_data.supply_regulator = NULL; @@ -478,6 +596,9 @@ static int __devinit smb349_probe(struct i2c_client *client, charger->rdev = regulator_register(&charger->reg_desc, charger->dev, &charger->reg_init_data, charger); + + smb349_debugfs_init(client); + if (IS_ERR(charger->rdev)) { dev_err(&client->dev, "failed to register %s\n", charger->reg_desc.name); @@ -485,6 +606,42 @@ static int __devinit smb349_probe(struct i2c_client *client, goto regulator_error; } + charger->otg_reg_desc.name = "vbus_otg"; + charger->otg_reg_desc.ops = &smb349_tegra_otg_regulator_ops; + charger->otg_reg_desc.type = REGULATOR_CURRENT; + charger->otg_reg_desc.id = pdata->otg_regulator_id; + charger->otg_reg_desc.type = REGULATOR_CURRENT; + charger->otg_reg_desc.owner = THIS_MODULE; + + charger->otg_reg_init_data.supply_regulator = NULL; + charger->otg_reg_init_data.num_consumer_supplies = + pdata->num_otg_consumer_supplies; + charger->otg_reg_init_data.regulator_init = NULL; + charger->otg_reg_init_data.consumer_supplies = + pdata->otg_consumer_supplies; + charger->otg_reg_init_data.driver_data = charger; + charger->otg_reg_init_data.constraints.name = "vbus_otg"; + charger->otg_reg_init_data.constraints.min_uA = 0; + charger->otg_reg_init_data.constraints.max_uA = 500000; + + charger->otg_reg_init_data.constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL | + REGULATOR_MODE_STANDBY; + + charger->otg_reg_init_data.constraints.valid_ops_mask = + REGULATOR_CHANGE_MODE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_CURRENT; + + charger->otg_rdev = regulator_register(&charger->otg_reg_desc, charger->dev, + &charger->otg_reg_init_data, charger); + if (IS_ERR(charger->otg_rdev)) { + dev_err(&client->dev, "failed to register %s\n", + charger->otg_reg_desc.name); + ret = PTR_ERR(charger->otg_rdev); + goto otg_regulator_error; + } + /* disable OTG */ ret = smb349_configure_otg(client, 0); if (ret < 0) { @@ -516,12 +673,11 @@ static int __devinit smb349_probe(struct i2c_client *client, } } - ret = register_otg_callback(smb349_otg_status, charger); - if (ret < 0) - goto error; - return 0; error: + smb349_debugfs_exit(client); + regulator_unregister(charger->otg_rdev); +otg_regulator_error: regulator_unregister(charger->rdev); regulator_error: kfree(charger); @@ -533,7 +689,9 @@ static int __devexit smb349_remove(struct i2c_client *client) { struct smb349_charger *charger = i2c_get_clientdata(client); + smb349_debugfs_exit(client); regulator_unregister(charger->rdev); + regulator_unregister(charger->otg_rdev); kfree(charger); return 0; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index d3cb8e6ff099..28c58a1c19b1 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -117,6 +117,15 @@ config REGULATOR_MAX8952 via I2C bus. Maxim 8952 has one voltage output and supports 4 DVS modes ranging from 0.77V to 1.40V by 0.01V steps. +config REGULATOR_MAX8973 + tristate "Maxim MAX8973 Power Regulator" + depends on I2C + select REGMAP_I2C + help + This driver supports MAX8973 voltage regulator chip. + The MAX8973 high-efficiency, three-phase, DC-DC step-down switching + regulator delivers up to 9A of output current. + config REGULATOR_MAX8997 tristate "Maxim 8997/8966 regulator" depends on MFD_MAX8997 @@ -248,6 +257,16 @@ config REGULATOR_AB3100 AB3100 analog baseband dealing with power regulators for the system. +config REGULATOR_RC5T583 + tristate "RICOH RC5T583 Power regulators" + depends on MFD_RC5T583 + help + Select this option to enable the power regulator of RICOH + PMIC RC5T583. + This driver supports the control of different power rails of device + through regulator interface. The device supports multiple DCDC/LDO + outputs which can be controlled by i2c communication. + config REGULATOR_TPS6105X tristate "TI TPS6105X Power regulators" depends on TPS6105X @@ -345,6 +364,16 @@ config REGULATOR_TPS62360 high-frequency synchronous step down dc-dc converter optimized for battery-powered portable applications. +config REGULATOR_TPS6238X0 + tristate "TI TPS623850/TPS623860/TPS623870 Power Regulator" + depends on I2C + select REGMAP_I2C + help + This driver supports TPS6238X0 voltage regulator chip. This + regulator is meant for processor core supply. This chip is + high-frequency synchronous step down dc-dc converter optimized + for battery-powered portable applications. + config REGULATOR_AAT2870 tristate "AnalogicTech AAT2870 Regulators" depends on MFD_AAT2870_CORE diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 4fc5c3f275ab..a25ff34afcbc 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o +obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX8907C) += max8907c-regulator.o @@ -39,6 +40,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o +obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o @@ -52,6 +54,7 @@ obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o +obj-$(CONFIG_REGULATOR_TPS6238X0) += tps6238x0-regulator.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_FAN53555) += fan53555-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/max77663-regulator.c b/drivers/regulator/max77663-regulator.c index c9001c0a3e9f..55d2526b4490 100644 --- a/drivers/regulator/max77663-regulator.c +++ b/drivers/regulator/max77663-regulator.c @@ -280,7 +280,7 @@ static int max77663_regulator_set_fps(struct max77663_regulator *reg) fps_mask |= FPS_PD_PERIOD_MASK; } - if (fps_val) + if (fps_val || fps_mask) ret = max77663_regulator_cache_write(reg, reg->regs[FPS_REG].addr, fps_mask, fps_val, ®->regs[FPS_REG].val); diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c new file mode 100644 index 000000000000..8d87970a1b31 --- /dev/null +++ b/drivers/regulator/max8973-regulator.c @@ -0,0 +1,606 @@ +/* + * max8973-regulator.c -- Maxim max8973 + * + * Regulator driver for MAXIM 8973 DC-DC step-down switching regulator. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/max8973-regulator.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +/* Register definitions */ +#define MAX8973_VOUT 0x0 +#define MAX8973_VOUT_DVS 0x1 +#define MAX8973_CONTROL1 0x2 +#define MAX8973_CONTROL2 0x3 +#define MAX8973_CHIPID1 0x4 +#define MAX8973_CHIPID2 0x5 + +#define MAX8973_MAX_VOUT_REG 2 + +/* MAX8973_VOUT */ +#define MAX8973_VOUT_ENABLE BIT(7) +#define MAX8973_VOUT_MASK 0x7F + +/* MAX8973_VOUT_DVS */ +#define MAX8973_DVS_VOUT_MASK 0x7F + +/* MAX8973_CONTROL1 */ +#define MAX8973_SNS_ENABLE BIT(7) +#define MAX8973_FPWM_EN_M BIT(6) +#define MAX8973_NFSR_ENABLE BIT(5) +#define MAX8973_AD_ENABLE BIT(4) +#define MAX8973_BIAS_ENABLE BIT(3) +#define MAX8973_FREQSHIFT_9PER BIT(2) + +#define MAX8973_RAMP_12mV_PER_US 0x0 +#define MAX8973_RAMP_25mV_PER_US 0x1 +#define MAX8973_RAMP_50mV_PER_US 0x2 +#define MAX8973_RAMP_200mV_PER_US 0x3 + +/* MAX8973_CONTROL2 */ +#define MAX8973_WDTMR_ENABLE BIT(6) +#define MAX8973_DISCH_ENBABLE BIT(5) +#define MAX8973_FT_ENABLE BIT(4) + +#define MAX8973_CKKADV_TRIP_DISABLE 0xC +#define MAX8973_CKKADV_TRIP_75mV_PER_US 0x0 +#define MAX8973_CKKADV_TRIP_150mV_PER_US 0x4 +#define MAX8973_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8 + +#define MAX8973_INDUCTOR_MIN_30_PER 0x0 +#define MAX8973_INDUCTOR_NOMINAL 0x1 +#define MAX8973_INDUCTOR_PLUS_30_PER 0x2 +#define MAX8973_INDUCTOR_PLUS_60_PER 0x3 + +#define MAX8973_MIN_VOLATGE 606250 +#define MAX8973_MAX_VOLATGE 1400000 +#define MAX8973_VOLATGE_STEP 6250 +#define MAX8973_BUCK_N_VOLTAGE \ + (((MAX8973_MAX_VOLATGE - MAX8973_MIN_VOLATGE) / MAX8973_VOLATGE_STEP) \ + + 1) + +/* Maxim 8973 chip information */ +struct max8973_chip { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regmap *regmap; + bool enable_external_control; + int dvs_gpio; + int lru_index[MAX8973_MAX_VOUT_REG]; + int curr_vout_val[MAX8973_MAX_VOUT_REG]; + int curr_vout_reg; + int curr_gpio_val; + int change_uv_per_us; + bool valid_dvs_gpio; +}; + +/* + * find_voltage_set_register: Find new voltage configuration register (VOUT). + * The finding of the new VOUT register will be based on the LRU mechanism. + * Each VOUT register will have different voltage configured . This + * Function will look if any of the VOUT register have requested voltage set + * or not. + * - If it is already there then it will make that register as most + * recently used and return as found so that caller need not to set + * the VOUT register but need to set the proper gpios to select this + * VOUT register. + * - If requested voltage is not found then it will use the least + * recently mechanism to get new VOUT register for new configuration + * and will return not_found so that caller need to set new VOUT + * register and then gpios (both). + */ +static bool find_voltage_set_register(struct max8973_chip *tps, + int req_vsel, int *vout_reg, int *gpio_val) +{ + int i; + bool found = false; + int new_vout_reg = tps->lru_index[MAX8973_MAX_VOUT_REG - 1]; + int found_index = MAX8973_MAX_VOUT_REG - 1; + + for (i = 0; i < MAX8973_MAX_VOUT_REG; ++i) { + if (tps->curr_vout_val[tps->lru_index[i]] == req_vsel) { + new_vout_reg = tps->lru_index[i]; + found_index = i; + found = true; + goto update_lru_index; + } + } + +update_lru_index: + for (i = found_index; i > 0; i--) + tps->lru_index[i] = tps->lru_index[i - 1]; + + tps->lru_index[0] = new_vout_reg; + *gpio_val = new_vout_reg; + *vout_reg = MAX8973_VOUT + new_vout_reg; + return found; +} + +static int max8973_dcdc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(max->regmap, max->curr_vout_reg, &data); + if (ret < 0) { + dev_err(max->dev, "%s(): register %d read failed with err %d\n", + __func__, max->curr_vout_reg, ret); + return ret; + } + return data & MAX8973_VOUT_MASK; +} + +static int max8973_dcdc_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int vsel; + int ret; + bool found = false; + int vout_reg = max->curr_vout_reg; + int gpio_val = max->curr_gpio_val; + + if ((max_uV < min_uV) || (max_uV < MAX8973_MIN_VOLATGE) || + (min_uV > MAX8973_MAX_VOLATGE)) + return -EINVAL; + + vsel = DIV_ROUND_UP(min_uV - MAX8973_MIN_VOLATGE, MAX8973_VOLATGE_STEP); + if (selector) + *selector = (vsel & MAX8973_VOUT_MASK); + + /* + * If gpios are available to select the VOUT register then least + * recently used register for new configuration. + */ + if (max->valid_dvs_gpio) + found = find_voltage_set_register(max, vsel, + &vout_reg, &gpio_val); + + if (!found) { + ret = regmap_update_bits(max->regmap, vout_reg, + MAX8973_VOUT_MASK, vsel); + if (ret < 0) { + dev_err(max->dev, + "%s(): register %d update failed with err %d\n", + __func__, vout_reg, ret); + return ret; + } + max->curr_vout_reg = vout_reg; + max->curr_vout_val[gpio_val] = vsel; + } + + /* Select proper VOUT register vio gpios */ + if (max->valid_dvs_gpio) { + gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1); + max->curr_gpio_val = gpio_val; + } + return 0; +} + +static int max8973_dcdc_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector >= MAX8973_BUCK_N_VOLTAGE) + return -EINVAL; + + return MAX8973_MIN_VOLATGE + selector * MAX8973_VOLATGE_STEP; +} + +static int max8973_dcdc_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int old_uV, new_uV; + + old_uV = max8973_dcdc_list_voltage(rdev, old_selector); + if (old_uV < 0) + return old_uV; + + new_uV = max8973_dcdc_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), max->change_uv_per_us); +} +static int max8973_dcdc_enable(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int ret; + + if (max->enable_external_control) + return 0; + + ret = regmap_update_bits(max->regmap, MAX8973_VOUT, + MAX8973_VOUT_ENABLE, MAX8973_VOUT_ENABLE); + if (ret < 0) + dev_err(max->dev, "%s(): register %d update failed with err %d", + __func__, MAX8973_VOUT, ret); + return ret; +} + +static int max8973_dcdc_disable(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int ret = 0; + + if (max->enable_external_control) + return 0; + + ret = regmap_update_bits(max->regmap, MAX8973_VOUT, + MAX8973_VOUT_ENABLE, 0); + if (ret < 0) + dev_err(max->dev, "%s(): register %d update failed with err %d", + __func__, MAX8973_VOUT, ret); + return ret; +} + +static int max8973_dcdc_is_enabled(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int ret; + unsigned int data; + + if (max->enable_external_control) + return 1; + + ret = regmap_read(max->regmap, MAX8973_VOUT, &data); + if (ret < 0) { + dev_err(max->dev, "%s(): register %d read failed with err %d", + __func__, max->curr_vout_reg, ret); + return ret; + } + + return !!(data & MAX8973_VOUT_ENABLE); +} + +static int max8973_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int ret; + int pwm; + + /* Enable force PWM mode in FAST mode only. */ + switch (mode) { + case REGULATOR_MODE_FAST: + pwm = MAX8973_FPWM_EN_M; + break; + + case REGULATOR_MODE_NORMAL: + pwm = 0; + break; + + default: + return -EINVAL; + } + + ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1, + MAX8973_FPWM_EN_M, pwm); + if (ret < 0) + dev_err(max->dev, + "%s(): register %d update failed with err %d\n", + __func__, MAX8973_CONTROL1, ret); + return ret; +} + +static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data); + if (ret < 0) { + dev_err(max->dev, "%s(): register %d read failed with err %d\n", + __func__, MAX8973_CONTROL1, ret); + return ret; + } + return (data & MAX8973_FPWM_EN_M) ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops max8973_dcdc_ops = { + .get_voltage_sel = max8973_dcdc_get_voltage_sel, + .set_voltage = max8973_dcdc_set_voltage, + .list_voltage = max8973_dcdc_list_voltage, + .set_voltage_time_sel = max8973_dcdc_set_voltage_time_sel, + .enable = max8973_dcdc_enable, + .disable = max8973_dcdc_disable, + .is_enabled = max8973_dcdc_is_enabled, + .set_mode = max8973_dcdc_set_mode, + .get_mode = max8973_dcdc_get_mode, +}; + +static int __devinit max8973_init_dcdc(struct max8973_chip *max, + struct max8973_regulator_platform_data *pdata) +{ + int ret; + uint8_t control1 = 0; + uint8_t control2 = 0; + + if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE) + control1 |= MAX8973_SNS_ENABLE; + + if (!(pdata->control_flags & MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE)) + control1 |= MAX8973_NFSR_ENABLE; + + if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE) + control1 |= MAX8973_AD_ENABLE; + + if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE) + control1 |= MAX8973_BIAS_ENABLE; + + if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE) + control1 |= MAX8973_FREQSHIFT_9PER; + + switch (pdata->control_flags & MAX8973_CONTROL_SLEW_RATE_200MV_PER_US) { + case MAX8973_CONTROL_SLEW_RATE_12_5mV_PER_US: + control1 = MAX8973_RAMP_12mV_PER_US; + max->change_uv_per_us = 12500; + break; + + case MAX8973_CONTROL_SLEW_RATE_25mV_PER_US: + control1 = MAX8973_RAMP_25mV_PER_US; + max->change_uv_per_us = 25000; + break; + + case MAX8973_CONTROL_SLEW_RATE_50mV_PER_US: + control1 = MAX8973_RAMP_50mV_PER_US; + max->change_uv_per_us = 50000; + break; + + case MAX8973_CONTROL_SLEW_RATE_200MV_PER_US: + control1 = MAX8973_RAMP_200mV_PER_US; + max->change_uv_per_us = 200000; + break; + } + + if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE)) + control2 |= MAX8973_DISCH_ENBABLE; + + switch (pdata->control_flags & + MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US) { + case MAX8973_CONTROL_CLKADV_TRIP_DISABLED: + control2 |= MAX8973_CKKADV_TRIP_DISABLE; + break; + + case MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US: + control2 |= MAX8973_CKKADV_TRIP_75mV_PER_US; + break; + + case MAX8973_CONTROL_CLKADV_TRIP_150mV_PER_US: + control2 |= MAX8973_CKKADV_TRIP_150mV_PER_US; + break; + + case MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US_HIST_DIS: + control2 |= MAX8973_CKKADV_TRIP_75mV_PER_US_HIST_DIS; + break; + } + + switch (pdata->control_flags & + MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_60_PER) { + case MAX8973_CONTROL_INDUCTOR_VALUE_NOMINAL: + control2 |= MAX8973_INDUCTOR_NOMINAL; + break; + + case MAX8973_CONTROL_INDUCTOR_VALUE_MINUS_30_PER: + control2 |= MAX8973_INDUCTOR_MIN_30_PER; + break; + + case MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_30_PER: + control2 |= MAX8973_INDUCTOR_PLUS_30_PER; + break; + + case MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_60_PER: + control2 |= MAX8973_INDUCTOR_PLUS_60_PER; + break; + } + + ret = regmap_write(max->regmap, MAX8973_CONTROL1, control1); + if (ret < 0) { + dev_err(max->dev, "%s(): register %d write failed with err %d", + __func__, MAX8973_CONTROL1, ret); + return ret; + } + + ret = regmap_write(max->regmap, MAX8973_CONTROL2, control2); + if (ret < 0) { + dev_err(max->dev, "%s(): register %d write failed with err %d", + __func__, MAX8973_CONTROL2, ret); + return ret; + } + + /* If external control is enabled then disable EN bit */ + if (max->enable_external_control) { + ret = regmap_update_bits(max->regmap, MAX8973_VOUT, + MAX8973_VOUT_ENABLE, 0); + if (ret < 0) + dev_err(max->dev, "%s(): register %d update failed with err %d", + __func__, MAX8973_VOUT, ret); + } + return ret; +} + +static const struct regmap_config max8973_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX8973_CHIPID2, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit max8973_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max8973_regulator_platform_data *pdata; + struct regulator_dev *rdev; + struct max8973_chip *max; + int ret; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "%s(): No Platform data", __func__); + return -EIO; + } + + max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL); + if (!max) { + dev_err(&client->dev, "%s(): Memory allocation failed\n", + __func__); + return -ENOMEM; + } + + max->dev = &client->dev; + + max->desc.name = id->name; + max->desc.id = 0; + max->desc.ops = &max8973_dcdc_ops; + max->desc.type = REGULATOR_VOLTAGE; + max->desc.owner = THIS_MODULE; + max->regmap = devm_regmap_init_i2c(client, &max8973_regmap_config); + if (IS_ERR(max->regmap)) { + ret = PTR_ERR(max->regmap); + dev_err(&client->dev, + "%s(): regmap allocation failed with err %d\n", + __func__, ret); + return ret; + } + i2c_set_clientdata(client, max); + + max->enable_external_control = pdata->enable_ext_control; + max->dvs_gpio = pdata->dvs_gpio; + max->curr_gpio_val = pdata->dvs_def_state; + max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state; + max->lru_index[0] = max->curr_vout_reg; + max->valid_dvs_gpio = false; + + if (gpio_is_valid(max->dvs_gpio)) { + int gpio_flags; + int i; + + gpio_flags = (pdata->dvs_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = gpio_request_one(max->dvs_gpio, + gpio_flags, "max8973-dvs"); + if (ret) { + dev_err(&client->dev, + "%s(): Could not obtain dvs GPIO %d: %d\n", + __func__, max->dvs_gpio, ret); + return ret; + } + max->valid_dvs_gpio = true; + + /* + * Initialize the lru index with vout_reg id + * The index 0 will be most recently used and + * set with the max->curr_vout_reg */ + for (i = 0; i < MAX8973_MAX_VOUT_REG; ++i) + max->lru_index[i] = i; + max->lru_index[0] = max->curr_vout_reg; + max->lru_index[max->curr_vout_reg] = 0; + } + + ret = max8973_init_dcdc(max, pdata); + if (ret < 0) { + dev_err(max->dev, "%s(): Init failed with err = %d\n", + __func__, ret); + goto err_init; + } + + /* Register the regulators */ + rdev = regulator_register(&max->desc, &client->dev, + pdata->reg_init_data, max); + if (IS_ERR(rdev)) { + dev_err(max->dev, + "%s(): regulator register failed with err %s\n", + __func__, id->name); + ret = PTR_ERR(rdev); + goto err_init; + } + + max->rdev = rdev; + return 0; + +err_init: + if (gpio_is_valid(max->dvs_gpio)) + gpio_free(max->dvs_gpio); + return ret; +} + +/** + * max8973_remove - max8973 driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister TPS driver as an i2c client device driver + */ +static int __devexit max8973_remove(struct i2c_client *client) +{ + struct max8973_chip *max = i2c_get_clientdata(client); + + if (gpio_is_valid(max->dvs_gpio)) + gpio_free(max->dvs_gpio); + + regulator_unregister(max->rdev); + return 0; +} + +static const struct i2c_device_id max8973_id[] = { + {.name = "max8973",}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, max8973_id); + +static struct i2c_driver max8973_i2c_driver = { + .driver = { + .name = "max8973", + .owner = THIS_MODULE, + }, + .probe = max8973_probe, + .remove = __devexit_p(max8973_remove), + .id_table = max8973_id, +}; + +static int __init max8973_init(void) +{ + return i2c_add_driver(&max8973_i2c_driver); +} +subsys_initcall(max8973_init); + +static void __exit max8973_cleanup(void) +{ + i2c_del_driver(&max8973_i2c_driver); +} +module_exit(max8973_cleanup); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("MAX8973 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c new file mode 100644 index 000000000000..81a03b281fa0 --- /dev/null +++ b/drivers/regulator/rc5t583-regulator.c @@ -0,0 +1,363 @@ +/* + * Regulator driver for RICOH RC5T583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/gpio.h> +#include <linux/mfd/rc5t583.h> + +struct rc5t583_regulator_info { + int deepsleep_id; + + /* Regulator register address.*/ + uint8_t reg_en_reg; + uint8_t en_bit; + uint8_t reg_disc_reg; + uint8_t disc_bit; + uint8_t vout_reg; + uint8_t vout_mask; + uint8_t deepsleep_reg; + + /* Chip constraints on regulator behavior */ + int min_uV; + int max_uV; + int step_uV; + + /* Regulator specific turn-on delay and voltage settling time*/ + int enable_uv_per_us; + int change_uv_per_us; + + /* Used by regulator core */ + struct regulator_desc desc; +}; + +struct rc5t583_regulator { + struct rc5t583_regulator_info *reg_info; + + /* Devices */ + struct device *dev; + struct rc5t583 *mfd; + struct regulator_dev *rdev; +}; + +static int rc5t583_reg_is_enabled(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + uint8_t control; + int ret; + + ret = rc5t583_read(reg->mfd->dev, ri->reg_en_reg, &control); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in reading the control register 0x%02x\n", + ri->reg_en_reg); + return ret; + } + return !!(control & BIT(ri->en_bit)); +} + +static int rc5t583_reg_enable(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + + ret = rc5t583_set_bits(reg->mfd->dev, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in setting bit of STATE register 0x%02x\n", + ri->reg_en_reg); + return ret; + } + return ret; +} + +static int rc5t583_reg_disable(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + + ret = rc5t583_clear_bits(reg->mfd->dev, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) + dev_err(&rdev->dev, + "Error in clearing bit of STATE register 0x%02x\n", + ri->reg_en_reg); + + return ret; +} + +static int rc5t583_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + return ri->min_uV + (ri->step_uV * selector); +} + +static int rc5t583_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + if (selector >= rdev->desc->n_voltages) { + dev_err(&rdev->dev, "Invalid selector 0x%02x\n", selector); + return -EINVAL; + } + + ret = rc5t583_update(reg->mfd->dev, ri->vout_reg, + selector, ri->vout_mask); + if (ret < 0) + dev_err(&rdev->dev, + "Error in update voltage register 0x%02x\n", ri->vout_reg); + return ret; +} + +static int rc5t583_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + uint8_t vsel; + int ret; + ret = rc5t583_read(reg->mfd->dev, ri->vout_reg, &vsel); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in reading voltage register 0x%02x\n", ri->vout_reg); + return ret; + } + return vsel & ri->vout_mask; +} + +static int rc5t583_regulator_enable_time(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int vsel = rc5t583_get_voltage_sel(rdev); + int curr_uV = rc5t583_list_voltage(rdev, vsel); + return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us); +} + +static int rc5t583_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int old_uV, new_uV; + old_uV = rc5t583_list_voltage(rdev, old_selector); + + if (old_uV < 0) + return old_uV; + + new_uV = rc5t583_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), + reg->reg_info->change_uv_per_us); +} + + +static struct regulator_ops rc5t583_ops = { + .is_enabled = rc5t583_reg_is_enabled, + .enable = rc5t583_reg_enable, + .disable = rc5t583_reg_disable, + .enable_time = rc5t583_regulator_enable_time, + .get_voltage_sel = rc5t583_get_voltage_sel, + .set_voltage_sel = rc5t583_set_voltage_sel, + .list_voltage = rc5t583_list_voltage, + .set_voltage_time_sel = rc5t583_set_voltage_time_sel, +}; + +#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, _vout_reg, \ + _vout_mask, _ds_reg, _min_mv, _max_mv, _step_uV, _enable_mv) \ +{ \ + .reg_en_reg = RC5T583_REG_##_en_reg, \ + .en_bit = _en_bit, \ + .reg_disc_reg = RC5T583_REG_##_disc_reg, \ + .disc_bit = _disc_bit, \ + .vout_reg = RC5T583_REG_##_vout_reg, \ + .vout_mask = _vout_mask, \ + .deepsleep_reg = RC5T583_REG_##_ds_reg, \ + .min_uV = _min_mv * 1000, \ + .max_uV = _max_mv * 1000, \ + .step_uV = _step_uV, \ + .enable_uv_per_us = _enable_mv * 1000, \ + .change_uv_per_us = 40 * 1000, \ + .deepsleep_id = RC5T583_DS_##_id, \ + .desc = { \ + .name = "rc5t583-regulator-"#_id, \ + .id = RC5T583_REGULATOR_##_id, \ + .n_voltages = (_max_mv - _min_mv) * 1000 / _step_uV + 1, \ + .ops = &rc5t583_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = { + RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, DC0DAC, 0x7F, DC0DAC_DS, + 700, 1500, 12500, 4), + RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, DC1DAC, 0x7F, DC1DAC_DS, + 700, 1500, 12500, 14), + RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, DC2DAC, 0x7F, DC2DAC_DS, + 900, 2400, 12500, 14), + RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, DC3DAC, 0x7F, DC3DAC_DS, + 900, 2400, 12500, 14), + RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, LDO0DAC, 0x7F, LDO0DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, LDO1DAC, 0x7F, LDO1DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, LDO2DAC, 0x7F, LDO2DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, LDO3DAC, 0x7F, LDO3DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, LDO4DAC, 0x3F, LDO4DAC_DS, + 750, 1500, 12500, 133), + RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, LDO5DAC, 0x7F, LDO5DAC_DS, + 900, 3400, 25000, 267), + RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, LDO6DAC, 0x7F, LDO6DAC_DS, + 900, 3400, 25000, 133), + RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, LDO7DAC, 0x7F, LDO7DAC_DS, + 900, 3400, 25000, 233), + RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, LDO8DAC, 0x7F, LDO8DAC_DS, + 900, 3400, 25000, 233), + RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, LDO9DAC, 0x7F, LDO9DAC_DS, + 900, 3400, 25000, 133), +}; + +static int __devinit rc5t583_regulator_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct regulator_init_data *reg_data; + struct rc5t583_regulator *reg = NULL; + struct rc5t583_regulator *regs; + struct regulator_dev *rdev; + struct rc5t583_regulator_info *ri; + int ret; + int id; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting...\n"); + return -ENODEV; + } + + regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX * + sizeof(struct rc5t583_regulator), GFP_KERNEL); + if (!regs) { + dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + return -ENOMEM; + } + + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { + reg_data = pdata->reg_init_data[id]; + + /* No need to register if there is no regulator data */ + if (!reg_data) + continue; + + reg = ®s[id]; + ri = &rc5t583_reg_info[id]; + reg->reg_info = ri; + reg->mfd = rc5t583; + reg->dev = &pdev->dev; + + if (ri->deepsleep_id == RC5T583_DS_NONE) + goto skip_ext_pwr_config; + + ret = rc5t583_ext_power_req_config(rc5t583->dev, + ri->deepsleep_id, + pdata->regulator_ext_pwr_control[id], + pdata->regulator_deepsleep_slot[id]); + /* + * Configuring external control is not a major issue, + * just give warning. + */ + if (ret < 0) + dev_warn(&pdev->dev, + "Failed to configure ext control %d\n", id); + +skip_ext_pwr_config: + rdev = regulator_register(&ri->desc, &pdev->dev, reg_data, reg); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + ret = PTR_ERR(rdev); + goto clean_exit; + } + reg->rdev = rdev; + } + platform_set_drvdata(pdev, regs); + return 0; + +clean_exit: + while (--id >= 0) + regulator_unregister(regs[id].rdev); + + return ret; +} + +static int __devexit rc5t583_regulator_remove(struct platform_device *pdev) +{ + struct rc5t583_regulator *regs = platform_get_drvdata(pdev); + int id; + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) + regulator_unregister(regs[id].rdev); + return 0; +} + +static struct platform_driver rc5t583_regulator_driver = { + .driver = { + .name = "rc5t583-regulator", + .owner = THIS_MODULE, + }, + .probe = rc5t583_regulator_probe, + .remove = __devexit_p(rc5t583_regulator_remove), +}; + +static int __init rc5t583_regulator_init(void) +{ + return platform_driver_register(&rc5t583_regulator_driver); +} +subsys_initcall(rc5t583_regulator_init); + +static void __exit rc5t583_regulator_exit(void) +{ + platform_driver_unregister(&rc5t583_regulator_driver); +} +module_exit(rc5t583_regulator_exit); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("RC5T583 regulator driver"); +MODULE_ALIAS("platform:rc5t583-regulator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index 7eaf08275376..7db148202436 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -1,7 +1,7 @@ /* * tps62360.c -- TI tps62360 * - * Driver for processor core supply tps62360 and tps62361B + * Driver for processor core supply tps62360, tps62361B, tps62362 and tps62363. * * Copyright (c) 2012, NVIDIA Corporation. * @@ -46,20 +46,20 @@ #define REG_RAMPCTRL 6 #define REG_CHIPID 8 -enum chips {TPS62360, TPS62361}; +#define FORCE_PWM_ENABLE BIT(7) -#define TPS62360_BASE_VOLTAGE 770 +enum chips {TPS62360, TPS62361, TPS62362, TPS62363}; + +#define TPS62360_BASE_VOLTAGE 770000 #define TPS62360_N_VOLTAGES 64 -#define TPS62361_BASE_VOLTAGE 500 +#define TPS62361_BASE_VOLTAGE 500000 #define TPS62361_N_VOLTAGES 128 /* tps 62360 chip information */ struct tps62360_chip { - const char *name; struct device *dev; struct regulator_desc desc; - struct i2c_client *client; struct regulator_dev *rdev; struct regmap *regmap; int chip_id; @@ -68,12 +68,12 @@ struct tps62360_chip { int voltage_base; u8 voltage_reg_mask; bool en_internal_pulldn; - bool en_force_pwm; bool en_discharge; bool valid_gpios; int lru_index[4]; int curr_vset_vsel[4]; int curr_vset_id; + int change_uv_per_us; }; /* @@ -99,6 +99,7 @@ static bool find_voltage_set_register(struct tps62360_chip *tps, bool found = false; int new_vset_reg = tps->lru_index[3]; int found_index = 3; + for (i = 0; i < 4; ++i) { if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) { new_vset_reg = tps->lru_index[i]; @@ -117,7 +118,7 @@ update_lru_index: return found; } -static int tps62360_dcdc_get_voltage(struct regulator_dev *dev) +static int tps62360_dcdc_get_voltage_sel(struct regulator_dev *dev) { struct tps62360_chip *tps = rdev_get_drvdata(dev); int vsel; @@ -126,12 +127,12 @@ static int tps62360_dcdc_get_voltage(struct regulator_dev *dev) ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); if (ret < 0) { - dev_err(tps->dev, "%s: Error in reading register %d\n", - __func__, REG_VSET0 + tps->curr_vset_id); + dev_err(tps->dev, "%s(): register %d read failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); return ret; } vsel = (int)data & tps->voltage_reg_mask; - return (tps->voltage_base + vsel * 10) * 1000; + return vsel; } static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, @@ -143,17 +144,13 @@ static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, bool found = false; int new_vset_id = tps->curr_vset_id; - if (max_uV < min_uV) - return -EINVAL; - - if (min_uV > - ((tps->voltage_base + (tps->desc.n_voltages - 1) * 10) * 1000)) + if ((max_uV < min_uV) || (max_uV < tps->voltage_base)) return -EINVAL; - if (max_uV < tps->voltage_base * 1000) + if (min_uV > (tps->voltage_base + (tps->desc.n_voltages - 1) * 10000)) return -EINVAL; - vsel = DIV_ROUND_UP(min_uV - (tps->voltage_base * 1000), 10000); + vsel = DIV_ROUND_UP(min_uV - tps->voltage_base, 10000); if (selector) *selector = (vsel & tps->voltage_reg_mask); @@ -168,8 +165,9 @@ static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id, tps->voltage_reg_mask, vsel); if (ret < 0) { - dev_err(tps->dev, "%s: Error in updating register %d\n", - __func__, REG_VSET0 + new_vset_id); + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + new_vset_id, ret); return ret; } tps->curr_vset_id = new_vset_id; @@ -178,8 +176,7 @@ static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, /* Select proper VSET register vio gpios */ if (tps->valid_gpios) { - gpio_set_value_cansleep(tps->vsel0_gpio, - new_vset_id & 0x1); + gpio_set_value_cansleep(tps->vsel0_gpio, new_vset_id & 0x1); gpio_set_value_cansleep(tps->vsel1_gpio, (new_vset_id >> 1) & 0x1); } @@ -191,82 +188,146 @@ static int tps62360_dcdc_list_voltage(struct regulator_dev *dev, { struct tps62360_chip *tps = rdev_get_drvdata(dev); - if ((selector < 0) || (selector >= tps->desc.n_voltages)) + if (selector >= tps->desc.n_voltages) return -EINVAL; - return (tps->voltage_base + selector * 10) * 1000; + + return tps->voltage_base + selector * 10000; } -static struct regulator_ops tps62360_dcdc_ops = { - .get_voltage = tps62360_dcdc_get_voltage, - .set_voltage = tps62360_dcdc_set_voltage, - .list_voltage = tps62360_dcdc_list_voltage, -}; +static int tps62360_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct tps62360_chip *tps = rdev_get_drvdata(rdev); + int old_uV, new_uV; -static int tps62360_init_force_pwm(struct tps62360_chip *tps, - struct tps62360_regulator_platform_data *pdata, - int vset_id) + old_uV = tps62360_dcdc_list_voltage(rdev, old_selector); + if (old_uV < 0) + return old_uV; + + new_uV = tps62360_dcdc_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), tps->change_uv_per_us); +} + +static int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode) { + struct tps62360_chip *tps = rdev_get_drvdata(rdev); + int i; + int val; + int ret; + + /* Enable force PWM mode in FAST mode only. */ + switch (mode) { + case REGULATOR_MODE_FAST: + val = FORCE_PWM_ENABLE; + break; + + case REGULATOR_MODE_NORMAL: + val = 0; + break; + + default: + return -EINVAL; + } + + if (!tps->valid_gpios) { + ret = regmap_update_bits(tps->regmap, + REG_VSET0 + tps->curr_vset_id, FORCE_PWM_ENABLE, val); + if (ret < 0) + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); + return ret; + } + + /* If gpios are valid then all register set need to be control */ + for (i = 0; i < 4; ++i) { + ret = regmap_update_bits(tps->regmap, + REG_VSET0 + i, FORCE_PWM_ENABLE, val); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + i, ret); + return ret; + } + } + return ret; +} + +static unsigned int tps62360_get_mode(struct regulator_dev *rdev) +{ + struct tps62360_chip *tps = rdev_get_drvdata(rdev); unsigned int data; int ret; - ret = regmap_read(tps->regmap, REG_VSET0 + vset_id, &data); + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); if (ret < 0) { - dev_err(tps->dev, "%s() fails in writing reg %d\n", - __func__, REG_VSET0 + vset_id); + dev_err(tps->dev, "%s(): register %d read failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); return ret; } - tps->curr_vset_vsel[vset_id] = data & tps->voltage_reg_mask; - if (pdata->en_force_pwm) - data |= BIT(7); - else - data &= ~BIT(7); - ret = regmap_write(tps->regmap, REG_VSET0 + vset_id, data); - if (ret < 0) - dev_err(tps->dev, "%s() fails in writing reg %d\n", - __func__, REG_VSET0 + vset_id); - return ret; + return (data & FORCE_PWM_ENABLE) ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; } -static int tps62360_init_dcdc(struct tps62360_chip *tps, +static struct regulator_ops tps62360_dcdc_ops = { + .get_voltage_sel = tps62360_dcdc_get_voltage_sel, + .set_voltage = tps62360_dcdc_set_voltage, + .list_voltage = tps62360_dcdc_list_voltage, + .set_voltage_time_sel = tps62360_set_voltage_time_sel, + .set_mode = tps62360_set_mode, + .get_mode = tps62360_get_mode, +}; + +static int __devinit tps62360_init_dcdc(struct tps62360_chip *tps, struct tps62360_regulator_platform_data *pdata) { int ret; - int i; + unsigned int ramp_ctrl; - /* Initailize internal pull up/down control */ + /* Initialize internal pull up/down control */ if (tps->en_internal_pulldn) ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0); else ret = regmap_write(tps->regmap, REG_CONTROL, 0x0); if (ret < 0) { - dev_err(tps->dev, "%s() fails in writing reg %d\n", - __func__, REG_CONTROL); + dev_err(tps->dev, + "%s(): register %d write failed with err %d\n", + __func__, REG_CONTROL, ret); return ret; } - /* Initailize force PWM mode */ - if (tps->valid_gpios) { - for (i = 0; i < 4; ++i) { - ret = tps62360_init_force_pwm(tps, pdata, i); - if (ret < 0) - return ret; - } - } else { - ret = tps62360_init_force_pwm(tps, pdata, tps->curr_vset_id); - if (ret < 0) - return ret; - } - /* Reset output discharge path to reduce power consumption */ ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0); - if (ret < 0) - dev_err(tps->dev, "%s() fails in updating reg %d\n", - __func__, REG_RAMPCTRL); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_RAMPCTRL, ret); + return ret; + } + + /* Get ramp value from ramp control register */ + ret = regmap_read(tps->regmap, REG_RAMPCTRL, &ramp_ctrl); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d read failed with err %d\n", + __func__, REG_RAMPCTRL, ret); + return ret; + } + ramp_ctrl = (ramp_ctrl >> 4) & 0x7; + + /* ramp mV/us = 32/(2^ramp_ctrl) */ + tps->change_uv_per_us = DIV_ROUND_UP(32000, BIT(ramp_ctrl)); return ret; } static const struct regmap_config tps62360_regmap_config = { - .reg_bits = 8, - .val_bits = 8, + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_CHIPID, + .cache_type = REGCACHE_RBTREE, }; static int __devinit tps62360_probe(struct i2c_client *client, @@ -280,42 +341,52 @@ static int __devinit tps62360_probe(struct i2c_client *client, pdata = client->dev.platform_data; if (!pdata) { - dev_err(&client->dev, "%s() Err: Platform data not found\n", + dev_err(&client->dev, "%s(): Platform data not found\n", __func__); return -EIO; } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) { - dev_err(&client->dev, "%s() Err: Memory allocation fails\n", + dev_err(&client->dev, "%s(): Memory allocation failed\n", __func__); return -ENOMEM; } - tps->en_force_pwm = pdata->en_force_pwm; tps->en_discharge = pdata->en_discharge; tps->en_internal_pulldn = pdata->en_internal_pulldn; tps->vsel0_gpio = pdata->vsel0_gpio; tps->vsel1_gpio = pdata->vsel1_gpio; - tps->client = client; tps->dev = &client->dev; - tps->name = id->name; - tps->voltage_base = (id->driver_data == TPS62360) ? - TPS62360_BASE_VOLTAGE : TPS62361_BASE_VOLTAGE; - tps->voltage_reg_mask = (id->driver_data == TPS62360) ? 0x3F : 0x7F; + + switch (id->driver_data) { + case TPS62360: + case TPS62362: + tps->voltage_base = TPS62360_BASE_VOLTAGE; + tps->voltage_reg_mask = 0x3F; + tps->desc.n_voltages = TPS62360_N_VOLTAGES; + break; + case TPS62361: + case TPS62363: + tps->voltage_base = TPS62361_BASE_VOLTAGE; + tps->voltage_reg_mask = 0x7F; + tps->desc.n_voltages = TPS62361_N_VOLTAGES; + break; + default: + return -ENODEV; + } tps->desc.name = id->name; tps->desc.id = 0; - tps->desc.n_voltages = (id->driver_data == TPS62360) ? - TPS62360_N_VOLTAGES : TPS62361_N_VOLTAGES; tps->desc.ops = &tps62360_dcdc_ops; tps->desc.type = REGULATOR_VOLTAGE; tps->desc.owner = THIS_MODULE; - tps->regmap = regmap_init_i2c(client, &tps62360_regmap_config); + tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config); if (IS_ERR(tps->regmap)) { ret = PTR_ERR(tps->regmap); - dev_err(&client->dev, "%s() Err: Failed to allocate register" - "map: %d\n", __func__, ret); + dev_err(&client->dev, + "%s(): regmap allocation failed with err %d\n", + __func__, ret); return ret; } i2c_set_clientdata(client, tps); @@ -326,35 +397,26 @@ static int __devinit tps62360_probe(struct i2c_client *client, tps->valid_gpios = false; if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) { - ret = gpio_request(tps->vsel0_gpio, "tps62360-vsel0"); + int gpio_flags; + gpio_flags = (pdata->vsel0_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = gpio_request_one(tps->vsel0_gpio, + gpio_flags, "tps62360-vsel0"); if (ret) { dev_err(&client->dev, - "Err: Could not obtain vsel0 GPIO %d: %d\n", - tps->vsel0_gpio, ret); - goto err_gpio0; - } - ret = gpio_direction_output(tps->vsel0_gpio, - pdata->vsel0_def_state); - if (ret) { - dev_err(&client->dev, "Err: Could not set direction of" - "vsel0 GPIO %d: %d\n", tps->vsel0_gpio, ret); - gpio_free(tps->vsel0_gpio); + "%s(): Could not obtain vsel0 GPIO %d: %d\n", + __func__, tps->vsel0_gpio, ret); goto err_gpio0; } - ret = gpio_request(tps->vsel1_gpio, "tps62360-vsel1"); + gpio_flags = (pdata->vsel1_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = gpio_request_one(tps->vsel1_gpio, + gpio_flags, "tps62360-vsel1"); if (ret) { dev_err(&client->dev, - "Err: Could not obtain vsel1 GPIO %d: %d\n", - tps->vsel1_gpio, ret); - goto err_gpio1; - } - ret = gpio_direction_output(tps->vsel1_gpio, - pdata->vsel1_def_state); - if (ret) { - dev_err(&client->dev, "Err: Could not set direction of" - "vsel1 GPIO %d: %d\n", tps->vsel1_gpio, ret); - gpio_free(tps->vsel1_gpio); + "%s(): Could not obtain vsel1 GPIO %d: %d\n", + __func__, tps->vsel1_gpio, ret); goto err_gpio1; } tps->valid_gpios = true; @@ -371,7 +433,7 @@ static int __devinit tps62360_probe(struct i2c_client *client, ret = tps62360_init_dcdc(tps, pdata); if (ret < 0) { - dev_err(tps->dev, "%s() Err: Init fails with = %d\n", + dev_err(tps->dev, "%s(): Init failed with err = %d\n", __func__, ret); goto err_init; } @@ -380,8 +442,9 @@ static int __devinit tps62360_probe(struct i2c_client *client, rdev = regulator_register(&tps->desc, &client->dev, &pdata->reg_init_data, tps); if (IS_ERR(rdev)) { - dev_err(tps->dev, "%s() Err: Failed to register %s\n", - __func__, id->name); + dev_err(tps->dev, + "%s(): regulator register failed with err %s\n", + __func__, id->name); ret = PTR_ERR(rdev); goto err_init; } @@ -396,7 +459,6 @@ err_gpio1: if (gpio_is_valid(tps->vsel0_gpio)) gpio_free(tps->vsel0_gpio); err_gpio0: - regmap_exit(tps->regmap); return ret; } @@ -417,7 +479,6 @@ static int __devexit tps62360_remove(struct i2c_client *client) gpio_free(tps->vsel0_gpio); regulator_unregister(tps->rdev); - regmap_exit(tps->regmap); return 0; } @@ -432,13 +493,16 @@ static void tps62360_shutdown(struct i2c_client *client) /* Configure the output discharge path */ st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2)); if (st < 0) - dev_err(tps->dev, "%s() fails in updating reg %d\n", - __func__, REG_RAMPCTRL); + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_RAMPCTRL, st); } static const struct i2c_device_id tps62360_id[] = { {.name = "tps62360", .driver_data = TPS62360}, {.name = "tps62361", .driver_data = TPS62361}, + {.name = "tps62362", .driver_data = TPS62362}, + {.name = "tps62363", .driver_data = TPS62363}, {}, }; @@ -468,5 +532,5 @@ static void __exit tps62360_cleanup(void) module_exit(tps62360_cleanup); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_DESCRIPTION("TPS62360 voltage regulator driver"); +MODULE_DESCRIPTION("TPS6236x voltage regulator driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6238x0-regulator.c b/drivers/regulator/tps6238x0-regulator.c new file mode 100644 index 000000000000..611b9425a263 --- /dev/null +++ b/drivers/regulator/tps6238x0-regulator.c @@ -0,0 +1,470 @@ +/* + * tps6238x0-regulator.c -- TI tps623850/tps623860/tps623870 + * + * Driver for processor core supply tps623850, tps623860 and tps623870 + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/tps6238x0-regulator.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +/* Register definitions */ +#define REG_VSET0 0 +#define REG_VSET1 1 +#define REG_MODE0 2 +#define REG_MODE1 3 +#define REG_CONTROL 4 +#define REG_EXCEPTION 5 +#define REG_RAMPCTRL 6 +#define REG_IOUT 7 +#define REG_CHIPID 8 + +#define TPS6238X0_BASE_VOLTAGE 500000 +#define TPS6238X0_N_VOLTAGES 128 +#define TPS6238X0_MAX_VSET 2 +#define TPS6238X0_VOUT_MASK 0x7F + +/* tps 6238x0 chip information */ +struct tps6238x0_chip { + const char *name; + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regmap *regmap; + int vsel_gpio; + int change_uv_per_us; + bool en_internal_pulldn; + bool valid_gpios; + int lru_index[TPS6238X0_MAX_VSET]; + int curr_vset_vsel[TPS6238X0_MAX_VSET]; + int curr_vset_id; +}; + +/* + * find_voltage_set_register: Find new voltage configuration register + * (VSET) id. + * The finding of the new VSET register will be based on the LRU mechanism. + * Each VSET register will have different voltage configured . This + * Function will look if any of the VSET register have requested voltage set + * or not. + * - If it is already there then it will make that register as most + * recently used and return as found so that caller need not to set + * the VSET register but need to set the proper gpios to select this + * VSET register. + * - If requested voltage is not found then it will use the least + * recently mechanism to get new VSET register for new configuration + * and will return not_found so that caller need to set new VSET + * register and then gpios (both). + */ +static bool find_voltage_set_register(struct tps6238x0_chip *tps, + int req_vsel, int *vset_reg_id) +{ + int i; + bool found = false; + int new_vset_reg = tps->lru_index[1]; + int found_index = 1; + for (i = 0; i < TPS6238X0_MAX_VSET; ++i) { + if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) { + new_vset_reg = tps->lru_index[i]; + found_index = i; + found = true; + goto update_lru_index; + } + } + +update_lru_index: + for (i = found_index; i > 0; i--) + tps->lru_index[i] = tps->lru_index[i - 1]; + + tps->lru_index[0] = new_vset_reg; + *vset_reg_id = new_vset_reg; + return found; +} + +static int tps6238x0_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(dev); + unsigned int data; + int ret; + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); + if (ret < 0) { + dev_err(tps->dev, "%s: Error in reading register %d\n", + __func__, REG_VSET0 + tps->curr_vset_id); + return ret; + } + return data & TPS6238X0_VOUT_MASK; +} + +static int tps6238x0_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(dev); + int vsel; + int ret; + bool found = false; + int new_vset_id = tps->curr_vset_id; + + if (min_uV > + ((TPS6238X0_BASE_VOLTAGE + (TPS6238X0_N_VOLTAGES - 1) * 10000))) + return -EINVAL; + + if ((max_uV < min_uV) || (max_uV < TPS6238X0_BASE_VOLTAGE)) + return -EINVAL; + + vsel = DIV_ROUND_UP(min_uV - TPS6238X0_BASE_VOLTAGE, 10000); + if (selector) + *selector = (vsel & TPS6238X0_VOUT_MASK); + + /* + * If gpios are available to select the VSET register then least + * recently used register for new configuration. + */ + if (tps->valid_gpios) + found = find_voltage_set_register(tps, vsel, &new_vset_id); + + if (!found) { + ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id, + TPS6238X0_VOUT_MASK, vsel); + if (ret < 0) { + dev_err(tps->dev, "%s: Error in updating register %d\n", + __func__, REG_VSET0 + new_vset_id); + return ret; + } + tps->curr_vset_id = new_vset_id; + tps->curr_vset_vsel[new_vset_id] = vsel; + } + + /* Select proper VSET register vio gpios */ + if (tps->valid_gpios) + gpio_set_value_cansleep(tps->vsel_gpio, new_vset_id & 0x1); + return 0; +} + +static int tps6238x0_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(dev); + + if ((selector < 0) || (selector >= tps->desc.n_voltages)) + return -EINVAL; + + return TPS6238X0_BASE_VOLTAGE + selector * 10000; +} + +static int tps6238x0_regulator_enable_time(struct regulator_dev *rdev) +{ + return 300; +} + +static int tps6238x0_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(rdev); + int old_uV, new_uV; + old_uV = tps6238x0_list_voltage(rdev, old_selector); + + if (old_uV < 0) + return old_uV; + + new_uV = tps6238x0_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), + tps->change_uv_per_us); +} + +static int tps6238x0_is_enable(struct regulator_dev *rdev) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); + if (ret < 0) { + dev_err(tps->dev, "%s: Error in reading register %d\n", + __func__, REG_VSET0 + tps->curr_vset_id); + return ret; + } + return !!(data & BIT(7)); +} + +static int tps6238x0_enable(struct regulator_dev *rdev) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(rdev); + int ret; + int i; + + /* Enable required VSET configuration */ + for (i = 0; i < TPS6238X0_MAX_VSET; ++i) { + unsigned int en = 0; + if (tps->valid_gpios || (i == tps->curr_vset_id)) + en = BIT(7); + + ret = regmap_update_bits(tps->regmap, REG_VSET0 + i, + BIT(7), en); + if (ret < 0) { + dev_err(tps->dev, "%s() fails in updating reg %d\n", + __func__, REG_VSET0 + i); + return ret; + } + } + return ret; +} + +static int tps6238x0_disable(struct regulator_dev *rdev) +{ + struct tps6238x0_chip *tps = rdev_get_drvdata(rdev); + int ret; + int i; + + /* Disable required VSET configuration */ + for (i = 0; i < TPS6238X0_MAX_VSET; ++i) { + ret = regmap_update_bits(tps->regmap, REG_VSET0 + i, + BIT(7), 0); + if (ret < 0) { + dev_err(tps->dev, "%s() fails in updating reg %d\n", + __func__, REG_VSET0 + i); + return ret; + } + } + return ret; +} + +static struct regulator_ops tps6238x0_ops = { + .is_enabled = tps6238x0_is_enable, + .enable = tps6238x0_enable, + .disable = tps6238x0_disable, + .enable_time = tps6238x0_regulator_enable_time, + .set_voltage_time_sel = tps6238x0_set_voltage_time_sel, + .get_voltage_sel = tps6238x0_get_voltage_sel, + .set_voltage = tps6238x0_set_voltage, + .list_voltage = tps6238x0_list_voltage, +}; + +static int __devinit tps6238x0_configure(struct tps6238x0_chip *tps, + struct tps6238x0_regulator_platform_data *pdata) +{ + int ret; + int i; + + /* Initailize internal pull up/down control */ + if (tps->en_internal_pulldn) + ret = regmap_write(tps->regmap, REG_CONTROL, 0xC0); + else + ret = regmap_write(tps->regmap, REG_CONTROL, 0x0); + if (ret < 0) { + dev_err(tps->dev, "%s() fails in writing reg %d\n", + __func__, REG_CONTROL); + return ret; + } + + /* Enable required VSET configuration */ + for (i = 0; i < TPS6238X0_MAX_VSET; ++i) { + unsigned int en = 0; + if (tps->valid_gpios || (i == tps->curr_vset_id)) + en = BIT(7); + + ret = regmap_update_bits(tps->regmap, REG_VSET0 + i, + BIT(7), en); + if (ret < 0) { + dev_err(tps->dev, "%s() fails in updating reg %d\n", + __func__, REG_VSET0 + i); + return ret; + } + } + + /* Enable output discharge path to have faster discharge */ + ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2)); + if (ret < 0) + dev_err(tps->dev, "%s() fails in updating reg %d\n", + __func__, REG_RAMPCTRL); + tps->change_uv_per_us = 312; + return ret; +} + +static const struct regmap_config tps6238x0_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_CHIPID, + .num_reg_defaults_raw = REG_CHIPID + 1, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit tps6238x0_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps6238x0_regulator_platform_data *pdata; + struct regulator_dev *rdev; + struct tps6238x0_chip *tps; + int ret; + int i; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "%s() Err: Platform data not found\n", + __func__); + return -EIO; + } + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) { + dev_err(&client->dev, "%s() Err: Memory allocation fails\n", + __func__); + return -ENOMEM; + } + + tps->en_internal_pulldn = pdata->en_internal_pulldn; + tps->vsel_gpio = pdata->vsel_gpio; + tps->dev = &client->dev; + + tps->desc.name = id->name; + tps->desc.id = 0; + tps->desc.n_voltages = TPS6238X0_N_VOLTAGES; + tps->desc.ops = &tps6238x0_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + tps->regmap = devm_regmap_init_i2c(client, &tps6238x0_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(&client->dev, "%s() Err: Failed to allocate register" + "map: %d\n", __func__, ret); + return ret; + } + i2c_set_clientdata(client, tps); + + tps->curr_vset_id = (pdata->vsel_def_state & 1); + tps->lru_index[0] = tps->curr_vset_id; + tps->valid_gpios = false; + + if (gpio_is_valid(tps->vsel_gpio)) { + int gpio_flag; + gpio_flag = (tps->curr_vset_id) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = gpio_request_one(tps->vsel_gpio, + gpio_flag, "tps6238x0-vsel0"); + if (ret) { + dev_err(&client->dev, + "Err: Could not obtain vsel GPIO %d: %d\n", + tps->vsel_gpio, ret); + return ret; + } + tps->valid_gpios = true; + + /* + * Initialize the lru index with vset_reg id + * The index 0 will be most recently used and + * set with the tps->curr_vset_id */ + for (i = 0; i < TPS6238X0_MAX_VSET; ++i) + tps->lru_index[i] = i; + tps->lru_index[0] = tps->curr_vset_id; + tps->lru_index[tps->curr_vset_id] = 0; + } + + ret = tps6238x0_configure(tps, pdata); + if (ret < 0) { + dev_err(tps->dev, "%s() Err: Init fails with = %d\n", + __func__, ret); + goto err_init; + } + + /* Register the regulators */ + rdev = regulator_register(&tps->desc, &client->dev, + pdata->init_data, tps); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "%s() Err: Failed to register %s\n", + __func__, id->name); + ret = PTR_ERR(rdev); + goto err_init; + } + + tps->rdev = rdev; + return 0; + +err_init: + if (gpio_is_valid(tps->vsel_gpio)) + gpio_free(tps->vsel_gpio); + + return ret; +} + +/** + * tps6238x0_remove - tps62360 driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister TPS driver as an i2c client device driver + */ +static int __devexit tps6238x0_remove(struct i2c_client *client) +{ + struct tps6238x0_chip *tps = i2c_get_clientdata(client); + + if (gpio_is_valid(tps->vsel_gpio)) + gpio_free(tps->vsel_gpio); + + regulator_unregister(tps->rdev); + return 0; +} + +static const struct i2c_device_id tps6238x0_id[] = { + {.name = "tps623850", }, + {.name = "tps623860", }, + {.name = "tps623870", }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tps6238x0_id); + +static struct i2c_driver tps6238x0_i2c_driver = { + .driver = { + .name = "tps6238x0", + .owner = THIS_MODULE, + }, + .probe = tps6238x0_probe, + .remove = __devexit_p(tps6238x0_remove), + .id_table = tps6238x0_id, +}; + +static int __init tps6238x0_init(void) +{ + return i2c_add_driver(&tps6238x0_i2c_driver); +} +subsys_initcall(tps6238x0_init); + +static void __exit tps6238x0_cleanup(void) +{ + i2c_del_driver(&tps6238x0_i2c_driver); +} +module_exit(tps6238x0_cleanup); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("TPS623850/TPS623860/TPS623870 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index fe2f1a8412b7..a2fef3880065 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -331,21 +331,16 @@ struct tps65910_reg { static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg) { - u8 val; + unsigned int val; int err; - err = pmic->mfd->read(pmic->mfd, reg, 1, &val); + err = tps65910_reg_read(pmic->mfd, reg, &val); if (err) return err; return val; } -static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val) -{ - return pmic->mfd->write(pmic->mfd, reg, 1, &val); -} - static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, u8 set_mask, u8 clear_mask) { @@ -362,7 +357,7 @@ static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, data &= ~clear_mask; data |= set_mask; - err = tps65910_write(pmic, reg, data); + err = tps65910_reg_write(pmic->mfd, reg, data); if (err) dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); @@ -371,7 +366,7 @@ out: return err; } -static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) +static int tps65910_reg_read_locked(struct tps65910_reg *pmic, u8 reg) { int data; @@ -385,13 +380,13 @@ static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) return data; } -static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val) +static int tps65910_reg_write_locked(struct tps65910_reg *pmic, u8 reg, u8 val) { int err; mutex_lock(&pmic->mutex); - err = tps65910_write(pmic, reg, val); + err = tps65910_reg_write(pmic->mfd, reg, val); if (err < 0) dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); @@ -476,7 +471,7 @@ static int tps65910_is_enabled(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -493,7 +488,7 @@ static int tps65910_enable(struct regulator_dev *dev) if (reg < 0) return reg; - return tps65910_set_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); + return tps65910_reg_set_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); } static int tps65910_disable(struct regulator_dev *dev) @@ -506,7 +501,7 @@ static int tps65910_disable(struct regulator_dev *dev) if (reg < 0) return reg; - return tps65910_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); + return tps65910_reg_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); } static int tps65910_enable_time(struct regulator_dev *dev) @@ -532,9 +527,9 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) LDO_ST_MODE_BIT); case REGULATOR_MODE_IDLE: value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; - return tps65910_set_bits(mfd, reg, value); + return tps65910_reg_set_bits(mfd, reg, value); case REGULATOR_MODE_STANDBY: - return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT); + return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT); } return -EINVAL; @@ -549,7 +544,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -569,28 +564,28 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) switch (id) { case TPS65910_REG_VDD1: - opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP); - mult = tps65910_reg_read(pmic, TPS65910_VDD1); + opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_OP); + mult = tps65910_reg_read_locked(pmic, TPS65910_VDD1); mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR); + srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_SR); sr = opvsel & VDD1_OP_CMD_MASK; opvsel &= VDD1_OP_SEL_MASK; srvsel &= VDD1_SR_SEL_MASK; vselmax = 75; break; case TPS65910_REG_VDD2: - opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP); - mult = tps65910_reg_read(pmic, TPS65910_VDD2); + opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_OP); + mult = tps65910_reg_read_locked(pmic, TPS65910_VDD2); mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR); + srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_SR); sr = opvsel & VDD2_OP_CMD_MASK; opvsel &= VDD2_OP_SEL_MASK; srvsel &= VDD2_SR_SEL_MASK; vselmax = 75; break; case TPS65911_REG_VDDCTRL: - opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP); - srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR); + opvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_OP); + srvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_SR); sr = opvsel & VDDCTRL_OP_CMD_MASK; opvsel &= VDDCTRL_OP_SEL_MASK; srvsel &= VDDCTRL_SR_SEL_MASK; @@ -630,7 +625,7 @@ static int tps65910_get_voltage(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -669,7 +664,7 @@ static int tps65911_get_voltage(struct regulator_dev *dev) reg = pmic->get_ctrl_reg(id); - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); switch (id) { case TPS65911_REG_LDO1: @@ -728,7 +723,7 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, tps65910_modify_bits(pmic, TPS65910_VDD1, (dcdc_mult << VDD1_VGAIN_SEL_SHIFT), VDD1_VGAIN_SEL_MASK); - tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65910_VDD1_OP, vsel); break; case TPS65910_REG_VDD2: dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; @@ -739,11 +734,11 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, tps65910_modify_bits(pmic, TPS65910_VDD2, (dcdc_mult << VDD2_VGAIN_SEL_SHIFT), VDD1_VGAIN_SEL_MASK); - tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65910_VDD2_OP, vsel); break; case TPS65911_REG_VDDCTRL: vsel = selector + 3; - tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65911_VDDCTRL_OP, vsel); } return 0; @@ -994,10 +989,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN1 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -1007,10 +1002,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN2 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -1022,10 +1017,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, if ((tps65910_chip_id(mfd) == TPS65910) && (id >= TPS65910_REG_VDIG1)) { if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -1037,10 +1032,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* Return if no external control is selected */ if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) { /* Clear all sleep controls */ - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); if (ret < 0) dev_err(mfd->dev, @@ -1059,32 +1054,33 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, (tps65910_chip_id(mfd) == TPS65911))) { int op_reg_add = pmic->get_ctrl_reg(id) + 1; int sr_reg_add = pmic->get_ctrl_reg(id) + 2; - int opvsel = tps65910_reg_read(pmic, op_reg_add); - int srvsel = tps65910_reg_read(pmic, sr_reg_add); + int opvsel = tps65910_reg_read_locked(pmic, op_reg_add); + int srvsel = tps65910_reg_read_locked(pmic, sr_reg_add); if (opvsel & VDD1_OP_CMD_MASK) { u8 reg_val = srvsel & VDD1_OP_SEL_MASK; - ret = tps65910_reg_write(pmic, op_reg_add, reg_val); + ret = tps65910_reg_write_locked(pmic, op_reg_add, + reg_val); if (ret < 0) { dev_err(mfd->dev, "Error in configuring op register\n"); return ret; } } - ret = tps65910_reg_write(pmic, sr_reg_add, 0); + ret = tps65910_reg_write_locked(pmic, sr_reg_add, 0); if (ret < 0) { dev_err(mfd->dev, "Error in settting sr register\n"); return ret; } } - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) { if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); } if (ret < 0) @@ -1117,7 +1113,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); /* Give control of all register to control port */ - tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL, + tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); switch(tps65910_chip_id(tps65910)) { diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c index 411b839f1a74..efc608c936ea 100644 --- a/drivers/regulator/tps80031-regulator.c +++ b/drivers/regulator/tps80031-regulator.c @@ -100,6 +100,7 @@ struct tps80031_regulator { /* chip constraints on regulator behavior */ u16 min_mV; u16 max_mV; + unsigned int tolerance_uv; /* regulator specific turn-on delay */ int delay; @@ -296,6 +297,8 @@ static int __tps80031_dcdc_set_voltage(struct device *parent, int vsel = 0; int ret; + min_uV = min_uV - ri->tolerance_uv; + switch (ri->flags) { case 0: if (min_uV == 0) @@ -801,11 +804,11 @@ static int tps80031_power_req_config(struct device *parent, struct tps80031_regulator *ri, struct tps80031_regulator_platform_data *tps80031_pdata) { - int ret; + int ret = 0; uint8_t reg_val; if (ri->preq_bit < 0) - return 0; + goto skip_pwr_req_config; ret = tps80031_ext_power_req_config(parent, ri->ext_ctrl_flag, ri->preq_bit, ri->state_reg, ri->trans_reg); @@ -821,6 +824,7 @@ static int tps80031_power_req_config(struct device *parent, return ret; } +skip_pwr_req_config: if (tps80031_pdata->ext_ctrl_flag & (PWR_OFF_ON_SLEEP | PWR_ON_ON_SLEEP)) { reg_val = (ri->trans_reg_cache & ~0xC); @@ -1024,6 +1028,7 @@ static int __devinit tps80031_regulator_probe(struct platform_device *pdev) ri->dev = &pdev->dev; if (tps_pdata->delay_us > 0) ri->delay = tps_pdata->delay_us; + ri->tolerance_uv = tps_pdata->tolerance_uv; check_smps_mode_mult(pdev->dev.parent, ri); ri->platform_flags = tps_pdata->flags; diff --git a/drivers/rtc/rtc-max77663.c b/drivers/rtc/rtc-max77663.c index 874a2df86dc1..13d8062e1def 100644 --- a/drivers/rtc/rtc-max77663.c +++ b/drivers/rtc/rtc-max77663.c @@ -2,8 +2,8 @@ * drivers/rtc/rtc-max77663.c * Max77663 RTC driver * - * Copyright 2011 Maxim Integrated Products, Inc. - * Copyright (C) 2011 NVIDIA Corporation + * Copyright 2011-2012, Maxim Integrated Products, Inc. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -97,6 +97,7 @@ struct max77663_rtc { struct mutex io_lock; int irq; u8 irq_mask; + bool shutdown_ongoing; }; static inline struct device *_to_parent(struct max77663_rtc *rtc) @@ -443,6 +444,11 @@ static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) u8 buf[RTC_NR]; int ret; + if (rtc->shutdown_ongoing) { + dev_warn(rtc->dev, "rtc_set_alarm: " + "Device shutdown on-going, skip alarm setting.\n"); + return -ESHUTDOWN; + } dev_dbg(rtc->dev, "rtc_set_alarm: " "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d [%s]\n", alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday, @@ -534,7 +540,7 @@ static int max77663_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "probe: kzalloc() failed\n"); return -ENOMEM; } - + rtc->shutdown_ongoing = false; dev_set_drvdata(&pdev->dev, rtc); rtc->dev = &pdev->dev; mutex_init(&rtc->io_lock); @@ -591,6 +597,17 @@ static int __devexit max77663_rtc_remove(struct platform_device *pdev) return 0; } +static void max77663_rtc_shutdown(struct platform_device *pdev) +{ + struct max77663_rtc *rtc = dev_get_drvdata(&pdev->dev); + u8 buf[RTC_NR] = { 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1 }; + + rtc->shutdown_ongoing = true; + dev_info(rtc->dev, "rtc_shutdown: clean alarm\n"); + max77663_rtc_write(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf), 1); + max77663_rtc_alarm_irq_enable(&pdev->dev, 0); +} + static struct platform_driver max77663_rtc_driver = { .probe = max77663_rtc_probe, .remove = __devexit_p(max77663_rtc_remove), @@ -598,6 +615,7 @@ static struct platform_driver max77663_rtc_driver = { .name = "max77663-rtc", .owner = THIS_MODULE, }, + .shutdown = max77663_rtc_shutdown, }; static int __init max77663_rtc_init(void) diff --git a/drivers/rtc/rtc-tps6591x.c b/drivers/rtc/rtc-tps6591x.c index 6223f1108f06..ebc46b4cf46e 100644 --- a/drivers/rtc/rtc-tps6591x.c +++ b/drivers/rtc/rtc-tps6591x.c @@ -162,7 +162,7 @@ static int tps6591x_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_hour = buff[2]; tm->tm_mday = buff[3]; tm->tm_mon = buff[4] - 1; - tm->tm_year = buff[5]; + tm->tm_year = buff[5] + RTC_YEAR_OFFSET; tm->tm_wday = buff[6]; print_time(dev, tm); return tps6591x_rtc_valid_tm(tm); @@ -250,7 +250,7 @@ static int tps6591x_rtc_set_time(struct device *dev, struct rtc_time *tm) buff[2] = tm->tm_hour; buff[3] = tm->tm_mday; buff[4] = tm->tm_mon + 1; - buff[5] = tm->tm_year; + buff[5] = tm->tm_year % RTC_YEAR_OFFSET; buff[6] = tm->tm_wday; print_time(dev, tm); @@ -345,7 +345,7 @@ static int tps6591x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) buff[2] = alrm->time.tm_hour; buff[3] = alrm->time.tm_mday; buff[4] = alrm->time.tm_mon + 1; - buff[5] = alrm->time.tm_year; + buff[5] = alrm->time.tm_year % RTC_YEAR_OFFSET; convert_decimal_to_bcd(buff, sizeof(buff)); err = tps6591x_write_regs(dev, RTC_ALARM, sizeof(buff), buff); if (err) @@ -369,7 +369,7 @@ static int tps6591x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->time.tm_hour = buff[2]; alrm->time.tm_mday = buff[3]; alrm->time.tm_mon = buff[4] - 1; - alrm->time.tm_year = buff[5]; + alrm->time.tm_year = buff[5] + RTC_YEAR_OFFSET; dev_info(dev->parent, "\n getting alarm time::\n"); print_time(dev, &alrm->time); diff --git a/drivers/rtc/rtc-tps80031.c b/drivers/rtc/rtc-tps80031.c index 70d6734a5708..b2b9d04171c5 100644 --- a/drivers/rtc/rtc-tps80031.c +++ b/drivers/rtc/rtc-tps80031.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/rtc.h> #include <linux/slab.h> +#include <linux/gpio.h> #define RTC_CTRL 0x10 #define RTC_STATUS 0x11 @@ -65,8 +66,25 @@ struct tps80031_rtc { int irq; struct rtc_device *rtc; u8 alarm_irq_enabled; + int msecure_gpio; }; +static inline void tps80031_enable_rtc_write(struct device *dev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->msecure_gpio >= 0) + gpio_set_value(rtc->msecure_gpio, 1); +} + +static inline void tps80031_disable_rtc_write(struct device *dev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->msecure_gpio >= 0) + gpio_set_value(rtc->msecure_gpio, 0); +} + static int tps80031_read_regs(struct device *dev, int reg, int len, uint8_t *val) { @@ -93,13 +111,16 @@ static int tps80031_write_regs(struct device *dev, int reg, int len, uint8_t *val) { int ret; + + tps80031_enable_rtc_write(dev); ret = tps80031_writes(dev->parent, 1, reg, len, val); if (ret < 0) { + tps80031_disable_rtc_write(dev); dev_err(dev->parent, "failed writing reg: %d\n", reg); WARN_ON(1); return ret; } - + tps80031_disable_rtc_write(dev); return 0; } @@ -150,18 +171,24 @@ static int tps80031_rtc_read_time(struct device *dev, struct rtc_time *tm) static int tps80031_rtc_stop(struct device *dev) { int err; + + tps80031_enable_rtc_write(dev); err = tps80031_clr_bits(dev->parent, 1, RTC_CTRL, STOP_RTC); if (err < 0) dev_err(dev->parent, "failed to stop RTC. err: %d\n", err); + tps80031_disable_rtc_write(dev); return err; } static int tps80031_rtc_start(struct device *dev) { int err; + + tps80031_enable_rtc_write(dev); err = tps80031_set_bits(dev->parent, 1, RTC_CTRL, STOP_RTC); if (err < 0) dev_err(dev->parent, "failed to start RTC. err: %d\n", err); + tps80031_disable_rtc_write(dev); return err; } @@ -262,7 +289,9 @@ static int tps80031_rtc_alarm_irq_enable(struct device *dev, if (rtc->alarm_irq_enabled) return 0; + tps80031_enable_rtc_write(dev); err = tps80031_set_bits(p, 1, RTC_INT, ENABLE_ALARM_INT); + tps80031_disable_rtc_write(dev); if (err < 0) { dev_err(p, "failed to set ALRM int. err: %d\n", err); return err; @@ -271,7 +300,9 @@ static int tps80031_rtc_alarm_irq_enable(struct device *dev, } else { if(!rtc->alarm_irq_enabled) return 0; + tps80031_enable_rtc_write(dev); err = tps80031_clr_bits(p, 1, RTC_INT, ENABLE_ALARM_INT); + tps80031_disable_rtc_write(dev); if (err < 0) { dev_err(p, "failed to clear ALRM int. err: %d\n", err); return err; @@ -303,8 +334,10 @@ static irqreturn_t tps80031_rtc_irq(int irq, void *data) return -EBUSY; } + tps80031_enable_rtc_write(dev); err = tps80031_force_update(dev->parent, 1, RTC_STATUS, ALARM_INT_STATUS, ALARM_INT_STATUS); + tps80031_disable_rtc_write(dev); if (err) { dev_err(dev->parent, "unable to set Alarm INT\n"); return -EBUSY; @@ -335,8 +368,19 @@ static int __devinit tps80031_rtc_probe(struct platform_device *pdev) if (pdata->irq < 0) dev_err(&pdev->dev, "no IRQ specified, wakeup is disabled\n"); + rtc->msecure_gpio = -1; + if (gpio_is_valid(pdata->msecure_gpio)) { + err = gpio_request(pdata->msecure_gpio, "tps80031 msecure"); + if (err == 0) { + rtc->msecure_gpio = pdata->msecure_gpio; + gpio_direction_output(rtc->msecure_gpio, 0); + } else + dev_warn(&pdev->dev, "could not get msecure GPIO\n"); + } + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &tps80031_rtc_ops, THIS_MODULE); + dev_set_drvdata(&pdev->dev, rtc); if (IS_ERR(rtc->rtc)) { err = PTR_ERR(rtc->rtc); @@ -379,7 +423,9 @@ static int __devinit tps80031_rtc_probe(struct platform_device *pdev) return -EBUSY; } + tps80031_enable_rtc_write(&pdev->dev); err = tps80031_set_bits(pdev->dev.parent, 1, RTC_INT, ENABLE_ALARM_INT); + tps80031_disable_rtc_write(&pdev->dev); if (err) { dev_err(&pdev->dev, "unable to program Interrupt Mask reg\n"); err = -EBUSY; @@ -388,7 +434,6 @@ static int __devinit tps80031_rtc_probe(struct platform_device *pdev) } else rtc->alarm_irq_enabled = 1; - dev_set_drvdata(&pdev->dev, rtc); if (pdata && (pdata->irq >= 0)) { rtc->irq = pdata->irq; err = request_threaded_irq(pdata->irq, NULL, tps80031_rtc_irq, diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 3f913389dd7c..cf1b7bf40c62 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -42,6 +42,8 @@ #include <mach/dma.h> #include <mach/clk.h> +#define SPI_PM_RUNTIME_ENABLE 0 + #define SLINK_COMMAND 0x000 #define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) #define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) @@ -144,7 +146,6 @@ #define DATA_DIR_TX (1 << 0) #define DATA_DIR_RX (1 << 1) -#define SPI_FIFO_DEPTH 32 #define SLINK_DMA_TIMEOUT (msecs_to_jiffies(1000)) @@ -169,12 +170,13 @@ static const unsigned long spi_tegra_req_sels[] = { RX_FIFO_FULL_COUNT_ZERO << 16) #define MAX_CHIP_SELECT 4 -#define SLINK_FIFO_DEPTH 4 +#define SLINK_FIFO_DEPTH 32 struct spi_tegra_data { struct spi_master *master; struct platform_device *pdev; spinlock_t lock; + spinlock_t reg_lock; char port_name[32]; struct clk *clk; @@ -215,7 +217,7 @@ struct spi_tegra_data { bool is_curr_dma_xfer; bool is_clkon_always; - bool clk_state; + int clk_state; bool is_suspended; bool is_hw_based_cs; @@ -247,22 +249,81 @@ struct spi_tegra_data { struct work_struct spi_transfer_work; }; +static int tegra_spi_runtime_idle(struct device *dev); +static int tegra_spi_runtime_resume(struct device *dev); + static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, unsigned long reg) { - if (!tspi->clk_state) + unsigned long flags; + unsigned long val; + + spin_lock_irqsave(&tspi->reg_lock, flags); + if (tspi->clk_state < 1) BUG(); - return readl(tspi->base + reg); + val = readl(tspi->base + reg); + spin_unlock_irqrestore(&tspi->reg_lock, flags); + return val; } static inline void spi_tegra_writel(struct spi_tegra_data *tspi, unsigned long val, unsigned long reg) { - if (!tspi->clk_state) + unsigned long flags; + + spin_lock_irqsave(&tspi->reg_lock, flags); + if (tspi->clk_state < 1) BUG(); writel(val, tspi->base + reg); + + /* Synchronize write by reading back the register */ + readl(tspi->base + SLINK_MAS_DATA); + spin_unlock_irqrestore(&tspi->reg_lock, flags); } +static int tegra_spi_clk_disable(struct spi_tegra_data *tspi) +{ + unsigned long flags; + + /* Flush all write which are in PPSB queue by reading back */ + spi_tegra_readl(tspi, SLINK_MAS_DATA); + + spin_lock_irqsave(&tspi->reg_lock, flags); + tspi->clk_state--; + spin_unlock_irqrestore(&tspi->reg_lock, flags); + clk_disable(tspi->clk); + clk_disable(tspi->sclk); + return 0; +} + +static int tegra_spi_clk_enable(struct spi_tegra_data *tspi) +{ + unsigned long flags; + + clk_enable(tspi->sclk); + clk_enable(tspi->clk); + spin_lock_irqsave(&tspi->reg_lock, flags); + tspi->clk_state++; + spin_unlock_irqrestore(&tspi->reg_lock, flags); + return 0; +} + +#if SPI_PM_RUNTIME_ENABLE +#define spi_pm_runtime_get_sync(dev) pm_runtime_get_sync(dev) +#define spi_pm_runtime_put_sync(dev) pm_runtime_put_sync(dev) +#define spi_pm_runtime_enable(dev) pm_runtime_enable(dev) +#define spi_pm_runtime_disable(dev) pm_runtime_disable(dev) +#define spi_pm_runtime_enabled(dev) pm_runtime_enabled(dev) +#define spi_pm_runtime_status_suspended(dev) pm_runtime_status_suspended(dev) +#else +#define spi_pm_runtime_get_sync(dev) tegra_spi_runtime_resume(dev) +#define spi_pm_runtime_put_sync(dev) tegra_spi_runtime_idle(dev) +#define spi_pm_runtime_enable(dev) do { } while(0) +#define spi_pm_runtime_disable(dev) do { } while(0) +#define spi_pm_runtime_enabled(dev) true +#define spi_pm_runtime_status_suspended(dev) true +#endif + static void cancel_dma(struct tegra_dma_channel *dma_chan, struct tegra_dma_req *req) { @@ -341,12 +402,12 @@ static unsigned spi_tegra_calculate_curr_xfer_param( if (tspi->is_packed) { max_len = min(remain_len, tspi->max_buf_size); tspi->curr_dma_words = max_len/tspi->bytes_per_word; - total_fifo_words = remain_len/4; + total_fifo_words = max_len/4; } else { max_word = (remain_len - 1) / tspi->bytes_per_word + 1; max_word = min(max_word, tspi->max_buf_size/4); tspi->curr_dma_words = max_word; - total_fifo_words = remain_len/tspi->bytes_per_word; + total_fifo_words = max_word; } return total_fifo_words; } @@ -441,7 +502,8 @@ static void spi_tegra_copy_client_txbuf_to_spi_txbuf( /* Make the dma buffer to read by cpu */ dma_sync_single_for_cpu(&tspi->pdev->dev, tspi->tx_buf_phys, - tspi->dma_buf_size, DMA_FROM_DEVICE); + tspi->dma_buf_size, DMA_TO_DEVICE); + if (tspi->is_packed) { len = tspi->curr_dma_words * tspi->bytes_per_word; memcpy(tspi->tx_buf, t->tx_buf + tspi->cur_pos, len); @@ -461,6 +523,7 @@ static void spi_tegra_copy_client_txbuf_to_spi_txbuf( } } tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + /* Make the dma buffer to read by dma */ dma_sync_single_for_device(&tspi->pdev->dev, tspi->tx_buf_phys, tspi->dma_buf_size, DMA_TO_DEVICE); @@ -499,7 +562,7 @@ static void spi_tegra_copy_spi_rxbuf_to_client_rxbuf( /* Make the dma buffer to read by dma */ dma_sync_single_for_device(&tspi->pdev->dev, tspi->rx_buf_phys, - tspi->dma_buf_size, DMA_TO_DEVICE); + tspi->dma_buf_size, DMA_FROM_DEVICE); } static int spi_tegra_start_dma_based_transfer( @@ -547,9 +610,6 @@ static int spi_tegra_start_dma_based_transfer( if (tspi->cur_direction & DATA_DIR_TX) { spi_tegra_copy_client_txbuf_to_spi_txbuf(tspi, t); wmb(); - /* Make the dma buffer to read by dma */ - dma_sync_single_for_device(&tspi->pdev->dev, tspi->tx_buf_phys, - tspi->dma_buf_size, DMA_TO_DEVICE); tspi->tx_dma_req.size = len; ret = tegra_dma_enqueue_req(tspi->tx_dma, &tspi->tx_dma_req); if (ret < 0) { @@ -567,7 +627,8 @@ static int spi_tegra_start_dma_based_transfer( if (tspi->cur_direction & DATA_DIR_RX) { /* Make the dma buffer to read by dma */ dma_sync_single_for_device(&tspi->pdev->dev, tspi->rx_buf_phys, - tspi->dma_buf_size, DMA_TO_DEVICE); + tspi->dma_buf_size, DMA_FROM_DEVICE); + tspi->rx_dma_req.size = len; ret = tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req); if (ret < 0) { @@ -585,6 +646,7 @@ static int spi_tegra_start_dma_based_transfer( udelay(1); wmb(); } + tspi->dma_control_reg = val; val |= SLINK_DMA_EN; spi_tegra_writel(tspi, val, SLINK_DMA_CTL); @@ -622,6 +684,7 @@ static int spi_tegra_start_cpu_based_transfer( udelay(1); wmb(); } + tspi->dma_control_reg = val; val |= SLINK_DMA_EN; spi_tegra_writel(tspi, val, SLINK_DMA_CTL); return 0; @@ -731,11 +794,11 @@ static void spi_tegra_start_transfer(struct spi_device *spi, command2 = tspi->def_command2_reg; if (is_first_of_msg) { - if (!tspi->is_clkon_always) { - if (!tspi->clk_state) { - pm_runtime_get_sync(&tspi->pdev->dev); - tspi->clk_state = 1; - } + if ((ret = spi_pm_runtime_get_sync(&tspi->pdev->dev)) < 0) { + dev_err(&tspi->pdev->dev, + "%s: spi_pm_runtime_get_sync() returns %d\n", + __func__, ret); + return; } spi_tegra_clear_status(tspi); @@ -806,7 +869,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi, spi_tegra_writel(tspi, command2, SLINK_COMMAND2); tspi->command2_reg = command2; - if (total_fifo_words > SPI_FIFO_DEPTH) + if (total_fifo_words > SLINK_FIFO_DEPTH) ret = spi_tegra_start_dma_based_transfer(tspi, t); else ret = spi_tegra_start_cpu_based_transfer(tspi, t); @@ -848,6 +911,8 @@ static int spi_tegra_setup(struct spi_device *spi) return -EINVAL; } + spi_pm_runtime_get_sync(&tspi->pdev->dev); + spin_lock_irqsave(&tspi->lock, flags); val = tspi->def_command_reg; if (spi->mode & SPI_CS_HIGH) @@ -855,20 +920,10 @@ static int spi_tegra_setup(struct spi_device *spi) else val &= ~cs_bit; tspi->def_command_reg = val; - - if (!tspi->is_clkon_always && !tspi->clk_state) { - spin_unlock_irqrestore(&tspi->lock, flags); - pm_runtime_get_sync(&tspi->pdev->dev); - spin_lock_irqsave(&tspi->lock, flags); - tspi->clk_state = 1; - } spi_tegra_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); - if (!tspi->is_clkon_always && tspi->clk_state) { - tspi->clk_state = 0; - spin_unlock_irqrestore(&tspi->lock, flags); - pm_runtime_put_sync(&tspi->pdev->dev); - } else - spin_unlock_irqrestore(&tspi->lock, flags); + spin_unlock_irqrestore(&tspi->lock, flags); + + spi_pm_runtime_put_sync(&tspi->pdev->dev); return 0; } @@ -968,6 +1023,11 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi, udelay(tspi->cur->delay_usecs); } + if (list_empty(&tspi->queue)) { + dev_err(&tspi->pdev->dev, "Handling empty list\n"); + return; + } + m = list_first_entry(&tspi->queue, struct spi_message, queue); if (err) m->status = -EIO; @@ -1010,19 +1070,11 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi, SLINK_COMMAND); spi_tegra_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); - if (!tspi->is_clkon_always) { - if (tspi->clk_state) { - /* Provide delay to stablize the signal - state */ - spin_unlock_irqrestore(&tspi->lock, - *irq_flags); - udelay(10); - pm_runtime_put_sync(&tspi->pdev->dev); - spin_lock_irqsave(&tspi->lock, - *irq_flags); - tspi->clk_state = 0; - } - } + /* Provide delay to stablize the signal state */ + spin_unlock_irqrestore(&tspi->lock, *irq_flags); + udelay(10); + spi_pm_runtime_put_sync(&tspi->pdev->dev); + spin_lock_irqsave(&tspi->lock, *irq_flags); tspi->is_transfer_in_progress = false; /* Check if any new request has come between * clock disable */ @@ -1056,6 +1108,9 @@ static void handle_cpu_based_xfer(void *context_data) (tspi->status_reg & SLINK_BSY)) { dev_err(&tspi->pdev->dev, "%s ERROR bit set 0x%x\n", __func__, tspi->status_reg); + dev_err(&tspi->pdev->dev, "%s 0x%08x:0x%08x:0x%08x\n", + __func__, tspi->command_reg, tspi->command2_reg, + tspi->dma_control_reg); tegra_periph_reset_assert(tspi->clk); udelay(2); tegra_periph_reset_deassert(tspi->clk); @@ -1144,6 +1199,9 @@ static irqreturn_t spi_tegra_isr_thread(int irq, void *context_data) if (err) { dev_err(&tspi->pdev->dev, "%s ERROR bit set 0x%x\n", __func__, tspi->status_reg); + dev_err(&tspi->pdev->dev, "%s 0x%08x:0x%08x:0x%08x\n", + __func__, tspi->command_reg, tspi->command2_reg, + tspi->dma_control_reg); tegra_periph_reset_assert(tspi->clk); udelay(2); tegra_periph_reset_deassert(tspi->clk); @@ -1173,7 +1231,7 @@ static irqreturn_t spi_tegra_isr_thread(int irq, void *context_data) /* Continue transfer in current message */ total_fifo_words = spi_tegra_calculate_curr_xfer_param(tspi->cur_spi, tspi, t); - if (total_fifo_words > SPI_FIFO_DEPTH) + if (total_fifo_words > SLINK_FIFO_DEPTH) err = spi_tegra_start_dma_based_transfer(tspi, t); else err = spi_tegra_start_cpu_based_transfer(tspi, t); @@ -1201,6 +1259,87 @@ static irqreturn_t spi_tegra_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } +static void spi_tegra_deinit_dma_param(struct spi_tegra_data *tspi, + bool dma_to_memory) +{ + struct tegra_dma_channel *tdc; + u32 *dma_buf; + dma_addr_t dma_phys; + + if (dma_to_memory) { + dma_buf = tspi->rx_buf; + tdc = tspi->rx_dma; + dma_phys = tspi->rx_buf_phys; + tspi->rx_dma = NULL; + tspi->rx_buf = NULL; + } else { + dma_buf = tspi->tx_buf; + tdc = tspi->tx_dma; + dma_phys = tspi->tx_buf_phys; + tspi->tx_buf = NULL; + tspi->tx_dma = NULL; + } + + dma_free_coherent(&tspi->pdev->dev, tspi->dma_buf_size, + dma_buf, dma_phys); + tegra_dma_free_channel(tdc); +} + +static int __init spi_tegra_init_dma_param(struct spi_tegra_data *tspi, + bool dma_to_memory) +{ + struct tegra_dma_req *dma_req; + struct tegra_dma_channel *tdc; + u32 *dma_buf; + dma_addr_t dma_phys; + + tdc = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT, "spi_%s_%d", + (dma_to_memory) ? "rx" : "tx", tspi->pdev->id); + if (!tdc) { + dev_err(&tspi->pdev->dev, "can not allocate rx dma channel\n"); + return -ENODEV; + } + + dma_buf = dma_alloc_coherent(&tspi->pdev->dev, tspi->dma_buf_size, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(&tspi->pdev->dev, "can not allocate rx bounce buffer"); + tegra_dma_free_channel(tdc); + return -ENOMEM; + } + + dma_req = (dma_to_memory) ? &tspi->rx_dma_req : &tspi->tx_dma_req; + memset(dma_req, 0, sizeof(*dma_req)); + + dma_req->req_sel = spi_tegra_req_sels[tspi->pdev->id]; + dma_req->dev = tspi; + dma_req->dest_bus_width = 32; + dma_req->source_bus_width = 32; + dma_req->to_memory = (dma_to_memory) ? 1 : 0; + dma_req->virt_addr = dma_buf; + dma_req->dest_wrap = 0; + dma_req->source_wrap = 0; + + if (dma_to_memory) { + dma_req->complete = tegra_spi_rx_dma_complete; + dma_req->dest_addr = dma_phys; + dma_req->source_addr = tspi->phys + SLINK_RX_FIFO; + dma_req->source_wrap = 4; + tspi->rx_buf_phys = dma_phys; + tspi->rx_buf = dma_buf; + tspi->rx_dma = tdc; + } else { + dma_req->complete = tegra_spi_tx_dma_complete; + dma_req->dest_addr = tspi->phys + SLINK_TX_FIFO; + dma_req->source_addr = dma_phys; + dma_req->dest_wrap = 4; + tspi->tx_buf = dma_buf; + tspi->tx_buf_phys = dma_phys; + tspi->tx_dma = tdc; + } + return 0; +} + static int __init spi_tegra_probe(struct platform_device *pdev) { struct spi_master *master; @@ -1234,57 +1373,53 @@ static int __init spi_tegra_probe(struct platform_device *pdev) tspi->is_transfer_in_progress = false; tspi->is_suspended = false; spin_lock_init(&tspi->lock); + spin_lock_init(&tspi->reg_lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); ret = -ENODEV; - goto fail_no_mem; - } - - if (!request_mem_region(r->start, resource_size(r), - dev_name(&pdev->dev))) { - ret = -EBUSY; - goto fail_no_mem; + goto exit_free_master; } - tspi->phys = r->start; - tspi->base = ioremap(r->start, resource_size(r)); + tspi->base = devm_request_and_ioremap(&pdev->dev, r); if (!tspi->base) { - dev_err(&pdev->dev, "can't ioremap iomem\n"); - ret = -ENOMEM; - goto fail_io_map; + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; } spi_irq = platform_get_irq(pdev, 0); if (unlikely(spi_irq < 0)) { dev_err(&pdev->dev, "can't find irq resource\n"); ret = -ENXIO; - goto fail_irq_req; + goto exit_free_master; } tspi->irq = spi_irq; sprintf(tspi->port_name, "tegra_spi_%d", pdev->id); - ret = request_threaded_irq(tspi->irq, spi_tegra_isr, - spi_tegra_isr_thread, IRQF_DISABLED, + ret = devm_request_threaded_irq(&pdev->dev, tspi->irq, + spi_tegra_isr, spi_tegra_isr_thread, IRQF_ONESHOT, tspi->port_name, tspi); if (ret < 0) { dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", tspi->irq); - goto fail_irq_req; + goto exit_free_master; } - tspi->clk = clk_get(&pdev->dev, "spi"); + tspi->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); ret = PTR_ERR(tspi->clk); - goto fail_clk_get; + goto exit_free_master; } - tspi->sclk = clk_get(&pdev->dev, "sclk"); + tspi->sclk = devm_clk_get(&pdev->dev, "sclk"); if (IS_ERR(tspi->sclk)) { dev_err(&pdev->dev, "can not get sclock\n"); ret = PTR_ERR(tspi->sclk); - goto fail_sclk_get; + goto exit_free_master; } INIT_LIST_HEAD(&tspi->queue); @@ -1327,92 +1462,41 @@ static int __init spi_tegra_probe(struct platform_device *pdev) init_completion(&tspi->tx_dma_complete); init_completion(&tspi->rx_dma_complete); - - tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT, - "spi_rx_%d", pdev->id); - if (!tspi->rx_dma) { - dev_err(&pdev->dev, "can not allocate rx dma channel\n"); - ret = -ENODEV; - goto fail_rx_dma_alloc; - } - - tspi->rx_buf = dma_alloc_coherent(&pdev->dev, tspi->dma_buf_size, - &tspi->rx_buf_phys, GFP_KERNEL); - if (!tspi->rx_buf) { - dev_err(&pdev->dev, "can not allocate rx bounce buffer\n"); - ret = -ENOMEM; - goto fail_rx_buf_alloc; - } - - /* Make the dma buffer to read by dma */ - dma_sync_single_for_device(&tspi->pdev->dev, tspi->rx_buf_phys, - tspi->dma_buf_size, DMA_TO_DEVICE); - - memset(&tspi->rx_dma_req, 0, sizeof(struct tegra_dma_req)); - tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; - tspi->rx_dma_req.to_memory = 1; - tspi->rx_dma_req.dest_addr = tspi->rx_buf_phys; - tspi->rx_dma_req.virt_addr = tspi->rx_buf; - tspi->rx_dma_req.dest_bus_width = 32; - tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO; - tspi->rx_dma_req.source_bus_width = 32; - tspi->rx_dma_req.source_wrap = 4; - tspi->rx_dma_req.dest_wrap = 0; - tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; - tspi->rx_dma_req.dev = tspi; - - tspi->tx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT, - "spi_tx_%d", pdev->id); - if (!tspi->tx_dma) { - dev_err(&pdev->dev, "can not allocate tx dma channel\n"); - ret = -ENODEV; - goto fail_tx_dma_alloc; + ret = spi_tegra_init_dma_param(tspi, true); + if (ret < 0) { + dev_err(&pdev->dev, "Error in rx dma init\n"); + goto exit_free_master; } - tspi->tx_buf = dma_alloc_coherent(&pdev->dev, tspi->dma_buf_size, - &tspi->tx_buf_phys, GFP_KERNEL); - if (!tspi->tx_buf) { - dev_err(&pdev->dev, "can not allocate tx bounce buffer\n"); - ret = -ENOMEM; - goto fail_tx_buf_alloc; + ret = spi_tegra_init_dma_param(tspi, false); + if (ret < 0) { + dev_err(&pdev->dev, "Error in tx dma init\n"); + goto exit_rx_dma_free; } - /* Make the dma buffer to read by dma */ - dma_sync_single_for_device(&tspi->pdev->dev, tspi->tx_buf_phys, - tspi->dma_buf_size, DMA_TO_DEVICE); - - memset(&tspi->tx_dma_req, 0, sizeof(struct tegra_dma_req)); - tspi->tx_dma_req.complete = tegra_spi_tx_dma_complete; - tspi->tx_dma_req.to_memory = 0; - tspi->tx_dma_req.dest_addr = tspi->phys + SLINK_TX_FIFO; - tspi->tx_dma_req.virt_addr = tspi->tx_buf; - tspi->tx_dma_req.dest_bus_width = 32; - tspi->tx_dma_req.dest_wrap = 4; - tspi->tx_dma_req.source_wrap = 0; - tspi->tx_dma_req.source_addr = tspi->tx_buf_phys; - tspi->tx_dma_req.source_bus_width = 32; - tspi->tx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; - tspi->tx_dma_req.dev = tspi; tspi->max_buf_size = tspi->dma_buf_size; tspi->def_command_reg = SLINK_CS_SW | SLINK_M_S; tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN; skip_dma_alloc: - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); - tspi->clk_state = 1; - master->dev.of_node = pdev->dev.of_node; - ret = spi_register_master(master); - if (!tspi->is_clkon_always) { - if (tspi->clk_state) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; + spi_pm_runtime_enable(&pdev->dev); + if (!spi_pm_runtime_enabled(&pdev->dev)) { + ret = tegra_spi_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "runtime resume failed %d", ret); + goto exit_pm_disable; } } + /* Enable clock if it is require to be enable always */ + if (tspi->is_clkon_always) + spi_pm_runtime_get_sync(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); - goto fail_master_register; + goto exit_pm_suspend; } /* create the workqueue for the kbc path */ @@ -1421,42 +1505,32 @@ skip_dma_alloc: if (!tspi->spi_workqueue) { dev_err(&pdev->dev, "Failed to create work queue\n"); ret = -ENODEV; - goto fail_workqueue; + goto exit_master_unregister; } INIT_WORK(&tspi->spi_transfer_work, tegra_spi_transfer_work); return ret; -fail_workqueue: +exit_master_unregister: spi_unregister_master(master); -fail_master_register: - if (tspi->tx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->tx_buf, tspi->tx_buf_phys); -fail_tx_buf_alloc: - if (tspi->tx_dma) - tegra_dma_free_channel(tspi->tx_dma); -fail_tx_dma_alloc: - if (tspi->rx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->rx_buf, tspi->rx_buf_phys); -fail_rx_buf_alloc: - if (tspi->rx_dma) - tegra_dma_free_channel(tspi->rx_dma); -fail_rx_dma_alloc: - pm_runtime_disable(&pdev->dev); - clk_put(tspi->sclk); -fail_sclk_get: - clk_put(tspi->clk); -fail_clk_get: - free_irq(tspi->irq, tspi); -fail_irq_req: - iounmap(tspi->base); -fail_io_map: - release_mem_region(r->start, resource_size(r)); -fail_no_mem: + if (tspi->is_clkon_always) + spi_pm_runtime_put_sync(&pdev->dev); + +exit_pm_suspend: + if (!spi_pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_idle(&pdev->dev); + +exit_pm_disable: + spi_pm_runtime_disable(&pdev->dev); + + spi_tegra_deinit_dma_param(tspi, false); + +exit_rx_dma_free: + spi_tegra_deinit_dma_param(tspi, true); + +exit_free_master: spi_master_put(master); return ret; } @@ -1465,56 +1539,44 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) { struct spi_master *master; struct spi_tegra_data *tspi; - struct resource *r; master = dev_get_drvdata(&pdev->dev); tspi = spi_master_get_devdata(master); spi_unregister_master(master); - if (tspi->tx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->tx_buf, tspi->tx_buf_phys); + if (tspi->tx_dma) - tegra_dma_free_channel(tspi->tx_dma); - if (tspi->rx_buf) - dma_free_coherent(&pdev->dev, tspi->dma_buf_size, - tspi->rx_buf, tspi->rx_buf_phys); + spi_tegra_deinit_dma_param(tspi, false); + if (tspi->rx_dma) - tegra_dma_free_channel(tspi->rx_dma); + spi_tegra_deinit_dma_param(tspi, true); - if (tspi->is_clkon_always) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; - } + /* Disable clock if it is always enabled */ + if (tspi->is_clkon_always) + spi_pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); - clk_put(tspi->sclk); - clk_put(tspi->clk); - iounmap(tspi->base); + spi_pm_runtime_disable(&pdev->dev); + if (!spi_pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_idle(&pdev->dev); destroy_workqueue(tspi->spi_workqueue); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - return 0; } #ifdef CONFIG_PM -static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) +static int spi_tegra_suspend(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; - unsigned limit = 50; + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); + unsigned limit = 50; unsigned long flags; - master = dev_get_drvdata(&pdev->dev); - tspi = spi_master_get_devdata(master); spin_lock_irqsave(&tspi->lock, flags); /* Wait for all transfer completes */ if (!list_empty(&tspi->queue)) - dev_warn(&pdev->dev, "The transfer list is not empty " + dev_warn(dev, "The transfer list is not empty " "Waiting for time %d ms to complete transfer\n", limit * 20); @@ -1528,7 +1590,7 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) tspi->is_suspended = true; if (!list_empty(&tspi->queue)) { limit = 50; - dev_err(&pdev->dev, "All transfer has not completed, " + dev_err(dev, "All transfer has not completed, " "Waiting for %d ms current transfer to complete\n", limit * 20); while (tspi->is_transfer_in_progress && limit--) { @@ -1539,7 +1601,7 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) } if (tspi->is_transfer_in_progress) { - dev_err(&pdev->dev, + dev_err(dev, "Spi transfer is in progress Avoiding suspend\n"); tspi->is_suspended = false; spin_unlock_irqrestore(&tspi->lock, flags); @@ -1547,33 +1609,32 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) } spin_unlock_irqrestore(&tspi->lock, flags); - if (tspi->is_clkon_always) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; - } + + /* Disable clock if it is always enabled */ + if (tspi->is_clkon_always) + spi_pm_runtime_put_sync(dev); + return 0; } -static int spi_tegra_resume(struct platform_device *pdev) +static int spi_tegra_resume(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); struct spi_message *m; struct spi_device *spi; struct spi_transfer *t = NULL; int single_xfer = 0; unsigned long flags; - master = dev_get_drvdata(&pdev->dev); - tspi = spi_master_get_devdata(master); + /* Enable clock if it is always enabled */ + if (tspi->is_clkon_always) + spi_pm_runtime_get_sync(dev); - pm_runtime_get_sync(&pdev->dev); - tspi->clk_state = 1; + spi_pm_runtime_get_sync(dev); spi_tegra_writel(tspi, tspi->command_reg, SLINK_COMMAND); - if (!tspi->is_clkon_always) { - pm_runtime_put_sync(&pdev->dev); - tspi->clk_state = 0; - } + spi_pm_runtime_put_sync(dev); + spin_lock_irqsave(&tspi->lock, flags); tspi->cur_speed = 0; @@ -1595,38 +1656,32 @@ static int spi_tegra_resume(struct platform_device *pdev) } #endif -#if defined(CONFIG_PM_RUNTIME) - static int tegra_spi_runtime_idle(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; - master = dev_get_drvdata(dev); - tspi = spi_master_get_devdata(master); + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); - clk_disable(tspi->clk); - clk_disable(tspi->sclk); - return 0; + return tegra_spi_clk_disable(tspi); } static int tegra_spi_runtime_resume(struct device *dev) { - struct spi_master *master; - struct spi_tegra_data *tspi; - master = dev_get_drvdata(dev); - tspi = spi_master_get_devdata(master); + struct spi_master *master = dev_get_drvdata(dev); + struct spi_tegra_data *tspi = spi_master_get_devdata(master); - clk_enable(tspi->sclk); - clk_enable(tspi->clk); - return 0; + return tegra_spi_clk_enable(tspi); } static const struct dev_pm_ops tegra_spi_dev_pm_ops = { +#if defined(CONFIG_PM_RUNTIME) .runtime_idle = tegra_spi_runtime_idle, .runtime_resume = tegra_spi_runtime_resume, -}; - #endif +#ifdef CONFIG_PM + .suspend = spi_tegra_suspend, + .resume = spi_tegra_resume, +#endif +}; MODULE_ALIAS("platform:spi_tegra"); @@ -1644,16 +1699,10 @@ static struct platform_driver spi_tegra_driver = { .driver = { .name = "spi_tegra", .owner = THIS_MODULE, -#if defined(CONFIG_PM_RUNTIME) .pm = &tegra_spi_dev_pm_ops, -#endif .of_match_table = spi_tegra_of_match_table, }, .remove = __devexit_p(spi_tegra_remove), -#ifdef CONFIG_PM - .suspend = spi_tegra_suspend, - .resume = spi_tegra_resume, -#endif }; static int __init spi_tegra_init(void) diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index f5c226cc1689..7d22559e54c4 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1120,13 +1120,8 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) sg_dma_address(sg) = dma->tx_buf_dma + sg->offset; } sg = dma->sg_tx_p; -<<<<<<< HEAD - desc_tx = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx, - sg, num, DMA_TO_DEVICE, -======= desc_tx = dmaengine_prep_slave_sg(dma->chan_tx, sg, num, DMA_MEM_TO_DEV, ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n", diff --git a/drivers/staging/iio/light/ltr558als.c b/drivers/staging/iio/light/ltr558als.c index 01e410b191fd..4fef5d4cc034 100644 --- a/drivers/staging/iio/light/ltr558als.c +++ b/drivers/staging/iio/light/ltr558als.c @@ -193,7 +193,7 @@ static bool ltr558_set_proxim_high_threshold(struct i2c_client *client, bool st; st = ltr558_i2c_write_reg(client, LTR558_PS_THRES_UP_0, thresh & 0xFF); - if (st) + if (!st) st = ltr558_i2c_write_reg(client, LTR558_PS_THRES_UP_1, (thresh >> 8) & 0x07); return st; @@ -205,7 +205,7 @@ static bool ltr558_set_proxim_low_threshold(struct i2c_client *client, bool st; st = ltr558_i2c_write_reg(client, LTR558_PS_THRES_LOW_0, thresh & 0xFF); - if (st) + if (!st) st = ltr558_i2c_write_reg(client, LTR558_PS_THRES_LOW_1, (thresh >> 8) & 0x07); return st; @@ -216,7 +216,7 @@ static bool ltr558_set_als_high_threshold(struct i2c_client *client, u32 thresh) bool st; st = ltr558_i2c_write_reg(client, LTR558_ALS_THRES_UP_0, thresh & 0xFF); - if (st) + if (!st) st = ltr558_i2c_write_reg(client, LTR558_ALS_THRES_UP_1, (thresh >> 8) & 0xFF); return st; @@ -227,7 +227,7 @@ static bool ltr558_set_als_low_threshold(struct i2c_client *client, u32 thresh) bool st; st = ltr558_i2c_write_reg(client, LTR558_ALS_THRES_LOW_0, thresh & 0xFF); - if (st) + if (!st) st = ltr558_i2c_write_reg(client, LTR558_ALS_THRES_LOW_1, ((thresh >> 8) & 0xFF)); return st; @@ -314,7 +314,7 @@ static ssize_t store_proxim_low_threshold(struct device *dev, mutex_lock(&chip->lock); st = ltr558_set_proxim_low_threshold(client, (u8)lval); - if (st) + if (!st) chip->prox_low_thres = (int)lval; else dev_err(dev, "Error in setting proximity low threshold\n"); @@ -355,7 +355,7 @@ static ssize_t store_proxim_high_threshold(struct device *dev, mutex_lock(&chip->lock); st = ltr558_set_proxim_high_threshold(client, lval); - if (st) + if (!st) chip->prox_high_thres = (int)lval; else dev_err(dev, "Error in setting proximity high threshold\n"); @@ -443,7 +443,7 @@ static ssize_t store_als_low_threshold(struct device *dev, mutex_lock(&chip->lock); st = ltr558_set_als_low_threshold(client, (int)lval); - if (st) + if (!st) chip->als_low_thres = (int)lval; else dev_err(dev, "Error in setting als low threshold\n"); @@ -483,7 +483,7 @@ static ssize_t store_als_high_threshold(struct device *dev, mutex_lock(&chip->lock); st = ltr558_set_als_high_threshold(client, (int)lval); - if (st) + if (!st) chip->als_high_thres = (int)lval; else dev_err(dev, "Error in setting als high threshold\n"); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index df8c8faad106..f20c82aa6d28 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -673,14 +673,9 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap) /* Start the RX DMA job */ sgbuf = uap->dmarx.use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; -<<<<<<< HEAD dma_dev = rxchan->device; - desc = rxchan->device->device_prep_slave_sg(rxchan, &sgbuf->sg, 1, - DMA_FROM_DEVICE, -======= desc = dmaengine_prep_slave_sg(rxchan, &sgbuf->sg, 1, DMA_DEV_TO_MEM, ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers DMA_PREP_INTERRUPT | DMA_CTRL_ACK); /* * If the DMA engine is busy and cannot prepare a diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 5c33cefe2953..9748681679eb 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -910,13 +910,8 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) sg_dma_len(sg) = size; } -<<<<<<< HEAD - desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, - priv->sg_tx_p, nent, DMA_TO_DEVICE, -======= desc = dmaengine_prep_slave_sg(priv->chan_tx, priv->sg_tx_p, nent, DMA_MEM_TO_DEV, ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(priv->port.dev, "%s:device_prep_slave_sg Failed\n", diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 3f4d9193b645..ef49f0a08364 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1328,13 +1328,8 @@ static void work_fn_tx(struct work_struct *work) BUG_ON(!sg_dma_len(sg)); -<<<<<<< HEAD - desc = chan->device->device_prep_slave_sg(chan, - sg, s->sg_len_tx, DMA_TO_DEVICE, -======= desc = dmaengine_prep_slave_sg(chan, sg, s->sg_len_tx, DMA_MEM_TO_DEV, ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { /* switch to PIO */ diff --git a/drivers/tty/serial/tegra_hsuart.c b/drivers/tty/serial/tegra_hsuart.c index 10560107e066..ea20de6ebb41 100644 --- a/drivers/tty/serial/tegra_hsuart.c +++ b/drivers/tty/serial/tegra_hsuart.c @@ -155,14 +155,6 @@ static inline void uart_writeb(struct tegra_uart_port *t, u8 val, writeb(val, t->uport.membase + (reg << t->uport.regshift)); } -static void cancel_dma(struct tegra_dma_channel *dma_chan, - struct tegra_dma_req *req) -{ - tegra_dma_cancel(dma_chan); - if (req->status == -TEGRA_DMA_REQ_ERROR_ABORTED) - req->complete(req); -} - static inline void uart_writel(struct tegra_uart_port *t, u32 val, unsigned long reg) { @@ -352,7 +344,7 @@ static void do_handle_rx_dma(struct tegra_uart_port *t) struct uart_port *u = &t->uport; if (t->rts_active) set_rts(t, false); - cancel_dma(t->rx_dma, &t->rx_dma_req); + tegra_dma_dequeue_req(t->rx_dma, &t->rx_dma_req); tty_flip_buffer_push(u->state->port.tty); /* enqueue the request again */ tegra_start_dma_rx(t); @@ -628,7 +620,7 @@ static void tegra_stop_rx(struct uart_port *u) t->rx_in_progress = 0; if (t->use_rx_dma && t->rx_dma) - cancel_dma(t->rx_dma, &t->rx_dma_req); + tegra_dma_dequeue_req(t->rx_dma, &t->rx_dma_req); else do_handle_rx_pio(t); @@ -903,7 +895,7 @@ static int tegra_startup(struct uart_port *u) goto fail; pdata = u->dev->platform_data; - if (pdata->is_loopback) + if (pdata && pdata->is_loopback) t->mcr_shadow |= UART_MCR_LOOP; dev_dbg(u->dev, "Requesting IRQ %d\n", u->irq); @@ -1077,7 +1069,7 @@ static void tegra_stop_tx(struct uart_port *u) t = container_of(u, struct tegra_uart_port, uport); if (t->use_tx_dma) - cancel_dma(t->tx_dma, &t->tx_dma_req); + tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req); return; } @@ -1271,6 +1263,12 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, if (t->rts_active) set_rts(t, false); + /* Clear all interrupts as configuration is going to be change */ + uart_writeb(t, t->ier_shadow | UART_IER_RDI, UART_IER); + uart_readb(t, UART_IER); + uart_writeb(t, 0, UART_IER); + uart_readb(t, UART_IER); + /* Parity */ lcr = t->lcr_shadow; lcr &= ~UART_LCR_PARITY; @@ -1343,6 +1341,13 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, /* update the port timeout based on new settings */ uart_update_timeout(u, termios->c_cflag, baud); + /* Make sure all write has completed */ + uart_readb(t, UART_IER); + + /* Reenable interrupt */ + uart_writeb(t, t->ier_shadow, UART_IER); + uart_readb(t, UART_IER); + spin_unlock_irqrestore(&u->lock, flags); dev_vdbg(t->uport.dev, "-tegra_set_termios\n"); return; @@ -1363,7 +1368,7 @@ static void tegra_flush_buffer(struct uart_port *u) t->tx_bytes = 0; if (t->use_tx_dma) { - cancel_dma(t->tx_dma, &t->tx_dma_req); + tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req); t->tx_dma_req.size = 0; } return; @@ -1646,7 +1651,7 @@ int tegra_uart_is_tx_empty(struct uart_port *uport) return tegra_tx_empty(uport); } -static struct platform_driver tegra_uart_platform_driver = { +static struct platform_driver tegra_uart_platform_driver __refdata= { .probe = tegra_uart_probe, .remove = __devexit_p(tegra_uart_remove), .suspend = tegra_uart_suspend, diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f92c3df69195..8e051d72d6aa 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -178,6 +178,17 @@ config USB_OMAP dynamically linked module called "omap_udc" and force all gadget drivers to also be dynamically linked. +config USB_TEGRA + tristate "Nvidia Highspeed USB 2.0 device controller" + depends on ARCH_TEGRA + select USB_GADGET_DUALSPEED + help + Enables TEGRA USB 2.0 pheripheral driver. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "tegra_udc" and force all + gadget drivers to also be dynamically linked. + config USB_PXA25X tristate "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 581a5ae7337e..c8c7c687010d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,8 +1,7 @@ # # USB peripheral controller drivers # -GCOV_PROFILE_fsl_tegra_udc.o := y -GCOV_PROFILE_fsl_udc_core.o := y +GCOV_PROFILE_tegra_udc.o := y ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG @@ -22,7 +21,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o fsl_usb2_udc-y := fsl_udc_core.o fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o -fsl_usb2_udc-$(CONFIG_ARCH_TEGRA) += fsl_tegra_udc.o +obj-$(CONFIG_USB_TEGRA) += tegra_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index fbafe8a3bca4..4805670cf6e3 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -319,6 +319,13 @@ static int mtp_function_ctrlrequest(struct android_usb_function *f, return mtp_ctrlrequest(cdev, c); } +static int ptp_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return mtp_ctrlrequest(cdev, c); +} + static struct android_usb_function mtp_function = { .name = "mtp", .init = mtp_function_init, @@ -333,6 +340,7 @@ static struct android_usb_function ptp_function = { .init = ptp_function_init, .cleanup = ptp_function_cleanup, .bind_config = ptp_function_bind_config, + .ctrlrequest = ptp_function_ctrlrequest, }; diff --git a/drivers/usb/gadget/fsl_tegra_udc.c b/drivers/usb/gadget/fsl_tegra_udc.c deleted file mode 100644 index 31b476ad2279..000000000000 --- a/drivers/usb/gadget/fsl_tegra_udc.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Description: - * Helper functions to support the tegra USB controller - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ -#include <linux/fsl_devices.h> -#include <linux/platform_device.h> -#include <linux/err.h> -#include <linux/io.h> -#include <mach/usb_phy.h> - -static struct tegra_usb_phy *phy; -static struct clk *udc_clk; -static struct clk *emc_clk; -static struct clk *sclk_clk; -static void *udc_base; - -int fsl_udc_clk_init(struct platform_device *pdev) -{ - struct resource *res; - int err; - int instance; - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - - udc_clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(udc_clk)) { - dev_err(&pdev->dev, "Can't get udc clock\n"); - return PTR_ERR(udc_clk); - } - - clk_enable(udc_clk); - - sclk_clk = clk_get(&pdev->dev, "sclk"); - if (IS_ERR(sclk_clk)) { - dev_err(&pdev->dev, "Can't get sclk clock\n"); - err = PTR_ERR(sclk_clk); - goto err_sclk; - } - - clk_set_rate(sclk_clk, 80000000); - clk_enable(sclk_clk); - - emc_clk = clk_get(&pdev->dev, "emc"); - if (IS_ERR(emc_clk)) { - dev_err(&pdev->dev, "Can't get emc clock\n"); - err = PTR_ERR(emc_clk); - goto err_emc; - } - - clk_enable(emc_clk); -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Set DDR busy hints to 150MHz. For Tegra 2x SOC, DDR rate is half of EMC rate */ - clk_set_rate(emc_clk, 300000000); -#else - /* Set DDR busy hints to 100MHz. For Tegra 3x SOC DDR rate equals to EMC rate */ - clk_set_rate(emc_clk, 100000000); -#endif - - /* we have to remap the registers ourselves as fsl_udc does not - * export them for us. - */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENXIO; - goto err0; - } - udc_base = ioremap(res->start, resource_size(res)); - if (!udc_base) { - err = -ENOMEM; - goto err0; - } - - instance = pdev->id; - if (instance == -1) - instance = 0; - - phy = tegra_usb_phy_open(instance, udc_base, pdata->phy_config, - TEGRA_USB_PHY_MODE_DEVICE, pdata->usb_phy_type); - if (IS_ERR(phy)) { - dev_err(&pdev->dev, "Can't open phy\n"); - err = PTR_ERR(phy); - goto err1; - } - tegra_usb_phy_power_on(phy, true); - - return 0; -err1: - iounmap(udc_base); -err0: - clk_disable(emc_clk); - clk_put(emc_clk); -err_emc: - clk_disable(sclk_clk); - clk_put(sclk_clk); -err_sclk: - clk_disable(udc_clk); - clk_put(udc_clk); - return err; -} - -void fsl_udc_clk_finalize(struct platform_device *pdev) -{ -} - -void fsl_udc_clk_release(void) -{ - tegra_usb_phy_close(phy); - - iounmap(udc_base); - - clk_disable(udc_clk); - clk_put(udc_clk); - - clk_disable(sclk_clk); - clk_put(sclk_clk); - - clk_disable(emc_clk); - clk_put(emc_clk); -} - -void fsl_udc_clk_suspend(bool is_dpd) -{ - tegra_usb_phy_power_off(phy, is_dpd); - clk_disable(udc_clk); - clk_disable(sclk_clk); - clk_disable(emc_clk); -} - -void fsl_udc_clk_resume(bool is_dpd) -{ - clk_enable(emc_clk); - clk_enable(sclk_clk); - clk_enable(udc_clk); - tegra_usb_phy_power_on(phy, is_dpd); -} - -void fsl_udc_clk_enable(void) -{ - clk_enable(udc_clk); -} - -void fsl_udc_clk_disable(void) -{ - clk_disable(udc_clk); -} - -bool fsl_udc_charger_detect(void) -{ - return tegra_usb_phy_charger_detect(phy); -} - -void fsl_udc_dtd_prepare(void) -{ - /* When we are programming two DTDs very close to each other, - * the second DTD is being prefetched before it is actually written - * to DDR. To prevent this, we disable prefetcher before programming - * any new DTD and re-enable it before priming endpoint. - */ - tegra_usb_phy_memory_prefetch_off(phy); -} - -void fsl_udc_ep_barrier(void) -{ - tegra_usb_phy_memory_prefetch_on(phy); -} diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f3a83cd0ef50..db7f521e5d0a 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -49,6 +49,7 @@ #define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name)) #define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name)) #define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) +#define gadget_is_tegra(g) (!strcmp("tegra-udc", (g)->name)) /** * usb_gadget_controller_number - support bcdDevice id convention @@ -115,7 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x30; else if (gadget_is_net2272(gadget)) return 0x31; - + else if (gadget_is_tegra(gadget)) + return 0x32; return -ENOENT; } diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c new file mode 100644 index 000000000000..aad99a6cd66e --- /dev/null +++ b/drivers/usb/gadget/tegra_udc.c @@ -0,0 +1,2790 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Description: + * High-speed USB device controller driver. + * The driver is based on Freescale driver code from Li Yang and Jiang Bo. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#undef VERBOSE + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/dmapool.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> +#include <linux/workqueue.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pm_qos_params.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/unaligned.h> +#include <asm/dma.h> + +#include <mach/usb_phy.h> +#include <mach/iomap.h> + +#include "tegra_udc.h" + + +#define DRIVER_DESC "Nvidia Tegra High-Speed USB SOC \ + Device Controller driver" + +#define DRIVER_AUTHOR "Venkat Moganty/Rakesh Bodla" +#define DRIVER_VERSION "Apr 30, 2012" +#define USB1_PREFETCH_ID 6 + +#define get_ep_by_pipe(udc, pipe) ((pipe == 1) ? &udc->eps[0] : \ + &udc->eps[pipe]) +#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \ + * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) + +#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) +#define ep_is_in(EP) ((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ + USB_DIR_IN) : ((EP)->desc->bEndpointAddress \ + & USB_DIR_IN) == USB_DIR_IN) + + +static const char driver_name[] = "tegra-udc"; +static const char driver_desc[] = DRIVER_DESC; + +static void tegra_ep_fifo_flush(struct usb_ep *_ep); +static int reset_queues(struct tegra_udc *udc); + +/* + * High speed test mode packet(53 bytes). + * See USB 2.0 spec, section 7.1.20. + */ +static const u8 tegra_udc_test_packet[53] = { + /* JKJKJKJK x9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK x8 */ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + /* JJJJKKKK x8 */ + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + /* JJJJJJJKKKKKKK x8 */ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* JJJJJJJK x8 */ + 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, + /* JKKKKKKK x10, JK */ + 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e +}; + +static struct tegra_udc *the_udc; + +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + static struct pm_qos_request_list boost_cpu_freq_req; + static u32 ep_queue_request_count; + static u8 boost_cpufreq_work_flag; +#endif + +static inline void udc_writel(struct tegra_udc *udc, u32 val, u32 offset) +{ + writel(val, udc->regs + offset); +} + +static inline unsigned int udc_readl(struct tegra_udc *udc, u32 offset) +{ + return readl(udc->regs + offset); +} + +/* checks vbus status */ +static inline bool vbus_enabled(struct tegra_udc *udc) +{ + bool status = false; + status = (udc_readl(udc, VBUS_WAKEUP_REG_OFFSET) & USB_SYS_VBUS_STATUS); + return status; +} + +/** + * done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * request is still in progress. + */ +static void done(struct tegra_ep *ep, struct tegra_req *req, int status) +{ + struct tegra_udc *udc = NULL; + unsigned char stopped = ep->stopped; + struct ep_td_struct *curr_td, *next_td; + int j; + BUG_ON(!(in_irq() || irqs_disabled())); + udc = (struct tegra_udc *)ep->udc; + /* Removed the req from tegra_ep->queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free dtd for the request */ + next_td = req->head; + for (j = 0; j < req->dtd_count; j++) { + curr_td = next_td; + if (j != req->dtd_count - 1) + next_td = curr_td->next_td_virt; + + dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma); + } + + if (req->mapped) { + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + if (status && (status != -ESHUTDOWN)) + VDBG("complete %s req %p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + ep->stopped = 1; +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + if (req->req.complete && req->req.length >= BOOST_TRIGGER_SIZE) { + ep_queue_request_count--; + if (!ep_queue_request_count) + schedule_work(&udc->boost_cpufreq_work); + } +#endif + + /* complete() is from gadget layer, + * eg fsg->bulk_in_complete() */ + if (req->req.complete) { + spin_unlock(&ep->udc->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->udc->lock); + } + + ep->stopped = stopped; +} + +/* + * nuke(): delete all requests related to this ep + * Must be called with spinlock held and interrupt disabled + */ +static void nuke(struct tegra_ep *ep, int status) +{ + ep->stopped = 1; + + /* Flush fifo */ + tegra_ep_fifo_flush(&ep->ep); + + /* Whether this eq has request linked */ + while (!list_empty(&ep->queue)) { + struct tegra_req *req = NULL; + + req = list_entry(ep->queue.next, struct tegra_req, queue); + done(ep, req, status); + } +} + +static int can_pullup(struct tegra_udc *udc) +{ + DBG("%s(%d) udc->softconnect = %d udc->vbus_active = %d\n", + __func__, __LINE__, udc->softconnect, udc->vbus_active); + return udc->driver && udc->softconnect && udc->vbus_active; +} + +static int dr_controller_reset(struct tegra_udc *udc) +{ + unsigned int tmp; + unsigned long timeout; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Stop and reset the usb controller */ + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + tmp &= ~USB_CMD_RUN_STOP; + udc_writel(udc, tmp, USB_CMD_REG_OFFSET); + + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + tmp |= USB_CMD_CTRL_RESET; + udc_writel(udc, tmp, USB_CMD_REG_OFFSET); + + /* Wait for reset to complete */ + timeout = jiffies + UDC_RESET_TIMEOUT_MS; + while (udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_CTRL_RESET) { + if (time_after(jiffies, timeout)) { + ERR("udc reset timeout!\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + +static int dr_controller_setup(struct tegra_udc *udc) +{ + unsigned int tmp, portctrl; + unsigned long timeout; + int status; + unsigned int port_control_reg_offset; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (udc->has_hostpc) + port_control_reg_offset = USB_HOSTPCX_DEVLC_REG_OFFSET; + else + port_control_reg_offset = PORTSCX_REG_OFFSET; + + /* Config PHY interface */ + portctrl = udc_readl(udc, port_control_reg_offset); + portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); + portctrl |= PORTSCX_PTS_UTMI; + udc_writel(udc, portctrl, port_control_reg_offset); + + status = dr_controller_reset(udc); + if (status) + return status; + + /* Set the controller as device mode */ + tmp = udc_readl(udc, USB_MODE_REG_OFFSET); + tmp |= USB_MODE_CTRL_MODE_DEVICE; + /* Disable Setup Lockout */ + tmp |= USB_MODE_SETUP_LOCK_OFF; + udc_writel(udc, tmp, USB_MODE_REG_OFFSET); + + /* Wait for controller to switch to device mode */ + timeout = jiffies + UDC_RESET_TIMEOUT_MS; + while ((udc_readl(udc, USB_MODE_REG_OFFSET) & + USB_MODE_CTRL_MODE_DEVICE) != USB_MODE_CTRL_MODE_DEVICE) { + if (time_after(jiffies, timeout)) { + ERR("udc device mode setup timeout!\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + /* Clear the setup status */ + udc_writel(udc, 0, USB_STS_REG_OFFSET); + + tmp = udc->ep_qh_dma; + tmp &= USB_EP_LIST_ADDRESS_MASK; + udc_writel(udc, tmp, USB_EP_LIST_ADDRESS_REG_OFFSET); + + VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", + udc->ep_qh, (int)tmp, + udc_readl(udc, USB_EP_LIST_ADDRESS_REG_OFFSET)); + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + +/* Enable DR irq and set controller to run state */ +static void dr_controller_run(struct tegra_udc *udc) +{ + u32 temp; + unsigned long timeout; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Clear stopped bit */ + udc->stopped = 0; + + /* If OTG transceiver is available, then it handles the VBUS detection*/ + if (!udc->transceiver) { + /* Enable cable detection interrupt, without setting the + * USB_SYS_VBUS_WAKEUP_INT bit. USB_SYS_VBUS_WAKEUP_INT is + * clear on write */ + temp = udc_readl(udc, VBUS_WAKEUP_REG_OFFSET); + temp |= (USB_SYS_VBUS_WAKEUP_INT_ENABLE + | USB_SYS_VBUS_WAKEUP_ENABLE); + temp &= ~USB_SYS_VBUS_WAKEUP_INT_STATUS; + udc_writel(udc, temp, VBUS_WAKEUP_REG_OFFSET); + } + /* Enable DR irq reg */ + temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN + | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN + | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + udc_writel(udc, temp, USB_INTR_REG_OFFSET); + + /* Set the controller as device mode */ + temp = udc_readl(udc, USB_MODE_REG_OFFSET); + temp |= USB_MODE_CTRL_MODE_DEVICE; + udc_writel(udc, temp, USB_MODE_REG_OFFSET); + + /* Set controller to Run */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + if (can_pullup(udc)) + temp |= USB_CMD_RUN_STOP; + else + temp &= ~USB_CMD_RUN_STOP; + udc_writel(udc, temp, USB_CMD_REG_OFFSET); + + if (can_pullup(udc)) { + /* Wait for controller to start */ + timeout = jiffies + UDC_RUN_TIMEOUT_MS; + while ((udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_RUN_STOP) + != USB_CMD_RUN_STOP) { + if (time_after(jiffies, timeout)) { + ERR("udc start timeout!\n"); + return; + } + cpu_relax(); + } + } + + DBG("%s(%d) END\n", __func__, __LINE__); + return; +} + +static void dr_controller_stop(struct tegra_udc *udc) +{ + unsigned int tmp; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Clear pending interrupt status bits */ + tmp = udc_readl(udc, USB_STS_REG_OFFSET); + udc_writel(udc, tmp, USB_STS_REG_OFFSET); + + /* disable all INTR */ + udc_writel(udc, 0, USB_INTR_REG_OFFSET); + + /* Set stopped bit for isr */ + udc->stopped = 1; + + /* set controller to Stop */ + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + tmp &= ~USB_CMD_RUN_STOP; + udc_writel(udc, tmp, USB_CMD_REG_OFFSET); + + DBG("%s(%d) END\n", __func__, __LINE__); + return; +} + +static void dr_ep_setup(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir, unsigned char ep_type) +{ + unsigned int tmp_epctrl = 0; + + tmp_epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (dir) { + if (ep_num) + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + if (ep_num) + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_RX_EP_TYPE_SHIFT); + } + + udc_writel(udc, tmp_epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); +} + +static void dr_ep_change_stall(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir, int value) +{ + u32 tmp_epctrl = 0; + + tmp_epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (value) { + /* set the stall bit */ + if (dir) + tmp_epctrl |= EPCTRL_TX_EP_STALL; + else + tmp_epctrl |= EPCTRL_RX_EP_STALL; + } else { + /* clear the stall bit and reset data toggle */ + if (dir) { + tmp_epctrl &= ~EPCTRL_TX_EP_STALL; + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + } else { + tmp_epctrl &= ~EPCTRL_RX_EP_STALL; + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + } + } + udc_writel(udc, tmp_epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); +} + +/* Get stall status of a specific ep + Return: 0: not stalled; 1:stalled */ +static int dr_ep_get_stall(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir) +{ + u32 epctrl; + + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (dir) + return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0; + else + return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0; +} + +/** + * struct_ep_qh_setup(): set the Endpoint Capabilites field of QH + * @zlt: Zero Length Termination Select (1: disable; 0: enable) + * @mult: Mult field + */ +static void struct_ep_qh_setup(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir, unsigned char ep_type, + unsigned int max_pkt_len, unsigned int zlt, unsigned char mult) +{ + struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir]; + unsigned int tmp = 0; + + /* set the Endpoint Capabilites in QH */ + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + /* Interrupt On Setup (IOS). for control ep */ + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | EP_QUEUE_HEAD_IOS; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + break; + default: + VDBG("error ep type is %d", ep_type); + return; + } + if (zlt) + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + + p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->next_dtd_ptr = 1; + p_QH->size_ioc_int_sts = 0; + + return; +} + +/* Setup qh structure and ep register for ep0. */ +static void ep0_setup(struct tegra_udc *udc) +{ + /* the intialization of an ep includes: fields in QH, Regs, + * tegra_ep struct */ + struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 1, 0); + struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 1, 0); + dr_ep_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL); + dr_ep_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL); + + return; + +} + +/** + * when configurations are set, or when interface settings change + * for example the do_set_interface() in gadget layer, + * the driver will enable or disable the relevant endpoints + * ep0 doesn't use this routine. It is always enabled. + */ +static int tegra_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct tegra_udc *udc = NULL; + struct tegra_ep *ep = NULL; + unsigned short max = 0; + unsigned char mult = 0, zlt; + int retval = -EINVAL; + unsigned long flags = 0; + + ep = container_of(_ep, struct tegra_ep, ep); + + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc + || (desc->bDescriptorType != USB_DT_ENDPOINT)) + return -EINVAL; + + udc = ep->udc; + + if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* Disable automatic zlp generation. Driver is responsible to indicate + * explicitly through req->req.zero. This is needed to enable multi-td + * request. + */ + zlt = 1; + + /* Assume the max packet size from gadget is always correct */ + switch (desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + /* mult = 0. Execute N Transactions as demonstrated by + * the USB variable length packet protocol where N is + * computed using the Maximum Packet Length (dQH) and + * the Total Bytes field (dTD) */ + mult = 0; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Calculate transactions needed for high bandwidth iso */ + mult = (unsigned char)(1 + ((max >> 11) & 0x03)); + max = max & 0x7ff; /* bit 0~10 */ + /* 3 transactions at most */ + if (mult > 3) + goto en_done; + break; + default: + goto en_done; + } + + spin_lock_irqsave(&udc->lock, flags); + ep->ep.maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + + /* Controller related setup + * Init EPx Queue Head (Ep Capabilites field in QH + * according to max, zlt, mult) + */ + struct_ep_qh_setup(udc, (unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK), + max, zlt, mult); + + /* Init endpoint ctrl register */ + dr_ep_setup(udc, (unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK)); + + spin_unlock_irqrestore(&udc->lock, flags); + retval = 0; + + VDBG("enabled %s (ep%d%s) maxpacket %d", ep->ep.name, + ep->desc->bEndpointAddress & 0x0f, + (desc->bEndpointAddress & USB_DIR_IN) + ? "in" : "out", max); +en_done: + return retval; +} + +/** + * @ep : the ep being unconfigured. May not be ep0 + * Any pending and uncomplete req will complete with status (-ESHUTDOWN) + */ +static int tegra_ep_disable(struct usb_ep *_ep) +{ + struct tegra_udc *udc = NULL; + struct tegra_ep *ep = NULL; + + unsigned long flags = 0; + u32 epctrl; + int ep_num; + + ep = container_of(_ep, struct tegra_ep, ep); + if (!_ep || !ep->desc) { + VDBG("%s not enabled", _ep ? ep->ep.name : NULL); + return -EINVAL; + } + udc = (struct tegra_udc *)ep->udc; + + /* disable ep on controller */ + ep_num = ep_index(ep); + + /* Touch the registers if cable is connected and phy is on */ + if (vbus_enabled(udc)) { + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + udc_writel(udc, epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + } + + spin_lock_irqsave(&udc->lock, flags); + + /* nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = NULL; + ep->stopped = 1; + spin_unlock_irqrestore(&udc->lock, flags); + + VDBG("disabled %s OK", _ep->name); + return 0; +} + +/** + * Allocate a request object used by this endpoint + * the main operation is to insert the req->queue to the eq->queue + * Returns the request, or null if one could not be allocated + */ +static struct usb_request * +tegra_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct tegra_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void tegra_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct tegra_req *req = NULL; + + req = container_of(_req, struct tegra_req, req); + + if (_req) + kfree(req); +} + +static void tegra_queue_td(struct tegra_ep *ep, struct tegra_req *req) +{ + int i = ep_index(ep) * 2 + ep_is_in(ep); + u32 temp, bitmask, tmp_stat; + struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; + struct tegra_udc *udc = ep->udc; + + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + /* Flush all the dTD structs out to memory */ + wmb(); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + /* Add td to the end */ + struct tegra_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct tegra_req, queue); + lastreq->tail->next_td_ptr = + cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + wmb(); + /* Read prime bit, if 1 goto done */ + if (udc_readl(udc, EP_PRIME_REG_OFFSET) & bitmask) + goto out; + + do { + /* Set ATDTW bit in USBCMD */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + temp |= USB_CMD_ATDTW; + udc_writel(udc, temp, USB_CMD_REG_OFFSET); + + /* Read correct status bit */ + tmp_stat = udc_readl(udc, EP_STATUS_REG_OFFSET) + & bitmask; + + } while (!(udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_ATDTW)); + + /* Write ATDTW bit to 0 */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + udc_writel(udc, temp & ~USB_CMD_ATDTW, USB_CMD_REG_OFFSET); + + if (tmp_stat) + goto out; + } + + /* Write dQH next pointer and terminate bit to 0 */ + temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + dQH->next_dtd_ptr = cpu_to_le32(temp); + + /* Clear active and halt bit */ + temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); + dQH->size_ioc_int_sts &= temp; + + tegra_usb_phy_memory_prefetch_on(udc->phy); + + /* Ensure that updates to the QH will occur before priming. */ + wmb(); + + /* Prime endpoint by writing 1 to ENDPTPRIME */ + temp = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + udc_writel(udc, temp, EP_PRIME_REG_OFFSET); +out: + return; +} + +/** + * Fill in the dTD structure + * @req : request that the transfer belongs to + * @length : return actually data length of the dTD + * @dma : return dma address of the dTD + * @is_last : return flag if it is the last dTD of the request + * return : pointer to the built dTD + */ +static struct ep_td_struct *tegra_build_dtd(struct tegra_req *req, + unsigned *length, dma_addr_t *dma, int *is_last, gfp_t gfp_flags) +{ + u32 swap_temp; + struct ep_td_struct *dtd; + + /* how big will this transfer be? */ + *length = min(req->req.length - req->req.actual, + (unsigned)EP_MAX_LENGTH_TRANSFER); + + dtd = dma_pool_alloc(the_udc->td_pool, gfp_flags, dma); + if (dtd == NULL) + return dtd; + + dtd->td_dma = *dma; + /* Clear reserved field */ + swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp &= ~DTD_RESERVED_FIELDS; + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + /* Init all of buffer page pointers */ + swap_temp = (u32) (req->req.dma + req->req.actual); + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + + req->req.actual += *length; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + if ((*is_last) == 0) + VDBG("multi-dtd request!"); + + /* Fill in the transfer size; set active bit */ + swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); + + /* Enable interrupt for the last dtd of a request */ + if (*is_last && !req->req.no_interrupt) + swap_temp |= DTD_IOC; + + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + mb(); + + VDBG("length = %d address= 0x%x", *length, (int)*dma); + + return dtd; +} + +/* Generate dtd chain for a request */ +static int tegra_req_to_dtd(struct tegra_req *req, gfp_t gfp_flags) +{ + unsigned count; + int is_last; + int is_first = 1; + struct ep_td_struct *last_dtd = NULL, *dtd; + dma_addr_t dma; + + tegra_usb_phy_memory_prefetch_off(the_udc->phy); + + do { + dtd = tegra_build_dtd(req, &count, &dma, &is_last, gfp_flags); + if (dtd == NULL) + return -ENOMEM; + + if (is_first) { + is_first = 0; + req->head = dtd; + } else { + last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_virt = dtd; + } + last_dtd = dtd; + + req->dtd_count++; + } while (!is_last); + + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + + req->tail = dtd; + + return 0; +} + +/* queues (submits) an I/O request to an endpoint */ +static int +tegra_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct tegra_ep *ep = container_of(_ep, struct tegra_ep, ep); + struct tegra_req *req = container_of(_req, struct tegra_req, req); + struct tegra_udc *udc = ep->udc; + unsigned long flags; + enum dma_data_direction dir; + int status; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + VDBG("%s, bad params", __func__); + return -EINVAL; + } + + spin_lock_irqsave(&udc->lock, flags); + + if (unlikely(!ep->desc)) { + VDBG("%s, bad ep", __func__); + spin_unlock_irqrestore(&udc->lock, flags); + return -EINVAL; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EMSGSIZE; + } + } + +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + if (req->req.length >= BOOST_TRIGGER_SIZE) { + ep_queue_request_count++; + if (ep_queue_request_count && boost_cpufreq_work_flag) + schedule_work(&udc->boost_cpufreq_work); + } +#endif + + dir = ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + spin_unlock_irqrestore(&udc->lock, flags); + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + req->ep = ep; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(udc->gadget.dev.parent, + req->req.buf, req->req.length, dir); + req->mapped = 1; + } else { + dma_sync_single_for_device(udc->gadget.dev.parent, + req->req.dma, req->req.length, dir); + req->mapped = 0; + } + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + + /* build dtds and push them to device queue */ + status = tegra_req_to_dtd(req, gfp_flags); + if (status) + goto err_unmap; + + spin_lock_irqsave(&udc->lock, flags); + + /* re-check if the ep has not been disabled */ + if (unlikely(!ep->desc)) { + spin_unlock_irqrestore(&udc->lock, flags); + status = -EINVAL; + goto err_unmap; + } + + tegra_queue_td(ep, req); + + /* Update ep0 state */ + if ((ep_index(ep) == 0)) + udc->ep0_state = DATA_STATE_XMIT; + + /* irq handler advances the queue */ + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; + +err_unmap: + if (req->mapped) { + dma_unmap_single(udc->gadget.dev.parent, + req->req.dma, req->req.length, dir); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } + return status; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int tegra_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct tegra_ep *ep = container_of(_ep, struct tegra_ep, ep); + struct tegra_req *req; + struct tegra_udc *udc = ep->udc; + unsigned long flags; + int ep_num, stopped, ret = 0; + u32 epctrl; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + stopped = ep->stopped; + + /* Stop the ep before we deal with the queue */ + ep->stopped = 1; + ep_num = ep_index(ep); + + /* Touch the registers if cable is connected and phy is on */ + if (vbus_enabled(udc)) { + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + udc_writel(udc, epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + } + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + tegra_ep_fifo_flush(_ep); /* flush current transfer */ + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + struct ep_queue_head *qh; + struct tegra_req *next_req; + + qh = ep->qh; + next_req = list_entry(req->queue.next, struct tegra_req, + queue); + + /* Point the QH to the first TD of next request */ + writel((u32) next_req->head, &qh->curr_dtd_ptr); + } + + /* The request hasn't been processed, patch up the TD chain */ + } else { + struct tegra_req *prev_req; + + prev_req = list_entry(req->queue.prev, struct tegra_req, queue); + writel(readl(&req->tail->next_td_ptr), + &prev_req->tail->next_td_ptr); + } + + done(ep, req, -ECONNRESET); + + /* Enable EP */ +out: + /* Touch the registers if cable is connected and phy is on */ + if (vbus_enabled(udc)) { + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (ep_is_in(ep)) + epctrl |= EPCTRL_TX_ENABLE; + else + epctrl |= EPCTRL_RX_ENABLE; + udc_writel(udc, epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + } + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return ret; +} + +/** + * modify the endpoint halt feature + * @ep: the non-isochronous endpoint being stalled + * @value: 1--set halt 0--clear halt + * Returns zero, or a negative error code. + */ +static int tegra_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct tegra_ep *ep = NULL; + unsigned long flags = 0; + int status = -EOPNOTSUPP; /* operation not supported */ + unsigned char ep_dir = 0, ep_num = 0; + struct tegra_udc *udc = NULL; + + ep = container_of(_ep, struct tegra_ep, ep); + udc = ep->udc; + if (!_ep || !ep->desc) { + status = -EINVAL; + goto out; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + status = 0; + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + ep_num = (unsigned char)(ep_index(ep)); + spin_lock_irqsave(&ep->udc->lock, flags); + dr_ep_change_stall(udc, ep_num, ep_dir, value); + spin_unlock_irqrestore(&ep->udc->lock, flags); + + if (ep_index(ep) == 0) { + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + } +out: + VDBG(" %s %s halt stat %d", ep->ep.name, + value ? "set" : "clear", status); + + return status; +} + +static int tegra_ep_fifo_status(struct usb_ep *_ep) +{ + struct tegra_ep *ep; + struct tegra_udc *udc; + int size = 0; + u32 bitmask; + struct ep_queue_head *d_qh; + + ep = container_of(_ep, struct tegra_ep, ep); + if (!_ep || (!ep->desc && ep_index(ep) != 0)) + return -ENODEV; + + udc = (struct tegra_udc *)ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; + + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + if (udc_readl(udc, EP_STATUS_REG_OFFSET) & bitmask) + size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + + pr_debug("%s %u\n", __func__, size); + return size; +} + +static void tegra_ep_fifo_flush(struct usb_ep *_ep) +{ + struct tegra_ep *ep; + struct tegra_udc *udc; + int ep_num, ep_dir; + u32 bits; + unsigned long timeout; + + if (!_ep) { + return; + } else { + ep = container_of(_ep, struct tegra_ep, ep); + if (!ep->desc) + return; + } + ep_num = ep_index(ep); + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + udc = ep->udc; + + if (ep_num == 0) + bits = (1 << 16) | 1; + else if (ep_dir == USB_SEND) + bits = 1 << (16 + ep_num); + else + bits = 1 << ep_num; + + /* Touch the registers if cable is connected and phy is on */ + if (!vbus_enabled(udc)) + return; + + timeout = jiffies + UDC_FLUSH_TIMEOUT_MS; + do { + udc_writel(udc, bits, EPFLUSH_REG_OFFSET); + + /* Wait until flush complete */ + while (udc_readl(udc, EPFLUSH_REG_OFFSET)) { + if (time_after(jiffies, timeout)) { + ERR("ep flush timeout\n"); + return; + } + cpu_relax(); + } + /* See if we need to flush again */ + } while (udc_readl(udc, EP_STATUS_REG_OFFSET) & bits); +} + +static struct usb_ep_ops tegra_ep_ops = { + .enable = tegra_ep_enable, + .disable = tegra_ep_disable, + + .alloc_request = tegra_alloc_request, + .free_request = tegra_free_request, + + .queue = tegra_ep_queue, + .dequeue = tegra_ep_dequeue, + + .set_halt = tegra_ep_set_halt, + .fifo_status = tegra_ep_fifo_status, + .fifo_flush = tegra_ep_fifo_flush, /* flush fifo */ +}; + +/* Get the current frame number (from DR frame_index Reg ) */ +static int tegra_get_frame(struct usb_gadget *gadget) +{ + struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); + return (int)(udc_readl(udc, USB_FRINDEX_REG_OFFSET) + & USB_FRINDEX_MASKS); +} + +#ifndef CONFIG_USB_ANDROID +/* Tries to wake up the host connected to this gadget */ +static int tegra_wakeup(struct usb_gadget *gadget) +{ + struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); + u32 portsc; + + /* Remote wakeup feature not enabled by host */ + if (!udc->remote_wakeup) + return -ENOTSUPP; + + portsc = udc_readl(udc, PORTSCX_REG_OFFSET); + /* not suspended? */ + if (!(portsc & PORTSCX_PORT_SUSPEND)) + return 0; + + /* trigger force resume */ + portsc |= PORTSCX_PORT_FORCE_RESUME; + udc_writel(udc, portsc, PORTSCX_REG_OFFSET); + return 0; +} +#endif + +static int tegra_set_selfpowered(struct usb_gadget *gadget, int is_on) +{ + struct tegra_udc *udc; + udc = container_of(gadget, struct tegra_udc, gadget); + udc->selfpowered = (is_on != 0); + return 0; +} + +/** + * Notify controller that VBUS is powered, called by whatever + * detects VBUS sessions + */ +static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); + unsigned long flags; + DBG("%s(%d) turn VBUS state from %s to %s", __func__, __LINE__, + udc->vbus_active ? "on" : "off", is_active ? "on" : "off"); + + if (udc->vbus_active && !is_active) { + /* If cable disconnected, cancel any delayed work */ + cancel_delayed_work(&udc->work); + spin_lock_irqsave(&udc->lock, flags); + /* reset all internal Queues and inform client driver */ + reset_queues(udc); + /* stop the controller and turn off the clocks */ + dr_controller_stop(udc); + dr_controller_reset(udc); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + spin_unlock_irqrestore(&udc->lock,flags); + tegra_usb_phy_power_off(udc->phy); + if (udc->vbus_reg) { + /* set the current limit to 0mA */ + regulator_set_current_limit( + udc->vbus_reg, 0, 0); + } + } else if (!udc->vbus_active && is_active) { + tegra_usb_phy_power_on(udc->phy); + /* setup the controller in the device mode */ + dr_controller_setup(udc); + /* setup EP0 for setup packet */ + ep0_setup(udc); + /* initialize the USB and EP states */ + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + udc->vbus_active = 1; + /* start the controller */ + dr_controller_run(udc); + if (udc->vbus_reg) { + /* set the current limit to 100mA */ + regulator_set_current_limit( + udc->vbus_reg, 0, 100); + } + /* Schedule work to wait for 1000 msec and check for + * charger if setup packet is not received */ + schedule_delayed_work(&udc->work, + USB_CHARGER_DETECTION_WAIT_TIME_MS); + } + return 0; +} + +/** + * Constrain controller's VBUS power usage. + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int tegra_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct tegra_udc *udc; + + udc = container_of(gadget, struct tegra_udc, gadget); + /* check udc regulator is available for drawing the vbus current */ + if (udc->vbus_reg) { + udc->current_limit = mA; + schedule_work(&udc->charger_work); + } + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -ENOTSUPP; +} + +/** + * Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnect + */ +static int tegra_pullup(struct usb_gadget *gadget, int is_on) +{ + struct tegra_udc *udc; + u32 tmp; + + udc = container_of(gadget, struct tegra_udc, gadget); + udc->softconnect = (is_on != 0); + if (udc->transceiver && udc->transceiver->state != + OTG_STATE_B_PERIPHERAL) + return 0; + + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + if (can_pullup(udc)) + udc_writel(udc, tmp | USB_CMD_RUN_STOP, + USB_CMD_REG_OFFSET); + else + udc_writel(udc, (tmp & ~USB_CMD_RUN_STOP), + USB_CMD_REG_OFFSET); + + return 0; +} + +/* Release udc structures */ +static void tegra_udc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_udc *udc = platform_get_drvdata(pdev); + + complete(udc->done); + tegra_usb_phy_close(udc->phy); + kfree(udc); +} + +static int tegra_udc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int tegra_udc_stop(struct usb_gadget_driver *driver); +/* defined in gadget.h */ +static struct usb_gadget_ops tegra_gadget_ops = { + .get_frame = tegra_get_frame, +#ifndef CONFIG_USB_ANDROID + .wakeup = tegra_wakeup, +#endif + .set_selfpowered = tegra_set_selfpowered, + .vbus_session = tegra_vbus_session, + .vbus_draw = tegra_vbus_draw, + .pullup = tegra_pullup, + .start = tegra_udc_start, + .stop = tegra_udc_stop, +}; + +static int tegra_udc_setup_gadget_dev(struct tegra_udc *udc) +{ + /* Setup gadget structure */ + udc->gadget.ops = &tegra_gadget_ops; + udc->gadget.is_dualspeed = 1; + udc->gadget.ep0 = &udc->eps[0].ep; + INIT_LIST_HEAD(&udc->gadget.ep_list); + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.name = driver_name; + + /* Setup gadget.dev and register with kernel */ + dev_set_name(&udc->gadget.dev, "gadget"); + udc->gadget.dev.release = tegra_udc_release; + udc->gadget.dev.parent = &udc->pdev->dev; + + return device_register(&udc->gadget.dev); +} + + +/** + * Set protocol stall on ep0, protocol stall will automatically be cleared + * on new transaction. + */ +static void ep0stall(struct tegra_udc *udc) +{ + u32 tmp; + + /* must set tx and rx to stall at the same time */ + tmp = udc_readl(udc, EP_CONTROL_REG_OFFSET); + tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; + udc_writel(udc, tmp, EP_CONTROL_REG_OFFSET); + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; +} + +/* Prime a status phase for ep0 */ +static int ep0_prime_status(struct tegra_udc *udc, int direction) +{ + struct tegra_req *req = udc->status_req; + struct tegra_ep *ep; + + if (direction == EP_DIR_IN) + udc->ep0_dir = USB_DIR_IN; + else + udc->ep0_dir = USB_DIR_OUT; + + ep = &udc->eps[0]; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + + req->ep = ep; + req->req.length = 0; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + if (tegra_req_to_dtd(req, GFP_ATOMIC) == 0) + tegra_queue_td(ep, req); + else + return -ENOMEM; + + list_add_tail(&req->queue, &ep->queue); + + return 0; +} + +static void udc_reset_ep_queue(struct tegra_udc *udc, u8 pipe) +{ + struct tegra_ep *ep = get_ep_by_pipe(udc, pipe); + + if (ep->name) + nuke(ep, -ESHUTDOWN); +} + +/* ch9 Set address */ +static void ch9setaddress(struct tegra_udc *udc, u16 value, u16 index, + u16 length) +{ + /* Save the new address to device struct */ + udc->device_address = (u8) value; + /* Update usb state */ + udc->usb_state = USB_STATE_ADDRESS; + /* Status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); +} + +/* ch9 Get status */ +static void ch9getstatus(struct tegra_udc *udc, u8 request_type, u16 value, + u16 index, u16 length) +{ + u16 tmp = 0; /* Status, cpu endian */ + struct tegra_req *req; + struct tegra_ep *ep; + + ep = &udc->eps[0]; + + if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* Get device status */ + if (udc->selfpowered) + tmp = 1 << USB_DEVICE_SELF_POWERED; + tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + /* Get interface status + * We don't have interface information in udc driver */ + tmp = 0; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { + /* Get endpoint status */ + struct tegra_ep *target_ep; + + target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index)); + + /* stall if endpoint doesn't exist */ + if (!target_ep->desc) + goto stall; + tmp = dr_ep_get_stall(udc, ep_index(target_ep), + ep_is_in(target_ep)) << USB_ENDPOINT_HALT; + } + + udc->ep0_dir = USB_DIR_IN; + /* Borrow the per device status_req */ + req = udc->status_req; + /* Fill in the reqest structure */ + *((u16 *) req->req.buf) = cpu_to_le16(tmp); + req->ep = ep; + req->req.length = 2; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 0; + } + + /* prime the data phase */ + if ((tegra_req_to_dtd(req, GFP_ATOMIC) == 0)) + tegra_queue_td(ep, req); + else /* no mem */ + goto stall; + + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + return; +stall: + ep0stall(udc); +} + +static void udc_test_mode(struct tegra_udc *udc, u32 test_mode) +{ + struct tegra_req *req; + struct tegra_ep *ep; + u32 portsc, bitmask; + unsigned long timeout; + + /* Ack the ep0 IN */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + + /* get the ep0 */ + ep = &udc->eps[0]; + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + timeout = jiffies + HZ; + /* Wait until ep0 IN endpoint txfr is complete */ + while (!(udc_readl(udc, EP_COMPLETE_REG_OFFSET) & bitmask)) { + if (time_after(jiffies, timeout)) { + pr_err("Timeout for Ep0 IN Ack\n"); + break; + } + cpu_relax(); + } + + switch (test_mode << PORTSCX_PTC_BIT_POS) { + case PORTSCX_PTC_JSTATE: + VDBG("TEST_J\n"); + break; + case PORTSCX_PTC_KSTATE: + VDBG("TEST_K\n"); + break; + case PORTSCX_PTC_SEQNAK: + VDBG("TEST_SE0_NAK\n"); + break; + case PORTSCX_PTC_PACKET: + VDBG("TEST_PACKET\n"); + + /* get the ep and configure for IN direction */ + ep = &udc->eps[0]; + udc->ep0_dir = USB_DIR_IN; + + /* Initialize ep0 status request structure */ + req = container_of(tegra_alloc_request(NULL, GFP_ATOMIC), + struct tegra_req, req); + /* allocate a small amount of memory to get valid address */ + req->req.buf = kmalloc(sizeof(tegra_udc_test_packet), + GFP_ATOMIC); + req->req.dma = virt_to_phys(req->req.buf); + + /* Fill in the reqest structure */ + memcpy(req->req.buf, tegra_udc_test_packet, + sizeof(tegra_udc_test_packet)); + req->ep = ep; + req->req.length = sizeof(tegra_udc_test_packet); + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + req->mapped = 0; + + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + /* prime the data phase */ + if ((tegra_req_to_dtd(req, GFP_ATOMIC) == 0)) + tegra_queue_td(ep, req); + else /* no mem */ + goto stall; + + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + break; + case PORTSCX_PTC_FORCE_EN: + VDBG("TEST_FORCE_EN\n"); + break; + default: + ERR("udc unknown test mode[%d]!\n", test_mode); + goto stall; + } + + /* read the portsc register */ + portsc = udc_readl(udc, PORTSCX_REG_OFFSET); + /* set the test mode selector */ + portsc |= test_mode << PORTSCX_PTC_BIT_POS; + udc_writel(udc, portsc, PORTSCX_REG_OFFSET); + + /* + * The device must have its power cycled to exit test mode. + * See USB 2.0 spec, section 9.4.9 for test modes operation + * in "Set Feature". + * See USB 2.0 spec, section 7.1.20 for test modes. + */ + pr_info("udc entering the test mode, power cycle to exit test mode\n"); + return; +stall: + ep0stall(udc); +} + +static void setup_received_irq(struct tegra_udc *udc, + struct usb_ctrlrequest *setup) +{ + u16 wValue = le16_to_cpu(setup->wValue); + u16 wIndex = le16_to_cpu(setup->wIndex); + u16 wLength = le16_to_cpu(setup->wLength); + + udc_reset_ep_queue(udc, 0); + + /* We process some stardard setup requests here */ + switch (setup->bRequest) { + case USB_REQ_GET_STATUS: + /* Data+Status phase from udc */ + if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength); + return; + + case USB_REQ_SET_ADDRESS: + /* Status phase from udc */ + if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD + | USB_RECIP_DEVICE)) + break; + /* This delay is necessary for some windows drivers to + * properly recognize the device */ + mdelay(1); + ch9setaddress(udc, wValue, wIndex, wLength); + return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* Status phase from udc */ + { + int rc = -EOPNOTSUPP; + + if (setup->bRequestType == USB_RECIP_DEVICE && + wValue == USB_DEVICE_TEST_MODE) { + /* + * If the feature selector is TEST_MODE, then the most + * significant byte of wIndex is used to specify the + * specific test mode and the lower byte of wIndex must + * be zero. + */ + udc_test_mode(udc, wIndex >> 8); + return; + + } else if ((setup->bRequestType & + (USB_RECIP_MASK | USB_TYPE_MASK)) == + (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { + int pipe = get_pipe_by_windex(wIndex); + struct tegra_ep *ep; + + if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) + break; + ep = get_ep_by_pipe(udc, pipe); + + spin_unlock(&udc->lock); + rc = tegra_ep_set_halt(&ep->ep, + (setup->bRequest == USB_REQ_SET_FEATURE) + ? 1 : 0); + spin_lock(&udc->lock); + + } else if ((setup->bRequestType & (USB_RECIP_MASK + | USB_TYPE_MASK)) == (USB_RECIP_DEVICE + | USB_TYPE_STANDARD)) { + /* Note: The driver has not include OTG support yet. + * This will be set when OTG support is added */ + if (!gadget_is_otg(&udc->gadget)) + break; + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + else + break; + rc = 0; + } else + break; + + if (rc == 0) { + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + } + return; + } + + default: + break; + } + + /* Requests handled by gadget */ + if (wLength) { + /* Data phase from gadget, status phase from udc */ + udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? USB_DIR_IN : USB_DIR_OUT; + spin_unlock(&udc->lock); + if (udc->driver && udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = (setup->bRequestType & USB_DIR_IN) + ? DATA_STATE_XMIT : DATA_STATE_RECV; + } else { + /* No data phase, IN status from gadget */ + udc->ep0_dir = USB_DIR_IN; + spin_unlock(&udc->lock); + if (udc->driver && udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = WAIT_FOR_OUT_STATUS; + } +} + +/* Process request for Data or Status phase of ep0 + * prime status phase if needed */ +static void ep0_req_complete(struct tegra_udc *udc, struct tegra_ep *ep0, + struct tegra_req *req) +{ + if (udc->usb_state == USB_STATE_ADDRESS) { + /* Set the new address */ + u32 new_address = (u32) udc->device_address; + udc_writel(udc, new_address << USB_DEVICE_ADDRESS_BIT_POS, + USB_DEVICE_ADDR_REG_OFFSET); + } + + done(ep0, req, 0); + + switch (udc->ep0_state) { + case DATA_STATE_XMIT: + /* receive status phase */ + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + break; + case DATA_STATE_RECV: + /* send status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + break; + case WAIT_FOR_OUT_STATUS: + udc->ep0_state = WAIT_FOR_SETUP; + break; + case WAIT_FOR_SETUP: + ERR("Unexpect ep0 packets\n"); + break; + default: + ep0stall(udc); + break; + } +} + +/* Tripwire mechanism to ensure a setup packet payload is extracted without + * being corrupted by another incoming setup packet */ +static void tripwire_handler(struct tegra_udc *udc, u8 ep_num, u8 *buffer_ptr) +{ + u32 temp; + struct ep_queue_head *qh; + + qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; + + /* Clear bit in ENDPTSETUPSTAT */ + temp = udc_readl(udc, EP_SETUP_STATUS_REG_OFFSET); + udc_writel(udc, temp | (1 << ep_num), EP_SETUP_STATUS_REG_OFFSET); + + /* while a hazard exists when setup package arrives */ + do { + /* Set Setup Tripwire */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + udc_writel(udc, temp | USB_CMD_SUTW, USB_CMD_REG_OFFSET); + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } while (!(udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_SUTW)); + + /* Clear Setup Tripwire */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + udc_writel(udc, temp & ~USB_CMD_SUTW, USB_CMD_REG_OFFSET); +} + +/* process-ep_req(): free the completed Tds for this req */ +static int process_ep_req(struct tegra_udc *udc, int pipe, + struct tegra_req *curr_req) +{ + struct ep_td_struct *curr_td; + int td_complete, actual, remaining_length, j, tmp; + int status = 0; + int errors = 0; + struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; + int direction = pipe % 2; + + curr_td = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + for (j = 0; j < curr_req->dtd_count; j++) { + /* Fence read for coherency of AHB master intiated writes */ + readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); + + dma_sync_single_for_cpu(udc->gadget.dev.parent, curr_td->td_dma, + sizeof(struct ep_td_struct), DMA_FROM_DEVICE); + + remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + actual -= remaining_length; + errors = le32_to_cpu(curr_td->size_ioc_sts); + if (errors & DTD_ERROR_MASK) { + if (errors & DTD_STATUS_HALTED) { + ERR("dTD error %08x QH=%d\n", errors, pipe); + /* Clear the errors and Halt condition */ + tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp &= ~errors; + curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + status = -EPIPE; + /* FIXME: continue with next queued TD? */ + + break; + } + if (errors & DTD_STATUS_DATA_BUFF_ERR) { + VDBG("Transfer overflow"); + status = -EPROTO; + break; + } else if (errors & DTD_STATUS_TRANSACTION_ERR) { + VDBG("ISO error"); + status = -EILSEQ; + break; + } else + ERR("Unknown error has occurred (0x%x)!\n", + errors); + + } else if (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_STATUS_ACTIVE) { + VDBG("Request not complete"); + status = REQ_UNCOMPLETE; + return status; + } else if (remaining_length) { + if (direction) { + VDBG("Transmit dTD remaining length not zero"); + status = -EPROTO; + break; + } else { + td_complete++; + break; + } + } else { + td_complete++; + VDBG("dTD transmitted successful"); + } + + if (j != curr_req->dtd_count - 1) + curr_td = (struct ep_td_struct *)curr_td->next_td_virt; + } + + if (status) + return status; + + curr_req->req.actual = actual; + + return 0; +} + +/* Process a DTD completion interrupt */ +static void dtd_complete_irq(struct tegra_udc *udc) +{ + u32 bit_pos; + int i, ep_num, direction, bit_mask, status; + struct tegra_ep *curr_ep; + struct tegra_req *curr_req, *temp_req; + + /* Clear the bits in the register */ + bit_pos = udc_readl(udc, EP_COMPLETE_REG_OFFSET); + udc_writel(udc, bit_pos, EP_COMPLETE_REG_OFFSET); + + if (!bit_pos) + return; + + for (i = 0; i < udc->max_ep; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_mask = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & bit_mask)) + continue; + + curr_ep = get_ep_by_pipe(udc, i); + + /* If the ep is configured */ + if (curr_ep->name == NULL) { + WARNING("Invalid EP?"); + continue; + } + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue, + queue) { + status = process_ep_req(udc, i, curr_req); + + VDBG("status of process_ep_req= %d, ep = %d", + status, ep_num); + if (status == REQ_UNCOMPLETE) + break; + /* write back status to req */ + curr_req->req.status = status; + + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else + done(curr_ep, curr_req, status); + } + } +} + +/* Process a port change interrupt */ +static void port_change_irq(struct tegra_udc *udc) +{ + u32 speed; + unsigned int port_control_reg_offset; + + if (udc->has_hostpc) + port_control_reg_offset = USB_HOSTPCX_DEVLC_REG_OFFSET; + else + port_control_reg_offset = PORTSCX_REG_OFFSET; + + /* Bus resetting is finished */ + if (!(udc_readl(udc, port_control_reg_offset) & PORTSCX_PORT_RESET)) { + /* Get the speed */ + speed = (udc_readl(udc, port_control_reg_offset) + & PORTSCX_PORT_SPEED_MASK); + if (speed == PORTSCX_PORT_SPEED_HIGH) + udc->gadget.speed = USB_SPEED_HIGH; + else if (speed == PORTSCX_PORT_SPEED_FULL) + udc->gadget.speed = USB_SPEED_FULL; + else if (speed == PORTSCX_PORT_SPEED_LOW) + udc->gadget.speed = USB_SPEED_LOW; + else + udc->gadget.speed = USB_SPEED_UNKNOWN; + } + + /* Update USB state */ + if (!udc->resume_state) + udc->usb_state = USB_STATE_DEFAULT; +} + +/* Process suspend interrupt */ +static void suspend_irq(struct tegra_udc *udc) +{ + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver, serial.c does not support this */ + if (udc->driver && udc->driver->suspend) + udc->driver->suspend(&udc->gadget); +} + +static void bus_resume(struct tegra_udc *udc) +{ + udc->usb_state = udc->resume_state; + udc->resume_state = 0; + + /* report resume to the driver, serial.c does not support this */ + if (udc->driver && udc->driver->resume) + udc->driver->resume(&udc->gadget); +} + +/* Clear up all ep queues */ +static int reset_queues(struct tegra_udc *udc) +{ + u8 pipe; + + for (pipe = 0; pipe < udc->max_pipes; pipe++) + udc_reset_ep_queue(udc, pipe); + + /* report disconnect; the driver is already quiesced */ + spin_unlock(&udc->lock); + if (udc->driver && udc->driver->disconnect) + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + + return 0; +} + +/* Process reset interrupt */ +static void reset_irq(struct tegra_udc *udc) +{ + u32 temp; + unsigned long timeout; + + /* Clear the device address */ + temp = udc_readl(udc, USB_DEVICE_ADDR_REG_OFFSET); + udc_writel(udc, temp & ~USB_DEVICE_ADDRESS_MASK, + USB_DEVICE_ADDR_REG_OFFSET); + + udc->device_address = 0; + + /* Clear usb state */ + udc->resume_state = 0; + udc->ep0_dir = 0; + udc->ep0_state = WAIT_FOR_SETUP; + udc->remote_wakeup = 0; /* default to 0 on reset */ + udc->gadget.b_hnp_enable = 0; + udc->gadget.a_hnp_support = 0; + udc->gadget.a_alt_hnp_support = 0; + + /* Clear all the setup token semaphores */ + temp = udc_readl(udc, EP_SETUP_STATUS_REG_OFFSET); + udc_writel(udc, temp, EP_SETUP_STATUS_REG_OFFSET); + + /* Clear all the endpoint complete status bits */ + temp = udc_readl(udc, EP_COMPLETE_REG_OFFSET); + udc_writel(udc, temp, EP_COMPLETE_REG_OFFSET); + + timeout = jiffies + 100; + while (udc_readl(udc, EP_PRIME_REG_OFFSET)) { + /* Wait until all endptprime bits cleared */ + if (time_after(jiffies, timeout)) { + ERR("Timeout for reset\n"); + break; + } + cpu_relax(); + } + + /* Write 1s to the flush register */ + udc_writel(udc, 0xffffffff, EPFLUSH_REG_OFFSET); + + /* When the bus reset is seen on Tegra, the PORTSCX_PORT_RESET bit + * is not set. Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + VDBG("Bus reset"); + reset_queues(udc); + udc->usb_state = USB_STATE_DEFAULT; +} + +static void tegra_udc_set_current_limit_work(struct work_struct *work) +{ + struct tegra_udc *udc = container_of(work, struct tegra_udc, + charger_work); + /* check udc regulator is available for drawing vbus current*/ + if (udc->vbus_reg) { + /* set the current limit in uA */ + regulator_set_current_limit( + udc->vbus_reg, 0, + udc->current_limit * 1000); + } +} + +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ +static void tegra_udc_boost_cpu_frequency_work(struct work_struct *work) +{ + if (ep_queue_request_count && boost_cpufreq_work_flag) { + pm_qos_update_request(&boost_cpu_freq_req, + (s32)CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ * 1000); + boost_cpufreq_work_flag = 0; + } else if (!ep_queue_request_count && !boost_cpufreq_work_flag) { + pm_qos_update_request(&boost_cpu_freq_req, + PM_QOS_DEFAULT_VALUE); + boost_cpufreq_work_flag = 1; + } +} +#endif + +static void tegra_udc_irq_work(struct work_struct *irq_work) +{ + struct tegra_udc *udc = container_of(irq_work, struct tegra_udc, + irq_work); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Check whether cable is connected*/ + if (vbus_enabled(udc)) + tegra_vbus_session(&udc->gadget, 1); + else + tegra_vbus_session(&udc->gadget, 0); + + DBG("%s(%d) END\n", __func__, __LINE__); +} + +/** + * If VBUS is detected and setup packet is not received in 100ms then + * work thread starts and checks for the USB charger detection. + */ +static void tegra_udc_charger_detect_work(struct work_struct *work) +{ + struct tegra_udc *udc = container_of(work, struct tegra_udc, work.work); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* check for the platform charger detection */ + if (tegra_usb_phy_charger_detected(udc->phy)) { + printk(KERN_INFO "USB compliant charger detected\n"); + /* check udc regulator is available for drawing vbus current*/ + if (udc->vbus_reg) { + /* set the current limit in uA */ + regulator_set_current_limit( + udc->vbus_reg, 0, + USB_CHARGING_CURRENT_LIMIT_MA*1000); + } + } + + DBG("%s(%d) END\n", __func__, __LINE__); +} + +/* Restart device controller in the OTG mode on VBUS detection */ +static void tegra_udc_restart(struct tegra_udc *udc) +{ + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* setup the controller in the device mode */ + dr_controller_setup(udc); + /* setup EP0 for setup packet */ + ep0_setup(udc); + udc->vbus_active = 1; + /* start the controller */ + dr_controller_run(udc); + /* initialize the USB and EP states */ + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + + DBG("%s(%d) END\n", __func__, __LINE__); +} + +/* USB device controller interrupt handler */ +static irqreturn_t tegra_udc_irq(int irq, void *_udc) +{ + struct tegra_udc *udc = _udc; + u32 irq_src, temp; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + if (!udc->transceiver) { + temp = udc_readl(udc, VBUS_WAKEUP_REG_OFFSET); + /* write back the register to clear the interrupt */ + udc_writel(udc, temp, VBUS_WAKEUP_REG_OFFSET); + if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) + schedule_work(&udc->irq_work); + status = IRQ_HANDLED; + } + + /* Disable ISR for OTG host mode */ + if (udc->stopped) { + spin_unlock_irqrestore(&udc->lock, flags); + return status; + } + + /* Fence read for coherency of AHB master intiated writes */ + readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); + + irq_src = udc_readl(udc, USB_STS_REG_OFFSET) & + udc_readl(udc, USB_INTR_REG_OFFSET); + + /* Clear notification bits */ + udc_writel(udc, irq_src, USB_STS_REG_OFFSET); + + /* Need to resume? */ + if (udc->usb_state == USB_STATE_SUSPENDED) + if (!(udc_readl(udc, PORTSCX_REG_OFFSET) + & PORTSCX_PORT_SUSPEND)) + bus_resume(udc); + + /* USB Interrupt */ + if (irq_src & USB_STS_INT) { + VDBG("Packet int"); + /* Setup package, we only support ep0 as control ep */ + if (udc_readl(udc, EP_SETUP_STATUS_REG_OFFSET) & + EP_SETUP_STATUS_EP0) { + /* Setup packet received, we are connected to host + * and not to charger. Cancel any delayed work */ + __cancel_delayed_work(&udc->work); + tripwire_handler(udc, 0, + (u8 *) (&udc->local_setup_buff)); + setup_received_irq(udc, &udc->local_setup_buff); + status = IRQ_HANDLED; + } + + /* completion of dtd */ + if (udc_readl(udc, EP_COMPLETE_REG_OFFSET)) { + dtd_complete_irq(udc); + status = IRQ_HANDLED; + } + } + + /* SOF (for ISO transfer) */ + if (irq_src & USB_STS_SOF) + status = IRQ_HANDLED; + + /* Port Change */ + if (irq_src & USB_STS_PORT_CHANGE) { + port_change_irq(udc); + status = IRQ_HANDLED; + } + + /* Reset Received */ + if (irq_src & USB_STS_RESET) { + reset_irq(udc); + status = IRQ_HANDLED; + } + + /* Sleep Enable (Suspend) */ + if (irq_src & USB_STS_SUSPEND) { + suspend_irq(udc); + status = IRQ_HANDLED; + } + + if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) + VDBG("Error IRQ %x", irq_src); + + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +/** + * Hook to gadget drivers + * Called by initialization code of gadget drivers + */ +static int tegra_udc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct tegra_udc *udc = the_udc; + int retval = -ENODEV; + unsigned long flags = 0; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (!udc) + return -ENODEV; + + if (!driver || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !bind || !driver->disconnect + || !driver->setup) + return -EINVAL; + + if (udc->driver) + return -EBUSY; + + /* lock is needed but whether should use this lock or another */ + spin_lock_irqsave(&udc->lock, flags); + + driver->driver.bus = NULL; + /* hook up the driver */ + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc->lock, flags); + + /* bind udc driver to gadget driver */ + retval = bind(&udc->gadget); + if (retval) { + VDBG("bind to %s --> %d", driver->driver.name, retval); + udc->gadget.dev.driver = NULL; + udc->driver = NULL; + goto out; + } + + + /* Enable DR IRQ reg and Set usbcmd reg Run bit */ + dr_controller_run(udc); + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + udc->vbus_active = vbus_enabled(udc); + + printk(KERN_INFO "%s: bind to driver %s\n", + udc->gadget.name, driver->driver.name); + +out: + if (retval) + printk(KERN_WARNING "gadget driver register failed %d\n", + retval); + + DBG("%s(%d) END\n", __func__, __LINE__); + return retval; +} + +/* Disconnect from gadget driver */ +static int tegra_udc_stop(struct usb_gadget_driver *driver) +{ + struct tegra_udc *udc = the_udc; + struct tegra_ep *loop_ep; + unsigned long flags; + + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + if (!udc) + return -ENODEV; + + if (!driver || driver != udc->driver || !driver->unbind) + return -EINVAL; + + /* stop DR, disable intr */ + dr_controller_stop(udc); + + /* in fact, no needed */ + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + + /* stand operation */ + spin_lock_irqsave(&udc->lock, flags); + udc->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc->eps[0], -ESHUTDOWN); + list_for_each_entry(loop_ep, &udc->gadget.ep_list, + ep.ep_list) + nuke(loop_ep, -ESHUTDOWN); + spin_unlock_irqrestore(&udc->lock, flags); + + /* report disconnect; the controller is already quiesced */ + driver->disconnect(&udc->gadget); + + /* unbind gadget and unhook driver. */ + driver->unbind(&udc->gadget); + udc->gadget.dev.driver = NULL; + udc->driver = NULL; + + printk(KERN_WARNING "unregistered gadget driver '%s'\n", + driver->driver.name); + + DBG("%s(%d) END\n", __func__, __LINE__); + + return 0; +} + + +/* Internal structure setup functions */ +static int tegra_udc_setup_qh(struct tegra_udc *udc) +{ + u32 dccparams; + size_t size; + + /* Read Device Controller Capability Parameters register */ + dccparams = udc_readl(udc, DCCPARAMS_REG_OFFSET); + if (!(dccparams & DCCPARAMS_DC)) { + ERR("This SOC doesn't support device role\n"); + return -ENODEV; + } + + /* Get max device endpoints */ + /* DEN is bidirectional ep number, max_ep doubles the number */ + udc->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; + + udc->eps = kzalloc(sizeof(struct tegra_ep) * udc->max_ep, GFP_KERNEL); + if (!udc->eps) { + ERR("malloc tegra_ep failed\n"); + return -1; + } + + /* Setup hardware queue heads */ + size = udc->max_ep * sizeof(struct ep_queue_head); + udc->ep_qh = (struct ep_queue_head *)((u8 *)(udc->regs) + QH_OFFSET); + udc->ep_qh_dma = platform_get_resource(udc->pdev, IORESOURCE_MEM + , 0)->start + QH_OFFSET; + udc->ep_qh_size = size; + + /* Initialize ep0 status request structure */ + /* FIXME: tegra_alloc_request() ignores ep argument */ + udc->status_req = container_of(tegra_alloc_request(NULL, GFP_KERNEL), + struct tegra_req, req); + /* Allocate a small amount of memory to get valid address */ + udc->status_req->req.buf = dma_alloc_coherent(&udc->pdev->dev, + STATUS_BUFFER_SIZE, &udc->status_req->req.dma, + GFP_KERNEL); + if (!udc->status_req->req.buf) { + ERR("alloc status_req buffer failed\n"); + kfree(udc->eps); + return -ENOMEM; + } + + udc->resume_state = USB_STATE_NOTATTACHED; + udc->usb_state = USB_STATE_POWERED; + udc->ep0_dir = 0; + udc->remote_wakeup = 0; /* default to 0 on reset */ + + return 0; +} + +/** + * Setup the tegra_ep struct for eps + * Link tegra_ep->ep to gadget->ep_list + * ep0out is not used so do nothing here + * ep0in should be taken care + */ +static int __init struct_ep_setup(struct tegra_udc *udc, unsigned char index, + char *name, int link) +{ + struct tegra_ep *ep = &udc->eps[index]; + + ep->udc = udc; + strcpy(ep->name, name); + ep->ep.name = ep->name; + + ep->ep.ops = &tegra_ep_ops; + ep->stopped = 0; + + /* for ep0: maxP defined in desc + * for other eps, maxP is set by epautoconfig() called by gadget layer + */ + ep->ep.maxpacket = (unsigned short) ~0; + + /* the queue lists any req for this ep */ + INIT_LIST_HEAD(&ep->queue); + + /* gagdet.ep_list used for ep_autoconfig so no ep0 */ + if (link) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + ep->gadget = &udc->gadget; + ep->qh = &udc->ep_qh[index]; + + return 0; +} + +static int tegra_udc_ep_setup(struct tegra_udc *udc) +{ + /* initialize EP0 descriptor */ + static const struct usb_endpoint_descriptor tegra_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, + }; + int i; + + /* setup QH and epctrl for ep0 */ + ep0_setup(udc); + + /* setup udc->eps[] for ep0 */ + struct_ep_setup(udc, 0, "ep0", 0); + /* for ep0: the desc defined here; + * for other eps, gadget layer called ep_enable with defined desc + */ + udc->eps[0].desc = &tegra_ep0_desc; + udc->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + + /* setup the udc->eps[] for non-control endpoints and link + * to gadget.ep_list */ + for (i = 1; i < (int)(udc->max_ep / 2); i++) { + char name[14]; + + sprintf(name, "ep%dout", i); + struct_ep_setup(udc, i * 2, name, 1); + sprintf(name, "ep%din", i); + struct_ep_setup(udc, i * 2 + 1, name, 1); + } + + return 0; +} + + +/* Driver probe function + * all intialization operations implemented here except enabling usb_intr reg + * board setup should have been done in the platform code + */ +static int __init tegra_udc_probe(struct platform_device *pdev) +{ + struct tegra_udc *udc; + struct resource *res; + int err = -ENODEV; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (strcmp(pdev->name, driver_name)) { + VDBG("Wrong device"); + return -ENODEV; + } + + the_udc = udc = kzalloc(sizeof(struct tegra_udc), GFP_KERNEL); + if (udc == NULL) { + ERR("malloc udc failed\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + ERR("failed to get platform resources\n"); + goto err_kfree; + } + + if (!request_mem_region(res->start, res->end - res->start + 1, + driver_name)) { + ERR("request mem region failed\n"); + err = -EBUSY; + goto err_kfree; + } + + udc->regs = ioremap(res->start, resource_size(res)); + if (!udc->regs) { + err = -ENOMEM; + ERR("failed to map mem region\n"); + goto err_rel_mem_region; + } + + udc->irq = platform_get_irq(pdev, 0); + if (!udc->irq) { + err = -ENODEV; + ERR("failed to get platform irq resources\n"); + goto err_iounmap; + } + + err = request_irq(udc->irq, tegra_udc_irq, + IRQF_SHARED | IRQF_TRIGGER_HIGH, + driver_name, udc); + if (err) { + ERR("cannot request irq %d err %d\n", udc->irq, err); + goto err_iounmap; + } + + err = enable_irq_wake(udc->irq); + if (err < 0) { + dev_warn(&pdev->dev, + "Couldn't enable USB udc mode wakeup, irq=%d, error=%d\n", + udc->irq, err); + err = 0; + } + + udc->phy = tegra_usb_phy_open(pdev); + if (IS_ERR(udc->phy)) { + dev_err(&pdev->dev, "failed to open USB phy\n"); + err = -ENXIO; + goto err_irq; + } + + err = tegra_usb_phy_power_on(udc->phy); + if (err) { + dev_err(&pdev->dev, "failed to power on the phy\n"); + goto err_phy; + } + + err = tegra_usb_phy_init(udc->phy); + if (err) { + dev_err(&pdev->dev, "failed to init the phy\n"); + goto err_phy; + } + spin_lock_init(&udc->lock); + udc->stopped = 1; + udc->pdev = pdev; + udc->has_hostpc = tegra_usb_phy_has_hostpc(udc->phy) ? 1 : 0; + platform_set_drvdata(pdev, udc); + + /* Initialize the udc structure including QH members */ + err = tegra_udc_setup_qh(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup udc QH\n"); + goto err_phy; + } + + /* Initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + err = dr_controller_setup(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup udc controller\n"); + goto err_phy; + } + + err = tegra_udc_setup_gadget_dev(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup udc gadget device\n"); + goto err_phy; + } + + err = tegra_udc_ep_setup(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup end points\n"); + goto err_unregister; + } + + /* Use dma_pool for TD management */ + udc->td_pool = dma_pool_create("udc_td", &pdev->dev, + sizeof(struct ep_td_struct), + DTD_ALIGNMENT, UDC_DMA_BOUNDARY); + if (!udc->td_pool) { + err = -ENOMEM; + goto err_unregister; + } + + err = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (err) + goto err_del_udc; +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + boost_cpufreq_work_flag = 1; + ep_queue_request_count = 0; + INIT_WORK(&udc->boost_cpufreq_work, + tegra_udc_boost_cpu_frequency_work); + pm_qos_add_request(&boost_cpu_freq_req, PM_QOS_CPU_FREQ_MIN, + PM_QOS_DEFAULT_VALUE); +#endif + + /* Create work for controlling clocks to the phy if otg is disabled */ + INIT_WORK(&udc->irq_work, tegra_udc_irq_work); + /* Create a delayed work for detecting the USB charger */ + INIT_DELAYED_WORK(&udc->work, tegra_udc_charger_detect_work); + INIT_WORK(&udc->charger_work, tegra_udc_set_current_limit_work); + + /* Get the regulator for drawing the vbus current in udc driver */ + udc->vbus_reg = regulator_get(NULL, "usb_bat_chg"); + if (IS_ERR(udc->vbus_reg)) { + dev_info(&pdev->dev, + "usb_bat_chg regulator not registered:" + " USB charging will not be enabled\n"); + udc->vbus_reg = NULL; + } + +#ifdef CONFIG_USB_OTG_UTILS + if (tegra_usb_phy_otg_supported(udc->phy)) + udc->transceiver = otg_get_transceiver(); + + if (udc->transceiver) { + dr_controller_stop(udc); + dr_controller_reset(udc); + tegra_usb_phy_power_off(udc->phy); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + otg_set_peripheral(udc->transceiver, &udc->gadget); + } +#else + /* Power down the phy if cable is not connected */ + if (!vbus_enabled()) + tegra_usb_phy_power_off(udc->phy); +#endif + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; + +err_del_udc: + dma_pool_destroy(udc->td_pool); + +err_unregister: + device_unregister(&udc->gadget.dev); + +err_phy: + tegra_usb_phy_close(udc->phy); + +err_irq: + free_irq(udc->irq, udc); + +err_iounmap: + iounmap(udc->regs); + +err_rel_mem_region: + release_mem_region(res->start, res->end - res->start + 1); + +err_kfree: + kfree(udc); + + return err; +} + +/* Driver removal function + * Free resources and finish pending transactions + */ +static int __exit tegra_udc_remove(struct platform_device *pdev) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + DECLARE_COMPLETION(done); + + if (!udc) + return -ENODEV; + + usb_del_gadget_udc(&udc->gadget); + udc->done = &done; + + cancel_delayed_work(&udc->work); +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + cancel_work_sync(&udc->boost_cpufreq_work); +#endif + + if (udc->vbus_reg) + regulator_put(udc->vbus_reg); + + if (udc->transceiver) + otg_set_peripheral(udc->transceiver, NULL); + + + /* Free allocated memory */ + dma_free_coherent(&pdev->dev, STATUS_BUFFER_SIZE, + udc->status_req->req.buf, + udc->status_req->req.dma); + kfree(udc->status_req); + kfree(udc->eps); + + dma_pool_destroy(udc->td_pool); + free_irq(udc->irq, udc); + iounmap(udc->regs); + release_mem_region(res->start, res->end - res->start + 1); + + device_unregister(&udc->gadget.dev); + /* Free udc -- wait for the release() finished */ + wait_for_completion(&done); + + return 0; +} + +static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + unsigned long flags; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* If the controller is in otg mode, return */ + if (udc->transceiver) + return 0; + + if (udc->vbus_active) { + spin_lock_irqsave(&udc->lock, flags); + /* Reset all internal Queues and inform client driver */ + reset_queues(udc); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + spin_unlock_irqrestore(&udc->lock, flags); + } + /* Stop the controller and turn off the clocks */ + dr_controller_stop(udc); + if (udc->transceiver) + udc->transceiver->state = OTG_STATE_UNDEFINED; + + tegra_usb_phy_power_off(udc->phy); + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + +static int tegra_udc_resume(struct platform_device *pdev) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (udc->transceiver) + return 0; + + tegra_usb_phy_power_on(udc->phy); + tegra_udc_restart(udc); + + /* Power down the phy if cable is not connected */ + if (!vbus_enabled(udc)) { + udc->vbus_active = 0; + tegra_usb_phy_power_off(udc->phy); + } + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + + +static struct platform_driver tegra_udc_driver = { + .remove = __exit_p(tegra_udc_remove), + .suspend = tegra_udc_suspend, + .resume = tegra_udc_resume, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION); + return platform_driver_probe(&tegra_udc_driver, tegra_udc_probe); +} +module_init(udc_init); +static void __exit udc_exit(void) +{ + platform_driver_unregister(&tegra_udc_driver); + printk(KERN_WARNING "%s unregistered\n", driver_desc); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tegra-udc"); diff --git a/drivers/usb/gadget/tegra_udc.h b/drivers/usb/gadget/tegra_udc.h new file mode 100644 index 000000000000..e94543fd98e3 --- /dev/null +++ b/drivers/usb/gadget/tegra_udc.h @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2011 NVIDIA Corporation + * + * Description: + * High-speed USB device controller driver. + * USB device/endpoint management registers. + * The driver is previously named as fsl_udc_core. Based on Freescale driver + * code from Li Yang and Jiang Bo. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __TEGRA_UDC_H +#define __TEGRA_UDC_H + +#ifdef VERBOSE +#define VDBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \ + __func__, ## args) +#else +#define VDBG(fmt, args...) do {} while (0) +#endif + + +#ifdef DEBUG +#define DBG(stuff...) pr_info("tegra_udc: " stuff) +#else +#define DBG(stuff...) do {} while (0) +#endif + +#define ERR(stuff...) pr_err("tegra_udc: " stuff) +#define WARNING(stuff...) pr_warning("tegra_udc: " stuff) + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) +#define STATUS_BUFFER_SIZE 8 + +#define USB_MAX_CTRL_PAYLOAD 64 + + /* Charger current limit=1800mA, as per the USB charger spec */ +#define USB_CHARGING_CURRENT_LIMIT_MA 1800 + /* 1 sec wait time for charger detection after vbus is detected */ +#define USB_CHARGER_DETECTION_WAIT_TIME_MS 1000 +#define BOOST_TRIGGER_SIZE 4096 + +#define UDC_RESET_TIMEOUT_MS 1000 +#define UDC_RUN_TIMEOUT_MS 1000 +#define UDC_FLUSH_TIMEOUT_MS 1000 + +/* ep0 transfer state */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* + * ### pipe direction macro from device view + */ +#define USB_RECV 0 /* OUT EP */ +#define USB_SEND 1 /* IN EP */ + +/* Device Controller Capability Parameter register */ +#define DCCPARAMS_REG_OFFSET 0x124 +#define DCCPARAMS_DC 0x00000080 +#define DCCPARAMS_DEN_MASK 0x0000001f + +/* USB CMD Register Bit Masks */ +#define USB_CMD_REG_OFFSET ((udc->has_hostpc) ? 0x130 : 0x140) +#define USB_CMD_RUN_STOP 0x00000001 +#define USB_CMD_CTRL_RESET 0x00000002 +#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 +#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 +#define USB_CMD_INT_AA_DOORBELL 0x00000040 +#define USB_CMD_ASP 0x00000300 +#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 +#define USB_CMD_SUTW 0x00002000 +#define USB_CMD_ATDTW 0x00004000 +#define USB_CMD_ITC 0x00FF0000 +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 0x00000000 +#define USB_CMD_FRAME_SIZE_512 0x00000004 +#define USB_CMD_FRAME_SIZE_256 0x00000008 +#define USB_CMD_FRAME_SIZE_128 0x0000000C +#define USB_CMD_FRAME_SIZE_64 0x00008000 +#define USB_CMD_FRAME_SIZE_32 0x00008004 +#define USB_CMD_FRAME_SIZE_16 0x00008008 +#define USB_CMD_FRAME_SIZE_8 0x0000800C +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 0x00000000 +#define USB_CMD_ASP_01 0x00000100 +#define USB_CMD_ASP_10 0x00000200 +#define USB_CMD_ASP_11 0x00000300 +#define USB_CMD_ASP_BIT_POS 8 +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 +#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 +#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 +#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 +#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 +#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 +#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 +#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 +#define USB_CMD_ITC_BIT_POS 16 + +/* USB STS Register Bit Masks */ +#define USB_STS_REG_OFFSET ((udc->has_hostpc) ? 0x134 : 0x144) +#define USB_STS_INT 0x00000001 +#define USB_STS_ERR 0x00000002 +#define USB_STS_PORT_CHANGE 0x00000004 +#define USB_STS_FRM_LST_ROLL 0x00000008 +#define USB_STS_SYS_ERR 0x00000010 +#define USB_STS_IAA 0x00000020 +#define USB_STS_RESET 0x00000040 +#define USB_STS_SOF 0x00000080 +#define USB_STS_SUSPEND 0x00000100 +#define USB_STS_HC_HALTED 0x00001000 +#define USB_STS_RCL 0x00002000 +#define USB_STS_PERIODIC_SCHEDULE 0x00004000 +#define USB_STS_ASYNC_SCHEDULE 0x00008000 + +/* USB INTR Register Bit Masks */ +#define USB_INTR_REG_OFFSET ((udc->has_hostpc) ? 0x138 : 0x148) +#define USB_INTR_INT_EN 0x00000001 +#define USB_INTR_ERR_INT_EN 0x00000002 +#define USB_INTR_PTC_DETECT_EN 0x00000004 +#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 +#define USB_INTR_SYS_ERR_EN 0x00000010 +#define USB_INTR_ASYN_ADV_EN 0x00000020 +#define USB_INTR_RESET_EN 0x00000040 +#define USB_INTR_SOF_EN 0x00000080 +#define USB_INTR_DEVICE_SUSPEND 0x00000100 + +/* Frame Index Register Bit Masks */ +#define USB_FRINDEX_REG_OFFSET ((udc->has_hostpc) ? 0x13c : 0x14c) +#define USB_FRINDEX_MASKS 0x3fff + +/* Device Address bit masks */ +#define USB_DEVICE_ADDR_REG_OFFSET ((udc->has_hostpc) ? 0x144 : 0x154) +#define USB_DEVICE_ADDRESS_MASK 0xFE000000 +#define USB_DEVICE_ADDRESS_BIT_POS 25 + +/* endpoint list address bit masks */ +#define USB_EP_LIST_ADDRESS_REG_OFFSET ((udc->has_hostpc) ? 0x148 : 0x158) +#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 + +/* PORTSCX Register Bit Masks */ +#define PORTSCX_REG_OFFSET ((udc->has_hostpc) ? 0x174 : 0x184) +#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001 +#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002 +#define PORTSCX_PORT_ENABLE 0x00000004 +#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008 +#define PORTSCX_OVER_CURRENT_ACT 0x00000010 +#define PORTSCX_OVER_CURRENT_CHG 0x00000020 +#define PORTSCX_PORT_FORCE_RESUME 0x00000040 +#define PORTSCX_PORT_SUSPEND 0x00000080 +#define PORTSCX_PORT_RESET 0x00000100 +#define PORTSCX_LINE_STATUS_BITS 0x00000C00 +#define PORTSCX_PORT_POWER 0x00001000 +#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000 +#define PORTSCX_PORT_TEST_CTRL 0x000F0000 +#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000 +#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000 +#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000 +#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000 + +/* In tegra3 the following fields have moved to new HOSTPC1_DEVLC reg and + * their offsets have changed. + * Keeping the name of bit masks same as before (PORTSCX_*) to have + * minimum changes to code */ +#define USB_HOSTPCX_DEVLC_REG_OFFSET 0x1b4 + +#define PORTSCX_PORT_FORCE_FULL_SPEED ((udc->has_hostpc) ? 0x00800000 \ + : 0x01000000) +#define PORTSCX_PORT_SPEED_MASK ((udc->has_hostpc) ? 0x06000000 : 0x0C000000) +#define PORTSCX_PORT_WIDTH ((udc->has_hostpc) ? 0x08000000 : 0x10000000) +#define PORTSCX_PHY_TYPE_SEL ((udc->has_hostpc) ? 0xE0000000 : 0xC0000000) + +/* bits for port speed */ +#define PORTSCX_PORT_SPEED_FULL ((udc->has_hostpc) ? 0x00000000 : 0x00000000) +#define PORTSCX_PORT_SPEED_LOW ((udc->has_hostpc) ? 0x02000000 : 0x04000000) +#define PORTSCX_PORT_SPEED_HIGH ((udc->has_hostpc) ? 0x04000000 : 0x08000000) +#define PORTSCX_PORT_SPEED_UNDEF ((udc->has_hostpc) ? 0x06000000 : 0x0C000000) +#define PORTSCX_SPEED_BIT_POS ((udc->has_hostpc) ? 25 : 26) + +/* bits for parallel transceiver width for UTMI interface */ +#define PORTSCX_PTW ((udc->has_hostpc) ? 0x08000000 : 0x10000000) +#define PORTSCX_PTW_8BIT ((udc->has_hostpc) ? 0x00000000 : 0x00000000) +#define PORTSCX_PTW_16BIT ((udc->has_hostpc) ? 0x08000000 : 0x10000000) + +/* bits for port transceiver select */ +#define PORTSCX_PTS_UTMI ((udc->has_hostpc) ? 0x00000000 : 0x00000000) +#define PORTSCX_PTS_ULPI ((udc->has_hostpc) ? 0x40000000 : 0x80000000) +#define PORTSCX_PTS_FSLS ((udc->has_hostpc) ? 0x60000000 : 0xC0000000) +#define PORTSCX_PTS_BIT_POS ((udc->has_hostpc) ? 29 : 30) + +/* bit 11-10 are line status */ +#define PORTSCX_LINE_STATUS_SE0 0x00000000 +#define PORTSCX_LINE_STATUS_JSTATE 0x00000400 +#define PORTSCX_LINE_STATUS_KSTATE 0x00000800 +#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00 +#define PORTSCX_LINE_STATUS_BIT_POS 10 + +/* bit 15-14 are port indicator control */ +#define PORTSCX_PIC_OFF 0x00000000 +#define PORTSCX_PIC_AMBER 0x00004000 +#define PORTSCX_PIC_GREEN 0x00008000 +#define PORTSCX_PIC_UNDEF 0x0000C000 +#define PORTSCX_PIC_BIT_POS 14 + +/* bit 19-16 are port test control */ +#define PORTSCX_PTC_DISABLE 0x00000000 +#define PORTSCX_PTC_JSTATE 0x00010000 +#define PORTSCX_PTC_KSTATE 0x00020000 +#define PORTSCX_PTC_SEQNAK 0x00030000 +#define PORTSCX_PTC_PACKET 0x00040000 +#define PORTSCX_PTC_FORCE_EN 0x00050000 +#define PORTSCX_PTC_BIT_POS 16 + + +/* USB MODE Register Bit Masks */ +#define USB_MODE_REG_OFFSET ((udc->has_hostpc) ? 0x1f8 : 0x1a8) +#define USB_MODE_CTRL_MODE_IDLE 0x00000000 +#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 +#define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_SETUP_LOCK_OFF 0x00000008 +#define USB_MODE_STREAM_DISABLE 0x00000010 + +/* Endpoint Setup Status bit masks */ +#define EP_SETUP_STATUS_REG_OFFSET ((udc->has_hostpc) ? 0x208 : 0x1ac) +#define EP_SETUP_STATUS_MASK 0x0000003F +#define EP_SETUP_STATUS_EP0 0x00000001 + +/* Endpoint Prime Register */ +#define EP_PRIME_REG_OFFSET ((udc->has_hostpc) ? 0x20c : 0x1b0) + +/* Endpoint Flush Register */ +#define EPFLUSH_REG_OFFSET ((udc->has_hostpc) ? 0x210 : 0x1b4) +#define EPFLUSH_TX_OFFSET 0x00010000 +#define EPFLUSH_RX_OFFSET 0x00000000 + +/* Endpoint Status Register */ +#define EP_STATUS_REG_OFFSET ((udc->has_hostpc) ? 0x214 : 0x1b8) + +/* Endpoint Complete Register */ +#define EP_COMPLETE_REG_OFFSET ((udc->has_hostpc) ? 0x218 : 0x1bc) + +/* Endpoint Control Registers */ +#define EP_CONTROL_REG_OFFSET ((udc->has_hostpc) ? 0x21c : 0x1c0) + +/* ENDPOINTCTRLx Register Bit Masks */ +#define EPCTRL_TX_ENABLE 0x00800000 +#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ +#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ +#define EPCTRL_TX_TYPE 0x000C0000 +#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ +#define EPCTRL_TX_EP_STALL 0x00010000 +#define EPCTRL_RX_ENABLE 0x00000080 +#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ +#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ +#define EPCTRL_RX_TYPE 0x0000000C +#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ +#define EPCTRL_RX_EP_STALL 0x00000001 + +/* bit 19-18 and 3-2 are endpoint type */ +#define EPCTRL_EP_TYPE_CONTROL 0 +#define EPCTRL_EP_TYPE_ISO 1 +#define EPCTRL_EP_TYPE_BULK 2 +#define EPCTRL_EP_TYPE_INTERRUPT 3 +#define EPCTRL_TX_EP_TYPE_SHIFT 18 +#define EPCTRL_RX_EP_TYPE_SHIFT 2 + +#define VBUS_SENSOR_REG_OFFSET 0x404 +#define VBUS_WAKEUP_REG_OFFSET 0x408 + +#define USB_SYS_VBUS_ASESSION_INT_EN 0x10000 +#define USB_SYS_VBUS_ASESSION_CHANGED 0x20000 +#define USB_SYS_VBUS_ASESSION 0x40000 +#define USB_SYS_VBUS_WAKEUP_ENABLE 0x40000000 +#define USB_SYS_VBUS_WAKEUP_INT_ENABLE 0x100 +#define USB_SYS_VBUS_WAKEUP_INT_STATUS 0x200 +#define USB_SYS_VBUS_STATUS 0x400 +#define USB_SYS_ID_PIN_STATUS 0x4 + + +/* Endpoint Queue Head Bit Masks */ +#define EP_QUEUE_HEAD_MULT_POS 30 +#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS 0x00008000 +#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 +#define EP_QUEUE_HEAD_IOC 0x00008000 +#define EP_QUEUE_HEAD_MULTO 0x00000C00 +#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 +#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 +#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 +#define EP_QUEUE_FRINDEX_MASK 0x000007FF +#define EP_MAX_LENGTH_TRANSFER 0x4000 + + + +/* Endpoint Transfer Descriptor bit Masks */ +#define DTD_NEXT_TERMINATE 0x00000001 +#define DTD_IOC 0x00008000 +#define DTD_STATUS_ACTIVE 0x00000080 +#define DTD_STATUS_HALTED 0x00000040 +#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 +#define DTD_STATUS_TRANSACTION_ERR 0x00000008 +#define DTD_RESERVED_FIELDS 0x80007300 +#define DTD_ADDR_MASK 0xFFFFFFE0 +#define DTD_PACKET_SIZE 0x7FFF0000 +#define DTD_LENGTH_BIT_POS 16 +#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) +/* Alignment requirements; must be a power of two */ +#define DTD_ALIGNMENT 0x80 +#define QH_ALIGNMENT 2048 +#define QH_OFFSET 0x1000 + +/* Controller dma boundary */ +#define UDC_DMA_BOUNDARY 0x1000 + +#define REQ_UNCOMPLETE 1 + +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +/* + * Endpoint Queue Head data struct + * Rem: all the variables of qh are LittleEndian Mode + * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr + */ +struct ep_queue_head { + u32 max_pkt_length; /* Mult(31-30), Zlt(29), Max Pkt len and IOS(15) */ + u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ + u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ + u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ + u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ + u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ + u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ + u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ + u32 res1; + u8 setup_buffer[8]; /* Setup data 8 bytes */ + u32 res2[4]; +}; + +/* Endpoint Transfer Descriptor data struct */ +/* Rem: all the variables of td are LittleEndian Mode */ +struct ep_td_struct { + u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set + indicate invalid */ + u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 res; + /* 32 bytes */ + dma_addr_t td_dma; /* dma address for this td */ + /* virtual address of next td specified in next_td_ptr */ + struct ep_td_struct *next_td_virt; +}; + + +struct tegra_req { + struct usb_request req; + struct list_head queue; + /* ep_queue() func will add + a request->queue into a udc_ep->queue 'd tail */ + struct tegra_ep *ep; + unsigned mapped:1; + + struct ep_td_struct *head, *tail; /* For dTD List + cpu endian Virtual addr */ + unsigned int dtd_count; +}; + +struct tegra_ep { + struct usb_ep ep; + struct list_head queue; + struct tegra_udc *udc; + struct ep_queue_head *qh; + const struct usb_endpoint_descriptor *desc; + struct usb_gadget *gadget; + + char name[14]; + unsigned stopped:1; +}; + +struct tegra_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct completion *done; /* to make sure release() is done */ + struct tegra_ep *eps; + struct platform_device *pdev; + struct tegra_usb_phy *phy; + struct usb_ctrlrequest local_setup_buff; + struct otg_transceiver *transceiver; + struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ + struct tegra_req *status_req; /* ep0 status request */ + struct dma_pool *td_pool; /* dma pool for DTD */ + struct delayed_work work; /* delayed work for charger detection */ + struct regulator *vbus_reg; /* regulator for drawing VBUS */ + /* work for setting regulator current limit */ + struct work_struct charger_work; + /* work for boosting cpu frequency */ + struct work_struct boost_cpufreq_work; + /* irq work for controlling the usb power */ + struct work_struct irq_work; + void __iomem *regs; + size_t ep_qh_size; /* size after alignment adjustment*/ + dma_addr_t ep_qh_dma; /* dma address of QH */ + unsigned int max_ep; + unsigned int irq; + u32 max_pipes; /* Device max pipes */ + u32 resume_state; /* USB state to resume */ + u32 usb_state; /* USB current state */ + u32 ep0_state; /* Endpoint zero state */ + u32 ep0_dir; /* Endpoint zero direction: USB_DIR_IN/USB_DIR_OUT */ + u8 device_address; /* Device USB address */ + u32 current_limit; + spinlock_t lock; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned stopped:1; + unsigned remote_wakeup:1; + unsigned selfpowered:1; + bool has_hostpc; +}; + + +#endif /* __TEGRA_UDC_H */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 25ed607aab9a..bf171c180987 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -145,7 +145,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, spin_lock_irqsave(&ehci->lock, flags); /* clear phy low-power mode before changing wakeup flags */ - if (ehci->has_hostpc) { + if (ehci->has_hostpc && !ehci->broken_hostpc_phcd) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; @@ -181,7 +181,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, } /* enter phy low-power mode again */ - if (ehci->has_hostpc) { + if (ehci->has_hostpc && !ehci->broken_hostpc_phcd) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; @@ -285,7 +285,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } #ifdef CONFIG_ARCH_TEGRA_2x_SOC - if (changed && ehci->has_hostpc) { + if (changed && ehci->has_hostpc && !ehci->broken_hostpc_phcd) { spin_unlock_irq(&ehci->lock); msleep(5); /* 5 ms for HCD to enter low-power mode */ spin_lock_irq(&ehci->lock); @@ -389,7 +389,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); /* clear phy low-power mode before resume */ - if (ehci->bus_suspended && ehci->has_hostpc) { + if (ehci->bus_suspended && ehci->has_hostpc && !ehci->broken_hostpc_phcd) { i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { @@ -731,7 +731,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (hostpc_reg && !ehci->broken_hostpc_phcd) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -979,7 +979,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (hostpc_reg && !ehci->broken_hostpc_phcd) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 5e646b65b3f0..33504e402611 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -1,8 +1,8 @@ /* * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs * - * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2009 - 2012 NVIDIA Corporation + * Copyright (c) 2010 Google, Inc. + * Copyright (c) 2009-2012 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,7 +16,6 @@ * */ -#include <linux/clk.h> #include <linux/platform_device.h> #include <linux/platform_data/tegra_usb.h> #include <linux/irq.h> @@ -24,191 +23,158 @@ #include <mach/usb_phy.h> #include <mach/iomap.h> -#define TEGRA_USB_PORTSC_PHCD (1 << 23) - -#define TEGRA_USB_SUSP_CTRL_OFFSET 0x400 -#define TEGRA_USB_SUSP_CLR (1 << 5) -#define TEGRA_USB_PHY_CLK_VALID (1 << 7) -#define TEGRA_USB_SRT (1 << 25) -#define TEGRA_USB_PHY_CLK_VALID_INT_ENB (1 << 9) -#define TEGRA_USB_PHY_CLK_VALID_INT_STS (1 << 8) - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -#define TEGRA_USB_PORTSC1_OFFSET 0x184 +#if 0 +#define EHCI_DBG(stuff...) pr_info("ehci-tegra: " stuff) #else -#define TEGRA_USB_PORTSC1_OFFSET 0x174 +#define EHCI_DBG(stuff...) do {} while (0) #endif -#define TEGRA_USB_PORTSC1_WKCN (1 << 20) -#define TEGRA_LVL2_CLK_GATE_OVRB 0xfc -#define TEGRA_USB2_CLK_OVR_ON (1 << 10) +static const char driver_name[] = "tegra-ehci"; #define TEGRA_USB_DMA_ALIGN 32 -#define STS_SRI (1<<7) /* SOF Recieved */ - -#define HOSTPC_REG_OFFSET 0x1b4 - -#define HOSTPC1_DEVLC_STS (1 << 28) -#define HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) - -#define USB1_PREFETCH_ID 6 -#define USB2_PREFETCH_ID 18 -#define USB3_PREFETCH_ID 17 - struct tegra_ehci_hcd { struct ehci_hcd *ehci; struct tegra_usb_phy *phy; - struct clk *clk; - struct clk *emc_clk; - struct clk *sclk_clk; +#ifdef CONFIG_USB_OTG_UTILS struct otg_transceiver *transceiver; - int host_resumed; - int bus_suspended; - int port_resuming; - int power_down_on_bus_suspend; - int default_enable; - enum tegra_usb_phy_port_speed port_speed; - struct work_struct clk_timer_work; - struct timer_list clk_timer; - bool clock_enabled; - bool timer_event; - struct mutex tegra_ehci_hcd_mutex; +#endif + struct mutex sync_lock; + bool port_resuming; unsigned int irq; bool bus_suspended_fail; }; -static void tegra_ehci_power_up(struct usb_hcd *hcd, bool is_dpd) +struct dma_align_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void free_align_buffer(struct urb *urb) { - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct dma_align_buffer *temp = container_of(urb->transfer_buffer, + struct dma_align_buffer, data); + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + /* In transaction, DMA from Device */ + if (usb_urb_dir_in(urb)) + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); - if (!tegra->default_enable) - clk_enable(tegra->clk); - tegra_usb_phy_power_on(tegra->phy, is_dpd); - tegra->host_resumed = 1; + urb->transfer_buffer = temp->old_xfer_buffer; + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; + kfree(temp->kmalloc_ptr); } -static void tegra_ehci_power_down(struct usb_hcd *hcd, bool is_dpd) +static int alloc_align_buffer(struct urb *urb, gfp_t mem_flags) { - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct dma_align_buffer *temp, *kmalloc_ptr; + size_t kmalloc_size; - tegra->host_resumed = 0; - tegra_usb_phy_power_off(tegra->phy, is_dpd); - if (!tegra->default_enable) - clk_disable(tegra->clk); + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) + return 0; + + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct dma_align_buffer) + TEGRA_USB_DMA_ALIGN - 1; + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + + if (!kmalloc_ptr) + return -ENOMEM; + + /* Position our struct dma_align_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + /* OUT transaction, DMA to Device */ + if (!usb_urb_dir_in(urb)) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + + urb->transfer_buffer = temp->data; + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; } -static int tegra_ehci_internal_port_reset( - struct ehci_hcd *ehci, - u32 __iomem *portsc_reg -) +static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { - u32 temp; - unsigned long flags; - int retval = 0; - int i, tries; - u32 saved_usbintr; - - spin_lock_irqsave(&ehci->lock, flags); - saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable); - /* disable USB interrupt */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - spin_unlock_irqrestore(&ehci->lock, flags); - - /* - * Here we have to do Port Reset at most twice for - * Port Enable bit to be set. - */ - for (i = 0; i < 2; i++) { - temp = ehci_readl(ehci, portsc_reg); - temp |= PORT_RESET; - ehci_writel(ehci, temp, portsc_reg); - mdelay(10); - temp &= ~PORT_RESET; - ehci_writel(ehci, temp, portsc_reg); - mdelay(1); - tries = 100; - do { - mdelay(1); - /* - * Up to this point, Port Enable bit is - * expected to be set after 2 ms waiting. - * USB1 usually takes extra 45 ms, for safety, - * we take 100 ms as timeout. - */ - temp = ehci_readl(ehci, portsc_reg); - } while (!(temp & PORT_PE) && tries--); - if (temp & PORT_PE) - break; + int ret; + + ret = alloc_align_buffer(urb, mem_flags); + if (ret) + return ret; + + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + + /* Control packets over dma */ + if (urb->setup_dma) + dma_sync_single_for_device(hcd->self.controller, + urb->setup_dma, sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + + /* urb buffers over dma */ + if (urb->transfer_dma) { + enum dma_data_direction dir; + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + dma_sync_single_for_device(hcd->self.controller, + urb->transfer_dma, urb->transfer_buffer_length, dir); } - if (i == 2) - retval = -ETIMEDOUT; - - /* - * Clear Connect Status Change bit if it's set. - * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared. - */ - if (temp & PORT_CSC) - ehci_writel(ehci, PORT_CSC, portsc_reg); - - /* - * Write to clear any interrupt status bits that might be set - * during port reset. - */ - temp = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, temp, &ehci->regs->status); - - /* restore original interrupt enable bits */ - ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable); - return retval; + + if (ret) + free_align_buffer(urb); + + return ret; } -static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) +static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb) +{ + + if (urb->transfer_dma) { + enum dma_data_direction dir; + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + if (dir == DMA_FROM_DEVICE) + dma_sync_single_for_cpu(hcd->self.controller, + urb->transfer_dma, urb->transfer_buffer_length, + DMA_FROM_DEVICE); + } + + usb_hcd_unmap_urb_for_dma(hcd, urb); + free_align_buffer(urb); +} + +static irqreturn_t tegra_ehci_irq(struct usb_hcd *hcd) { - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct ehci_regs __iomem *hw = ehci->regs; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - u32 val; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); irqreturn_t irq_status; bool pmc_remote_wakeup = false; - /* Fence read for coherency of AHB master intiated writes */ - if (tegra->phy->instance == 0) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); - else if (tegra->phy->instance == 1) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID)); - else if (tegra->phy->instance == 2) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID)); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc)) { - /* check if there is any remote wake event */ - if (tegra_usb_phy_is_remotewake_detected(tegra->phy)) { - pmc_remote_wakeup = true; - spin_lock (&ehci->lock); - usb_hcd_resume_root_hub(hcd); - spin_unlock (&ehci->lock); - } - } - if (tegra->phy->hotplug) { - spin_lock(&ehci->lock); - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - if ((val & TEGRA_USB_PHY_CLK_VALID_INT_STS)) { - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB | - TEGRA_USB_PHY_CLK_VALID_INT_STS; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - - val = readl(&hw->status); - if (!(val & STS_PCD)) { - spin_unlock(&ehci->lock); - return 0; - } - val = readl(hcd->regs + TEGRA_USB_PORTSC1_OFFSET); - val &= ~(TEGRA_USB_PORTSC1_WKCN | PORT_RWC_BITS); - writel(val , (hcd->regs + TEGRA_USB_PORTSC1_OFFSET)); - } + spin_lock(&ehci->lock); + irq_status = tegra_usb_phy_irq(tegra->phy); + if (irq_status == IRQ_NONE) { spin_unlock(&ehci->lock); + return irq_status; + } + if (tegra_usb_phy_remote_wakeup(tegra->phy)) { + ehci_info(ehci, "remote wakeup detected\n"); + pmc_remote_wakeup = true; + usb_hcd_resume_root_hub(hcd); } + spin_unlock(&ehci->lock); + + EHCI_DBG("%s() cmd = 0x%x, int_sts = 0x%x, portsc = 0x%x\n", __func__, + ehci_readl(ehci, &ehci->regs->command), + ehci_readl(ehci, &ehci->regs->status), + ehci_readl(ehci, &ehci->regs->port_status[0])); irq_status = ehci_irq(hcd); @@ -218,61 +184,37 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) if (ehci->controller_remote_wakeup) { ehci->controller_remote_wakeup = false; - /* disable interrupts */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - tegra_usb_phy_preresume(tegra->phy, true); + tegra_usb_phy_pre_resume(tegra->phy, true); tegra->port_resuming = 1; } return irq_status; } + static int tegra_ehci_hub_control( struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength ) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int ports = HCS_N_PORTS(ehci->hcs_params); - u32 temp, status, cmd_run; - u32 __iomem *status_reg; - u32 usbsts_reg; - + struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; - int retval = 0; - unsigned selector; - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - bool hsic = false; + int retval = 0; + u32 __iomem *status_reg; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if (!tegra->host_resumed) { + if (!tegra_usb_phy_hw_accessible(tegra->phy)) { if (buf) - memset (buf, 0, wLength); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + memset(buf, 0, wLength); return retval; } - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; - - spin_lock_irqsave(&ehci->lock, flags); - - /* - * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits - * that are write on clear, by writing back the register read value, so - * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits - */ - if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { - temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; - ehci_writel(ehci, temp & ~PORT_PE, status_reg); - goto done; - } else if (typeReq == GetPortStatus) { - temp = ehci_readl(ehci, status_reg); - /* check port is in resume state */ + /* Do tegra phy specific actions based on the type request */ + switch (typeReq) { + case GetPortStatus: if (tegra->port_resuming) { int delay = ehci->reset_done[wIndex-1] - jiffies; /* Sometimes it seems we get called too soon... In that case, wait.*/ @@ -280,501 +222,79 @@ static int tegra_ehci_hub_control( ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); mdelay(jiffies_to_msecs(delay)); } + status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { - pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + EHCI_DBG("%s: timeout waiting for SUSPEND to clear\n", __func__); } + tegra_usb_phy_post_resume(tegra->phy); tegra->port_resuming = 0; - tegra_usb_phy_postresume(tegra->phy, false); - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { + /* If run bit is not set by now enable it */ + if (ehci->command & CMD_RUN) { ehci->command |= CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run |= CMD_RUN; - /* - * ehci run bit is disabled to avoid SOF. - * 2LS WAR is executed by now enable the run bit. - */ - ehci_writel(ehci, cmd_run, &ehci->regs->command); - /* Now we can safely re-enable irqs */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); + ehci_writel(ehci, ehci->command, &ehci->regs->command); } + /* Now we can safely re-enable irqs */ + ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); } - - } else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { - temp = ehci_readl(ehci, status_reg); - if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { - retval = -EPIPE; - goto done; - } - - temp &= ~PORT_WKCONN_E; - temp |= PORT_WKDISC_E | PORT_WKOC_E; - ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - - /* Need a 4ms delay before the controller goes to suspend */ - mdelay(4); - - /* - * If a transaction is in progress, there may be a delay in - * suspending the port. Poll until the port is suspended. - */ - if (handshake(ehci, status_reg, PORT_SUSPEND, - PORT_SUSPEND, 5000)) - pr_err("%s: timeout waiting for SUSPEND\n", __func__); - - set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); - /* - * If RUN bit is disabled interrupt is not generated after suspend. - * This change on T20 will allow ASE interrupt generated after suspend - * which will unlink the qheads. - */ -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* Disable RUN bit. */ - ehci->command &= ~CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run &= ~CMD_RUN; - ehci_writel(ehci, cmd_run, &ehci->regs->command); - if (handshake (ehci, &ehci->regs->status, - STS_HALT, STS_HALT, 16 * 125)) - pr_err("%s() timeout waiting for STS_HALT\n", __func__); + break; + case ClearPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + tegra_usb_phy_pre_resume(tegra->phy, false); + tegra->port_resuming = 1; + } else if (wValue == USB_PORT_FEAT_ENABLE) { + u32 temp; + temp = ehci_readl(ehci, &ehci->regs->port_status[0]) & ~PORT_RWC_BITS; + ehci_writel(ehci, temp & ~PORT_PE, &ehci->regs->port_status[0]); + return retval; } -#endif - tegra_usb_phy_postsuspend(tegra->phy, false); - - goto done; - } - - /* For USB1 port we need to issue Port Reset twice internally */ - if (tegra->phy->instance == 0 && - (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { - spin_unlock_irqrestore(&ehci->lock, flags); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return tegra_ehci_internal_port_reset(ehci, status_reg); + break; } - /* - * Tegra host controller will time the resume operation to clear the bit - * when the port control state switches to HS or FS Idle. This behavior - * is different from EHCI where the host controller driver is required - * to set this bit to a zero after the resume duration is timed in the - * driver. - */ - else if (typeReq == ClearPortFeature && - wValue == USB_PORT_FEAT_SUSPEND) { - temp = ehci_readl(ehci, status_reg); - if ((temp & PORT_RESET) || !(temp & PORT_PE)) { - retval = -EPIPE; - goto done; - } - - if (!(temp & PORT_SUSPEND)) - goto done; - - tegra->port_resuming = 1; - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* disable interrupts */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - /* Disable RUN bit. */ - ehci->command &= ~CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run &= ~CMD_RUN; - ehci_writel(ehci, cmd_run, &ehci->regs->command); - if (handshake (ehci, &ehci->regs->status, - STS_HALT, STS_HALT, 16 * 125)) - pr_err("%s() timeout waiting for STS_HALT\n", __func__); - } - - /* Disable disconnect detection during port resume */ - tegra_usb_phy_preresume(tegra->phy, false); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP) { -#endif - ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__, - ehci_readl(ehci, &ehci->regs->status)); - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, usbsts_reg, &ehci->regs->status); - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - udelay(20); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) - pr_err("%s: timeout set for STS_SRI\n", __func__); - - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, usbsts_reg, &ehci->regs->status); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, 0, 2000)) - pr_err("%s: timeout clear STS_SRI\n", __func__); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) - pr_err("%s: timeout set STS_SRI\n", __func__); - - udelay(20); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - } -#endif - temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - /* start resume signaling */ - ehci_writel(ehci, temp | PORT_RESUME, status_reg); - - ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); - /* whoever resumes must GetPortStatus to complete it!! */ - goto done; - } + /* handle ehci hub control request */ + retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); - /* Handle port reset here */ - if ((hsic) && (typeReq == SetPortFeature) && - ((wValue == USB_PORT_FEAT_RESET) || (wValue == USB_PORT_FEAT_POWER))) { - selector = wIndex >> 8; - wIndex &= 0xff; - if (!wIndex || wIndex > ports) { - retval = -EPIPE; - goto done; - } - wIndex--; - status = 0; - temp = ehci_readl(ehci, status_reg); - if (temp & PORT_OWNER) - goto done; - temp &= ~PORT_RWC_BITS; - - switch (wValue) { - case USB_PORT_FEAT_RESET: - { - if (temp & PORT_RESUME) { - retval = -EPIPE; - goto done; - } - /* line status bits may report this as low speed, - * which can be fine if this root hub has a - * transaction translator built in. - */ - if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT - && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { - ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); - temp |= PORT_OWNER; - ehci_writel(ehci, temp, status_reg); - } else { - ehci_vdbg(ehci, "port %d reset\n", wIndex + 1); - temp &= ~PORT_PE; - /* - * caller must wait, then call GetPortStatus - * usb 2.0 spec says 50 ms resets on root - */ - ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(50); - ehci_writel(ehci, temp, status_reg); - if (hsic && (wIndex == 0)) + /* do tegra phy specific actions based on the type request */ + if (!retval) { + switch (typeReq) { + case SetPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + /* Need a 4ms delay for controller to suspend */ + mdelay(4); + tegra_usb_phy_post_suspend(tegra->phy); + } else if (wValue == USB_PORT_FEAT_RESET) { + if (wIndex == 1) tegra_usb_phy_bus_reset(tegra->phy); + } else if (wValue == USB_PORT_FEAT_POWER) { + if (wIndex == 1) + tegra_usb_phy_port_power(tegra->phy); } - break; - } - case USB_PORT_FEAT_POWER: - { - if (HCS_PPC(ehci->hcs_params)) - ehci_writel(ehci, temp | PORT_POWER, status_reg); - if (hsic && (wIndex == 0)) - tegra_usb_phy_bus_connect(tegra->phy); + case ClearPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + /* tegra USB controller needs 25 ms to resume the port */ + ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); + } break; } - } - goto done; } - spin_unlock_irqrestore(&ehci->lock, flags); - - /* Handle the hub control events here */ - retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return retval; -done: - spin_unlock_irqrestore(&ehci->lock, flags); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } -#ifdef CONFIG_PM -static void tegra_ehci_restart(struct usb_hcd *hcd, bool is_dpd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - unsigned int temp; - - ehci->controller_resets_phy = 0; - tegra_ehci_pre_reset(tegra->phy, false); - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) - ehci->controller_resets_phy = 1; - - /* setup the frame list and Async q heads */ - ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); - ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); - /* setup the command register and set the controller in RUN mode */ - ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - /* dont start RS here for HSIC, it will be set by bus_reset */ - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_HSIC) -#endif - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - - /* Enable the root Port Power */ - if (HCS_PPC(ehci->hcs_params)) { - temp = ehci_readl(ehci, &ehci->regs->port_status[0]); - ehci_writel(ehci, temp | PORT_POWER, &ehci->regs->port_status[0]); - } - - down_write(&ehci_cf_port_reset_rwsem); - if(is_dpd) - hcd->state = HC_STATE_SUSPENDED; - else - hcd->state = HC_STATE_RUNNING; - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - /* flush posted writes */ - ehci_readl(ehci, &ehci->regs->command); - up_write(&ehci_cf_port_reset_rwsem); - - /* Turn On Interrupts */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); -} - -static int tegra_usb_suspend(struct usb_hcd *hcd, bool is_dpd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_regs __iomem *hw = tegra->ehci->regs; - unsigned long flags; - int hsic = 0; - - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - spin_lock_irqsave(&tegra->ehci->lock, flags); - - if (tegra->ehci->has_hostpc) - tegra->port_speed = (readl(hcd->regs + HOSTPC_REG_OFFSET) >> 25) & 0x3; - else - tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - ehci_halt(tegra->ehci); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* - * Ehci run bit is disabled by now read this into command variable - * so that bus resume will not enable run bit immedialty. - * this is required for 2LS WAR on UTMIP interface. - */ - tegra->ehci->command = ehci_readl(tegra->ehci, - &tegra->ehci->regs->command); - } -#endif - - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - tegra_ehci_power_down(hcd, is_dpd); - return 0; -} - -static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_regs __iomem *hw = ehci->regs; - unsigned long val; - bool hsic; - bool null_ulpi; - bool utmip_remote_wakeup = false; - - null_ulpi = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI); - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - tegra_ehci_power_up(hcd, is_dpd); - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - if ((tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) || (hsic) || - (null_ulpi)) - goto restart; - - /* Force the phy to keep data lines in suspend state */ - tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - ehci_reset(ehci); - } - - /* Enable host mode */ - tdi_reset(ehci); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc)) { - val = readl(hcd->regs + HOSTPC_REG_OFFSET); - val &= ~HOSTPC1_DEVLC_PTS(~0); - val |= HOSTPC1_DEVLC_STS; - writel(val, hcd->regs + HOSTPC_REG_OFFSET); - } - - /* Enable Port Power */ - val = readl(&hw->port_status[0]); - val |= PORT_POWER; - writel(val, &hw->port_status[0]); - udelay(10); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc) && (tegra->phy->remote_wakeup)) { - utmip_remote_wakeup = true; - } - - /* Check if the phy resume from LP0. When the phy resume from LP0 - * USB register will be reset. */ - if (!readl(&hw->async_next)) { -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Start the controller */ - val = readl(&hw->command); - writel((val | CMD_RUN), &hw->command); -#endif - /* Program the field PTC based on the saved speed mode */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) - val |= PORT_TEST_FORCE; - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) - val |= PORT_TEST(6); - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= PORT_TEST(7); - writel(val, &hw->port_status[0]); - udelay(10); - - /* Disable test mode by setting PTC field to NORMAL_OP */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - writel(val, &hw->port_status[0]); - udelay(10); - } - - /* Poll until CCS is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, - PORT_CONNECT, 2000)) { - pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); - goto restart; - } - - /* Poll until PE is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_PE, - PORT_PE, 2000)) { - pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); - goto restart; - } - - /* Clear the PCI status, to avoid an interrupt taken upon resume */ - val = readl(&hw->status); - val |= STS_PCD; - writel(val, &hw->status); - - /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ - val = readl(&hw->port_status[0]); - if ((val & PORT_POWER) && (val & PORT_PE)) { - val |= PORT_SUSPEND; - writel(val, &hw->port_status[0]); - - /* Need a 4ms delay before the controller goes to suspend */ - mdelay(4); - - /* Wait until port suspend completes */ - if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, - PORT_SUSPEND, 1000)) { - pr_err("%s: timeout waiting for PORT_SUSPEND\n", - __func__); - goto restart; - } - } - - tegra_ehci_phy_restore_end(tegra->phy); - if (utmip_remote_wakeup) { - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - } - return 0; - -restart: - if (null_ulpi) { - bool LP0 = !readl(&hw->async_next); - - if (LP0) { - static int cnt = 1; - - pr_info("LP0 restart %d\n", cnt++); - tegra_ehci_phy_restore_start(tegra->phy, - tegra->port_speed); - } - - val = readl(&hw->port_status[0]); - if (!((val & PORT_POWER) && (val & PORT_PE))) { - tegra_ehci_restart(hcd, is_dpd); - } - - if (LP0) - tegra_ehci_phy_restore_end(tegra->phy); - - return 0; - } - - if ((tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) && (!hsic)) - tegra_ehci_phy_restore_end(tegra->phy); - if (hsic) { - val = readl(&hw->port_status[0]); - if (!((val & PORT_POWER) && (val & PORT_PE))) - tegra_ehci_restart(hcd, false); - - tegra_usb_phy_bus_idle(tegra->phy); - if (!tegra_usb_phy_is_device_connected(tegra->phy)) - pr_err("%s: no hsic device conenction\n", __func__); - } else { - tegra_ehci_restart(hcd, false); - } - - return 0; -} -#endif - -/* - * Disable PHY clock valid interrupts and wait for the interrupt handler to - * finish. - * - * Requires a lock on tegra_ehci_hcd_mutex - * Must not be called with a lock on ehci->lock - */ -static void tegra_ehci_disable_phy_interrupt(struct usb_hcd *hcd) { - struct tegra_ehci_hcd *tegra; - u32 val; - if (hcd->irq >= 0) { - tegra = dev_get_drvdata(hcd->self.controller); - if (tegra->phy->hotplug) { - /* Disable PHY clock valid interrupts */ - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - } - /* Wait for the interrupt handler to finish */ - synchronize_irq(hcd->irq); - } -} - static void tegra_ehci_shutdown(struct usb_hcd *hcd) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - tegra_ehci_disable_phy_interrupt(hcd); - /* ehci_shutdown touches the USB controller registers, make sure - * controller has clocks to it */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, false); - - ehci_shutdown(hcd); - - /* we are ready to shut down, powerdown the phy */ - tegra_ehci_power_down(hcd, false); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + mutex_lock(&tegra->sync_lock); + del_timer_sync(&ehci->watchdog); + del_timer_sync(&ehci->iaa_watchdog); + if (tegra_usb_phy_hw_accessible(tegra->phy)) { + spin_lock_irq(&ehci->lock); + ehci_silence_controller(ehci); + spin_unlock_irq(&ehci->lock); + } + mutex_unlock(&tegra->sync_lock); } static int tegra_ehci_setup(struct usb_hcd *hcd) @@ -786,24 +306,18 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci->has_hostpc = tegra_usb_phy_has_hostpc(tegra->phy) ? 1 : 0; + ehci->broken_hostpc_phcd = true; -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - ehci->has_hostpc = 1; -#endif hcd->has_tt = 1; - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_NULL_ULPI) { - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - } - retval = ehci_halt(ehci); if (retval) return retval; @@ -815,242 +329,49 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) ehci->sbrn = 0x20; ehci->controller_remote_wakeup = false; - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) { - tegra_ehci_pre_reset(tegra->phy, false); - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - - /* - * Resetting the controller has the side effect of resetting the PHY. - * So, never reset the controller after the calling - * tegra_ehci_reinit API. - */ - ehci->controller_resets_phy = 1; - } + ehci_reset(ehci); + tegra_usb_phy_reset(tegra->phy); ehci_port_power(ehci, 1); return retval; } + #ifdef CONFIG_PM static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int error_status = 0; - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); + int err = 0; + EHCI_DBG("%s() BEGIN\n", __func__); + mutex_lock(&tegra->sync_lock); tegra->bus_suspended_fail = false; - tegra_ehci_disable_phy_interrupt(hcd); - /* ehci_shutdown touches the USB controller registers, make sure - * controller has clocks to it */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, false); - error_status = ehci_bus_suspend(hcd); - if (error_status) + err = ehci_bus_suspend(hcd); + if (err) tegra->bus_suspended_fail = true; - if (!error_status && tegra->power_down_on_bus_suspend) { - tegra_usb_suspend(hcd, false); - tegra->bus_suspended = 1; - } - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + else + tegra_usb_phy_suspend(tegra->phy); + mutex_unlock(&tegra->sync_lock); + EHCI_DBG("%s() END\n", __func__); - return error_status; + return err; } static int tegra_ehci_bus_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int ehci_bus_resumed; + int err = 0; + EHCI_DBG("%s() BEGIN\n", __func__); - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if (tegra->bus_suspended && tegra->power_down_on_bus_suspend) { - tegra_usb_resume(hcd, false); - tegra->bus_suspended = 0; - } + mutex_lock(&tegra->sync_lock); + tegra_usb_phy_resume(tegra->phy); + err = ehci_bus_resume(hcd); + mutex_unlock(&tegra->sync_lock); + EHCI_DBG("%s() END\n", __func__); - ehci_bus_resumed = ehci_bus_resume(hcd); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ehci_bus_resumed; + return err; } #endif -struct dma_aligned_buffer { - void *kmalloc_ptr; - void *old_xfer_buffer; - u8 data[0]; -}; - -static void free_dma_aligned_buffer(struct urb *urb) -{ - struct dma_aligned_buffer *temp = container_of(urb->transfer_buffer, - struct dma_aligned_buffer, data); - - if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) - return; - - if(usb_urb_dir_in(urb)) - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); - urb->transfer_buffer = temp->old_xfer_buffer; - kfree(temp->kmalloc_ptr); - urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; -} - -static int alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags) -{ - struct dma_aligned_buffer *temp, *kmalloc_ptr; - size_t kmalloc_size; - - if (urb->num_sgs || urb->sg || - urb->transfer_buffer_length == 0 || - !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) - return 0; - - /* Allocate a buffer with enough padding for alignment */ - kmalloc_size = urb->transfer_buffer_length + - sizeof(struct dma_aligned_buffer) + TEGRA_USB_DMA_ALIGN - 1; - - kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); - if (!kmalloc_ptr) - return -ENOMEM; - - /* Position our struct dma_aligned_buffer such that data is aligned */ - temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; - temp->kmalloc_ptr = kmalloc_ptr; - temp->old_xfer_buffer = urb->transfer_buffer; - if (!usb_urb_dir_in(urb)) - memcpy(temp->data, urb->transfer_buffer, - urb->transfer_buffer_length); - urb->transfer_buffer = temp->data; - urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; - - return 0; -} - -static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, - gfp_t mem_flags) -{ - int ret; - - ret = alloc_dma_aligned_buffer(urb, mem_flags); - if (ret) - return ret; - - ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); - if (ret) - free_dma_aligned_buffer(urb); - - return ret; -} - -static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - /* Fence read for coherency of AHB master intiated writes */ - if (tegra->phy->instance == 0) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); - else if (tegra->phy->instance == 1) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID)); - else if (tegra->phy->instance == 2) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID)); - - usb_hcd_unmap_urb_for_dma(hcd, urb); - free_dma_aligned_buffer(urb); -} - -void clk_timer_callback(unsigned long data) -{ - struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd*) data; - unsigned long flags; - - if (!timer_pending(&tegra->clk_timer)) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->timer_event = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - schedule_work(&tegra->clk_timer_work); - } -} - -static void clk_timer_work_handler(struct work_struct* clk_timer_work) { - struct tegra_ehci_hcd *tegra = container_of(clk_timer_work, - struct tegra_ehci_hcd, clk_timer_work); - int ret; - unsigned long flags; - bool clock_enabled, timer_event; - - spin_lock_irqsave(&tegra->ehci->lock, flags); - clock_enabled = tegra->clock_enabled; - timer_event = tegra->timer_event; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - if (timer_event) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 0; - tegra->timer_event = 0; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - clk_disable(tegra->emc_clk); - clk_disable(tegra->sclk_clk); - return; - } - - if ((!clock_enabled)) { - ret = mod_timer(&tegra->clk_timer, jiffies + msecs_to_jiffies(2000)); - if (ret) - pr_err("tegra_ehci_urb_enqueue timer modify failed \n"); - clk_enable(tegra->emc_clk); - clk_enable(tegra->sclk_clk); - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - } else { - if (timer_pending(&tegra->clk_timer)) { - mod_timer_pending (&tegra->clk_timer, jiffies - + msecs_to_jiffies(2000)); - } - } -} - -static int tegra_ehci_urb_enqueue ( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags) -{ - struct tegra_ehci_hcd *pdata; - int xfertype; - int transfer_buffer_length; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - pdata = dev_get_drvdata(hcd->self.controller); - - xfertype = usb_endpoint_type(&urb->ep->desc); - transfer_buffer_length = urb->transfer_buffer_length; - spin_lock_irqsave(&ehci->lock,flags); - /* Turn on the USB busy hints */ - switch (xfertype) { - case USB_ENDPOINT_XFER_INT: - if (transfer_buffer_length < 255) { - /* Do nothing for interrupt buffers < 255 */ - } else { - /* signal to set the busy hints */ - schedule_work(&pdata->clk_timer_work); - } - break; - case USB_ENDPOINT_XFER_ISOC: - case USB_ENDPOINT_XFER_BULK: - /* signal to set the busy hints */ - schedule_work(&pdata->clk_timer_work); - break; - case USB_ENDPOINT_XFER_CONTROL: - default: - /* Do nothing special here */ - break; - } - spin_unlock_irqrestore(&ehci->lock,flags); - return ehci_urb_enqueue(hcd, urb, mem_flags); -} - static const struct hc_driver tegra_ehci_hc_driver = { .description = hcd_name, .product_desc = "Tegra EHCI Host Controller", @@ -1060,9 +381,10 @@ static const struct hc_driver tegra_ehci_hc_driver = { /* standard ehci functions */ .start = ehci_run, .stop = ehci_stop, + .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, + .endpoint_reset = ehci_endpoint_reset, .get_frame_number = ehci_get_frame, .hub_status_data = ehci_hub_status_data, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, @@ -1076,10 +398,9 @@ static const struct hc_driver tegra_ehci_hc_driver = { .map_urb_for_dma = tegra_ehci_map_urb_for_dma, .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma, .hub_control = tegra_ehci_hub_control, - .urb_enqueue = tegra_ehci_urb_enqueue, #ifdef CONFIG_PM - .bus_suspend = tegra_ehci_bus_suspend, - .bus_resume = tegra_ehci_bus_resume, + .bus_suspend = tegra_ehci_bus_suspend, + .bus_resume = tegra_ehci_bus_resume, #endif }; @@ -1088,76 +409,30 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; struct tegra_ehci_hcd *tegra; - struct tegra_ehci_platform_data *pdata; int err = 0; int irq; - int instance = pdev->id; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "Platform data missing\n"); - return -EINVAL; - } - - tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL); - if (!tegra) + tegra = devm_kzalloc(&pdev->dev, sizeof(struct tegra_ehci_hcd), + GFP_KERNEL); + if (!tegra) { + dev_err(&pdev->dev, "memory alloc failed\n"); return -ENOMEM; + } - mutex_init(&tegra->tegra_ehci_hcd_mutex); + mutex_init(&tegra->sync_lock); hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { - dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto fail_hcd; + dev_err(&pdev->dev, "unable to create HCD\n"); + return -ENOMEM; } platform_set_drvdata(pdev, tegra); - tegra->default_enable = pdata->default_enable; - - tegra->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(tegra->clk)) { - dev_err(&pdev->dev, "Can't get ehci clock\n"); - err = PTR_ERR(tegra->clk); - goto fail_clk; - } - - err = clk_enable(tegra->clk); - if (err) - goto fail_clken; - - - tegra->sclk_clk = clk_get(&pdev->dev, "sclk"); - if (IS_ERR(tegra->sclk_clk)) { - dev_err(&pdev->dev, "Can't get sclk clock\n"); - err = PTR_ERR(tegra->sclk_clk); - goto fail_sclk_clk; - } - - clk_set_rate(tegra->sclk_clk, 80000000); - - tegra->emc_clk = clk_get(&pdev->dev, "emc"); - if (IS_ERR(tegra->emc_clk)) { - dev_err(&pdev->dev, "Can't get emc clock\n"); - err = PTR_ERR(tegra->emc_clk); - goto fail_emc_clk; - } - init_timer(&tegra->clk_timer); - tegra->clk_timer.function = clk_timer_callback; - tegra->clk_timer.data = (unsigned long) tegra; - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Set DDR busy hints to 150MHz. For Tegra 2x SOC, DDR rate is half of EMC rate */ - clk_set_rate(tegra->emc_clk, 300000000); -#else - /* Set DDR busy hints to 100MHz. For Tegra 3x SOC DDR rate equals to EMC rate */ - clk_set_rate(tegra->emc_clk, 100000000); -#endif res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&pdev->dev, "Failed to get I/O memory\n"); + dev_err(&pdev->dev, "failed to get I/O memory\n"); err = -ENXIO; goto fail_io; } @@ -1165,179 +440,94 @@ static int tegra_ehci_probe(struct platform_device *pdev) hcd->rsrc_len = resource_size(res); hcd->regs = ioremap(res->start, resource_size(res)); if (!hcd->regs) { - dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + dev_err(&pdev->dev, "failed to remap I/O memory\n"); err = -ENOMEM; goto fail_io; } - INIT_WORK(&tegra->clk_timer_work, clk_timer_work_handler); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + err = -ENODEV; + goto fail_irq; + } + set_irq_flags(irq, IRQF_VALID); + tegra->irq = irq; - tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config, - TEGRA_USB_PHY_MODE_HOST, pdata->phy_type); + tegra->phy = tegra_usb_phy_open(pdev); if (IS_ERR(tegra->phy)) { - dev_err(&pdev->dev, "Failed to open USB phy\n"); + dev_err(&pdev->dev, "failed to open USB phy\n"); err = -ENXIO; - goto fail_phy; + goto fail_irq; } - tegra->phy->hotplug = pdata->hotplug; - err = tegra_usb_phy_power_on(tegra->phy, true); + err = tegra_usb_phy_power_on(tegra->phy); if (err) { - dev_err(&pdev->dev, "Failed to power on the phy\n"); - goto fail; - } - - tegra->host_resumed = 1; - tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend; - tegra->ehci = hcd_to_ehci(hcd); - - irq = platform_get_irq(pdev, 0); - if (!irq) { - dev_err(&pdev->dev, "Failed to get IRQ\n"); - err = -ENODEV; - goto fail; + dev_err(&pdev->dev, "failed to power on the phy\n"); + goto fail_phy; } - set_irq_flags(irq, IRQF_VALID); - tegra->irq = irq; -#ifdef CONFIG_USB_OTG_UTILS - if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = otg_get_transceiver(); - if (tegra->transceiver) - otg_set_host(tegra->transceiver, &hcd->self); + err = tegra_usb_phy_init(tegra->phy); + if (err) { + dev_err(&pdev->dev, "failed to init the phy\n"); + goto fail_phy; } -#endif - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_TRIGGER_HIGH); if (err) { - dev_err(&pdev->dev, "Failed to add USB HCD error = %d\n", err); - goto fail; + dev_err(&pdev->dev, "Failed to add USB HCD, error=%d\n", err); + goto fail_phy; } err = enable_irq_wake(tegra->irq); if (err < 0) { dev_warn(&pdev->dev, - "Couldn't enable USB host mode wakeup, irq=%d, " - "error=%d\n", tegra->irq, err); + "Couldn't enable USB host mode wakeup, irq=%d, " + "error=%d\n", irq, err); err = 0; tegra->irq = 0; } - return err; + tegra->ehci = hcd_to_ehci(hcd); -fail: #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { - otg_set_host(tegra->transceiver, NULL); - otg_put_transceiver(tegra->transceiver); + if (tegra_usb_phy_otg_supported(tegra->phy)) { + tegra->transceiver = otg_get_transceiver(); + if (tegra->transceiver) + otg_set_host(tegra->transceiver, &hcd->self); } #endif - tegra_usb_phy_close(tegra->phy); + return err; + fail_phy: + tegra_usb_phy_close(tegra->phy); +fail_irq: iounmap(hcd->regs); fail_io: - clk_disable(tegra->emc_clk); - clk_put(tegra->emc_clk); -fail_emc_clk: - clk_disable(tegra->sclk_clk); - clk_put(tegra->sclk_clk); -fail_sclk_clk: - clk_disable(tegra->clk); -fail_clken: - clk_put(tegra->clk); -fail_clk: usb_put_hcd(hcd); -fail_hcd: - kfree(tegra); + return err; } + #ifdef CONFIG_PM -static int tegra_ehci_resume_noirq(struct device *dev) +static int tegra_ehci_resume(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(dev); struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - if (tegra->default_enable) - clk_enable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - if (tegra->default_enable) - clk_enable(tegra->clk); - - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; + return tegra_usb_phy_power_on(tegra->phy); } -static int tegra_ehci_resume(struct device *dev) +static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state) { - struct platform_device *pdev = to_platform_device(dev); struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - int ret; - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - ret = tegra_usb_resume(hcd, true); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ret; -} -static int tegra_ehci_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - int ret; - u32 val; - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - /* if bus suspend is failed means there is remote wakeup resume, - then abort the PM suspend */ - if (tegra->bus_suspended_fail) { - tegra->bus_suspended_fail = false; - pr_err("%s: bus suspend failed, aborting driver suspend\n", __func__); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + /* bus suspend could have failed because of remote wakeup resume */ + if (tegra->bus_suspended_fail) return -EBUSY; - } - if (tegra->phy->hotplug) { - /* Disable PHY clock valid interrupts while going into suspend*/ - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - } - - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - if (tegra->default_enable) - clk_disable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - if (time_before(jiffies, tegra->ehci->next_statechange)) - msleep(10); - - ret = tegra_usb_suspend(hcd, true); - if (tegra->default_enable) - clk_disable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ret; + else + return tegra_usb_phy_power_off(tegra->phy); } - -static struct dev_pm_ops tegra_ehci_dev_pm_ops = { - .suspend = tegra_ehci_suspend, - .resume = tegra_ehci_resume, - .resume_noirq = tegra_ehci_resume_noirq, -}; - #endif static int tegra_ehci_remove(struct platform_device *pdev) @@ -1347,9 +537,6 @@ static int tegra_ehci_remove(struct platform_device *pdev) if (tegra == NULL || hcd == NULL) return -EINVAL; - /* make sure controller is on as we will touch its registers */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, true); #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { @@ -1358,30 +545,19 @@ static int tegra_ehci_remove(struct platform_device *pdev) } #endif - /* Turn Off Interrupts */ - ehci_writel(tegra->ehci, 0, &tegra->ehci->regs->intr_enable); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (tegra->irq) disable_irq_wake(tegra->irq); + + /* Make sure phy is powered ON to access USB register */ + if(!tegra_usb_phy_hw_accessible(tegra->phy)) + tegra_usb_phy_power_on(tegra->phy); + usb_remove_hcd(hcd); usb_put_hcd(hcd); - tegra_usb_phy_power_off(tegra->phy, true); + tegra_usb_phy_power_off(tegra->phy); tegra_usb_phy_close(tegra->phy); iounmap(hcd->regs); - del_timer_sync(&tegra->clk_timer); - - clk_disable(tegra->clk); - clk_put(tegra->clk); - - if (tegra->clock_enabled) { - clk_disable(tegra->sclk_clk); - clk_disable(tegra->emc_clk); - } - clk_put(tegra->sclk_clk); - clk_put(tegra->emc_clk); - - kfree(tegra); return 0; } @@ -1396,12 +572,13 @@ static void tegra_ehci_hcd_shutdown(struct platform_device *pdev) static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, - .remove = tegra_ehci_remove, + .remove = tegra_ehci_remove, .shutdown = tegra_ehci_hcd_shutdown, - .driver = { - .name = "tegra-ehci", #ifdef CONFIG_PM - .pm = &tegra_ehci_dev_pm_ops, + .suspend = tegra_ehci_suspend, + .resume = tegra_ehci_resume, #endif + .driver = { + .name = driver_name, } }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 509934ceb4a9..cfbdf32ec0b2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -143,6 +143,7 @@ struct ehci_hcd { /* one per controller */ #ifdef CONFIG_USB_EHCI_TEGRA unsigned controller_resets_phy:1; unsigned controller_remote_wakeup:1; + unsigned broken_hostpc_phcd:1; #endif /* required for usb32 quirk */ diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 68d402afb25e..a5e85bf90d14 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -3,7 +3,7 @@ * * OTG transceiver driver for Tegra UTMI phy * - * Copyright (C) 2010 NVIDIA Corp. + * Copyright (C) 2010-2012 NVIDIA CORPORATION. All rights reserved. * Copyright (C) 2010 Google, Inc. * * This program is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ #include <linux/usb.h> #include <linux/usb/otg.h> #include <linux/usb/gadget.h> -#include <linux/usb/hcd.h> #include <linux/platform_device.h> #include <linux/platform_data/tegra_usb.h> #include <linux/clk.h> @@ -41,10 +40,8 @@ #define USB_VBUS_INT_EN (1 << 8) #define USB_VBUS_INT_STATUS (1 << 9) #define USB_VBUS_STATUS (1 << 10) -#define USB_INTS (USB_VBUS_INT_STATUS | USB_ID_INT_STATUS) - -typedef void (*callback_t)(enum usb_otg_state to, - enum usb_otg_state from, void *args); +#define USB_INT_EN (USB_VBUS_INT_EN | USB_ID_INT_EN | \ + USB_VBUS_WAKEUP_EN | USB_ID_PIN_WAKEUP_EN) #ifdef DEBUG #define DBG(stuff...) pr_info("tegra-otg: " stuff) @@ -63,11 +60,11 @@ struct tegra_otg_data { struct work_struct work; unsigned int intr_reg_data; bool clk_enabled; - callback_t charger_cb; - void *charger_cb_data; - bool interrupt_mode; + bool builtin_host; + bool suspended }; + static struct tegra_otg_data *tegra_clone; static inline unsigned long otg_readl(struct tegra_otg_data *tegra, @@ -82,20 +79,6 @@ static inline void otg_writel(struct tegra_otg_data *tegra, unsigned long val, writel(val, tegra->regs + offset); } -static void tegra_otg_enable_clk(void) -{ - if (!tegra_clone->clk_enabled) - clk_enable(tegra_clone->clk); - tegra_clone->clk_enabled = true; -} - -static void tegra_otg_disable_clk(void) -{ - if (tegra_clone->clk_enabled) - clk_disable(tegra_clone->clk); - tegra_clone->clk_enabled = false; -} - static const char *tegra_state_name(enum usb_otg_state state) { switch (state) { @@ -119,12 +102,13 @@ static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en) clk_enable(tegra->clk); val = otg_readl(tegra, USB_PHY_WAKEUP); if (en) { - val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); - val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); - } else { - val &= ~(USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); - val &= ~(USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); + if (tegra->builtin_host) + val |= USB_INT_EN; + else + val = USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN | USB_ID_PIN_WAKEUP_EN; } + else + val &= ~USB_INT_EN; otg_writel(tegra, val, USB_PHY_WAKEUP); /* Add delay to make sure register is updated */ udelay(1); @@ -133,81 +117,70 @@ static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en) return val; } -static struct platform_device * -tegra_usb_otg_host_register(struct platform_device *ehci_device, - struct tegra_ehci_platform_data *pdata) +static void tegra_start_host(struct tegra_otg_data *tegra) { - struct platform_device *pdev; + struct tegra_usb_otg_data *pdata = tegra->otg.dev->platform_data; + struct platform_device *pdev, *ehci_device = pdata->ehci_device; void *platform_data; int val; + DBG("%s(%d) Begin\n", __func__, __LINE__); + + if (tegra->pdev) + return ; + /* prepare device structure for registering host*/ pdev = platform_device_alloc(ehci_device->name, ehci_device->id); if (!pdev) - return NULL; + return ; val = platform_device_add_resources(pdev, ehci_device->resource, ehci_device->num_resources); if (val) goto error; - pdev->dev.dma_mask = ehci_device->dev.dma_mask; + pdev->dev.dma_mask = ehci_device->dev.dma_mask; pdev->dev.coherent_dma_mask = ehci_device->dev.coherent_dma_mask; - platform_data = kmalloc(sizeof(struct tegra_ehci_platform_data), - GFP_KERNEL); + platform_data = kmalloc(sizeof(struct tegra_usb_platform_data), GFP_KERNEL); if (!platform_data) goto error; - memcpy(platform_data, pdata, sizeof(struct tegra_ehci_platform_data)); + memcpy(platform_data, pdata->ehci_pdata, + sizeof(struct tegra_usb_platform_data)); pdev->dev.platform_data = platform_data; - val = platform_device_add(pdev); if (val) goto error_add; - return pdev; + tegra->pdev = pdev; + DBG("%s(%d) End\n", __func__, __LINE__); + return ; error_add: kfree(platform_data); error: pr_err("%s: failed to add the host controller device\n", __func__); platform_device_put(pdev); - return NULL; + tegra->pdev = NULL; } -static void tegra_usb_otg_host_unregister(struct platform_device *pdev) +static void tegra_stop_host(struct tegra_otg_data *tegra) { - kfree(pdev->dev.platform_data); - pdev->dev.platform_data = NULL; - platform_device_unregister(pdev); -} + struct platform_device *pdev = tegra->pdev; -void tegra_start_host(struct tegra_otg_data *tegra) -{ - struct tegra_otg_platform_data *pdata = tegra->otg.dev->platform_data; - if (!tegra->pdev) { - tegra->pdev = tegra_usb_otg_host_register(pdata->ehci_device, - pdata->ehci_pdata); - } -} + DBG("%s(%d) Begin\n", __func__, __LINE__); -void tegra_stop_host(struct tegra_otg_data *tegra) -{ - if (tegra->pdev) { - tegra_usb_otg_host_unregister(tegra->pdev); + if (pdev) { + /* unregister host from otg */ + kfree(pdev->dev.platform_data); + pdev->dev.platform_data = NULL; + platform_device_unregister(pdev); tegra->pdev = NULL; } -} -int register_otg_callback(callback_t cb, void *args) -{ - if (!tegra_clone) - return -ENODEV; - tegra_clone->charger_cb = cb; - tegra_clone->charger_cb_data = args; - return 0; + DBG("%s(%d) End\n", __func__, __LINE__); } -EXPORT_SYMBOL_GPL(register_otg_callback); + static void tegra_change_otg_state(struct tegra_otg_data *tegra, enum usb_otg_state to) @@ -228,9 +201,6 @@ static void tegra_change_otg_state(struct tegra_otg_data *tegra, dev_info(tegra->otg.dev, "%s --> %s\n", tegra_state_name(from), tegra_state_name(to)); - if (tegra->charger_cb) - tegra->charger_cb(to, from, tegra->charger_cb_data); - if (from == OTG_STATE_A_SUSPEND) { if (to == OTG_STATE_B_PERIPHERAL && otg->gadget) usb_gadget_vbus_connect(otg->gadget); @@ -256,10 +226,7 @@ static void irq_work(struct work_struct *work) unsigned long flags; unsigned long status; - clk_enable(tegra->clk); - spin_lock_irqsave(&tegra->lock, flags); - status = tegra->int_status; /* Debug prints */ @@ -274,7 +241,7 @@ static void irq_work(struct work_struct *work) DBG("%s(%d) got vbus interrupt\n", __func__, __LINE__); } - if (!(status & USB_ID_STATUS)) + if (!(status & USB_ID_STATUS) && (status & USB_ID_INT_EN)) to = OTG_STATE_A_HOST; else if (status & USB_VBUS_STATUS && from != OTG_STATE_A_HOST) to = OTG_STATE_B_PERIPHERAL; @@ -283,8 +250,6 @@ static void irq_work(struct work_struct *work) spin_unlock_irqrestore(&tegra->lock, flags); tegra_change_otg_state(tegra, to); - clk_disable(tegra->clk); - tegra_otg_disable_clk(); } static irqreturn_t tegra_otg_irq(int irq, void *data) @@ -294,51 +259,52 @@ static irqreturn_t tegra_otg_irq(int irq, void *data) unsigned long val; spin_lock_irqsave(&tegra->lock, flags); - val = otg_readl(tegra, USB_PHY_WAKEUP); + DBG("%s(%d) interrupt val = 0x%x\n", __func__, __LINE__, val); + if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) { + DBG("%s(%d) PHY_WAKEUP = 0x%x\n", __func__, __LINE__, val); otg_writel(tegra, val, USB_PHY_WAKEUP); if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) { tegra->int_status = val; schedule_work(&tegra->work); } } - spin_unlock_irqrestore(&tegra->lock, flags); return IRQ_HANDLED; } -void tegra_otg_check_vbus_detection(void) -{ - tegra_otg_enable_clk(); -} -EXPORT_SYMBOL(tegra_otg_check_vbus_detection); static int tegra_otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) { struct tegra_otg_data *tegra; unsigned long val; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); tegra = container_of(otg, struct tegra_otg_data, otg); otg->gadget = gadget; val = enable_interrupt(tegra, true); - if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) { + if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) val |= USB_VBUS_INT_STATUS; - } else if (!(val & USB_ID_STATUS)) { - val |= USB_ID_INT_STATUS; - } else { - val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS); + else if (!(val & USB_ID_STATUS)) { + if(!tegra->builtin_host) + val &= ~USB_ID_INT_STATUS; + else + val |= USB_ID_INT_STATUS; } + else + val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS); if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) { tegra->int_status = val; - schedule_work (&tegra->work); + schedule_work(&tegra->work); } + DBG("%s(%d) END\n", __func__, __LINE__); return 0; } @@ -347,6 +313,7 @@ static int tegra_otg_set_host(struct otg_transceiver *otg, { struct tegra_otg_data *tegra; unsigned long val; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); tegra = container_of(otg, struct tegra_otg_data, otg); otg->host = host; @@ -354,11 +321,11 @@ static int tegra_otg_set_host(struct otg_transceiver *otg, clk_enable(tegra->clk); val = otg_readl(tegra, USB_PHY_WAKEUP); val &= ~(USB_VBUS_INT_STATUS | USB_ID_INT_STATUS); - val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); otg_writel(tegra, val, USB_PHY_WAKEUP); clk_disable(tegra->clk); + DBG("%s(%d) END\n", __func__, __LINE__); return 0; } @@ -389,12 +356,9 @@ static ssize_t store_host_en(struct device *dev, struct device_attribute *attr, struct platform_device *pdev = to_platform_device(dev); struct tegra_otg_data *tegra = platform_get_drvdata(pdev); unsigned long host; - int err; - err = kstrtoul(buf, 10, &host); - if (err < 0) { - return err; - } + if (sscanf(buf, "%d", &host) != 1 || host < 0 || host > 1) + return -EINVAL; if (host) { enable_interrupt(tegra, false); @@ -415,9 +379,8 @@ static DEVICE_ATTR(enable_host, 0644, show_host_en, store_host_en); static int tegra_otg_probe(struct platform_device *pdev) { struct tegra_otg_data *tegra; - struct tegra_otg_platform_data *otg_pdata; - struct tegra_ehci_platform_data *ehci_pdata; struct resource *res; + struct tegra_usb_otg_data *pdata = dev_get_platdata(&pdev->dev); int err; tegra = kzalloc(sizeof(struct tegra_otg_data), GFP_KERNEL); @@ -425,8 +388,6 @@ static int tegra_otg_probe(struct platform_device *pdev) return -ENOMEM; tegra->otg.dev = &pdev->dev; - otg_pdata = tegra->otg.dev->platform_data; - ehci_pdata = otg_pdata->ehci_pdata; tegra->otg.label = "tegra-otg"; tegra->otg.state = OTG_STATE_UNDEFINED; tegra->otg.set_host = tegra_otg_set_host; @@ -435,10 +396,14 @@ static int tegra_otg_probe(struct platform_device *pdev) tegra->otg.set_power = tegra_otg_set_power; spin_lock_init(&tegra->lock); + if (pdata) { + tegra->builtin_host = !pdata->ehci_pdata->builtin_host_disabled; + } + platform_set_drvdata(pdev, tegra); tegra_clone = tegra; - tegra->clk_enabled = false; tegra->interrupt_mode = true; + tegra->suspended = false; tegra->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(tegra->clk)) { @@ -480,15 +445,23 @@ static int tegra_otg_probe(struct platform_device *pdev) tegra->irq = res->start; err = request_threaded_irq(tegra->irq, tegra_otg_irq, NULL, - IRQF_SHARED, "tegra-otg", tegra); + IRQF_SHARED | IRQF_TRIGGER_HIGH, + "tegra-otg", tegra); if (err) { dev_err(&pdev->dev, "Failed to register IRQ\n"); goto err_irq; } - INIT_WORK (&tegra->work, irq_work); - if (!ehci_pdata->default_enable) - clk_disable(tegra->clk); + err = enable_irq_wake(tegra->irq); + if (err < 0) { + dev_warn(&pdev->dev, + "Couldn't enable USB otg mode wakeup, irq=%d, error=%d\n", + tegra->irq, err); + err = 0; + } + + INIT_WORK(&tegra->work, irq_work); + dev_info(&pdev->dev, "otg transceiver registered\n"); err = device_create_file(&pdev->dev, &dev_attr_enable_host); @@ -497,6 +470,8 @@ static int tegra_otg_probe(struct platform_device *pdev) goto err_irq; } + clk_disable(tegra->clk); + return 0; err_irq: @@ -532,58 +507,67 @@ static int __exit tegra_otg_remove(struct platform_device *pdev) static int tegra_otg_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); - struct otg_transceiver *otg = &tegra_otg->otg; - enum usb_otg_state from = otg->state; - unsigned int val; - - /* store the interupt enable for cable ID and VBUS */ - clk_enable(tegra_otg->clk); - tegra_otg->intr_reg_data = readl(tegra_otg->regs + USB_PHY_WAKEUP); - val = tegra_otg->intr_reg_data & ~(USB_ID_INT_EN | USB_VBUS_INT_EN); - writel(val, (tegra_otg->regs + USB_PHY_WAKEUP)); - clk_disable(tegra_otg->clk); - - if (from == OTG_STATE_B_PERIPHERAL && otg->gadget) { - usb_gadget_vbus_disconnect(otg->gadget); - otg->state = OTG_STATE_A_SUSPEND; - } - tegra_otg_disable_clk(); + struct tegra_otg_data *tegra = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &tegra->otg; + int val; + DBG("%s(%d) BEGIN state : %s\n", __func__, __LINE__, + tegra_state_name(otg->state)); + + clk_enable(tegra->clk); + val = otg_readl(tegra, USB_PHY_WAKEUP); + val &= ~(USB_ID_INT_EN | USB_VBUS_INT_EN); + otg_writel(tegra, val, USB_PHY_WAKEUP); + clk_disable(tegra->clk); + + /* Suspend peripheral mode, host mode is taken care by host driver */ + if (otg->state == OTG_STATE_B_PERIPHERAL) + tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND); + + tegra->suspended = true; + + DBG("%s(%d) END\n", __func__, __LINE__); return 0; } static void tegra_otg_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); + struct tegra_otg_data *tegra = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &tegra->otg; int val; unsigned long flags; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); - tegra_otg_enable_clk(); - - /* Following delay is intentional. - * It is placed here after observing system hang. - * Root cause is not confirmed. - */ - msleep(1); - /* restore the interupt enable for cable ID and VBUS */ - clk_enable(tegra_otg->clk); - writel(tegra_otg->intr_reg_data, (tegra_otg->regs + USB_PHY_WAKEUP)); - val = readl(tegra_otg->regs + USB_PHY_WAKEUP); - clk_disable(tegra_otg->clk); - - /* A device might be connected while CPU is in sleep mode. In this case no interrupt - * will be triggered - * force irq_work to recheck connected devices - */ - if (!(val & USB_ID_STATUS)) { - spin_lock_irqsave(&tegra_otg->lock, flags); - tegra_otg->int_status = (val | USB_ID_INT_STATUS ); - schedule_work(&tegra_otg->work); - spin_unlock_irqrestore(&tegra_otg->lock, flags); - } + if (!tegra->suspended) + return; + + /* Clear pending interrupts */ + clk_enable(tegra->clk); + val = otg_readl(tegra, USB_PHY_WAKEUP); + otg_writel(tegra, val, USB_PHY_WAKEUP); + DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val); + clk_disable(tegra->clk); + + /* Handle if host cable is replaced with device during suspend state */ + if (otg->state == OTG_STATE_A_HOST && (val & USB_ID_STATUS)) + tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND); + + /* Enable interrupt and call work to set to appropriate state */ + spin_lock_irqsave(&tegra->lock, flags); + if (tegra->builtin_host) + tegra->int_status = val | USB_INT_EN; + else + tegra->int_status = val | USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN | + USB_ID_PIN_WAKEUP_EN; + + spin_unlock_irqrestore(&tegra->lock, flags); + irq_work(&tegra->work); + + enable_interrupt(tegra, true); + + tegra->suspended = false; - return; + DBG("%s(%d) END\n", __func__, __LINE__); } static const struct dev_pm_ops tegra_otg_pm_ops = { diff --git a/drivers/usb/serial/baseband_usb_chr.c b/drivers/usb/serial/baseband_usb_chr.c index cad33d5b6f49..96db92715207 100644 --- a/drivers/usb/serial/baseband_usb_chr.c +++ b/drivers/usb/serial/baseband_usb_chr.c @@ -3,7 +3,7 @@ * * USB character driver to communicate with baseband modems. * - * Copyright (c) 2012, NVIDIA Corporation. + * Copyright (c) 2012, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,16 +40,32 @@ MODULE_LICENSE("GPL"); -unsigned long baseband_usb_chr_vid = 0x058b; -unsigned long baseband_usb_chr_pid = 0x0041; -unsigned long baseband_usb_chr_intf = 0x01; +/* To add new usb devices, update + * (1) baseband_usb_driver_id_table + * - usb vendor id / product id + * (2) baseband_usb_driver_intf_table + * - usb interface number + */ + +static struct usb_device_id baseband_usb_driver_id_table[] = { + /* XMM modem #1 BOOT ROM */ + { USB_DEVICE(0x058b, 0x0041), }, + /* XMM modem #2 BOOT ROM */ + { USB_DEVICE(0x8087, 0x0716), }, + /* empty entry required to terminate list */ + { }, +}; + +static unsigned int baseband_usb_driver_intf_table[] = { + /* XMM modem #1 BOOT ROM */ + 0x01, + /* XMM modem #2 BOOT ROM */ + 0x00, + /* empty entry required to terminate list */ + 0x00, +}; -module_param(baseband_usb_chr_vid, ulong, 0644); -MODULE_PARM_DESC(baseband_usb_chr_vid, "baseband (usb chr) - USB VID"); -module_param(baseband_usb_chr_pid, ulong, 0644); -MODULE_PARM_DESC(baseband_usb_chr_pid, "baseband (usb chr) - USB PID"); -module_param(baseband_usb_chr_intf, ulong, 0644); -MODULE_PARM_DESC(baseband_usb_chr_intf, "baseband (usb chr) - USB interface"); +MODULE_DEVICE_TABLE(usb, baseband_usb_driver_id_table); static struct baseband_usb *baseband_usb_chr; static struct usb_interface *probe_usb_intf; @@ -400,7 +416,7 @@ static ssize_t baseband_ipc_file_write(struct baseband_ipc *ipc, /* do not accept write if previous tx not finished */ if (peek_ipc_tx_bufsiz(ipc, USB_CHR_TX_BUFSIZ) != 0) { - pr_info("%s: not accepting write of %u bytes" + pr_debug("%s: not accepting write of %u bytes" " - previous tx not finished\n", __func__, count); return 0; @@ -649,10 +665,15 @@ static void baseband_usb_chr_rx_urb_comp(struct urb *urb) } switch (urb->status) { + case 0: + /* success */ + break; case -ENOENT: case -ESHUTDOWN: case -EPROTO: pr_info("%s: link down\n", __func__); + default: + pr_err("%s: urb error status %d\n", __func__, urb->status); return; } @@ -787,6 +808,8 @@ static void find_usb_pipe(struct baseband_usb *usb) static int baseband_usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id) { + int i; + pr_debug("%s(%d) { intf %p id %p\n", __func__, __LINE__, intf, id); pr_debug("intf->cur_altsetting->desc.bInterfaceNumber %02x\n", @@ -805,10 +828,14 @@ static int baseband_usb_driver_probe(struct usb_interface *intf, intf->cur_altsetting->desc.iInterface); /* usb interface mismatch */ - if (baseband_usb_chr_intf != - intf->cur_altsetting->desc.bInterfaceNumber) { - pr_debug("%s(%d) } -ENODEV\n", __func__, __LINE__); - return -ENODEV; + for (i = 0; baseband_usb_driver_id_table[i].match_flags; i++) { + if (id == &baseband_usb_driver_id_table[i]) { + if (baseband_usb_driver_intf_table[i] != + intf->cur_altsetting->desc.bInterfaceNumber) { + pr_debug("%s(%d) } -ENODEV\n", __func__, __LINE__); + return -ENODEV; + } + } } /* usb interface match */ @@ -844,12 +871,8 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf) pr_debug("%s(%d) }\n", __func__, __LINE__); } -static char baseband_usb_driver_name[32]; - -static struct usb_device_id baseband_usb_driver_id_table[2]; - static struct usb_driver baseband_usb_driver = { - .name = baseband_usb_driver_name, + .name = "bb_usb_chr", .probe = baseband_usb_driver_probe, .disconnect = baseband_usb_driver_disconnect, .id_table = baseband_usb_driver_id_table, @@ -924,6 +947,9 @@ static void baseband_usb_close(struct baseband_usb *usb) if (!usb) return; + /* we need proper lock, maybe...*/ + usb_device_connection = false; + /* free re-usable rx urb + rx urb transfer buffer */ if (usb->usb.rx_urb) { pr_debug("%s: free rx urb\n", __func__); @@ -936,7 +962,6 @@ static void baseband_usb_close(struct baseband_usb *usb) } if (usb->ipc) { - usb_device_connection = false; flush_work_sync(&usb->ipc->work); flush_work_sync(&usb->ipc->rx_work); } @@ -963,10 +988,7 @@ static void baseband_usb_close(struct baseband_usb *usb) pr_debug("baseband_usb_close }\n"); } -static struct baseband_usb *baseband_usb_open(unsigned int vid, - unsigned int pid, - unsigned int intf, - work_func_t work_func, +static struct baseband_usb *baseband_usb_open(work_func_t work_func, work_func_t rx_work_func, work_func_t tx_work_func) { @@ -994,13 +1016,6 @@ static struct baseband_usb *baseband_usb_open(unsigned int vid, /* open usb driver */ probe_usb_intf = (struct usb_interface *) 0; - sprintf(baseband_usb_driver_name, - "baseband_usb_%x_%x_%x", - vid, pid, intf); - baseband_usb_driver_id_table[0].match_flags - = USB_DEVICE_ID_MATCH_DEVICE; - baseband_usb_driver_id_table[0].idVendor = vid; - baseband_usb_driver_id_table[0].idProduct = pid; usb->usb.driver = &baseband_usb_driver; err = usb_register(&baseband_usb_driver); if (err < 0) { @@ -1091,10 +1106,7 @@ static int baseband_usb_chr_open(struct inode *inode, struct file *file) } /* open baseband usb */ - baseband_usb_chr = baseband_usb_open(baseband_usb_chr_vid, - baseband_usb_chr_pid, - baseband_usb_chr_intf, - baseband_usb_chr_work, + baseband_usb_chr = baseband_usb_open(baseband_usb_chr_work, baseband_usb_chr_rx_urb_comp_work, (work_func_t) 0); if (!baseband_usb_chr) { diff --git a/drivers/video/backlight/tegra_pwm_bl.c b/drivers/video/backlight/tegra_pwm_bl.c index 4be691c54d3a..a9a429e5c837 100644 --- a/drivers/video/backlight/tegra_pwm_bl.c +++ b/drivers/video/backlight/tegra_pwm_bl.c @@ -20,6 +20,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/tegra_pwm_bl.h> +#include <linux/gpio.h> #include <mach/dc.h> struct tegra_pwm_bl_data { @@ -112,11 +113,17 @@ static int tegra_pwm_backlight_probe(struct platform_device *pdev) tbl->check_fb = data->check_fb; tbl->params.which_pwm = data->which_pwm; tbl->params.gpio_conf_to_sfio = data->gpio_conf_to_sfio; - tbl->params.switch_to_sfio = data->switch_to_sfio; tbl->params.period = data->period; tbl->params.clk_div = data->clk_div; tbl->params.clk_select = data->clk_select; + /* If backlight pin is sfio, request for it */ + if (gpio_is_valid(tbl->params.gpio_conf_to_sfio)) { + ret = gpio_request(tbl->params.gpio_conf_to_sfio, "disp_bl"); + if (ret) + dev_err(&pdev->dev, "backlight gpio request failed\n"); + } + memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = data->max_brightness; @@ -129,6 +136,9 @@ static int tegra_pwm_backlight_probe(struct platform_device *pdev) } bl->props.brightness = data->dft_brightness; + + if (gpio_is_valid(tbl->params.gpio_conf_to_sfio)) + gpio_free(tbl->params.gpio_conf_to_sfio); backlight_update_status(bl); platform_set_drvdata(pdev, bl); diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 473875a3ae78..5e15a92609b0 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -1103,13 +1103,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var, if (mx3_fbi->txd) async_tx_ack(mx3_fbi->txd); -<<<<<<< HEAD - txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg + - mx3_fbi->cur_ipu_buf, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT); -======= txd = dmaengine_prep_slave_sg(dma_chan, sg + mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers if (!txd) { dev_err(fbi->device, "Error preparing a DMA transaction descriptor.\n"); diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index 468a5566b667..b5540a5793b2 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -7,6 +7,21 @@ config TEGRA_GRHOST help Driver for the Tegra graphics host hardware. +config TEGRA_GRHOST_USE_NVMAP + bool "Use nvmap as graphics memory manager" + default y + help + Use nvmap as the graphics memory manager. This is the only + choice at the moment. + +config TEGRA_GRHOST_DEFAULT_TIMEOUT + depends on TEGRA_GRHOST + int "Default timeout for submits" + default 0 if TEGRA_SIMULATION_PLATFORM + default 30000 + help + Default timeout for jobs in milliseconds. Set to zero for no timeout. + config TEGRA_DC tristate "Tegra Display Contoller" depends on ARCH_TEGRA && TEGRA_GRHOST @@ -35,6 +50,7 @@ config TEGRA_DC_EXTENSIONS config TEGRA_NVMAP bool "Tegra GPU memory management driver (nvmap)" + select ARM_DMA_USE_IOMMU if IOMMU_API default y help Say Y here to include the memory management driver for the Tegra @@ -42,7 +58,7 @@ config TEGRA_NVMAP config NVMAP_RECLAIM_UNPINNED_VM bool "Virtualize IOVMM memory in nvmap" - depends on TEGRA_NVMAP && TEGRA_IOVMM + depends on TEGRA_NVMAP && (TEGRA_IOVMM || IOMMU_API) default y help Say Y here to enable nvmap to reclaim I/O virtual memory after @@ -61,7 +77,7 @@ config NVMAP_ALLOW_SYSMEM config NVMAP_HIGHMEM_ONLY bool "Use only HIGHMEM for nvmap" - depends on TEGRA_NVMAP && (NVMAP_ALLOW_SYSMEM || TEGRA_IOVMM) && HIGHMEM + depends on TEGRA_NVMAP && (NVMAP_ALLOW_SYSMEM || TEGRA_IOVMM || IOMMU_API) && HIGHMEM default n help Say Y here to restrict nvmap system memory allocations (both @@ -85,6 +101,31 @@ config NVMAP_CARVEOUT_COMPACTOR heap and retries the failed allocation. Say Y here to let nvmap to keep carveout fragmentation under control. +config NVMAP_PAGE_POOLS + bool "Use page pools to reduce allocation overhead" + depends on TEGRA_NVMAP + default y + help + say Y here to reduce the alloction overhead, which is significant + for uncached, writecombine and inner cacheable memories as it + involves changing page attributes during every allocation per page + and flushing cache. Alloc time is reduced by allcoating the pages + ahead and keeping them aside. The reserved pages would be released + when system is low on memory and acquired back during release of + memory. + +config NVMAP_PAGE_POOL_SIZE + hex + default 0x0 + +config NVMAP_CACHE_MAINT_BY_SET_WAYS + bool "Enalbe cache maintenance by set/ways" + depends on TEGRA_NVMAP + help + Say Y here to reduce cache maintenance overhead by MVA. + This helps in reducing cache maintenance overhead in the systems, + where inner cache includes only L1. For the systems, where inner cache + includes L1 and L2, keep this option disabled. config NVMAP_VPR bool "Enable VPR Heap." @@ -102,7 +143,7 @@ config TEGRA_DSI config NVMAP_CONVERT_CARVEOUT_TO_IOVMM bool "Convert carveout to IOVMM" - depends on TEGRA_NVMAP && TEGRA_IOVMM_SMMU + depends on TEGRA_NVMAP && (TEGRA_IOVMM_SMMU || IOMMU_API) default y help Say Y here to force to convert carveout memory requests to diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile index 2299a3c5eaa3..ce581d2c81a3 100644 --- a/drivers/video/tegra/Makefile +++ b/drivers/video/tegra/Makefile @@ -1,4 +1,6 @@ GCOV_PROFILE := y +subdir-ccflags-y := -Werror +EXTRA_CFLAGS += -Idrivers/video/tegra/host obj-$(CONFIG_TEGRA_GRHOST) += host/ obj-$(CONFIG_TEGRA_DC) += dc/ obj-$(CONFIG_FB_TEGRA) += fb.o diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile index 01f13918ca63..59104c681bae 100644 --- a/drivers/video/tegra/dc/Makefile +++ b/drivers/video/tegra/dc/Makefile @@ -1,5 +1,6 @@ GCOV_PROFILE := y -obj-y += dc.o +EXTRA_CFLAGS += -Idrivers/video/tegra/host +obj-y += dc.o bandwidth.o mode.o clock.o lut.o csc.o window.o obj-y += rgb.o obj-y += hdmi.o obj-$(CONFIG_TEGRA_NVHDCP) += nvhdcp.o @@ -7,4 +8,5 @@ obj-y += edid.o obj-y += nvsd.o obj-y += dsi.o obj-y += dc_sysfs.o +obj-y += dc_config.o obj-$(CONFIG_TEGRA_DC_EXTENSIONS) += ext/ diff --git a/drivers/video/tegra/dc/bandwidth.c b/drivers/video/tegra/dc/bandwidth.c new file mode 100644 index 000000000000..0b307f4bc4a2 --- /dev/null +++ b/drivers/video/tegra/dc/bandwidth.c @@ -0,0 +1,284 @@ +/* + * drivers/video/tegra/dc/bandwidth.c + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/clk.h> + +#include <mach/clk.h> +#include <mach/dc.h> +#include <mach/fb.h> +#include <mach/mc.h> +#include <linux/nvhost.h> +#include <mach/latency_allowance.h> + +#include "dc_reg.h" +#include "dc_config.h" +#include "dc_priv.h" + +static int use_dynamic_emc = 1; + +module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR); + +/* uses the larger of w->bandwidth or w->new_bandwidth */ +static void tegra_dc_set_latency_allowance(struct tegra_dc *dc, + struct tegra_dc_win *w) +{ + /* windows A, B, C for first and second display */ + static const enum tegra_la_id la_id_tab[2][3] = { + /* first display */ + { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B, + TEGRA_LA_DISPLAY_0C }, + /* second display */ + { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB, + TEGRA_LA_DISPLAY_0CB }, + }; + /* window B V-filter tap for first and second display. */ + static const enum tegra_la_id vfilter_tab[2] = { + TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB, + }; + unsigned long bw; + + BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab)); + BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab)); + BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab)); + + bw = max(w->bandwidth, w->new_bandwidth); + + /* tegra_dc_get_bandwidth() treats V filter windows as double + * bandwidth, but LA has a seperate client for V filter */ + if (w->idx == 1 && win_use_v_filter(dc, w)) + bw /= 2; + + /* our bandwidth is in kbytes/sec, but LA takes MBps. + * round up bandwidth to next 1MBps */ + bw = bw / 1000 + 1; + +#ifdef CONFIG_TEGRA_SILICON_PLATFORM + tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw); + /* if window B, also set the 1B client for the 2-tap V filter. */ + if (w->idx == 1) + tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw); +#endif +} + +static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a, + struct tegra_dc_win *b) +{ + if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b)) + return 0; + + /* because memory access to load the fifo can overlap, only care + * if windows overlap vertically */ + return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) || + ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y)); +} + +static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[], + int n) +{ + unsigned i; + unsigned j; + unsigned overlap_count; + unsigned max_bw = 0; + + WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely" + "inaccurate.\n"); + + /* If we had a large number of windows, we would compute adjacency + * graph representing 2 window overlaps, find all cliques in the graph, + * assign bandwidth to each clique, and then select the clique with + * maximum bandwidth. But because we have at most 3 windows, + * implementing proper Bron-Kerbosh algorithm would be an overkill, + * brute force will suffice. + * + * Thus: find maximum bandwidth for either single or a pair of windows + * and count number of window pair overlaps. If there are three + * pairs, all 3 window overlap. + */ + + overlap_count = 0; + for (i = 0; i < n; i++) { + unsigned int bw1; + + if (wins[i] == NULL) + continue; + bw1 = wins[i]->new_bandwidth; + if (bw1 > max_bw) + /* Single window */ + max_bw = bw1; + + for (j = i + 1; j < n; j++) { + if (wins[j] == NULL) + continue; + if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) { + unsigned int bw2 = wins[j]->new_bandwidth; + if (bw1 + bw2 > max_bw) + /* Window pair overlaps */ + max_bw = bw1 + bw2; + overlap_count++; + } + } + } + + if (overlap_count == 3) + /* All three windows overlap */ + max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth + + wins[2]->new_bandwidth; + + return max_bw; +} + +/* + * Calculate peak EMC bandwidth for each enabled window = + * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor * + * (windows_tiling ? 2 : 1) + * + * note: + * (*) We use 2 tap V filter, so need double BW if use V filter + * (*) Tiling mode on T30 and DDR3 requires double BW + * + * return: + * bandwidth in kBps + */ +static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc, + struct tegra_dc_win *w) +{ + unsigned long ret; + int tiled_windows_bw_multiplier; + unsigned long bpp; + + if (!WIN_IS_ENABLED(w)) + return 0; + + if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 || + w->out_w == 0 || w->out_h == 0) + return 0; + + tiled_windows_bw_multiplier = + tegra_mc_get_tiled_memory_bandwidth_multiplier(); + + /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel, + * but the size reported by tegra_dc_fmt_bpp for the planar version + * is of the luma plane's size only. */ + bpp = tegra_dc_is_yuv_planar(w->fmt) ? + 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt); + ret = dc->mode.pclk / 1000UL * bpp / 8 * ( + win_use_v_filter(dc, w) ? 2 : 1) * + dfixed_trunc(w->w) / w->out_w * (WIN_IS_TILED(w) ? + tiled_windows_bw_multiplier : 1); + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + /* + * Assuming 60% efficiency: i.e. if we calculate we need 70MBps, we + * will request 117MBps from EMC. + */ + ret = ret + (17 * ret / 25); +#endif + return ret; +} + +static unsigned long tegra_dc_get_bandwidth( + struct tegra_dc_win *windows[], int n) +{ + int i; + + BUG_ON(n > DC_N_WINDOWS); + + /* emc rate and latency allowance both need to know per window + * bandwidths */ + for (i = 0; i < n; i++) { + struct tegra_dc_win *w = windows[i]; + + if (w) + w->new_bandwidth = + tegra_dc_calc_win_bandwidth(w->dc, w); + } + + return tegra_dc_find_max_bandwidth(windows, n); +} + +/* to save power, call when display memory clients would be idle */ +void tegra_dc_clear_bandwidth(struct tegra_dc *dc) +{ + trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__, + dc->emc_clk_rate); + if (tegra_is_clk_enabled(dc->emc_clk)) + clk_disable(dc->emc_clk); + dc->emc_clk_rate = 0; +} + +/* use the larger of dc->emc_clk_rate or dc->new_emc_clk_rate, and copies + * dc->new_emc_clk_rate into dc->emc_clk_rate. + * calling this function both before and after a flip is sufficient to select + * the best possible frequency and latency allowance. + * set use_new to true to force dc->new_emc_clk_rate programming. + */ +void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new) +{ + unsigned i; + + if (use_new || dc->emc_clk_rate != dc->new_emc_clk_rate) { + /* going from 0 to non-zero */ + if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk)) + clk_enable(dc->emc_clk); + + clk_set_rate(dc->emc_clk, + max(dc->emc_clk_rate, dc->new_emc_clk_rate)); + dc->emc_clk_rate = dc->new_emc_clk_rate; + + /* going from non-zero to 0 */ + if (!dc->new_emc_clk_rate && tegra_is_clk_enabled(dc->emc_clk)) + clk_disable(dc->emc_clk); + } + + for (i = 0; i < DC_N_WINDOWS; i++) { + struct tegra_dc_win *w = &dc->windows[i]; + + if ((use_new || w->bandwidth != w->new_bandwidth) && + w->new_bandwidth != 0) + tegra_dc_set_latency_allowance(dc, w); + w->bandwidth = w->new_bandwidth; + trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx, + w->bandwidth); + } +} + +int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n) +{ + unsigned long new_rate; + struct tegra_dc *dc; + + if (!use_dynamic_emc) + return 0; + + dc = windows[0]->dc; + + /* calculate the new rate based on this POST */ + new_rate = tegra_dc_get_bandwidth(windows, n); + if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n")) + new_rate = ULONG_MAX; + else + new_rate = EMC_BW_TO_FREQ(new_rate * 1000); + + if (tegra_dc_has_multiple_dc()) + new_rate = ULONG_MAX; + + trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate); + dc->new_emc_clk_rate = new_rate; + + return 0; +} diff --git a/drivers/video/tegra/dc/clock.c b/drivers/video/tegra/dc/clock.c new file mode 100644 index 000000000000..c785282693e6 --- /dev/null +++ b/drivers/video/tegra/dc/clock.c @@ -0,0 +1,150 @@ +/* + * drivers/video/tegra/dc/clock.c + * + * Copyright (C) 2010 Google, Inc. + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/types.h> +#include <linux/clk.h> + +#include <mach/clk.h> +#include <mach/dc.h> + +#include "dc_reg.h" +#include "dc_priv.h" + +unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk) +{ + unsigned long rate; + unsigned long div; + + rate = tegra_dc_clk_get_rate(dc); + + div = DIV_ROUND_CLOSEST(rate * 2, pclk); + + if (div < 2) + return 0; + + return rate * 2 / div; +} + +static unsigned long tegra_dc_pclk_predict_rate(struct clk *parent, int pclk) +{ + unsigned long rate; + unsigned long div; + + rate = clk_get_rate(parent); + + div = DIV_ROUND_CLOSEST(rate * 2, pclk); + + if (div < 2) + return 0; + + return rate * 2 / div; +} + +void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk) +{ + int pclk; + + if (dc->out->type == TEGRA_DC_OUT_RGB) { + unsigned long rate; + struct clk *parent_clk = + clk_get_sys(NULL, dc->out->parent_clk ? : "pll_p"); + + if (dc->out->parent_clk_backup && + (parent_clk == clk_get_sys(NULL, "pll_p"))) { + rate = tegra_dc_pclk_predict_rate( + parent_clk, dc->mode.pclk); + /* use pll_d as last resort */ + if (rate < (dc->mode.pclk / 100 * 99) || + rate > (dc->mode.pclk / 100 * 109)) + parent_clk = clk_get_sys( + NULL, dc->out->parent_clk_backup); + } + + if (clk_get_parent(clk) != parent_clk) + clk_set_parent(clk, parent_clk); + + if (parent_clk != clk_get_sys(NULL, "pll_p")) { + struct clk *base_clk = clk_get_parent(parent_clk); + + /* Assuming either pll_d or pll_d2 is used */ + rate = dc->mode.pclk * 2; + + if (rate != clk_get_rate(base_clk)) + clk_set_rate(base_clk, rate); + } + } + + if (dc->out->type == TEGRA_DC_OUT_HDMI) { + unsigned long rate; + struct clk *parent_clk = clk_get_sys(NULL, + dc->out->parent_clk ? : "pll_d_out0"); + struct clk *base_clk = clk_get_parent(parent_clk); + + /* + * Providing dynamic frequency rate setting for T20/T30 HDMI. + * The required rate needs to be setup at 4x multiplier, + * as out0 is 1/2 of the actual PLL output. + */ + + rate = dc->mode.pclk * 4; + if (rate != clk_get_rate(base_clk)) + clk_set_rate(base_clk, rate); + + if (clk_get_parent(clk) != parent_clk) + clk_set_parent(clk, parent_clk); + } + + if (dc->out->type == TEGRA_DC_OUT_DSI) { + unsigned long rate; + struct clk *parent_clk; + struct clk *base_clk; + + if (clk == dc->clk) { + parent_clk = clk_get_sys(NULL, + dc->out->parent_clk ? : "pll_d_out0"); + base_clk = clk_get_parent(parent_clk); + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_DSI_OUT_ENB, 1); + } else { + if (dc->pdata->default_out->dsi->dsi_instance) { + parent_clk = clk_get_sys(NULL, + dc->out->parent_clk ? : "pll_d2_out0"); + base_clk = clk_get_parent(parent_clk); + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_CSI_OUT_ENB, 1); + } else { + parent_clk = clk_get_sys(NULL, + dc->out->parent_clk ? : "pll_d_out0"); + base_clk = clk_get_parent(parent_clk); + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_DSI_OUT_ENB, 1); + } + } + + rate = dc->mode.pclk * dc->shift_clk_div * 2; + if (rate != clk_get_rate(base_clk)) + clk_set_rate(base_clk, rate); + + if (clk_get_parent(clk) != parent_clk) + clk_set_parent(clk, parent_clk); + } + + pclk = tegra_dc_pclk_round_rate(dc, dc->mode.pclk); + tegra_dvfs_set_rate(clk, pclk); +} diff --git a/drivers/video/tegra/dc/csc.c b/drivers/video/tegra/dc/csc.c new file mode 100644 index 000000000000..74fa900352a1 --- /dev/null +++ b/drivers/video/tegra/dc/csc.c @@ -0,0 +1,67 @@ +/* + * drivers/video/tegra/dc/csc.c + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/types.h> +#include <mach/dc.h> + +#include "dc_reg.h" +#include "dc_priv.h" + +void tegra_dc_init_csc_defaults(struct tegra_dc_csc *csc) +{ + csc->yof = 0x00f0; + csc->kyrgb = 0x012a; + csc->kur = 0x0000; + csc->kvr = 0x0198; + csc->kug = 0x039b; + csc->kvg = 0x032f; + csc->kub = 0x0204; + csc->kvb = 0x0000; +} + +void tegra_dc_set_csc(struct tegra_dc *dc, struct tegra_dc_csc *csc) +{ + tegra_dc_writel(dc, csc->yof, DC_WIN_CSC_YOF); + tegra_dc_writel(dc, csc->kyrgb, DC_WIN_CSC_KYRGB); + tegra_dc_writel(dc, csc->kur, DC_WIN_CSC_KUR); + tegra_dc_writel(dc, csc->kvr, DC_WIN_CSC_KVR); + tegra_dc_writel(dc, csc->kug, DC_WIN_CSC_KUG); + tegra_dc_writel(dc, csc->kvg, DC_WIN_CSC_KVG); + tegra_dc_writel(dc, csc->kub, DC_WIN_CSC_KUB); + tegra_dc_writel(dc, csc->kvb, DC_WIN_CSC_KVB); +} + +int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx) +{ + mutex_lock(&dc->lock); + + if (!dc->enabled) { + mutex_unlock(&dc->lock); + return -EFAULT; + } + + tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, + DC_CMD_DISPLAY_WINDOW_HEADER); + + tegra_dc_set_csc(dc, &dc->windows[win_idx].csc); + + mutex_unlock(&dc->lock); + + return 0; +} +EXPORT_SYMBOL(tegra_dc_update_csc); + diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index c42df4c1957e..1f7e2ce67682 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (C) 2010-2012 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -34,13 +34,13 @@ #include <linux/seq_file.h> #include <linux/backlight.h> #include <linux/gpio.h> +#include <linux/nvhost.h> #include <video/tegrafb.h> #include <drm/drm_fixed.h> #ifdef CONFIG_SWITCH #include <linux/switch.h> #endif - #include <mach/clk.h> #include <mach/dc.h> #include <mach/fb.h> @@ -49,6 +49,7 @@ #include <mach/latency_allowance.h> #include "dc_reg.h" +#include "dc_config.h" #include "dc_priv.h" #include "nvsd.h" @@ -57,14 +58,6 @@ #define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL 0x01000000 #define DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL 0x0 -#ifndef CONFIG_TEGRA_FPGA_PLATFORM -#define ALL_UF_INT (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT) -#else -/* ignore underflows when on simulation and fpga platform */ -#define ALL_UF_INT (0) -#endif - -static int no_vsync; static struct fb_videomode tegra_dc_hdmi_fallback_mode = { .refresh = 60, .xres = 640, @@ -80,119 +73,29 @@ static struct fb_videomode tegra_dc_hdmi_fallback_mode = { .sync = 0, }; -static void _tegra_dc_controller_disable(struct tegra_dc *dc); - -module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR); - -static int use_dynamic_emc = 1; +static struct tegra_dc_mode override_disp_mode[3]; -module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR); +static void _tegra_dc_controller_disable(struct tegra_dc *dc); struct tegra_dc *tegra_dcs[TEGRA_MAX_DC]; DEFINE_MUTEX(tegra_dc_lock); DEFINE_MUTEX(shared_lock); -static const struct { - bool h; - bool v; -} can_filter[] = { - /* Window A has no filtering */ - { false, false }, - /* Window B has both H and V filtering */ - { true, true }, - /* Window C has only H filtering */ - { false, true }, -}; -static inline bool win_use_v_filter(const struct tegra_dc_win *win) +static inline void tegra_dc_clk_enable(struct tegra_dc *dc) { - return can_filter[win->idx].v && - win->h.full != dfixed_const(win->out_h); -} -static inline bool win_use_h_filter(const struct tegra_dc_win *win) -{ - return can_filter[win->idx].h && - win->w.full != dfixed_const(win->out_w); -} - -static inline int tegra_dc_fmt_bpp(int fmt) -{ - switch (fmt) { - case TEGRA_WIN_FMT_P1: - return 1; - - case TEGRA_WIN_FMT_P2: - return 2; - - case TEGRA_WIN_FMT_P4: - return 4; - - case TEGRA_WIN_FMT_P8: - return 8; - - case TEGRA_WIN_FMT_B4G4R4A4: - case TEGRA_WIN_FMT_B5G5R5A: - case TEGRA_WIN_FMT_B5G6R5: - case TEGRA_WIN_FMT_AB5G5R5: - return 16; - - case TEGRA_WIN_FMT_B8G8R8A8: - case TEGRA_WIN_FMT_R8G8B8A8: - case TEGRA_WIN_FMT_B6x2G6x2R6x2A8: - case TEGRA_WIN_FMT_R6x2G6x2B6x2A8: - return 32; - - /* for planar formats, size of the Y plane, 8bit */ - case TEGRA_WIN_FMT_YCbCr420P: - case TEGRA_WIN_FMT_YUV420P: - case TEGRA_WIN_FMT_YCbCr422P: - case TEGRA_WIN_FMT_YUV422P: - case TEGRA_WIN_FMT_YCbCr422R: - case TEGRA_WIN_FMT_YUV422R: - case TEGRA_WIN_FMT_YCbCr422RA: - case TEGRA_WIN_FMT_YUV422RA: - return 8; - - /* YUYV packed into 32-bits */ - case TEGRA_WIN_FMT_YCbCr422: - case TEGRA_WIN_FMT_YUV422: - return 16; - } - return 0; -} - -static inline bool tegra_dc_is_yuv(int fmt) -{ - switch (fmt) { - case TEGRA_WIN_FMT_YUV420P: - case TEGRA_WIN_FMT_YCbCr420P: - case TEGRA_WIN_FMT_YCbCr422P: - case TEGRA_WIN_FMT_YUV422P: - case TEGRA_WIN_FMT_YCbCr422: - case TEGRA_WIN_FMT_YUV422: - case TEGRA_WIN_FMT_YCbCr422R: - case TEGRA_WIN_FMT_YUV422R: - case TEGRA_WIN_FMT_YCbCr422RA: - case TEGRA_WIN_FMT_YUV422RA: - return true; + if (!tegra_is_clk_enabled(dc->clk)) { + clk_enable(dc->clk); + tegra_dvfs_set_rate(dc->clk, dc->mode.pclk); } - return false; } -static inline bool tegra_dc_is_yuv_planar(int fmt) +static inline void tegra_dc_clk_disable(struct tegra_dc *dc) { - switch (fmt) { - case TEGRA_WIN_FMT_YUV420P: - case TEGRA_WIN_FMT_YCbCr420P: - case TEGRA_WIN_FMT_YCbCr422P: - case TEGRA_WIN_FMT_YUV422P: - case TEGRA_WIN_FMT_YCbCr422R: - case TEGRA_WIN_FMT_YUV422R: - case TEGRA_WIN_FMT_YCbCr422RA: - case TEGRA_WIN_FMT_YUV422RA: - return true; + if (tegra_is_clk_enabled(dc->clk)) { + clk_disable(dc->clk); + tegra_dvfs_set_rate(dc->clk, 0); } - return false; } #define DUMP_REG(a) do { \ @@ -201,23 +104,6 @@ static inline bool tegra_dc_is_yuv_planar(int fmt) print(data, buff); \ } while (0) -#define print_mode_info(dc, mode) do { \ - trace_printk("%s:Mode settings: " \ - "ref_to_sync: H = %d V = %d, " \ - "sync_width: H = %d V = %d, " \ - "back_porch: H = %d V = %d, " \ - "active: H = %d V = %d, " \ - "front_porch: H = %d V = %d, " \ - "pclk = %d, stereo mode = %d\n", \ - dc->ndev->name, \ - mode.h_ref_to_sync, mode.v_ref_to_sync, \ - mode.h_sync_width, mode.v_sync_width, \ - mode.h_back_porch, mode.v_back_porch, \ - mode.h_active, mode.v_active, \ - mode.h_front_porch, mode.v_front_porch, \ - mode.pclk, mode.stereo_mode); \ - } while (0) - #define print_underflow_info(dc) do { \ trace_printk("%s:Underflow stats: underflows : %llu, " \ "undeflows_a : %llu, " \ @@ -236,7 +122,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data, char buff[256]; tegra_dc_io_start(dc); - clk_enable(dc->clk); + tegra_dc_clk_enable(dc); DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); DUMP_REG(DC_CMD_DISPLAY_COMMAND); @@ -386,7 +272,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data, DUMP_REG(DC_COM_PM1_DUTY_CYCLE); DUMP_REG(DC_DISP_SD_CONTROL); - clk_disable(dc->clk); + tegra_dc_clk_disable(dc); tegra_dc_io_end(dc); } @@ -579,7 +465,7 @@ out: return ret; } -static unsigned int tegra_dc_has_multiple_dc(void) +unsigned int tegra_dc_has_multiple_dc(void) { unsigned int idx; unsigned int cnt = 0; @@ -593,6 +479,22 @@ static unsigned int tegra_dc_has_multiple_dc(void) return (cnt > 1); } +/* get the stride size of a window. + * return: stride size in bytes for window win. or 0 if unavailble. */ +int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win) +{ + u32 stride; + + if (!dc->enabled) + return 0; + BUG_ON(win > DC_N_WINDOWS); + tegra_dc_writel(dc, WINDOW_A_SELECT << win, + DC_CMD_DISPLAY_WINDOW_HEADER); + stride = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE); + return GET_LINE_STRIDE(stride); +} +EXPORT_SYMBOL(tegra_dc_get_stride); + struct tegra_dc *tegra_dc_get_dc(unsigned idx) { if (idx < TEGRA_MAX_DC) @@ -611,18 +513,6 @@ struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win) } EXPORT_SYMBOL(tegra_dc_get_window); -static int get_topmost_window(u32 *depths, unsigned long *wins) -{ - int idx, best = -1; - - for_each_set_bit(idx, wins, DC_N_WINDOWS) { - if (best == -1 || depths[idx] < depths[best]) - best = idx; - } - clear_bit(best, wins); - return best; -} - bool tegra_dc_get_connected(struct tegra_dc *dc) { return dc->connected; @@ -643,222 +533,6 @@ bool tegra_dc_hpd(struct tegra_dc *dc) } EXPORT_SYMBOL(tegra_dc_hpd); -static u32 blend_topwin(u32 flags) -{ - if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE) - return BLEND(NOKEY, ALPHA, 0xff, 0xff); - else if (flags & TEGRA_WIN_FLAG_BLEND_PREMULT) - return BLEND(NOKEY, PREMULT, 0xff, 0xff); - else - return BLEND(NOKEY, FIX, 0xff, 0xff); -} - -static u32 blend_2win(int idx, unsigned long behind_mask, u32* flags, int xy) -{ - int other; - - for (other = 0; other < DC_N_WINDOWS; other++) { - if (other != idx && (xy-- == 0)) - break; - } - if (BIT(other) & behind_mask) - return blend_topwin(flags[idx]); - else if (flags[other]) - return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); - else - return BLEND(NOKEY, FIX, 0x00, 0x00); -} - -static u32 blend_3win(int idx, unsigned long behind_mask, u32* flags) -{ - unsigned long infront_mask; - int first; - - infront_mask = ~(behind_mask | BIT(idx)); - infront_mask &= (BIT(DC_N_WINDOWS) - 1); - first = ffs(infront_mask) - 1; - - if (!infront_mask) - return blend_topwin(flags[idx]); - else if (behind_mask && first != -1 && flags[first]) - return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); - else - return BLEND(NOKEY, FIX, 0x0, 0x0); -} - -static void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend) -{ - unsigned long mask = BIT(DC_N_WINDOWS) - 1; - - while (mask) { - int idx = get_topmost_window(blend->z, &mask); - - tegra_dc_writel(dc, WINDOW_A_SELECT << idx, - DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), - DC_WIN_BLEND_NOKEY); - tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), - DC_WIN_BLEND_1WIN); - tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0), - DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1), - DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags), - DC_WIN_BLEND_3WIN_XY); - } -} - -static void tegra_dc_init_csc_defaults(struct tegra_dc_csc *csc) -{ - csc->yof = 0x00f0; - csc->kyrgb = 0x012a; - csc->kur = 0x0000; - csc->kvr = 0x0198; - csc->kug = 0x039b; - csc->kvg = 0x032f; - csc->kub = 0x0204; - csc->kvb = 0x0000; -} - -static void tegra_dc_set_csc(struct tegra_dc *dc, struct tegra_dc_csc *csc) -{ - tegra_dc_writel(dc, csc->yof, DC_WIN_CSC_YOF); - tegra_dc_writel(dc, csc->kyrgb, DC_WIN_CSC_KYRGB); - tegra_dc_writel(dc, csc->kur, DC_WIN_CSC_KUR); - tegra_dc_writel(dc, csc->kvr, DC_WIN_CSC_KVR); - tegra_dc_writel(dc, csc->kug, DC_WIN_CSC_KUG); - tegra_dc_writel(dc, csc->kvg, DC_WIN_CSC_KVG); - tegra_dc_writel(dc, csc->kub, DC_WIN_CSC_KUB); - tegra_dc_writel(dc, csc->kvb, DC_WIN_CSC_KVB); -} - -int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx) -{ - mutex_lock(&dc->lock); - - if (!dc->enabled) { - mutex_unlock(&dc->lock); - return -EFAULT; - } - - tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, - DC_CMD_DISPLAY_WINDOW_HEADER); - - tegra_dc_set_csc(dc, &dc->windows[win_idx].csc); - - mutex_unlock(&dc->lock); - - return 0; -} -EXPORT_SYMBOL(tegra_dc_update_csc); - -static void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut) -{ - int i; - for (i = 0; i < 256; i++) - lut->r[i] = lut->g[i] = lut->b[i] = (u8)i; -} - -static int tegra_dc_loop_lut(struct tegra_dc *dc, - struct tegra_dc_win *win, - int(*lambda)(struct tegra_dc *dc, int i, u32 rgb)) -{ - struct tegra_dc_lut *lut = &win->lut; - struct tegra_dc_lut *global_lut = &dc->fb_lut; - int i; - for (i = 0; i < 256; i++) { - - u32 r = (u32)lut->r[i]; - u32 g = (u32)lut->g[i]; - u32 b = (u32)lut->b[i]; - - if (!(win->ppflags & TEGRA_WIN_PPFLAG_CP_FBOVERRIDE)) { - r = (u32)global_lut->r[r]; - g = (u32)global_lut->g[g]; - b = (u32)global_lut->b[b]; - } - - if (!lambda(dc, i, r | (g<<8) | (b<<16))) - return 0; - } - return 1; -} - -static int tegra_dc_lut_isdefaults_lambda(struct tegra_dc *dc, int i, u32 rgb) -{ - if (rgb != (i | (i<<8) | (i<<16))) - return 0; - return 1; -} - -static int tegra_dc_set_lut_setreg_lambda(struct tegra_dc *dc, int i, u32 rgb) -{ - tegra_dc_writel(dc, rgb, DC_WIN_COLOR_PALETTE(i)); - return 1; -} - -static void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win* win) -{ - unsigned long val = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); - - tegra_dc_loop_lut(dc, win, tegra_dc_set_lut_setreg_lambda); - - if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) - val |= CP_ENABLE; - else - val &= ~CP_ENABLE; - - tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); -} - -static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr) -{ - struct tegra_dc_win *win = &dc->windows[win_idx]; - - mutex_lock(&dc->lock); - - if (!dc->enabled) { - mutex_unlock(&dc->lock); - return -EFAULT; - } - - if (fbovr > 0) - win->ppflags |= TEGRA_WIN_PPFLAG_CP_FBOVERRIDE; - else if (fbovr == 0) - win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_FBOVERRIDE; - - if (!tegra_dc_loop_lut(dc, win, tegra_dc_lut_isdefaults_lambda)) - win->ppflags |= TEGRA_WIN_PPFLAG_CP_ENABLE; - else - win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_ENABLE; - - tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, - DC_CMD_DISPLAY_WINDOW_HEADER); - - tegra_dc_set_lut(dc, win); - - mutex_unlock(&dc->lock); - - tegra_dc_update_windows(&win, 1); - - return 0; -} - -int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride) -{ - if (win_idx > -1) - return tegra_dc_update_winlut(dc, win_idx, fboveride); - - for (win_idx = 0; win_idx < DC_N_WINDOWS; win_idx++) { - int err = tegra_dc_update_winlut(dc, win_idx, fboveride); - if (err) - return err; - } - - return 0; -} -EXPORT_SYMBOL(tegra_dc_update_lut); - static void tegra_dc_set_scaling_filter(struct tegra_dc *dc) { unsigned i; @@ -876,510 +550,21 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc) } } -static void tegra_dc_set_latency_allowance(struct tegra_dc *dc, - struct tegra_dc_win *w) -{ - /* windows A, B, C for first and second display */ - static const enum tegra_la_id la_id_tab[2][3] = { - /* first display */ - { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B, - TEGRA_LA_DISPLAY_0C }, - /* second display */ - { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB, - TEGRA_LA_DISPLAY_0CB }, - }; - /* window B V-filter tap for first and second display. */ - static const enum tegra_la_id vfilter_tab[2] = { - TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB, - }; - unsigned long bw; - - BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab)); - BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab)); - BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab)); - - bw = w->new_bandwidth; - - /* tegra_dc_get_bandwidth() treats V filter windows as double - * bandwidth, but LA has a seperate client for V filter */ - if (w->idx == 1 && win_use_v_filter(w)) - bw /= 2; - - /* our bandwidth is in kbytes/sec, but LA takes MBps. - * round up bandwidth to next 1MBps */ - bw = bw / 1000 + 1; - -#ifdef CONFIG_TEGRA_SILICON_PLATFORM - tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw); - /* if window B, also set the 1B client for the 2-tap V filter. */ - if (w->idx == 1) - tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw); -#endif - - w->bandwidth = w->new_bandwidth; -} - -static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a, - struct tegra_dc_win *b) -{ - if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b)) - return 0; - - /* because memory access to load the fifo can overlap, only care - * if windows overlap vertically */ - return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) || - ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y)); -} - -static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[], - int n) -{ - unsigned i; - unsigned j; - unsigned overlap_count; - unsigned max_bw = 0; - - WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely" - "inaccurate.\n"); - - /* If we had a large number of windows, we would compute adjacency - * graph representing 2 window overlaps, find all cliques in the graph, - * assign bandwidth to each clique, and then select the clique with - * maximum bandwidth. But because we have at most 3 windows, - * implementing proper Bron-Kerbosh algorithm would be an overkill, - * brute force will suffice. - * - * Thus: find maximum bandwidth for either single or a pair of windows - * and count number of window pair overlaps. If there are three - * pairs, all 3 window overlap. - */ - - overlap_count = 0; - for (i = 0; i < n; i++) { - unsigned int bw1; - - if (wins[i] == NULL) - continue; - bw1 = wins[i]->new_bandwidth; - if (bw1 > max_bw) - /* Single window */ - max_bw = bw1; - - for (j = i + 1; j < n; j++) { - if (wins[j] == NULL) - continue; - if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) { - unsigned int bw2 = wins[j]->new_bandwidth; - if (bw1 + bw2 > max_bw) - /* Window pair overlaps */ - max_bw = bw1 + bw2; - overlap_count++; - } - } - } - - if (overlap_count == 3) - /* All three windows overlap */ - max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth + - wins[2]->new_bandwidth; - - return max_bw; -} - -/* - * Calculate peak EMC bandwidth for each enabled window = - * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor * - * (windows_tiling ? 2 : 1) - * - * note: - * (*) We use 2 tap V filter, so need double BW if use V filter - * (*) Tiling mode on T30 and DDR3 requires double BW - * - * return: - * bandwidth in kBps - */ -static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc, - struct tegra_dc_win *w) +void tegra_dc_host_suspend(struct tegra_dc *dc) { - unsigned long ret; - int tiled_windows_bw_multiplier; - unsigned long bpp; - - if (!WIN_IS_ENABLED(w)) - return 0; - - if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 || - w->out_w == 0 || w->out_h == 0) - return 0; - - tiled_windows_bw_multiplier = - tegra_mc_get_tiled_memory_bandwidth_multiplier(); - - /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel, - * but the size reported by tegra_dc_fmt_bpp for the planar version - * is of the luma plane's size only. */ - bpp = tegra_dc_is_yuv_planar(w->fmt) ? - 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt); - ret = dc->mode.pclk / 1000UL * bpp / 8 * (win_use_v_filter(w) ? 2 : 1) - * dfixed_trunc(w->w) / w->out_w * - (WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1); - /* - * Assuming ~35% efficiency: i.e. if we calculate we need 70MBps, we - * will request 200MBps from EMC. - */ - ret = ret * 29 / 10; - - return ret; + tegra_dsi_host_suspend(dc); + tegra_dc_clk_disable(dc); } -static unsigned long tegra_dc_get_bandwidth( - struct tegra_dc_win *windows[], int n) -{ - int i; - - BUG_ON(n > DC_N_WINDOWS); - - /* emc rate and latency allowance both need to know per window - * bandwidths */ - for (i = 0; i < n; i++) { - struct tegra_dc_win *w = windows[i]; - - if (w) - w->new_bandwidth = - tegra_dc_calc_win_bandwidth(w->dc, w); - } - - return tegra_dc_find_max_bandwidth(windows, n); +void tegra_dc_host_resume(struct tegra_dc *dc) { + tegra_dc_clk_enable(dc); + tegra_dsi_host_resume(dc); } -/* to save power, call when display memory clients would be idle */ -static void tegra_dc_clear_bandwidth(struct tegra_dc *dc) -{ - trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__, - dc->emc_clk_rate); - if (tegra_is_clk_enabled(dc->emc_clk)) - clk_disable(dc->emc_clk); - dc->emc_clk_rate = 0; -} - -static void tegra_dc_program_bandwidth(struct tegra_dc *dc) -{ - unsigned i; - - if (dc->emc_clk_rate != dc->new_emc_clk_rate) { - /* going from 0 to non-zero */ - if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk)) - clk_enable(dc->emc_clk); - - dc->emc_clk_rate = dc->new_emc_clk_rate; - clk_set_rate(dc->emc_clk, dc->emc_clk_rate); - - if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */ - clk_disable(dc->emc_clk); - } - - for (i = 0; i < DC_N_WINDOWS; i++) { - struct tegra_dc_win *w = &dc->windows[i]; - - if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0) - tegra_dc_set_latency_allowance(dc, w); - trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx, - w->bandwidth); - } -} - -static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n) -{ - unsigned long new_rate; - struct tegra_dc *dc; - - if (!use_dynamic_emc) - return 0; - - dc = windows[0]->dc; - - /* calculate the new rate based on this POST */ - new_rate = tegra_dc_get_bandwidth(windows, n); - if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n")) - new_rate = ULONG_MAX; - else - new_rate = EMC_BW_TO_FREQ(new_rate * 1000); - - if (tegra_dc_has_multiple_dc()) - new_rate = ULONG_MAX; - - trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate); - dc->new_emc_clk_rate = new_rate; - - return 0; -} - -static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int, - bool v, unsigned Bpp) -{ - /* - * min(round((prescaled_size_in_pixels - 1) * 0x1000 / - * (post_scaled_size_in_pixels - 1)), MAX) - * Where the value of MAX is as follows: - * For V_DDA_INCREMENT: 15.0 (0xF000) - * For H_DDA_INCREMENT: 4.0 (0x4000) for 4 Bytes/pix formats. - * 8.0 (0x8000) for 2 Bytes/pix formats. - */ - - fixed20_12 out = dfixed_init(out_int); - u32 dda_inc; - int max; - - if (v) { - max = 15; - } else { - switch (Bpp) { - default: - WARN_ON_ONCE(1); - /* fallthrough */ - case 4: - max = 4; - break; - case 2: - max = 8; - break; - } - } - - out.full = max_t(u32, out.full - dfixed_const(1), dfixed_const(1)); - in.full -= dfixed_const(1); - - dda_inc = dfixed_div(in, out); - - dda_inc = min_t(u32, dda_inc, dfixed_const(max)); - - return dda_inc; -} - -static inline u32 compute_initial_dda(fixed20_12 in) -{ - return dfixed_frac(in); -} - -/* does not support updating windows on multiple dcs in one call */ -int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) +static inline void disable_dc_irq(unsigned int irq) { - struct tegra_dc *dc; - unsigned long update_mask = GENERAL_ACT_REQ; - unsigned long val; - bool update_blend = false; - int i; - - dc = windows[0]->dc; - - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { - /* Acquire one_shot_lock to avoid race condition between - * cancellation of old delayed work and schedule of new - * delayed work. */ - mutex_lock(&dc->one_shot_lock); - cancel_delayed_work_sync(&dc->one_shot_work); - } - mutex_lock(&dc->lock); - - if (!dc->enabled) { - mutex_unlock(&dc->lock); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) - mutex_unlock(&dc->one_shot_lock); - return -EFAULT; - } - - if (no_vsync) - tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, DC_CMD_STATE_ACCESS); - else - tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, DC_CMD_STATE_ACCESS); - - for (i = 0; i < n; i++) { - struct tegra_dc_win *win = windows[i]; - unsigned h_dda; - unsigned v_dda; - fixed20_12 h_offset, v_offset; - bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0; - bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0; - bool yuv = tegra_dc_is_yuv(win->fmt); - bool yuvp = tegra_dc_is_yuv_planar(win->fmt); - unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8; - /* Bytes per pixel of bandwidth, used for dda_inc calculation */ - unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1); - const bool filter_h = win_use_h_filter(win); - const bool filter_v = win_use_v_filter(win); - - if (win->z != dc->blend.z[win->idx]) { - dc->blend.z[win->idx] = win->z; - update_blend = true; - } - if ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) != - dc->blend.flags[win->idx]) { - dc->blend.flags[win->idx] = - win->flags & TEGRA_WIN_BLEND_FLAGS_MASK; - update_blend = true; - } - - tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx, - DC_CMD_DISPLAY_WINDOW_HEADER); - - if (!no_vsync) - update_mask |= WIN_A_ACT_REQ << win->idx; - - if (!WIN_IS_ENABLED(win)) { - tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS); - continue; - } - - tegra_dc_writel(dc, win->fmt & 0x1f, DC_WIN_COLOR_DEPTH); - tegra_dc_writel(dc, win->fmt >> 6, DC_WIN_BYTE_SWAP); - - tegra_dc_writel(dc, - V_POSITION(win->out_y) | H_POSITION(win->out_x), - DC_WIN_POSITION); - tegra_dc_writel(dc, - V_SIZE(win->out_h) | H_SIZE(win->out_w), - DC_WIN_SIZE); - tegra_dc_writel(dc, - V_PRESCALED_SIZE(dfixed_trunc(win->h)) | - H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp), - DC_WIN_PRESCALED_SIZE); - - h_dda = compute_dda_inc(win->w, win->out_w, false, Bpp_bw); - v_dda = compute_dda_inc(win->h, win->out_h, true, Bpp_bw); - tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda), - DC_WIN_DDA_INCREMENT); - h_dda = compute_initial_dda(win->x); - v_dda = compute_initial_dda(win->y); - tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); - tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); - - tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); - tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); - tegra_dc_writel(dc, - (unsigned long)win->phys_addr, - DC_WINBUF_START_ADDR); - - if (!yuvp) { - tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE); - } else { - tegra_dc_writel(dc, - (unsigned long)win->phys_addr_u, - DC_WINBUF_START_ADDR_U); - tegra_dc_writel(dc, - (unsigned long)win->phys_addr_v, - DC_WINBUF_START_ADDR_V); - tegra_dc_writel(dc, - LINE_STRIDE(win->stride) | - UV_LINE_STRIDE(win->stride_uv), - DC_WIN_LINE_STRIDE); - } - - h_offset = win->x; - if (invert_h) { - h_offset.full += win->w.full - dfixed_const(1); - } - - v_offset = win->y; - if (invert_v) { - v_offset.full += win->h.full - dfixed_const(1); - } - - tegra_dc_writel(dc, dfixed_trunc(h_offset) * Bpp, - DC_WINBUF_ADDR_H_OFFSET); - tegra_dc_writel(dc, dfixed_trunc(v_offset), - DC_WINBUF_ADDR_V_OFFSET); - - if (WIN_IS_TILED(win)) - tegra_dc_writel(dc, - DC_WIN_BUFFER_ADDR_MODE_TILE | - DC_WIN_BUFFER_ADDR_MODE_TILE_UV, - DC_WIN_BUFFER_ADDR_MODE); - else - tegra_dc_writel(dc, - DC_WIN_BUFFER_ADDR_MODE_LINEAR | - DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV, - DC_WIN_BUFFER_ADDR_MODE); - - val = WIN_ENABLE; - if (yuv) - val |= CSC_ENABLE; - else if (tegra_dc_fmt_bpp(win->fmt) < 24) - val |= COLOR_EXPAND; - - if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) - val |= CP_ENABLE; - - if (filter_h) - val |= H_FILTER_ENABLE; - if (filter_v) - val |= V_FILTER_ENABLE; - - if (invert_h) - val |= H_DIRECTION_DECREMENT; - if (invert_v) - val |= V_DIRECTION_DECREMENT; - - tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); - - win->dirty = no_vsync ? 0 : 1; - - dev_dbg(&dc->ndev->dev, "%s():idx=%d z=%d x=%d y=%d w=%d h=%d " - "out_x=%u out_y=%u out_w=%u out_h=%u " - "fmt=%d yuvp=%d Bpp=%u filter_h=%d filter_v=%d", - __func__, win->idx, win->z, - dfixed_trunc(win->x), dfixed_trunc(win->y), - dfixed_trunc(win->w), dfixed_trunc(win->h), - win->out_x, win->out_y, win->out_w, win->out_h, - win->fmt, yuvp, Bpp, filter_h, filter_v); - trace_printk("%s:win%u in:%ux%u out:%ux%u fmt=%d\n", - dc->ndev->name, win->idx, dfixed_trunc(win->w), - dfixed_trunc(win->h), win->out_w, win->out_h, win->fmt); - } - - if (update_blend) { - tegra_dc_set_blending(dc, &dc->blend); - for (i = 0; i < DC_N_WINDOWS; i++) { - if (!no_vsync) - dc->windows[i].dirty = 1; - update_mask |= WIN_A_ACT_REQ << i; - } - } - - tegra_dc_set_dynamic_emc(windows, n); - - tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL); - - tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS); - if (!no_vsync) { - val = tegra_dc_readl(dc, DC_CMD_INT_MASK); - val |= (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); - tegra_dc_writel(dc, val, DC_CMD_INT_MASK); - } else { - val = tegra_dc_readl(dc, DC_CMD_INT_MASK); - val &= ~(FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); - tegra_dc_writel(dc, val, DC_CMD_INT_MASK); - } - - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) - schedule_delayed_work(&dc->one_shot_work, - msecs_to_jiffies(dc->one_shot_delay_ms)); - - /* update EMC clock if calculated bandwidth has changed */ - tegra_dc_program_bandwidth(dc); - - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) - update_mask |= NC_HOST_TRIG; - - tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); - trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask); - - mutex_unlock(&dc->lock); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) - mutex_unlock(&dc->one_shot_lock); - - return 0; + disable_irq(irq); } -EXPORT_SYMBOL(tegra_dc_update_windows); u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc, int i) { @@ -1392,7 +577,7 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i) u32 max; mutex_lock(&dc->lock); - max = nvhost_syncpt_incr_max(&nvhost_get_host(dc->ndev)->syncpt, + max = nvhost_syncpt_incr_max_ext(dc->ndev, dc->syncpt[i].id, ((dc->enabled) ? 1 : 0)); dc->syncpt[i].max = max; mutex_unlock(&dc->lock); @@ -1406,491 +591,11 @@ void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val) if ( dc->enabled ) while (dc->syncpt[i].min < val) { dc->syncpt[i].min++; - nvhost_syncpt_cpu_incr( - &nvhost_get_host(dc->ndev)->syncpt, - dc->syncpt[i].id); + nvhost_syncpt_cpu_incr_ext(dc->ndev, dc->syncpt[i].id); } mutex_unlock(&dc->lock); } -static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[], - int n) -{ - int i; - - for (i = 0; i < n; i++) { - if (windows[i]->dirty) - return false; - } - - return true; -} - -/* does not support syncing windows on multiple dcs in one call */ -int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n) -{ - int ret; - if (n < 1 || n > DC_N_WINDOWS) - return -EINVAL; - - if (!windows[0]->dc->enabled) - return -EFAULT; - -#ifdef CONFIG_TEGRA_SIMULATION_PLATFORM - /* Don't want to timeout on simulator */ - ret = wait_event_interruptible(windows[0]->dc->wq, - tegra_dc_windows_are_clean(windows, n)); -#else - trace_printk("%s:Before wait_event_interruptible_timeout\n", - windows[0]->dc->ndev->name); - ret = wait_event_interruptible_timeout(windows[0]->dc->wq, - tegra_dc_windows_are_clean(windows, n), - HZ); - trace_printk("%s:After wait_event_interruptible_timeout\n", - windows[0]->dc->ndev->name); -#endif - return ret; -} -EXPORT_SYMBOL(tegra_dc_sync_windows); - -static unsigned long tegra_dc_clk_get_rate(struct tegra_dc *dc) -{ -#ifdef CONFIG_TEGRA_SILICON_PLATFORM - return clk_get_rate(dc->clk); -#else - return 27000000; -#endif -} - -static unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk) -{ - unsigned long rate; - unsigned long div; - - rate = tegra_dc_clk_get_rate(dc); - - div = DIV_ROUND_CLOSEST(rate * 2, pclk); - - if (div < 2) - return 0; - - return rate * 2 / div; -} - -static unsigned long tegra_dc_pclk_predict_rate(struct clk *parent, int pclk) -{ - unsigned long rate; - unsigned long div; - - rate = clk_get_rate(parent); - - div = DIV_ROUND_CLOSEST(rate * 2, pclk); - - if (div < 2) - return 0; - - return rate * 2 / div; -} - -void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk) -{ - int pclk; - - if (dc->out->type == TEGRA_DC_OUT_RGB) { - unsigned long rate; - struct clk *parent_clk = - clk_get_sys(NULL, dc->out->parent_clk ? : "pll_p"); - - if (dc->out->parent_clk_backup && - (parent_clk == clk_get_sys(NULL, "pll_p"))) { - rate = tegra_dc_pclk_predict_rate( - parent_clk, dc->mode.pclk); - /* use pll_d as last resort */ - if (rate < (dc->mode.pclk / 100 * 99) || - rate > (dc->mode.pclk / 100 * 109)) - parent_clk = clk_get_sys( - NULL, dc->out->parent_clk_backup); - } - - if (clk_get_parent(clk) != parent_clk) - clk_set_parent(clk, parent_clk); - - if (parent_clk != clk_get_sys(NULL, "pll_p")) { - struct clk *base_clk = clk_get_parent(parent_clk); - - /* Assuming either pll_d or pll_d2 is used */ - rate = dc->mode.pclk * 2; - - if (rate != clk_get_rate(base_clk)) - clk_set_rate(base_clk, rate); - } - } - - if (dc->out->type == TEGRA_DC_OUT_HDMI) { - unsigned long rate; - struct clk *parent_clk = - clk_get_sys(NULL, dc->out->parent_clk ? : "pll_d_out0"); - struct clk *base_clk = clk_get_parent(parent_clk); - - /* - * Providing dynamic frequency rate setting for T20/T30 HDMI. - * The required rate needs to be setup at 4x multiplier, - * as out0 is 1/2 of the actual PLL output. - */ - - rate = dc->mode.pclk * 4; - if (rate != clk_get_rate(base_clk)) - clk_set_rate(base_clk, rate); - - if (clk_get_parent(clk) != parent_clk) - clk_set_parent(clk, parent_clk); - } - - if (dc->out->type == TEGRA_DC_OUT_DSI) { - unsigned long rate; - struct clk *parent_clk; - struct clk *base_clk; - - if (clk == dc->clk) { - parent_clk = clk_get_sys(NULL, - dc->out->parent_clk ? : "pll_d_out0"); - base_clk = clk_get_parent(parent_clk); - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_DSI_OUT_ENB, 1); - } else { - if (dc->pdata->default_out->dsi->dsi_instance) { - parent_clk = clk_get_sys(NULL, - dc->out->parent_clk ? : "pll_d2_out0"); - base_clk = clk_get_parent(parent_clk); - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_CSI_OUT_ENB, 1); - } else { - parent_clk = clk_get_sys(NULL, - dc->out->parent_clk ? : "pll_d_out0"); - base_clk = clk_get_parent(parent_clk); - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_DSI_OUT_ENB, 1); - } - } - - rate = dc->mode.pclk * dc->shift_clk_div * 2; - if (rate != clk_get_rate(base_clk)) - clk_set_rate(base_clk, rate); - - if (clk_get_parent(clk) != parent_clk) - clk_set_parent(clk, parent_clk); - } - - pclk = tegra_dc_pclk_round_rate(dc, dc->mode.pclk); - tegra_dvfs_set_rate(clk, pclk); -} - -/* return non-zero if constraint is violated */ -static int calc_h_ref_to_sync(const struct tegra_dc_mode *mode, int *href) -{ - long a, b; - - /* Constraint 5: H_REF_TO_SYNC >= 0 */ - a = 0; - - /* Constraint 6: H_FRONT_PORT >= (H_REF_TO_SYNC + 1) */ - b = mode->h_front_porch - 1; - - /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11 */ - if (a + mode->h_sync_width + mode->h_back_porch <= 11) - a = 1 + 11 - mode->h_sync_width - mode->h_back_porch; - /* check Constraint 1 and 6 */ - if (a > b) - return 1; - - /* Constraint 4: H_SYNC_WIDTH >= 1 */ - if (mode->h_sync_width < 1) - return 4; - - /* Constraint 7: H_DISP_ACTIVE >= 16 */ - if (mode->h_active < 16) - return 7; - - if (href) { - if (b > a && a % 2) - *href = a + 1; /* use smallest even value */ - else - *href = a; /* even or only possible value */ - } - - return 0; -} - -static int calc_v_ref_to_sync(const struct tegra_dc_mode *mode, int *vref) -{ - long a; - a = 1; /* Constraint 5: V_REF_TO_SYNC >= 1 */ - - /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1 */ - if (a + mode->v_sync_width + mode->v_back_porch <= 1) - a = 1 + 1 - mode->v_sync_width - mode->v_back_porch; - - /* Constraint 6 */ - if (mode->v_front_porch < a + 1) - a = mode->v_front_porch - 1; - - /* Constraint 4: V_SYNC_WIDTH >= 1 */ - if (mode->v_sync_width < 1) - return 4; - - /* Constraint 7: V_DISP_ACTIVE >= 16 */ - if (mode->v_active < 16) - return 7; - - if (vref) - *vref = a; - return 0; -} - -static int calc_ref_to_sync(struct tegra_dc_mode *mode) -{ - int ret; - ret = calc_h_ref_to_sync(mode, &mode->h_ref_to_sync); - if (ret) - return ret; - ret = calc_v_ref_to_sync(mode, &mode->v_ref_to_sync); - if (ret) - return ret; - - return 0; -} - -static bool check_ref_to_sync(struct tegra_dc_mode *mode) -{ - /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11. */ - if (mode->h_ref_to_sync + mode->h_sync_width + mode->h_back_porch <= 11) - return false; - - /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1. */ - if (mode->v_ref_to_sync + mode->v_sync_width + mode->v_back_porch <= 1) - return false; - - /* Constraint 3: V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH > 1 - * (vertical blank). */ - if (mode->v_front_porch + mode->v_sync_width + mode->v_back_porch <= 1) - return false; - - /* Constraint 4: V_SYNC_WIDTH >= 1; H_SYNC_WIDTH >= 1. */ - if (mode->v_sync_width < 1 || mode->h_sync_width < 1) - return false; - - /* Constraint 5: V_REF_TO_SYNC >= 1; H_REF_TO_SYNC >= 0. */ - if (mode->v_ref_to_sync < 1 || mode->h_ref_to_sync < 0) - return false; - - /* Constraint 6: V_FRONT_PORT >= (V_REF_TO_SYNC + 1); - * H_FRONT_PORT >= (H_REF_TO_SYNC + 1). */ - if (mode->v_front_porch < mode->v_ref_to_sync + 1 || - mode->h_front_porch < mode->h_ref_to_sync + 1) - return false; - - /* Constraint 7: H_DISP_ACTIVE >= 16; V_DISP_ACTIVE >= 16. */ - if (mode->h_active < 16 || mode->v_active < 16) - return false; - - return true; -} - -#ifdef DEBUG -/* return in 1000ths of a Hertz */ -static int calc_refresh(const struct tegra_dc_mode *m) -{ - long h_total, v_total, refresh; - h_total = m->h_active + m->h_front_porch + m->h_back_porch + - m->h_sync_width; - v_total = m->v_active + m->v_front_porch + m->v_back_porch + - m->v_sync_width; - refresh = m->pclk / h_total; - refresh *= 1000; - refresh /= v_total; - return refresh; -} - -static void print_mode(struct tegra_dc *dc, - const struct tegra_dc_mode *mode, const char *note) -{ - if (mode) { - int refresh = calc_refresh(dc, mode); - dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n", - note ? note : "", - mode->h_active, mode->v_active, - refresh / 1000, refresh % 1000, - mode->pclk); - } -} -#else /* !DEBUG */ -static inline void print_mode(struct tegra_dc *dc, - const struct tegra_dc_mode *mode, const char *note) { } -#endif /* DEBUG */ - -static inline void enable_dc_irq(unsigned int irq) -{ -#ifndef CONFIG_TEGRA_FPGA_PLATFORM - enable_irq(irq); -#else - /* Always disable DC interrupts on FPGA. */ - disable_irq(irq); -#endif -} - -static inline void disable_dc_irq(unsigned int irq) -{ - disable_irq(irq); -} - -static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) -{ - unsigned long val; - unsigned long rate; - unsigned long div; - unsigned long pclk; - - print_mode(dc, mode, __func__); - - /* use default EMC rate when switching modes */ - dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc); - tegra_dc_program_bandwidth(dc); - - tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); - tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16), - DC_DISP_REF_TO_SYNC); - tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16), - DC_DISP_SYNC_WIDTH); - tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16), - DC_DISP_BACK_PORCH); - tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16), - DC_DISP_DISP_ACTIVE); - tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16), - DC_DISP_FRONT_PORCH); - - tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL, - DC_DISP_DATA_ENABLE_OPTIONS); - - /* TODO: MIPI/CRT/HDMI clock cals */ - - val = DISP_DATA_FORMAT_DF1P1C; - - if (dc->out->align == TEGRA_DC_ALIGN_MSB) - val |= DISP_DATA_ALIGNMENT_MSB; - else - val |= DISP_DATA_ALIGNMENT_LSB; - - if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE) - val |= DISP_DATA_ORDER_RED_BLUE; - else - val |= DISP_DATA_ORDER_BLUE_RED; - - tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL); - - rate = tegra_dc_clk_get_rate(dc); - - pclk = tegra_dc_pclk_round_rate(dc, mode->pclk); - trace_printk("%s:pclk=%ld\n", dc->ndev->name, pclk); - if (pclk < (mode->pclk / 100 * 99) || - pclk > (mode->pclk / 100 * 109)) { - dev_err(&dc->ndev->dev, - "can't divide %ld clock to %d -1/+9%% %ld %d %d\n", - rate, mode->pclk, - pclk, (mode->pclk / 100 * 99), - (mode->pclk / 100 * 109)); - return -EINVAL; - } - - div = (rate * 2 / pclk) - 2; - trace_printk("%s:div=%ld\n", dc->ndev->name, div); - - tegra_dc_writel(dc, 0x00010001, - DC_DISP_SHIFT_CLOCK_OPTIONS); - tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div), - DC_DISP_DISP_CLOCK_CONTROL); - -#ifdef CONFIG_SWITCH - switch_set_state(&dc->modeset_switch, - (mode->h_active << 16) | mode->v_active); -#endif - - tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); - - print_mode_info(dc, dc->mode); - return 0; -} - - -int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode) -{ - memcpy(&dc->mode, mode, sizeof(dc->mode)); - - print_mode(dc, mode, __func__); - - return 0; -} -EXPORT_SYMBOL(tegra_dc_set_mode); - -int tegra_dc_set_fb_mode(struct tegra_dc *dc, - const struct fb_videomode *fbmode, bool stereo_mode) -{ - struct tegra_dc_mode mode; - - if (!fbmode->pixclock) - return -EINVAL; - - mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000; - mode.h_sync_width = fbmode->hsync_len; - mode.v_sync_width = fbmode->vsync_len; - mode.h_back_porch = fbmode->left_margin; - mode.v_back_porch = fbmode->upper_margin; - mode.h_active = fbmode->xres; - mode.v_active = fbmode->yres; - mode.h_front_porch = fbmode->right_margin; - mode.v_front_porch = fbmode->lower_margin; - mode.stereo_mode = stereo_mode; - if (dc->out->type == TEGRA_DC_OUT_HDMI) { - /* HDMI controller requires h_ref=1, v_ref=1 */ - mode.h_ref_to_sync = 1; - mode.v_ref_to_sync = 1; - } else { - calc_ref_to_sync(&mode); - } - if (!check_ref_to_sync(&mode)) { - dev_err(&dc->ndev->dev, - "Display timing doesn't meet restrictions.\n"); - return -EINVAL; - } - dev_info(&dc->ndev->dev, "Using mode %dx%d pclk=%d href=%d vref=%d\n", - mode.h_active, mode.v_active, mode.pclk, - mode.h_ref_to_sync, mode.v_ref_to_sync - ); - -#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT - /* Double the pixel clock and update v_active only for frame packed mode */ - if (mode.stereo_mode) { - mode.pclk *= 2; - /* total v_active = yres*2 + activespace */ - mode.v_active = fbmode->yres*2 + - fbmode->vsync_len + - fbmode->upper_margin + - fbmode->lower_margin; - } -#endif - - mode.flags = 0; - - if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT)) - mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC; - - if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT)) - mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC; - - return tegra_dc_set_mode(dc, &mode); -} -EXPORT_SYMBOL(tegra_dc_set_fb_mode); - void tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg) { @@ -1904,6 +609,9 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg) return; } + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + ctrl = ((cfg->period << PM_PERIOD_SHIFT) | (cfg->clk_div << PM_CLK_DIVIDER_SHIFT) | cfg->clk_select); @@ -1912,11 +620,6 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg) cmd_state = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); tegra_dc_writel(dc, (cmd_state | (1 << 2)), DC_CMD_STATE_ACCESS); - if (cfg->switch_to_sfio && cfg->gpio_conf_to_sfio) - cfg->switch_to_sfio(cfg->gpio_conf_to_sfio); - else - dev_err(&dc->ndev->dev, "Error: Need gpio_conf_to_sfio\n"); - switch (cfg->which_pwm) { case TEGRA_PWM_PM0: /* Select the LM0 on PM0 */ @@ -2011,11 +714,27 @@ void tegra_dc_set_out_pin_polars(struct tegra_dc *dc, tegra_dc_writel(dc, pol3, DC_COM_PIN_OUTPUT_POLARITY3); } +static struct tegra_dc_mode *tegra_dc_get_override_mode(struct tegra_dc *dc) +{ + if (dc->out->type == TEGRA_DC_OUT_RGB || + dc->out->type == TEGRA_DC_OUT_HDMI || + dc->out->type == TEGRA_DC_OUT_DSI) + return override_disp_mode[dc->out->type].pclk ? + &override_disp_mode[dc->out->type] : NULL; + else + return NULL; +} + static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out) { + struct tegra_dc_mode *mode; + dc->out = out; + mode = tegra_dc_get_override_mode(dc); - if (out->n_modes > 0) + if (mode) + tegra_dc_set_mode(dc, mode); + else if (out->n_modes > 0) tegra_dc_set_mode(dc, &dc->out->modes[0]); switch (out->type) { @@ -2093,7 +812,7 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc) { int crc = 0; - if(!dc) { + if (!dc) { dev_err(&dc->ndev->dev, "Failed to get dc.\n"); goto crc_error; } @@ -2107,6 +826,28 @@ crc_error: return crc; } +static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc) +{ +#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM + u32 val; + + val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + if (val & (WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ)) + return true; +#endif + return false; +} + +static inline void enable_dc_irq(unsigned int irq) +{ +#ifndef CONFIG_TEGRA_FPGA_PLATFORM + enable_irq(irq); +#else + /* Always disable DC interrupts on FPGA. */ + disable_irq(irq); +#endif +} + static void tegra_dc_vblank(struct work_struct *work) { struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work); @@ -2114,9 +855,36 @@ static void tegra_dc_vblank(struct work_struct *work) mutex_lock(&dc->lock); + if (!dc->enabled) { + mutex_unlock(&dc->lock); + return; + } + + /* use the new frame's bandwidth setting instead of max(current, new), + * skip this if we're using tegra_dc_one_shot_worker() */ + if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)) + tegra_dc_program_bandwidth(dc, true); + + /* Clear the V_BLANK_FLIP bit of vblank ref-count if update is clean. */ + if (!tegra_dc_windows_are_dirty(dc)) + clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count); + /* Update the SD brightness */ - if (dc->enabled && dc->out->sd_settings) + if (dc->enabled && dc->out->sd_settings) { nvsd_updated = nvsd_update_brightness(dc); + /* Ref-count vblank if nvsd is on-going. Otherwise, clean the + * V_BLANK_NVSD bit of vblank ref-count. */ + if (nvsd_updated) { + set_bit(V_BLANK_NVSD, &dc->vblank_ref_count); + tegra_dc_unmask_interrupt(dc, V_BLANK_INT); + } else { + clear_bit(V_BLANK_NVSD, &dc->vblank_ref_count); + } + } + + /* Mask vblank interrupt if ref-count is zero. */ + if (!dc->vblank_ref_count) + tegra_dc_mask_interrupt(dc, V_BLANK_INT); mutex_unlock(&dc->lock); @@ -2131,30 +899,18 @@ static void tegra_dc_vblank(struct work_struct *work) } } -/* Must acquire dc lock and dc one-shot lock before invoking this function. - * Acquire dc one-shot lock first and then dc lock. */ -void tegra_dc_host_trigger(struct tegra_dc *dc) -{ - /* We release the lock here to prevent deadlock between - * cancel_delayed_work_sync and one-shot work. */ - mutex_unlock(&dc->lock); - - cancel_delayed_work_sync(&dc->one_shot_work); - mutex_lock(&dc->lock); - - schedule_delayed_work(&dc->one_shot_work, - msecs_to_jiffies(dc->one_shot_delay_ms)); - tegra_dc_program_bandwidth(dc); - tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL); -} - static void tegra_dc_one_shot_worker(struct work_struct *work) { struct tegra_dc *dc = container_of( to_delayed_work(work), struct tegra_dc, one_shot_work); mutex_lock(&dc->lock); + /* memory client has gone idle */ tegra_dc_clear_bandwidth(dc); + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_suspend(dc); + mutex_unlock(&dc->lock); } @@ -2204,6 +960,26 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc) dc->ndev->name, (65 + i)); } #endif +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + if (dc->windows[i].underflows > 4) { + printk("%s:dc in underflow state." + " enable UF_LINE_FLUSH to clear up\n", + __func__); + tegra_dc_writel(dc, UF_LINE_FLUSH, + DC_DISP_DISP_MISC_CONTROL); + tegra_dc_writel(dc, GENERAL_UPDATE, + DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, GENERAL_ACT_REQ, + DC_CMD_STATE_CONTROL); + + tegra_dc_writel(dc, 0, + DC_DISP_DISP_MISC_CONTROL); + tegra_dc_writel(dc, GENERAL_UPDATE, + DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, GENERAL_ACT_REQ, + DC_CMD_STATE_CONTROL); + } +#endif } else { dc->windows[i].underflows = 0; } @@ -2218,63 +994,6 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc) } #ifndef CONFIG_TEGRA_FPGA_PLATFORM -static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc) -{ -#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM - u32 val; - - val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); - if (val & (WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE)) - return true; -#endif - return false; -} - -static void tegra_dc_trigger_windows(struct tegra_dc *dc) -{ - u32 val, i; - u32 completed = 0; - u32 dirty = 0; - - val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); - for (i = 0; i < DC_N_WINDOWS; i++) { -#ifdef CONFIG_TEGRA_SIMULATION_PLATFORM - /* FIXME: this is not needed when the simulator - clears WIN_x_UPDATE bits as in HW */ - dc->windows[i].dirty = 0; - completed = 1; -#else - if (!(val & (WIN_A_UPDATE << i))) { - dc->windows[i].dirty = 0; - completed = 1; - } else { - dirty = 1; - } -#endif - } - - if (!dirty) { - val = tegra_dc_readl(dc, DC_CMD_INT_MASK); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) - val &= ~V_BLANK_INT; - else - val &= ~FRAME_END_INT; - tegra_dc_writel(dc, val, DC_CMD_INT_MASK); - } - - if (completed) { - if (!dirty) { - /* With the last completed window, go ahead - and enable the vblank interrupt for nvsd. */ - val = tegra_dc_readl(dc, DC_CMD_INT_MASK); - val |= V_BLANK_INT; - tegra_dc_writel(dc, val, DC_CMD_INT_MASK); - } - - wake_up(&dc->wq); - } -} - static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status) { if (status & V_BLANK_INT) { @@ -2282,7 +1001,7 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status) tegra_dc_trigger_windows(dc); /* Schedule any additional bottom-half vblank actvities. */ - schedule_work(&dc->vblank_work); + queue_work(system_freezable_wq, &dc->vblank_work); } if (status & FRAME_END_INT) { @@ -2294,19 +1013,9 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status) static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status) { - if (status & V_BLANK_INT) { - /* Schedule any additional bottom-half vblank actvities. */ - schedule_work(&dc->vblank_work); - - /* All windows updated. Mask subsequent V_BLANK interrupts */ - if (!tegra_dc_windows_are_dirty(dc)) { - u32 val; - - val = tegra_dc_readl(dc, DC_CMD_INT_MASK); - val &= ~V_BLANK_INT; - tegra_dc_writel(dc, val, DC_CMD_INT_MASK); - } - } + /* Schedule any additional bottom-half vblank actvities. */ + if (status & V_BLANK_INT) + queue_work(system_freezable_wq, &dc->vblank_work); if (status & FRAME_END_INT) { /* Mark the frame_end as complete. */ @@ -2326,7 +1035,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) unsigned long underflow_mask; u32 val; - if (!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)) { + if (!nvhost_module_powered_ext(nvhost_get_parent(dc->ndev))) { WARN(1, "IRQ when DC not powered!\n"); tegra_dc_io_start(dc); status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); @@ -2509,7 +1218,9 @@ static int tegra_dc_init(struct tegra_dc *dc) tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY); tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY); tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER); - +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL); +#endif /* enable interrupts for vblank, frame_end and underflows */ tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT), DC_CMD_INT_ENABLE); @@ -2534,11 +1245,10 @@ static int tegra_dc_init(struct tegra_dc *dc) dc->syncpt[i].id = syncpt; dc->syncpt[i].min = dc->syncpt[i].max = - nvhost_syncpt_read(&nvhost_get_host(dc->ndev)->syncpt, - syncpt); + nvhost_syncpt_read_ext(dc->ndev, syncpt); } - print_mode(dc, &dc->mode, __func__); + print_mode_info(dc, dc->mode); if (dc->mode.pclk) if (tegra_dc_program_mode(dc, &dc->mode)) @@ -2559,7 +1269,7 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc) dc->out->enable(); tegra_dc_setup_clk(dc, dc->clk); - clk_enable(dc->clk); + tegra_dc_clk_enable(dc); /* do not accept interrupts during initialization */ tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE); @@ -2576,15 +1286,19 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc) if (dc->out_ops && dc->out_ops->enable) dc->out_ops->enable(dc); - if (dc->out->postpoweron) - dc->out->postpoweron(); - /* force a full blending update */ dc->blend.z[0] = -1; tegra_dc_ext_enable(dc->ext); trace_printk("%s:enable\n", dc->ndev->name); + + tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + + if (dc->out->postpoweron) + dc->out->postpoweron(); + return true; } @@ -2597,7 +1311,7 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc) dc->out->enable(); tegra_dc_setup_clk(dc, dc->clk); - clk_enable(dc->clk); + tegra_dc_clk_enable(dc); if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) { mutex_lock(&tegra_dcs[1]->lock); @@ -2704,6 +1418,9 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc) { unsigned i; + if (dc->out && dc->out->prepoweroff) + dc->out->prepoweroff(); + if (dc->out_ops && dc->out_ops->disable) dc->out_ops->disable(dc); @@ -2712,8 +1429,7 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc) disable_irq(dc->irq); tegra_dc_clear_bandwidth(dc); - clk_disable(dc->clk); - tegra_dvfs_set_rate(dc->clk, 0); + tegra_dc_clk_disable(dc); if (dc->out && dc->out->disable) dc->out->disable(); @@ -2733,9 +1449,7 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc) trace_printk("%s:syncpt flush id=%d\n", dc->ndev->name, dc->syncpt[i].id); dc->syncpt[i].min++; - nvhost_syncpt_cpu_incr( - &nvhost_get_host(dc->ndev)->syncpt, - dc->syncpt[i].id); + nvhost_syncpt_cpu_incr_ext(dc->ndev, dc->syncpt[i].id); } } trace_printk("%s:disabled\n", dc->ndev->name); @@ -2809,6 +1523,9 @@ void tegra_dc_disable(struct tegra_dc *dc) mutex_lock(&dc->lock); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + if (dc->enabled) { dc->enabled = false; @@ -2836,7 +1553,8 @@ static void tegra_dc_reset_worker(struct work_struct *work) mutex_lock(&shared_lock); - dev_warn(&dc->ndev->dev, "overlay stuck in underflow state. resetting.\n"); + dev_warn(&dc->ndev->dev, + "overlay stuck in underflow state. resetting.\n"); tegra_dc_ext_disable(dc->ext); @@ -2887,6 +1605,9 @@ static void tegra_dc_underflow_worker(struct work_struct *work) to_delayed_work(work), struct tegra_dc, underflow_work); mutex_lock(&dc->lock); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + if (dc->enabled) { tegra_dc_underflow_handler(dc); } @@ -2906,9 +1627,11 @@ static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf) } #endif -static int tegra_dc_probe(struct nvhost_device *ndev) +static int tegra_dc_probe(struct nvhost_device *ndev, + struct nvhost_device_id *id_table) { struct tegra_dc *dc; + struct tegra_dc_mode *mode; struct clk *clk; struct clk *emc_clk; struct resource *res; @@ -2944,7 +1667,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev) goto err_free; } - base_res = request_mem_region(res->start, resource_size(res), ndev->name); + base_res = request_mem_region(res->start, resource_size(res), + ndev->name); if (!base_res) { dev_err(&ndev->dev, "request_mem_region failed\n"); ret = -EBUSY; @@ -3001,6 +1725,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev) INIT_WORK(&dc->reset_work, tegra_dc_reset_worker); #endif INIT_WORK(&dc->vblank_work, tegra_dc_vblank); + dc->vblank_ref_count = 0; INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker); INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker); @@ -3030,6 +1755,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev) switch_dev_register(&dc->modeset_switch); #endif + tegra_dc_feature_register(dc); + if (dc->pdata->default_out) tegra_dc_set_out(dc, dc->pdata->default_out); else @@ -3044,22 +1771,19 @@ static int tegra_dc_probe(struct nvhost_device *ndev) dc->ext = NULL; } + mutex_lock(&dc->lock); + if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) + dc->enabled = _tegra_dc_enable(dc); + mutex_unlock(&dc->lock); + /* interrupt handler must be registered before tegra_fb_register() */ - if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED, + if (request_irq(irq, tegra_dc_irq, 0, dev_name(&ndev->dev), dc)) { dev_err(&ndev->dev, "request_irq %d failed\n", irq); ret = -EBUSY; goto err_put_emc_clk; } - /* hack to balance enable_irq calls in _tegra_dc_enable() */ - disable_dc_irq(dc->irq); - - mutex_lock(&dc->lock); - if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) - dc->enabled = _tegra_dc_enable(dc); - mutex_unlock(&dc->lock); - tegra_dc_create_debugfs(dc); dev_info(&ndev->dev, "probed\n"); @@ -3076,6 +1800,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev) tegra_dc_fmt_bpp(fmt); } + mode = tegra_dc_get_override_mode(dc); + if (mode) { + dc->pdata->fb->xres = mode->h_active; + dc->pdata->fb->yres = mode->v_active; + } + dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem); if (IS_ERR_OR_NULL(dc->fb)) dc->fb = NULL; @@ -3206,6 +1936,17 @@ static int tegra_dc_resume(struct nvhost_device *ndev) #endif /* CONFIG_PM */ +static void tegra_dc_shutdown(struct nvhost_device *ndev) +{ + struct tegra_dc *dc = nvhost_get_drvdata(ndev); + + if (!dc || !dc->enabled) + return; + + tegra_dc_blank(dc); + tegra_dc_disable(dc); +} + extern int suspend_set(const char *val, struct kernel_param *kp) { if (!strcmp(val, "dump")) @@ -3240,8 +1981,77 @@ struct nvhost_driver tegra_dc_driver = { .suspend = tegra_dc_suspend, .resume = tegra_dc_resume, #endif + .shutdown = tegra_dc_shutdown, }; +#ifndef MODULE +static int __init parse_disp_params(char *options, struct tegra_dc_mode *mode) +{ + int i, params[11]; + char *p; + + for (i = 0; i < ARRAY_SIZE(params); i++) { + if ((p = strsep(&options, ",")) != NULL) { + if (*p) + params[i] = simple_strtoul(p, &p, 10); + } else + return -EINVAL; + } + + if ((mode->pclk = params[0]) == 0) + return -EINVAL; + + mode->h_active = params[1]; + mode->v_active = params[2]; + mode->h_ref_to_sync = params[3]; + mode->v_ref_to_sync = params[4]; + mode->h_sync_width = params[5]; + mode->v_sync_width = params[6]; + mode->h_back_porch = params[7]; + mode->v_back_porch = params[8]; + mode->h_front_porch = params[9]; + mode->v_front_porch = params[10]; + + return 0; +} + +static int __init tegra_dc_mode_override(char *str) +{ + char *p = str, *options; + + if (!p || !*p) + return -EINVAL; + + p = strstr(str, "hdmi:"); + if (p) { + p += 5; + options = strsep(&p, ";"); + if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_HDMI])) + return -EINVAL; + } + + p = strstr(str, "rgb:"); + if (p) { + p += 4; + options = strsep(&p, ";"); + if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_RGB])) + return -EINVAL; + } + + p = strstr(str, "dsi:"); + if (p) { + p += 4; + options = strsep(&p, ";"); + if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_DSI])) + return -EINVAL; + } + + return 0; +} + +__setup("disp_params=", tegra_dc_mode_override); +#endif + static int __init tegra_dc_module_init(void) { int ret = tegra_dc_ext_module_init(); diff --git a/drivers/video/tegra/dc/dc_config.c b/drivers/video/tegra/dc/dc_config.c new file mode 100644 index 000000000000..f238faddab12 --- /dev/null +++ b/drivers/video/tegra/dc/dc_config.c @@ -0,0 +1,247 @@ +/* + * drivers/video/tegra/dc/dc_config.c + * + * Copyright (c) 2012, NVIDIA CORPORATION, All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "dc_config.h" + +static struct tegra_dc_feature_entry t20_feature_entries_a[] = { + { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, + { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, + { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, + { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, + { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, +}; + +static struct tegra_dc_feature_entry t20_feature_entries_b[] = { + { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, + { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, + { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, + { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, + { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, +}; + +struct tegra_dc_feature t20_feature_table_a = { + ARRAY_SIZE(t20_feature_entries_a), t20_feature_entries_a, +}; + +struct tegra_dc_feature t20_feature_table_b = { + ARRAY_SIZE(t20_feature_entries_b), t20_feature_entries_b, +}; + +static struct tegra_dc_feature_entry t30_feature_entries_a[] = { + { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, + { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, + { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} }, + { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} }, + { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, + { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, + { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} }, + { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, +}; + +static struct tegra_dc_feature_entry t30_feature_entries_b[] = { + { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, + { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, + { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, + { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, + + { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, + { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, + { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, + { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, + { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} }, +}; + +struct tegra_dc_feature t30_feature_table_a = { + ARRAY_SIZE(t30_feature_entries_a), t30_feature_entries_a, +}; + +struct tegra_dc_feature t30_feature_table_b = { + ARRAY_SIZE(t30_feature_entries_b), t30_feature_entries_b, +}; + +int tegra_dc_get_feature(struct tegra_dc_feature *feature, int win_idx, + enum tegra_dc_feature_option option) +{ + int i; + struct tegra_dc_feature_entry *entry; + + if (!feature) + return -EINVAL; + + for (i = 0; i < feature->num_entries; i++) { + entry = &feature->entries[i]; + if (entry->window_index == win_idx && entry->option == option) + return i; + } + + return -EINVAL; +} + +long *tegra_dc_parse_feature(struct tegra_dc *dc, int win_idx, int operation) +{ + int idx; + struct tegra_dc_feature_entry *entry; + enum tegra_dc_feature_option option; + struct tegra_dc_feature *feature = dc->feature; + + switch (operation) { + case GET_WIN_FORMATS: + option = TEGRA_DC_FEATURE_FORMATS; + break; + case GET_WIN_SIZE: + option = TEGRA_DC_FEATURE_MAXIMUM_SIZE; + break; + case HAS_SCALE: + option = TEGRA_DC_FEATURE_MAXIMUM_SCALE; + break; + case HAS_TILED: + option = TEGRA_DC_FEATURE_LAYOUT_TYPE; + break; + case HAS_V_FILTER: + option = TEGRA_DC_FEATURE_FILTER_TYPE; + break; + case HAS_H_FILTER: + option = TEGRA_DC_FEATURE_FILTER_TYPE; + break; + case HAS_GEN2_BLEND: + option = TEGRA_DC_FEATURE_BLEND_TYPE; + break; + default: + return NULL; + } + + idx = tegra_dc_get_feature(feature, win_idx, option); + if (IS_ERR_VALUE(idx)) + return NULL; + entry = &feature->entries[idx]; + + return entry->arg; +} + +int tegra_dc_feature_has_scaling(struct tegra_dc *dc, int win_idx) +{ + int i; + long *addr = tegra_dc_parse_feature(dc, win_idx, HAS_SCALE); + + for (i = 0; i < ENTRY_SIZE; i++) + if (addr[i] != 1) + return 1; + return 0; +} + +int tegra_dc_feature_has_tiling(struct tegra_dc *dc, int win_idx) +{ + long *addr = tegra_dc_parse_feature(dc, win_idx, HAS_TILED); + + return addr[TILED_LAYOUT]; +} + +int tegra_dc_feature_has_filter(struct tegra_dc *dc, int win_idx, int operation) +{ + long *addr = tegra_dc_parse_feature(dc, win_idx, operation); + + if (operation == HAS_V_FILTER) + return addr[V_FILTER]; + else + return addr[H_FILTER]; +} + +void tegra_dc_feature_register(struct tegra_dc *dc) +{ +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + if (!dc->ndev->id) + dc->feature = &t20_feature_table_a; + else + dc->feature = &t20_feature_table_b; +#elif defined(CONFIG_ARCH_TEGRA_3x_SOC) + if (!dc->ndev->id) + dc->feature = &t30_feature_table_a; + else + dc->feature = &t30_feature_table_b; +#endif +} diff --git a/drivers/video/tegra/dc/dc_config.h b/drivers/video/tegra/dc/dc_config.h new file mode 100644 index 000000000000..6f5d08ccd82a --- /dev/null +++ b/drivers/video/tegra/dc/dc_config.h @@ -0,0 +1,162 @@ +/* + * drivers/video/tegra/dc/dc_config.c + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_CONFIG_H +#define __DRIVERS_VIDEO_TEGRA_DC_DC_CONFIG_H + +#include <linux/errno.h> +#include <mach/dc.h> + +#include "dc_priv.h" + +#define ENTRY_SIZE 4 /* Size of feature entry args */ + +/* Define the supported formats. TEGRA_WIN_FMT_WIN_x macros are defined + * based on T20/T30 formats. */ +#define TEGRA_WIN_FMT_BASE_CNT (TEGRA_WIN_FMT_YUV422RA + 1) +#define TEGRA_WIN_FMT_BASE ((1 << TEGRA_WIN_FMT_P8) | \ + (1 << TEGRA_WIN_FMT_B4G4R4A4) | \ + (1 << TEGRA_WIN_FMT_B5G5R5A) | \ + (1 << TEGRA_WIN_FMT_B5G6R5) | \ + (1 << TEGRA_WIN_FMT_AB5G5R5) | \ + (1 << TEGRA_WIN_FMT_B8G8R8A8) | \ + (1 << TEGRA_WIN_FMT_R8G8B8A8) | \ + (1 << TEGRA_WIN_FMT_YCbCr422) | \ + (1 << TEGRA_WIN_FMT_YUV422) | \ + (1 << TEGRA_WIN_FMT_YCbCr420P) | \ + (1 << TEGRA_WIN_FMT_YUV420P) | \ + (1 << TEGRA_WIN_FMT_YCbCr422P) | \ + (1 << TEGRA_WIN_FMT_YUV422P) | \ + (1 << TEGRA_WIN_FMT_YCbCr422R) | \ + (1 << TEGRA_WIN_FMT_YUV422R)) + +#define TEGRA_WIN_FMT_WIN_A ((1 << TEGRA_WIN_FMT_P1) | \ + (1 << TEGRA_WIN_FMT_P2) | \ + (1 << TEGRA_WIN_FMT_P4) | \ + (1 << TEGRA_WIN_FMT_P8) | \ + (1 << TEGRA_WIN_FMT_B4G4R4A4) | \ + (1 << TEGRA_WIN_FMT_B5G5R5A) | \ + (1 << TEGRA_WIN_FMT_B5G6R5) | \ + (1 << TEGRA_WIN_FMT_AB5G5R5) | \ + (1 << TEGRA_WIN_FMT_B8G8R8A8) | \ + (1 << TEGRA_WIN_FMT_R8G8B8A8) | \ + (1 << TEGRA_WIN_FMT_B6x2G6x2R6x2A8) | \ + (1 << TEGRA_WIN_FMT_R6x2G6x2B6x2A8)) + +#define TEGRA_WIN_FMT_WIN_B (TEGRA_WIN_FMT_BASE | \ + (1 << TEGRA_WIN_FMT_B6x2G6x2R6x2A8) | \ + (1 << TEGRA_WIN_FMT_R6x2G6x2B6x2A8) | \ + (1 << TEGRA_WIN_FMT_YCbCr422RA) | \ + (1 << TEGRA_WIN_FMT_YUV422RA)) + +#define TEGRA_WIN_FMT_WIN_C (TEGRA_WIN_FMT_BASE | \ + (1 << TEGRA_WIN_FMT_B6x2G6x2R6x2A8) | \ + (1 << TEGRA_WIN_FMT_R6x2G6x2B6x2A8) | \ + (1 << TEGRA_WIN_FMT_YCbCr422RA) | \ + (1 << TEGRA_WIN_FMT_YUV422RA)) + +/* preferred formats do not include 32-bpp formats */ +#define TEGRA_WIN_PREF_FMT_WIN_B (TEGRA_WIN_FMT_WIN_B & \ + ~(1 << TEGRA_WIN_FMT_B8G8R8A8) & \ + ~(1 << TEGRA_WIN_FMT_R8G8B8A8)) + + + +/* For each entry, we define the offset to read specific feature. Define the + * offset for TEGRA_DC_FEATURE_MAXIMUM_SCALE */ +#define H_SCALE_UP 0 +#define V_SCALE_UP 1 +#define H_FILTER_DOWN 2 +#define V_FILTER_DOWN 3 + +/* Define the offset for TEGRA_DC_FEATURE_MAXIMUM_SIZE */ +#define MAX_WIDTH 0 +#define MIN_WIDTH 1 +#define MAX_HEIGHT 2 +#define MIN_HEIGHT 3 +#define CHECK_SIZE(val, min, max) ( \ + ((val) < (min) || (val) > (max)) ? -EINVAL : 0) + +/* Define the offset for TEGRA_DC_FEATURE_FILTER_TYPE */ +#define V_FILTER 0 +#define H_FILTER 1 + +/* Define the offset for TEGRA_DC_FEATURE_INVERT_TYPE */ +#define H_INVERT 0 +#define V_INVERT 1 +#define SCAN_COLUMN 2 + +/* Define the offset for TEGRA_DC_FEATURE_LAYOUT_TYPE. */ +#define PITCHED_LAYOUT 0 +#define TILED_LAYOUT 1 + +/* Available operations on feature table. */ +enum { + HAS_SCALE, + HAS_TILED, + HAS_V_FILTER, + HAS_H_FILTER, + HAS_GEN2_BLEND, + GET_WIN_FORMATS, + GET_WIN_SIZE, +}; + +enum tegra_dc_feature_option { + TEGRA_DC_FEATURE_FORMATS, + TEGRA_DC_FEATURE_BLEND_TYPE, + TEGRA_DC_FEATURE_MAXIMUM_SIZE, + TEGRA_DC_FEATURE_MAXIMUM_SCALE, + TEGRA_DC_FEATURE_FILTER_TYPE, + TEGRA_DC_FEATURE_LAYOUT_TYPE, + TEGRA_DC_FEATURE_INVERT_TYPE, + TEGRA_DC_FEATURE_PREFERRED_FORMATS, +}; + +struct tegra_dc_feature_entry { + u32 window_index; + u32 option; + long arg[ENTRY_SIZE]; +}; + +struct tegra_dc_feature { + u32 num_entries; + struct tegra_dc_feature_entry *entries; +}; + +int tegra_dc_feature_has_scaling(struct tegra_dc *dc, int win_idx); +int tegra_dc_feature_has_tiling(struct tegra_dc *dc, int win_idx); +int tegra_dc_feature_has_filter(struct tegra_dc *dc, int win_idx, int operation); + +long *tegra_dc_parse_feature(struct tegra_dc *dc, int win_idx, int operation); +void tegra_dc_feature_register(struct tegra_dc *dc); + +static inline bool win_use_v_filter(struct tegra_dc *dc, + const struct tegra_dc_win *win) +{ + return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) && + win->h.full != dfixed_const(win->out_h); +} +static inline bool win_use_h_filter(struct tegra_dc *dc, + const struct tegra_dc_win *win) +{ + return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) && + win->w.full != dfixed_const(win->out_w); +} + +#endif diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index d16279a4c63d..fb1243593587 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -4,6 +4,8 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. @@ -22,15 +24,17 @@ #include <linux/mutex.h> #include <linux/wait.h> #include <linux/fb.h> +#include <linux/clk.h> #include <linux/completion.h> #include <linux/switch.h> +#include <linux/nvhost.h> #include <mach/dc.h> -#include "../host/dev.h" -#include "../host/host1x/host1x_syncpt.h" - #include <mach/tegra_dc_ext.h> +#include <mach/clk.h> + +#include "dc_reg.h" #define WIN_IS_TILED(win) ((win)->flags & TEGRA_WIN_FLAG_TILED) #define WIN_IS_ENABLED(win) ((win)->flags & TEGRA_WIN_FLAG_ENABLED) @@ -46,6 +50,13 @@ #define EMC_BW_TO_FREQ(bw) (DDR_BW_TO_FREQ(bw) * 2) #endif +#ifndef CONFIG_TEGRA_FPGA_PLATFORM +#define ALL_UF_INT (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT) +#else +/* ignore underflows when on simulation and fpga platform */ +#define ALL_UF_INT (0) +#endif + struct tegra_dc; struct tegra_dc_blend { @@ -69,7 +80,7 @@ struct tegra_dc_out_ops { /* resume output. dc clocks are on at this point */ void (*resume)(struct tegra_dc *dc); /* mode filter. to provide a list of supported modes*/ - bool (*mode_filter)(struct tegra_dc *dc, + bool (*mode_filter)(const struct tegra_dc *dc, struct fb_videomode *mode); }; @@ -126,6 +137,7 @@ struct tegra_dc { struct completion frame_end_complete; struct work_struct vblank_work; + long vblank_ref_count; struct { u64 underflows; @@ -136,6 +148,8 @@ struct tegra_dc { struct tegra_dc_ext *ext; + struct tegra_dc_feature *feature; + #ifdef CONFIG_DEBUG_FS struct dentry *debugdir; #endif @@ -145,14 +159,31 @@ struct tegra_dc { struct delayed_work one_shot_work; }; +#define print_mode_info(dc, mode) do { \ + trace_printk("%s:Mode settings: " \ + "ref_to_sync: H = %d V = %d, " \ + "sync_width: H = %d V = %d, " \ + "back_porch: H = %d V = %d, " \ + "active: H = %d V = %d, " \ + "front_porch: H = %d V = %d, " \ + "pclk = %d, stereo mode = %d\n", \ + dc->ndev->name, \ + mode.h_ref_to_sync, mode.v_ref_to_sync, \ + mode.h_sync_width, mode.v_sync_width, \ + mode.h_back_porch, mode.v_back_porch, \ + mode.h_active, mode.v_active, \ + mode.h_front_porch, mode.v_front_porch, \ + mode.pclk, mode.stereo_mode); \ + } while (0) + static inline void tegra_dc_io_start(struct tegra_dc *dc) { - nvhost_module_busy(nvhost_get_host(dc->ndev)->dev); + nvhost_module_busy_ext(nvhost_get_parent(dc->ndev)); } static inline void tegra_dc_io_end(struct tegra_dc *dc) { - nvhost_module_idle(nvhost_get_host(dc->ndev)->dev); + nvhost_module_idle_ext(nvhost_get_parent(dc->ndev)); } static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, @@ -160,7 +191,10 @@ static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, { unsigned long ret; - BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)); + BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dc->ndev))); + if (!tegra_is_clk_enabled(dc->clk)) + WARN(1, "DC is clock-gated.\n"); + ret = readl(dc->base + reg * 4); trace_printk("readl %p=%#08lx\n", dc->base + reg * 4, ret); return ret; @@ -169,7 +203,10 @@ static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val, unsigned long reg) { - BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)); + BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dc->ndev))); + if (!tegra_is_clk_enabled(dc->clk)) + WARN(1, "DC is clock-gated.\n"); + trace_printk("writel %p=%#08lx\n", dc->base + reg * 4, val); writel(val, dc->base + reg * 4); } @@ -197,12 +234,117 @@ static inline void *tegra_dc_get_outdata(struct tegra_dc *dc) } static inline unsigned long tegra_dc_get_default_emc_clk_rate( - struct tegra_dc *dc) + struct tegra_dc *dc) { return dc->pdata->emc_clk_rate ? dc->pdata->emc_clk_rate : ULONG_MAX; } -void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk); +static inline int tegra_dc_fmt_bpp(int fmt) +{ + switch (fmt) { + case TEGRA_WIN_FMT_P1: + return 1; + + case TEGRA_WIN_FMT_P2: + return 2; + + case TEGRA_WIN_FMT_P4: + return 4; + + case TEGRA_WIN_FMT_P8: + return 8; + + case TEGRA_WIN_FMT_B4G4R4A4: + case TEGRA_WIN_FMT_B5G5R5A: + case TEGRA_WIN_FMT_B5G6R5: + case TEGRA_WIN_FMT_AB5G5R5: + return 16; + + case TEGRA_WIN_FMT_B8G8R8A8: + case TEGRA_WIN_FMT_R8G8B8A8: + case TEGRA_WIN_FMT_B6x2G6x2R6x2A8: + case TEGRA_WIN_FMT_R6x2G6x2B6x2A8: + return 32; + + /* for planar formats, size of the Y plane, 8bit */ + case TEGRA_WIN_FMT_YCbCr420P: + case TEGRA_WIN_FMT_YUV420P: + case TEGRA_WIN_FMT_YCbCr422P: + case TEGRA_WIN_FMT_YUV422P: + case TEGRA_WIN_FMT_YCbCr422R: + case TEGRA_WIN_FMT_YUV422R: + case TEGRA_WIN_FMT_YCbCr422RA: + case TEGRA_WIN_FMT_YUV422RA: + return 8; + + /* YUYV packed into 32-bits */ + case TEGRA_WIN_FMT_YCbCr422: + case TEGRA_WIN_FMT_YUV422: + return 16; + } + return 0; +} + +static inline bool tegra_dc_is_yuv(int fmt) +{ + switch (fmt) { + case TEGRA_WIN_FMT_YUV420P: + case TEGRA_WIN_FMT_YCbCr420P: + case TEGRA_WIN_FMT_YCbCr422P: + case TEGRA_WIN_FMT_YUV422P: + case TEGRA_WIN_FMT_YCbCr422: + case TEGRA_WIN_FMT_YUV422: + case TEGRA_WIN_FMT_YCbCr422R: + case TEGRA_WIN_FMT_YUV422R: + case TEGRA_WIN_FMT_YCbCr422RA: + case TEGRA_WIN_FMT_YUV422RA: + return true; + } + return false; +} + +static inline bool tegra_dc_is_yuv_planar(int fmt) +{ + switch (fmt) { + case TEGRA_WIN_FMT_YUV420P: + case TEGRA_WIN_FMT_YCbCr420P: + case TEGRA_WIN_FMT_YCbCr422P: + case TEGRA_WIN_FMT_YUV422P: + case TEGRA_WIN_FMT_YCbCr422R: + case TEGRA_WIN_FMT_YUV422R: + case TEGRA_WIN_FMT_YCbCr422RA: + case TEGRA_WIN_FMT_YUV422RA: + return true; + } + return false; +} + +static inline void tegra_dc_unmask_interrupt(struct tegra_dc *dc, u32 int_val) +{ + u32 val; + + val = tegra_dc_readl(dc, DC_CMD_INT_MASK); + val |= int_val; + tegra_dc_writel(dc, val, DC_CMD_INT_MASK); +} + +static inline void tegra_dc_mask_interrupt(struct tegra_dc *dc, u32 int_val) +{ + u32 val; + + val = tegra_dc_readl(dc, DC_CMD_INT_MASK); + val &= ~int_val; + tegra_dc_writel(dc, val, DC_CMD_INT_MASK); +} + +static inline unsigned long tegra_dc_clk_get_rate(struct tegra_dc *dc) +{ +#ifdef CONFIG_TEGRA_SILICON_PLATFORM + return clk_get_rate(dc->clk); +#else + return dc->mode.pclk; +#endif +} extern struct tegra_dc_out_ops tegra_dc_rgb_ops; extern struct tegra_dc_out_ops tegra_dc_hdmi_ops; @@ -224,5 +366,31 @@ void tegra_dc_disable_crc(struct tegra_dc *dc); void tegra_dc_set_out_pin_polars(struct tegra_dc *dc, const struct tegra_dc_out_pin *pins, const unsigned int n_pins); -#endif +/* defined in dc.c, used in bandwidth.c */ +unsigned int tegra_dc_has_multiple_dc(void); + +/* defined in bandwidth.c, used in dc.c */ +void tegra_dc_clear_bandwidth(struct tegra_dc *dc); +void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new); +int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n); + +/* defined in mode.c, used in dc.c */ +int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode); +int tegra_dc_calc_refresh(const struct tegra_dc_mode *m); + +/* defined in clock.c, used in dc.c, dsi.c and hdmi.c */ +void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk); +unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk); + +/* defined in lut.c, used in dc.c */ +void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut); +void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win *win); + +/* defined in csc.c, used in dc.c */ +void tegra_dc_init_csc_defaults(struct tegra_dc_csc *csc); +void tegra_dc_set_csc(struct tegra_dc *dc, struct tegra_dc_csc *csc); +/* defined in window.c, used in dc.c */ +void tegra_dc_trigger_windows(struct tegra_dc *dc); + +#endif diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h index 22379a194082..86b1029d3bba 100644 --- a/drivers/video/tegra/dc/dc_reg.h +++ b/drivers/video/tegra/dc/dc_reg.h @@ -4,7 +4,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -367,6 +367,7 @@ #define DC_DISP_MCCIF_DISPLAY1B_HYST 0x484 #define DC_DISP_DAC_CRT_CTRL 0x4c0 #define DC_DISP_DISP_MISC_CONTROL 0x4c1 +#define UF_LINE_FLUSH (1 << 1) #define DC_WIN_COLOR_PALETTE(x) (0x500 + (x)) @@ -430,6 +431,8 @@ #define DC_WIN_LINE_STRIDE 0x70a #define LINE_STRIDE(x) (x) #define UV_LINE_STRIDE(x) (((x) & 0xffff) << 16) +#define GET_LINE_STRIDE(x) ((x) & 0xffff) +#define GET_UV_LINE_STRIDE(x) (((x) >> 16) & 0xffff) #define DC_WIN_BUF_STRIDE 0x70b #define DC_WIN_UV_BUF_STRIDE 0x70c #define DC_WIN_BUFFER_ADDR_MODE 0x70d @@ -459,6 +462,12 @@ #define DC_WIN_HP_FETCH_CONTROL 0x714 + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +#define DC_WIN_GLOBAL_ALPHA 0x715 +#define GLOBAL_ALPHA_ENABLE 0x10000 +#endif + #define DC_WINBUF_START_ADDR 0x800 #define DC_WINBUF_START_ADDR_NS 0x801 #define DC_WINBUF_START_ADDR_U 0x802 diff --git a/drivers/video/tegra/dc/dc_sysfs.c b/drivers/video/tegra/dc/dc_sysfs.c index 0b259c374360..bf27e963f233 100644 --- a/drivers/video/tegra/dc/dc_sysfs.c +++ b/drivers/video/tegra/dc/dc_sysfs.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/dc_sysfs.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 4b055ec529c6..7ee9375f58f1 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/dsi.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -14,23 +14,25 @@ * */ +#include <linux/kernel.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/fb.h> #include <linux/gpio.h> #include <linux/interrupt.h> -#include <linux/kernel.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/nvhost.h> #include <mach/clk.h> #include <mach/dc.h> #include <mach/fb.h> #include <mach/csi.h> +#include <mach/iomap.h> #include <linux/nvhost.h> #include "dc_reg.h" @@ -38,6 +40,9 @@ #include "dsi_regs.h" #include "dsi.h" +#define APB_MISC_GP_MIPI_PAD_CTRL_0 (TEGRA_APB_MISC_BASE + 0x820) +#define DSIB_MODE_ENABLE 0x2 + #define DSI_USE_SYNC_POINTS 1 #define S_TO_MS(x) (1000 * (x)) @@ -109,6 +114,7 @@ struct tegra_dc_dsi_data { struct clk *dc_clk; struct clk *dsi_clk; + struct clk *dsi_fixed_clk; bool clk_ref; struct mutex lock; @@ -120,6 +126,10 @@ struct tegra_dc_dsi_data { struct dsi_phy_timing_inclk phy_timing; + bool ulpm; + bool enabled; + bool host_suspended; + u8 driven_mode; u8 controller_index; @@ -141,9 +151,6 @@ struct tegra_dc_dsi_data { u32 current_dsi_clk_khz; u32 dsi_control_val; - - bool ulpm; - bool enabled; }; const u32 dsi_pkt_seq_reg[NUMOF_PKT_SEQ] = { @@ -291,7 +298,7 @@ inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg) { unsigned long ret; - BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev)); + BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dsi->dc->ndev))); ret = readl(dsi->base + reg * 4); trace_printk("readl %p=%#08lx\n", dsi->base + reg * 4, ret); return ret; @@ -300,7 +307,7 @@ EXPORT_SYMBOL(tegra_dsi_readl); inline void tegra_dsi_writel(struct tegra_dc_dsi_data *dsi, u32 val, u32 reg) { - BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev)); + BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dsi->dc->ndev))); trace_printk("writel %p=%#08x\n", dsi->base + reg * 4, val); writel(val, dsi->base + reg * 4); } @@ -411,6 +418,22 @@ static inline void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi) { } #endif +static inline void tegra_dsi_clk_enable(struct tegra_dc_dsi_data *dsi) +{ + if (!tegra_is_clk_enabled(dsi->dsi_clk)) { + clk_enable(dsi->dsi_clk); + clk_enable(dsi->dsi_fixed_clk); + } +} + +static inline void tegra_dsi_clk_disable(struct tegra_dc_dsi_data *dsi) +{ + if (tegra_is_clk_enabled(dsi->dsi_clk)) { + clk_disable(dsi->dsi_clk); + clk_disable(dsi->dsi_fixed_clk); + } +} + static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi) { u32 val; @@ -418,17 +441,15 @@ static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi) ret = 0; - dsi->syncpt_val = nvhost_syncpt_read( - &nvhost_get_host(dsi->dc->ndev)->syncpt, - dsi->syncpt_id); + dsi->syncpt_val = nvhost_syncpt_read_ext(dsi->dc->ndev, dsi->syncpt_id); val = DSI_INCR_SYNCPT_COND(OP_DONE) | DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); /* TODO: Use interrupt rather than polling */ - ret = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt, - dsi->syncpt_id, dsi->syncpt_val + 1); + ret = nvhost_syncpt_wait_timeout_ext(dsi->dc->ndev, dsi->syncpt_id, + dsi->syncpt_val + 1, MAX_SCHEDULE_TIMEOUT, NULL); if (ret < 0) { dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n"); goto fail; @@ -1052,7 +1073,7 @@ static u32 tegra_dsi_sol_delay_burst(struct tegra_dc *dc, u32 dsi_to_pixel_clk_ratio; u32 temp; u32 temp1; - u32 mipi_clk_adj_kHz; + u32 mipi_clk_adj_kHz = 0; u32 sol_delay; struct tegra_dc_mode *dc_modes = &dc->mode; @@ -1450,11 +1471,9 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc, /* Enable DSI clock */ tegra_dc_setup_clk(dc, dsi->dsi_clk); - if (!dsi->clk_ref) { - dsi->clk_ref = true; - clk_enable(dsi->dsi_clk); - tegra_periph_reset_deassert(dsi->dsi_clk); - } + tegra_dsi_clk_enable(dsi); + tegra_periph_reset_deassert(dsi->dsi_clk); + dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000; dsi->current_bit_clk_ns = 1000*1000 / (dsi->current_dsi_clk_khz * 2); } @@ -1619,6 +1638,15 @@ static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi) tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG); } +static void tegra_dsi_panelB_enable(void) +{ + unsigned int val; + + val = readl(IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0)); + val |= DSIB_MODE_ENABLE; + writel(val, (IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0))); +} + static int tegra_dsi_init_hw(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) { @@ -1632,7 +1660,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc, tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_lp_clk_khz); if (dsi->info.dsi_instance) { - /* TODO:Set the misc register*/ + tegra_dsi_panelB_enable(); } /* TODO: only need to change the timing for bta */ @@ -1929,6 +1957,10 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission( if (tegra_dsi_host_busy(dsi)) { tegra_dsi_soft_reset(dsi); + + /* WAR to stop host write in middle */ + tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_TRIGGER); + if (tegra_dsi_host_busy(dsi)) { err = -EBUSY; dev_err(&dc->ndev->dev, "DSI host busy\n"); @@ -2174,6 +2206,9 @@ int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len) int err = 0, count = 0; struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + data_len_orig = data_len; if (pdata != NULL) { while (data_len) { @@ -2240,9 +2275,7 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); #if DSI_USE_SYNC_POINTS - /* FIXME: Workaround for nvhost_syncpt_read */ - dsi->syncpt_val = nvhost_syncpt_update_min( - &nvhost_get_host(dsi->dc->ndev)->syncpt, + dsi->syncpt_val = nvhost_syncpt_read_ext(dsi->dc->ndev, dsi->syncpt_id); val = DSI_INCR_SYNCPT_COND(OP_DONE) | @@ -2250,8 +2283,8 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); /* TODO: Use interrupt rather than polling */ - err = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt, - dsi->syncpt_id, dsi->syncpt_val + 1); + err = nvhost_syncpt_wait_timeout_ext(dsi->dc->ndev, dsi->syncpt_id, + dsi->syncpt_val + 1, MAX_SCHEDULE_TIMEOUT, NULL); if (err < 0) dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n"); @@ -2582,6 +2615,53 @@ fail: } +static void tegra_dsi_send_dc_frames(struct tegra_dc *dc, + struct tegra_dc_dsi_data *dsi, + int no_of_frames) +{ + int err; + u32 frame_period = DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate); + u8 lp_op = dsi->status.lp_op; + bool switch_to_lp = (dsi->status.lphs == DSI_LPHS_IN_LP_MODE); + + if (dsi->status.lphs != DSI_LPHS_IN_HS_MODE) { + err = tegra_dsi_set_to_hs_mode(dc, dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "Switch to HS host mode failed\n"); + return; + } + } + + /* + * Some panels need DC frames be sent under certain + * conditions. We are working on the right fix for this + * requirement, while using this current fix. + */ + tegra_dsi_start_dc_stream(dc, dsi); + + /* + * Send frames in Continuous or One-shot mode. + */ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { + while (no_of_frames--) { + tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG, + DC_CMD_STATE_CONTROL); + mdelay(frame_period); + } + } else + mdelay(no_of_frames * frame_period); + + tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); + + if (switch_to_lp) { + err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op); + if (err < 0) + dev_err(&dc->ndev->dev, + "DSI failed to go to LP mode\n"); + } +} + static void tegra_dc_dsi_enable(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); @@ -2591,6 +2671,8 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) tegra_dc_io_start(dc); mutex_lock(&dsi->lock); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); /* Stop DC stream before configuring DSI registers * to avoid visible glitches on panel during transition * from bootloader to kernel driver @@ -2607,6 +2689,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) } if (dsi->info.panel_reset) { + /* + * Certain panels need dc frames be sent before + * waking panel. + */ + if (dsi->info.panel_send_dc_frames) + tegra_dsi_send_dc_frames(dc, dsi, 2); + err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, dsi->info.n_init_cmd); @@ -2660,6 +2749,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) } } + /* + * Certain panels need dc frames be sent before + * waking panel. + */ + if (dsi->info.panel_send_dc_frames) + tegra_dsi_send_dc_frames(dc, dsi, 2); + err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); if (err < 0) { dev_err(&dc->ndev->dev, @@ -2732,8 +2828,8 @@ static int tegra_dc_dsi_cp_info(struct tegra_dc_dsi_data *dsi, struct tegra_dsi_out *p_dsi) { struct tegra_dsi_cmd *p_init_cmd; - struct tegra_dsi_cmd *p_early_suspend_cmd; - struct tegra_dsi_cmd *p_late_resume_cmd; + struct tegra_dsi_cmd *p_early_suspend_cmd = NULL; + struct tegra_dsi_cmd *p_late_resume_cmd = NULL; struct tegra_dsi_cmd *p_suspend_cmd; int err; @@ -2861,6 +2957,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) void __iomem *base; struct clk *dc_clk = NULL; struct clk *dsi_clk = NULL; + struct clk *dsi_fixed_clk = NULL; struct tegra_dsi_out *dsi_pdata; int err; @@ -2903,8 +3000,9 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) dsi_clk = clk_get(&dc->ndev->dev, "dsib"); else dsi_clk = clk_get(&dc->ndev->dev, "dsia"); + dsi_fixed_clk = clk_get(&dc->ndev->dev, "dsi-fixed"); - if (IS_ERR_OR_NULL(dsi_clk)) { + if (IS_ERR_OR_NULL(dsi_clk) || IS_ERR_OR_NULL(dsi_fixed_clk)) { dev_err(&dc->ndev->dev, "dsi: can't get clock\n"); err = -EBUSY; goto err_release_regs; @@ -2924,6 +3022,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) dsi->base_res = base_res; dsi->dc_clk = dc_clk; dsi->dsi_clk = dsi_clk; + dsi->dsi_fixed_clk = dsi_fixed_clk; err = tegra_dc_dsi_cp_info(dsi, dsi_pdata); if (err < 0) @@ -2937,6 +3036,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) err_dsi_data: err_clk_put: clk_put(dsi_clk); + clk_put(dsi_fixed_clk); err_release_regs: release_resource(base_res); err_free_dsi: @@ -2983,78 +3083,180 @@ static void tegra_dc_dsi_destroy(struct tegra_dc *dc) kfree(dsi); } -static int tegra_dsi_deep_sleep(struct tegra_dc *dc, - struct tegra_dc_dsi_data *dsi) +static void tegra_dsi_config_phy_clk(struct tegra_dc_dsi_data *dsi, + u32 settings) { - int err = 0; - int val; struct clk *parent_clk = NULL; struct clk *base_clk = NULL; + /* Disable dsi fast and slow clock */ + parent_clk = clk_get_parent(dsi->dsi_clk); + base_clk = clk_get_parent(parent_clk); + if (dsi->info.dsi_instance) + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_CSI_OUT_ENB, + settings); + else + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_DSI_OUT_ENB, + settings); +} + +static int tegra_dsi_deep_sleep(struct tegra_dc *dc, + struct tegra_dc_dsi_data *dsi, u32 suspend_aggr) +{ + int val = 0; + int err = 0; + if (!dsi->enabled) { err = -EPERM; goto fail; } - err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); - if (err < 0) { - dev_err(&dc->ndev->dev, - "DSI failed to go to LP mode\n"); - goto fail; - } + switch (suspend_aggr) { + case DSI_SUSPEND_FULL: + /* Suspend DSI panel */ + err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to go to LP mode\n"); + goto fail; + } - /* Suspend panel */ - err = tegra_dsi_send_panel_cmd(dc, dsi, - dsi->info.dsi_suspend_cmd, - dsi->info.n_suspend_cmd); - if (err < 0) { - dev_err(&dc->ndev->dev, - "dsi: Error sending suspend cmd\n"); - goto fail; - } + err = tegra_dsi_send_panel_cmd(dc, dsi, + dsi->info.dsi_suspend_cmd, + dsi->info.n_suspend_cmd); + /* + * Certain panels need dc frames be sent after + * putting panel to sleep. + */ + if (dsi->info.panel_send_dc_frames) + tegra_dsi_send_dc_frames(dc, dsi, 2); - if (!dsi->ulpm) { - err = tegra_dsi_enter_ulpm(dsi); if (err < 0) { dev_err(&dc->ndev->dev, - "DSI failed to enter ulpm\n"); + "dsi: Error sending suspend cmd\n"); goto fail; } + case DSI_HOST_SUSPEND_LV2: + /* Set DSI to ULPM and suspend pads. DSI will be set to the + * lowest power state in this level. */ + if (!dsi->ulpm) { + err = tegra_dsi_enter_ulpm(dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to enter ulpm\n"); + goto fail; + } + } + + val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + + /* Suspend core-logic */ + val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); + tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); + case DSI_HOST_SUSPEND_LV1: + /* Disable dsi fast and slow clock */ + tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_DISABLE); + case DSI_HOST_SUSPEND_LV0: + /* Disable dsi source clock */ + tegra_dsi_clk_disable(dsi); + break; + case DSI_NO_SUSPEND: + break; + default: + dev_err(&dc->ndev->dev, "DSI suspend aggressiveness" + "is not supported.\n"); } - /* - * Suspend pad - * It is ok to overwrite previous value of DSI_PAD_CONTROL reg - * because it will be restored properly in resume sequence - */ - val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | - DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | - DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); - tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + dsi->enabled = false; - /* Suspend core-logic */ - val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); - tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); + return 0; +fail: + return err; +} - /* Disable dsi fast and slow clock */ - parent_clk = clk_get_parent(dsi->dsi_clk); - base_clk = clk_get_parent(parent_clk); - if (dsi->info.dsi_instance) - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_CSI_OUT_ENB, - 0); - else - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_DSI_OUT_ENB, - 0); - /* Disable dsi source clock */ - clk_disable(dsi->dsi_clk); +int tegra_dsi_host_suspend(struct tegra_dc *dc) +{ + int err = 0; + struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); - dsi->clk_ref = false; - dsi->enabled = false; + if (dsi->host_suspended) + return 0; - return 0; + tegra_dsi_stop_dc_stream(dc, dsi); + + err = tegra_dsi_deep_sleep(dc, dsi, dsi->info.suspend_aggr); + if (err < 0) + dev_err(&dc->ndev->dev, + "DSI failed to enter deep sleep\n"); + + dsi->host_suspended = true; + + return err; +} + + +int tegra_dsi_host_resume(struct tegra_dc *dc) +{ + int val = 0; + int err = 0; + struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + + if (!dsi->host_suspended) + return 0; + + switch (dsi->info.suspend_aggr) { + case DSI_HOST_SUSPEND_LV0: + tegra_dsi_clk_enable(dsi); + break; + case DSI_HOST_SUSPEND_LV1: + tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_ENABLE); + tegra_dsi_clk_enable(dsi); + break; + case DSI_HOST_SUSPEND_LV2: + tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_ENABLE); + tegra_dsi_clk_enable(dsi); + + tegra_dsi_writel(dsi, + DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE), + DSI_POWER_CONTROL); + + if (dsi->ulpm) { + err = tegra_dsi_enter_ulpm(dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to enter ulpm\n"); + goto fail; + } + + val = tegra_dsi_readl(dsi, DSI_PAD_CONTROL); + val &= ~(DSI_PAD_CONTROL_PAD_PDIO(0x3) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0x1)); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + + if (tegra_dsi_exit_ulpm(dsi) < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to exit ulpm\n"); + goto fail; + } + } + break; + case DSI_NO_SUSPEND: + break; + default: + dev_err(&dc->ndev->dev, "DSI suspend aggressivenes" + "is not supported.\n"); + } + + dsi->enabled = true; + dsi->host_suspended = false; + tegra_dsi_start_dc_stream(dc, dsi); fail: return err; } @@ -3071,7 +3273,7 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc) tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); if (dsi->info.power_saving_suspend) { - if (tegra_dsi_deep_sleep(dc, dsi) < 0) { + if (tegra_dsi_deep_sleep(dc, dsi, DSI_SUSPEND_FULL) < 0) { dev_err(&dc->ndev->dev, "DSI failed to enter deep sleep\n"); goto fail; @@ -3124,7 +3326,7 @@ static void tegra_dc_dsi_suspend(struct tegra_dc *dc) } } - if (tegra_dsi_deep_sleep(dc, dsi) < 0) { + if (tegra_dsi_deep_sleep(dc, dsi, DSI_SUSPEND_FULL) < 0) { dev_err(&dc->ndev->dev, "DSI failed to enter deep sleep\n"); goto fail; diff --git a/drivers/video/tegra/dc/dsi.h b/drivers/video/tegra/dc/dsi.h index 18ea9c959e8d..bf54913a1794 100644 --- a/drivers/video/tegra/dc/dsi.h +++ b/drivers/video/tegra/dc/dsi.h @@ -1,18 +1,18 @@ /* - * drivers/video/tegra/dc/dsi.h - * - * Copyright (c) 2011, NVIDIA Corporation. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ + * drivers/video/tegra/dc/dsi.h + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ #ifndef __DRIVERS_VIDEO_TEGRA_DC_DSI_H__ #define __DRIVERS_VIDEO_TEGRA_DC_DSI_H__ diff --git a/drivers/video/tegra/dc/dsi_regs.h b/drivers/video/tegra/dc/dsi_regs.h index 203ac32bd92d..71045fcec29e 100644 --- a/drivers/video/tegra/dc/dsi_regs.h +++ b/drivers/video/tegra/dc/dsi_regs.h @@ -1,18 +1,18 @@ /* - * drivers/video/tegra/dc/dsi_regs.h - * - * Copyright (c) 2011, NVIDIA Corporation. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ + * drivers/video/tegra/dc/dsi_regs.h + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ #ifndef __DRIVERS_VIDEO_TEGRA_DC_DSI_REG_H__ #define __DRIVERS_VIDEO_TEGRA_DC_DSI_REG_H__ diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c index 8776c5393410..b1de7ab1dfb5 100644 --- a/drivers/video/tegra/dc/edid.c +++ b/drivers/video/tegra/dc/edid.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and diff --git a/drivers/video/tegra/dc/ext/Makefile b/drivers/video/tegra/dc/ext/Makefile index 19860ab5db11..16e4cdf43ebb 100644 --- a/drivers/video/tegra/dc/ext/Makefile +++ b/drivers/video/tegra/dc/ext/Makefile @@ -1,3 +1,5 @@ +GCOV_PROFILE := y +EXTRA_CFLAGS += -Idrivers/video/tegra/host obj-y += dev.o obj-y += util.o obj-y += cursor.o diff --git a/drivers/video/tegra/dc/ext/control.c b/drivers/video/tegra/dc/ext/control.c index 9caf3e11c16c..ed812be2eab5 100644 --- a/drivers/video/tegra/dc/ext/control.c +++ b/drivers/video/tegra/dc/ext/control.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/control.c * - * Copyright (C) 2011, NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * @@ -67,7 +67,7 @@ static int get_output_edid(struct tegra_dc_ext_control_output_edid *edid) struct tegra_dc *dc; size_t user_size = edid->size; struct tegra_dc_edid *dc_edid = NULL; - int ret; + int ret = 0; /* TODO: this should be more dynamic */ if (edid->handle > 2) @@ -262,10 +262,7 @@ int tegra_dc_ext_control_init(void) return ret; control->dev = device_create(tegra_dc_ext_class, - NULL, - tegra_dc_ext_devno, - NULL, - "tegra_dc_ctrl"); + NULL, tegra_dc_ext_devno, NULL, "tegra_dc_ctrl"); if (IS_ERR(control->dev)) { ret = PTR_ERR(control->dev); cdev_del(&control->cdev); diff --git a/drivers/video/tegra/dc/ext/cursor.c b/drivers/video/tegra/dc/ext/cursor.c index d8fa5fd8e6d9..970f38f5ac09 100644 --- a/drivers/video/tegra/dc/ext/cursor.c +++ b/drivers/video/tegra/dc/ext/cursor.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/cursor.c * - * Copyright (C) 2011, NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c index 04553e778390..f9c76f8f0d0d 100644 --- a/drivers/video/tegra/dc/ext/dev.c +++ b/drivers/video/tegra/dc/ext/dev.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/dev.c * - * Copyright (C) 2011-2012, NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * Some code based on fbdev extensions written by: @@ -27,11 +27,12 @@ #include <video/tegra_dc_ext.h> #include <mach/dc.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <mach/tegra_dc_ext.h> /* XXX ew */ #include "../dc_priv.h" +#include "../dc_config.h" /* XXX ew 2 */ #include "../../host/dev.h" /* XXX ew 3 */ @@ -172,10 +173,39 @@ void tegra_dc_ext_disable(struct tegra_dc_ext *ext) } } +int tegra_dc_ext_check_windowattr(struct tegra_dc_ext *ext, + struct tegra_dc_win *win) +{ + long *addr; + struct tegra_dc *dc = ext->dc; + + /* Check the window format */ + addr = tegra_dc_parse_feature(dc, win->idx, GET_WIN_FORMATS); + if (!test_bit(win->fmt, addr)) { + dev_err(&dc->ndev->dev, "Color format of window %d is" + " invalid.\n", win->idx); + goto fail; + } + + /* Check window size */ + addr = tegra_dc_parse_feature(dc, win->idx, GET_WIN_SIZE); + if (CHECK_SIZE(win->out_w, addr[MIN_WIDTH], addr[MAX_WIDTH]) || + CHECK_SIZE(win->out_h, addr[MIN_HEIGHT], addr[MAX_HEIGHT])) { + dev_err(&dc->ndev->dev, "Size of window %d is" + " invalid.\n", win->idx); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext, struct tegra_dc_win *win, const struct tegra_dc_ext_flip_win *flip_win) { + int err = 0; struct tegra_dc_ext_win *ext_win = &ext->win[win->idx]; if (flip_win->handle[TEGRA_DC_Y] == NULL) { @@ -195,6 +225,10 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext, win->flags |= TEGRA_WIN_FLAG_INVERT_H; if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_INVERT_V) win->flags |= TEGRA_WIN_FLAG_INVERT_V; + if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_GLOBAL_ALPHA) + win->global_alpha = flip_win->attr.global_alpha; + else + win->global_alpha = 255; win->fmt = flip_win->attr.pixformat; win->x.full = flip_win->attr.x; win->y.full = flip_win->attr.y; @@ -223,6 +257,11 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext, win->stride = flip_win->attr.stride; win->stride_uv = flip_win->attr.stride_uv; + err = tegra_dc_ext_check_windowattr(ext, win); + if (err < 0) + dev_err(&ext->dc->ndev->dev, + "Window atrributes are invalid.\n"); + if ((s32)flip_win->attr.pre_syncpt_id >= 0) { nvhost_syncpt_wait_timeout( &nvhost_get_host(ext->dc->ndev)->syncpt, @@ -408,7 +447,7 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user, { struct tegra_dc_ext *ext = user->ext; struct tegra_dc_ext_flip_data *data; - int work_index; + int work_index = -1; int i, ret = 0; #ifdef CONFIG_ANDROID @@ -520,6 +559,10 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user, atomic_inc(&ext->win[work_index].nr_pending_flips); } + if (work_index < 0) { + ret = -EINVAL; + goto unlock; + } queue_work(ext->win[work_index].flip_wq, &data->work); unlock_windows_for_flip(user, args); @@ -669,6 +712,21 @@ static int tegra_dc_ext_get_status(struct tegra_dc_ext_user *user, return 0; } +static int tegra_dc_ext_get_feature(struct tegra_dc_ext_user *user, + struct tegra_dc_ext_feature *feature) +{ + struct tegra_dc *dc = user->ext->dc; + struct tegra_dc_feature *table = dc->feature; + + if (dc->enabled && feature->entries) { + feature->length = table->num_entries; + memcpy(feature->entries, table->entries, table->num_entries * + sizeof(struct tegra_dc_feature_entry)); + } + + return 0; +} + static long tegra_dc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -766,6 +824,22 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd, return tegra_dc_ext_set_lut(user, &args); } + case TEGRA_DC_EXT_GET_FEATURES: + { + struct tegra_dc_ext_feature args; + int ret; + + if (copy_from_user(&args, user_arg, sizeof(args))) + return -EFAULT; + + ret = tegra_dc_ext_get_feature(user, &args); + + if (copy_to_user(user_arg, &args, sizeof(args))) + return -EFAULT; + + return ret; + } + default: return -EINVAL; } diff --git a/drivers/video/tegra/dc/ext/events.c b/drivers/video/tegra/dc/ext/events.c index 150a1501fced..577d056e2436 100644 --- a/drivers/video/tegra/dc/ext/events.c +++ b/drivers/video/tegra/dc/ext/events.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/events.c * - * Copyright (C) 2011, NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h index 95a637d5a52a..f68c7d5c93c2 100644 --- a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h +++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h * - * Copyright (C) 2011, NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * @@ -25,7 +25,7 @@ #include <linux/poll.h> #include <mach/dc.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <video/tegra_dc_ext.h> diff --git a/drivers/video/tegra/dc/ext/util.c b/drivers/video/tegra/dc/ext/util.c index 747085579f15..bd8b3c012c0e 100644 --- a/drivers/video/tegra/dc/ext/util.c +++ b/drivers/video/tegra/dc/ext/util.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/util.c * - * Copyright (C) 2011, NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * @@ -20,7 +20,7 @@ #include <linux/types.h> #include <mach/dc.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> /* ugh */ #include "../../nvmap/nvmap.h" diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 6a7cfaea6672..79478ea48f83 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -1213,22 +1213,6 @@ static int tegra_dc_calc_clock_per_frame(const struct fb_videomode *mode) (mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len); } -static bool tegra_dc_hdmi_mode_equal(const struct fb_videomode *mode1, - const struct fb_videomode *mode2) -{ - int clock_per_frame1 = tegra_dc_calc_clock_per_frame(mode1); - int clock_per_frame2 = tegra_dc_calc_clock_per_frame(mode2); - - /* allows up to 1Hz of pixclock difference */ - return (clock_per_frame1 == clock_per_frame2 && - mode1->xres == mode2->xres && - mode1->yres == mode2->yres && - mode1->vmode == mode2->vmode && - (mode1->pixclock == mode2->pixclock || - (abs(PICOS2KHZ(mode1->pixclock) - - PICOS2KHZ(mode2->pixclock)) * - 1000 / clock_per_frame1 <= 1))); -} static bool tegra_dc_hdmi_valid_pixclock(const struct tegra_dc *dc, const struct fb_videomode *mode) @@ -1515,10 +1499,10 @@ static void tegra_dc_hdmi_resume(struct tegra_dc *dc) tegra_nvhdcp_resume(hdmi->nvhdcp); } +#ifdef CONFIG_SWITCH static ssize_t underscan_show(struct device *dev, struct device_attribute *attr, char *buf) { -#ifdef CONFIG_SWITCH struct tegra_dc_hdmi_data *hdmi = container_of(dev_get_drvdata(dev), struct tegra_dc_hdmi_data, hpd_switch); @@ -1526,19 +1510,19 @@ static ssize_t underscan_show(struct device *dev, return sprintf(buf, "%d\n", tegra_edid_underscan_supported(hdmi->edid)); else return 0; -#else - return 0; -#endif } static DEVICE_ATTR(underscan, S_IRUGO | S_IWUSR, underscan_show, NULL); +#endif static int tegra_dc_hdmi_init(struct tegra_dc *dc) { struct tegra_dc_hdmi_data *hdmi; struct resource *res; struct resource *base_res; +#ifdef CONFIG_SWITCH int ret; +#endif void __iomem *base; struct clk *clk = NULL; struct clk *disp1_clk = NULL; @@ -1685,8 +1669,10 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc) return 0; +#ifdef CONFIG_TEGRA_NVHDCP err_edid_destroy: tegra_edid_destroy(hdmi->edid); +#endif err_free_irq: free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc); err_put_clock: @@ -2082,6 +2068,11 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi) avi.r = HDMI_AVI_R_SAME; + if ((dc->mode.h_active == 720) && ((dc->mode.v_active == 480) || (dc->mode.v_active == 576))) + tegra_dc_writel(dc, 0x00101010, DC_DISP_BORDER_COLOR); + else + tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); + if (dc->mode.v_active == 480) { if (dc->mode.h_active == 640) { avi.m = HDMI_AVI_M_4_3; @@ -2114,9 +2105,12 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi) (dc->mode.v_active == 2205 && dc->mode.stereo_mode)) { /* VIC for both 1080p and 1080p 3D mode */ avi.m = HDMI_AVI_M_16_9; - if (dc->mode.h_front_porch == 88) - avi.vic = 16; /* 60 Hz */ - else if (dc->mode.h_front_porch == 528) + if (dc->mode.h_front_porch == 88) { + if (dc->mode.pclk > 74250000) + avi.vic = 16; /* 60 Hz */ + else + avi.vic = 34; /* 30 Hz */ + } else if (dc->mode.h_front_porch == 528) avi.vic = 31; /* 50 Hz */ else avi.vic = 32; /* 24 Hz */ @@ -2277,10 +2271,16 @@ static void tegra_dc_hdmi_enable(struct tegra_dc *dc) VSYNC_WINDOW_ENABLE, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); - tegra_hdmi_writel(hdmi, - (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | - ARM_VIDEO_RANGE_LIMITED, - HDMI_NV_PDISP_INPUT_CONTROL); + if ((dc->mode.h_active == 720) && ((dc->mode.v_active == 480) || (dc->mode.v_active == 576))) + tegra_hdmi_writel(hdmi, + (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | + ARM_VIDEO_RANGE_FULL, + HDMI_NV_PDISP_INPUT_CONTROL); + else + tegra_hdmi_writel(hdmi, + (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | + ARM_VIDEO_RANGE_LIMITED, + HDMI_NV_PDISP_INPUT_CONTROL); clk_disable(hdmi->disp1_clk); clk_disable(hdmi->disp2_clk); diff --git a/drivers/video/tegra/dc/hdmi.h b/drivers/video/tegra/dc/hdmi.h index 702ab16e87f0..5b4c42a31ffa 100644 --- a/drivers/video/tegra/dc/hdmi.h +++ b/drivers/video/tegra/dc/hdmi.h @@ -6,7 +6,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and diff --git a/drivers/video/tegra/dc/hdmi_reg.h b/drivers/video/tegra/dc/hdmi_reg.h index 0bdda43199e3..4a5fdcb2aaa6 100644 --- a/drivers/video/tegra/dc/hdmi_reg.h +++ b/drivers/video/tegra/dc/hdmi_reg.h @@ -4,6 +4,8 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. diff --git a/drivers/video/tegra/dc/lut.c b/drivers/video/tegra/dc/lut.c new file mode 100644 index 000000000000..7ce8fc6768a0 --- /dev/null +++ b/drivers/video/tegra/dc/lut.c @@ -0,0 +1,130 @@ +/* + * drivers/video/tegra/dc/lut.c + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/types.h> +#include <mach/dc.h> + +#include "dc_reg.h" +#include "dc_priv.h" + +void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut) +{ + int i; + for (i = 0; i < 256; i++) + lut->r[i] = lut->g[i] = lut->b[i] = (u8)i; +} + +static int tegra_dc_loop_lut(struct tegra_dc *dc, + struct tegra_dc_win *win, + int(*lambda)(struct tegra_dc *dc, int i, u32 rgb)) +{ + struct tegra_dc_lut *lut = &win->lut; + struct tegra_dc_lut *global_lut = &dc->fb_lut; + int i; + for (i = 0; i < 256; i++) { + + u32 r = (u32)lut->r[i]; + u32 g = (u32)lut->g[i]; + u32 b = (u32)lut->b[i]; + + if (!(win->ppflags & TEGRA_WIN_PPFLAG_CP_FBOVERRIDE)) { + r = (u32)global_lut->r[r]; + g = (u32)global_lut->g[g]; + b = (u32)global_lut->b[b]; + } + + if (!lambda(dc, i, r | (g<<8) | (b<<16))) + return 0; + } + return 1; +} + +static int tegra_dc_lut_isdefaults_lambda(struct tegra_dc *dc, int i, u32 rgb) +{ + if (rgb != (i | (i<<8) | (i<<16))) + return 0; + return 1; +} + +static int tegra_dc_set_lut_setreg_lambda(struct tegra_dc *dc, int i, u32 rgb) +{ + tegra_dc_writel(dc, rgb, DC_WIN_COLOR_PALETTE(i)); + return 1; +} + +void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win *win) +{ + unsigned long val = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + + tegra_dc_loop_lut(dc, win, tegra_dc_set_lut_setreg_lambda); + + if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) + val |= CP_ENABLE; + else + val &= ~CP_ENABLE; + + tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); +} + +static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr) +{ + struct tegra_dc_win *win = &dc->windows[win_idx]; + + mutex_lock(&dc->lock); + + if (!dc->enabled) { + mutex_unlock(&dc->lock); + return -EFAULT; + } + + if (fbovr > 0) + win->ppflags |= TEGRA_WIN_PPFLAG_CP_FBOVERRIDE; + else if (fbovr == 0) + win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_FBOVERRIDE; + + if (!tegra_dc_loop_lut(dc, win, tegra_dc_lut_isdefaults_lambda)) + win->ppflags |= TEGRA_WIN_PPFLAG_CP_ENABLE; + else + win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_ENABLE; + + tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, + DC_CMD_DISPLAY_WINDOW_HEADER); + + tegra_dc_set_lut(dc, win); + + mutex_unlock(&dc->lock); + + tegra_dc_update_windows(&win, 1); + + return 0; +} + +int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride) +{ + if (win_idx > -1) + return tegra_dc_update_winlut(dc, win_idx, fboveride); + + for (win_idx = 0; win_idx < DC_N_WINDOWS; win_idx++) { + int err = tegra_dc_update_winlut(dc, win_idx, fboveride); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL(tegra_dc_update_lut); + diff --git a/drivers/video/tegra/dc/mode.c b/drivers/video/tegra/dc/mode.c new file mode 100644 index 000000000000..49cc5f5abd53 --- /dev/null +++ b/drivers/video/tegra/dc/mode.c @@ -0,0 +1,318 @@ +/* + * drivers/video/tegra/dc/mode.c + * + * Copyright (C) 2010 Google, Inc. + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/types.h> +#include <linux/clk.h> + +#include <mach/clk.h> +#include <mach/dc.h> + +#include "dc_reg.h" +#include "dc_priv.h" + +/* return non-zero if constraint is violated */ +static int calc_h_ref_to_sync(const struct tegra_dc_mode *mode, int *href) +{ + long a, b; + + /* Constraint 5: H_REF_TO_SYNC >= 0 */ + a = 0; + + /* Constraint 6: H_FRONT_PORT >= (H_REF_TO_SYNC + 1) */ + b = mode->h_front_porch - 1; + + /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11 */ + if (a + mode->h_sync_width + mode->h_back_porch <= 11) + a = 1 + 11 - mode->h_sync_width - mode->h_back_porch; + /* check Constraint 1 and 6 */ + if (a > b) + return 1; + + /* Constraint 4: H_SYNC_WIDTH >= 1 */ + if (mode->h_sync_width < 1) + return 4; + + /* Constraint 7: H_DISP_ACTIVE >= 16 */ + if (mode->h_active < 16) + return 7; + + if (href) { + if (b > a && a % 2) + *href = a + 1; /* use smallest even value */ + else + *href = a; /* even or only possible value */ + } + + return 0; +} + +static int calc_v_ref_to_sync(const struct tegra_dc_mode *mode, int *vref) +{ + long a; + a = 1; /* Constraint 5: V_REF_TO_SYNC >= 1 */ + + /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1 */ + if (a + mode->v_sync_width + mode->v_back_porch <= 1) + a = 1 + 1 - mode->v_sync_width - mode->v_back_porch; + + /* Constraint 6 */ + if (mode->v_front_porch < a + 1) + a = mode->v_front_porch - 1; + + /* Constraint 4: V_SYNC_WIDTH >= 1 */ + if (mode->v_sync_width < 1) + return 4; + + /* Constraint 7: V_DISP_ACTIVE >= 16 */ + if (mode->v_active < 16) + return 7; + + if (vref) + *vref = a; + return 0; +} + +static int calc_ref_to_sync(struct tegra_dc_mode *mode) +{ + int ret; + ret = calc_h_ref_to_sync(mode, &mode->h_ref_to_sync); + if (ret) + return ret; + ret = calc_v_ref_to_sync(mode, &mode->v_ref_to_sync); + if (ret) + return ret; + + return 0; +} + +static bool check_ref_to_sync(struct tegra_dc_mode *mode) +{ + /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11. */ + if (mode->h_ref_to_sync + mode->h_sync_width + mode->h_back_porch <= 11) + return false; + + /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1. */ + if (mode->v_ref_to_sync + mode->v_sync_width + mode->v_back_porch <= 1) + return false; + + /* Constraint 3: V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH > 1 + * (vertical blank). */ + if (mode->v_front_porch + mode->v_sync_width + mode->v_back_porch <= 1) + return false; + + /* Constraint 4: V_SYNC_WIDTH >= 1; H_SYNC_WIDTH >= 1. */ + if (mode->v_sync_width < 1 || mode->h_sync_width < 1) + return false; + + /* Constraint 5: V_REF_TO_SYNC >= 1; H_REF_TO_SYNC >= 0. */ + if (mode->v_ref_to_sync < 1 || mode->h_ref_to_sync < 0) + return false; + + /* Constraint 6: V_FRONT_PORT >= (V_REF_TO_SYNC + 1); + * H_FRONT_PORT >= (H_REF_TO_SYNC + 1). */ + if (mode->v_front_porch < mode->v_ref_to_sync + 1 || + mode->h_front_porch < mode->h_ref_to_sync + 1) + return false; + + /* Constraint 7: H_DISP_ACTIVE >= 16; V_DISP_ACTIVE >= 16. */ + if (mode->h_active < 16 || mode->v_active < 16) + return false; + + return true; +} + +/* return in 1000ths of a Hertz */ +int tegra_dc_calc_refresh(const struct tegra_dc_mode *m) +{ + long h_total, v_total, refresh; + h_total = m->h_active + m->h_front_porch + m->h_back_porch + + m->h_sync_width; + v_total = m->v_active + m->v_front_porch + m->v_back_porch + + m->v_sync_width; + refresh = m->pclk / h_total; + refresh *= 1000; + refresh /= v_total; + return refresh; +} + +#ifdef DEBUG +static void print_mode(struct tegra_dc *dc, + const struct tegra_dc_mode *mode, const char *note) +{ + if (mode) { + int refresh = tegra_dc_calc_refresh(mode); + dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n", + note ? note : "", + mode->h_active, mode->v_active, + refresh / 1000, refresh % 1000, + mode->pclk); + } +} +#else /* !DEBUG */ +static inline void print_mode(struct tegra_dc *dc, + const struct tegra_dc_mode *mode, const char *note) { } +#endif /* DEBUG */ + +int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) +{ + unsigned long val; + unsigned long rate; + unsigned long div; + unsigned long pclk; + + print_mode(dc, mode, __func__); + + /* use default EMC rate when switching modes */ + dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc); + tegra_dc_program_bandwidth(dc, true); + + tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); + tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16), + DC_DISP_REF_TO_SYNC); + tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16), + DC_DISP_SYNC_WIDTH); + tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16), + DC_DISP_BACK_PORCH); + tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16), + DC_DISP_DISP_ACTIVE); + tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16), + DC_DISP_FRONT_PORCH); + + tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL, + DC_DISP_DATA_ENABLE_OPTIONS); + + /* TODO: MIPI/CRT/HDMI clock cals */ + + val = DISP_DATA_FORMAT_DF1P1C; + + if (dc->out->align == TEGRA_DC_ALIGN_MSB) + val |= DISP_DATA_ALIGNMENT_MSB; + else + val |= DISP_DATA_ALIGNMENT_LSB; + + if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE) + val |= DISP_DATA_ORDER_RED_BLUE; + else + val |= DISP_DATA_ORDER_BLUE_RED; + + tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL); + + rate = tegra_dc_clk_get_rate(dc); + + pclk = tegra_dc_pclk_round_rate(dc, mode->pclk); + trace_printk("%s:pclk=%ld\n", dc->ndev->name, pclk); + if (pclk < (mode->pclk / 100 * 99) || + pclk > (mode->pclk / 100 * 109)) { + dev_err(&dc->ndev->dev, + "can't divide %ld clock to %d -1/+9%% %ld %d %d\n", + rate, mode->pclk, + pclk, (mode->pclk / 100 * 99), + (mode->pclk / 100 * 109)); + return -EINVAL; + } + + div = (rate * 2 / pclk) - 2; + trace_printk("%s:div=%ld\n", dc->ndev->name, div); + + tegra_dc_writel(dc, 0x00010001, + DC_DISP_SHIFT_CLOCK_OPTIONS); + tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div), + DC_DISP_DISP_CLOCK_CONTROL); + +#ifdef CONFIG_SWITCH + switch_set_state(&dc->modeset_switch, + (mode->h_active << 16) | mode->v_active); +#endif + + tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + + print_mode_info(dc, dc->mode); + return 0; +} + +int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode) +{ + memcpy(&dc->mode, mode, sizeof(dc->mode)); + + print_mode(dc, mode, __func__); + + return 0; +} +EXPORT_SYMBOL(tegra_dc_set_mode); + +int tegra_dc_set_fb_mode(struct tegra_dc *dc, + const struct fb_videomode *fbmode, bool stereo_mode) +{ + struct tegra_dc_mode mode; + + if (!fbmode->pixclock) + return -EINVAL; + + mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000; + mode.h_sync_width = fbmode->hsync_len; + mode.v_sync_width = fbmode->vsync_len; + mode.h_back_porch = fbmode->left_margin; + mode.v_back_porch = fbmode->upper_margin; + mode.h_active = fbmode->xres; + mode.v_active = fbmode->yres; + mode.h_front_porch = fbmode->right_margin; + mode.v_front_porch = fbmode->lower_margin; + mode.stereo_mode = stereo_mode; + if (dc->out->type == TEGRA_DC_OUT_HDMI) { + /* HDMI controller requires h_ref=1, v_ref=1 */ + mode.h_ref_to_sync = 1; + mode.v_ref_to_sync = 1; + } else { + calc_ref_to_sync(&mode); + } + if (!check_ref_to_sync(&mode)) { + dev_err(&dc->ndev->dev, + "Display timing doesn't meet restrictions.\n"); + return -EINVAL; + } + dev_info(&dc->ndev->dev, "Using mode %dx%d pclk=%d href=%d vref=%d\n", + mode.h_active, mode.v_active, mode.pclk, + mode.h_ref_to_sync, mode.v_ref_to_sync + ); + +#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT + /* Double the pixel clock and update v_active only for + * frame packed mode */ + if (mode.stereo_mode) { + mode.pclk *= 2; + /* total v_active = yres*2 + activespace */ + mode.v_active = fbmode->yres * 2 + + fbmode->vsync_len + + fbmode->upper_margin + + fbmode->lower_margin; + } +#endif + + mode.flags = 0; + + if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT)) + mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC; + + if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT)) + mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC; + + return tegra_dc_set_mode(dc, &mode); +} +EXPORT_SYMBOL(tegra_dc_set_fb_mode); diff --git a/drivers/video/tegra/dc/nvhdcp.c b/drivers/video/tegra/dc/nvhdcp.c index 263de07a3da0..3566e2bd33b5 100644 --- a/drivers/video/tegra/dc/nvhdcp.c +++ b/drivers/video/tegra/dc/nvhdcp.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/nvhdcp.c * - * Copyright (c) 2010-2011, NVIDIA Corporation. + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and diff --git a/drivers/video/tegra/dc/nvhdcp.h b/drivers/video/tegra/dc/nvhdcp.h index 90ea0be36d19..ce4c7f806745 100644 --- a/drivers/video/tegra/dc/nvhdcp.h +++ b/drivers/video/tegra/dc/nvhdcp.h @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/nvhdcp.h * - * Copyright (c) 2010-2011, NVIDIA Corporation. + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c index 65d518759243..e3058b596f69 100644 --- a/drivers/video/tegra/dc/nvsd.c +++ b/drivers/video/tegra/dc/nvsd.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/nvsd.c * - * Copyright (c) 2010-2012, NVIDIA Corporation. + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -378,7 +378,10 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); if (val & SD_ENABLE_NORMAL) - i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); + if (settings->phase_in_adjustments) + i = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES); + else + i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); else i = 0; /* 0 values for RGB = 1.0, i.e. non-affected */ diff --git a/drivers/video/tegra/dc/nvsd.h b/drivers/video/tegra/dc/nvsd.h index f7fc4a1ead6e..d6f9dc148320 100644 --- a/drivers/video/tegra/dc/nvsd.h +++ b/drivers/video/tegra/dc/nvsd.h @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/nvsd.h * - * Copyright (c) 2010-2011, NVIDIA Corporation. + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c index 6787ec38d38d..b4097d98a9ba 100644 --- a/drivers/video/tegra/dc/rgb.c +++ b/drivers/video/tegra/dc/rgb.c @@ -4,6 +4,8 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. @@ -90,7 +92,7 @@ static const u32 tegra_dc_rgb_disable_pintable[] = { DC_COM_PIN_OUTPUT_SELECT6, 0x00000000, }; -void tegra_dc_rgb_enable(struct tegra_dc *dc) +static void tegra_dc_rgb_enable(struct tegra_dc *dc) { int i; u32 out_sel_pintable[ARRAY_SIZE(tegra_dc_rgb_enable_out_sel_pintable)]; @@ -150,7 +152,7 @@ void tegra_dc_rgb_enable(struct tegra_dc *dc) tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); } -void tegra_dc_rgb_disable(struct tegra_dc *dc) +static void tegra_dc_rgb_disable(struct tegra_dc *dc) { tegra_dc_writel(dc, 0x00000000, DC_CMD_DISPLAY_POWER_CONTROL); diff --git a/drivers/video/tegra/dc/window.c b/drivers/video/tegra/dc/window.c new file mode 100644 index 000000000000..5161dd4f7003 --- /dev/null +++ b/drivers/video/tegra/dc/window.c @@ -0,0 +1,466 @@ +/* + * drivers/video/tegra/dc/window.c + * + * Copyright (C) 2010 Google, Inc. + * + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/err.h> +#include <linux/types.h> +#include <mach/dc.h> + +#include "dc_reg.h" +#include "dc_config.h" +#include "dc_priv.h" + +static int no_vsync; + +module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR); + +static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[], + int n) +{ + int i; + + for (i = 0; i < n; i++) { + if (windows[i]->dirty) + return false; + } + + return true; +} + +static int get_topmost_window(u32 *depths, unsigned long *wins) +{ + int idx, best = -1; + + for_each_set_bit(idx, wins, DC_N_WINDOWS) { + if (best == -1 || depths[idx] < depths[best]) + best = idx; + } + clear_bit(best, wins); + return best; +} + +static u32 blend_topwin(u32 flags) +{ + if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE) + return BLEND(NOKEY, ALPHA, 0xff, 0xff); + else if (flags & TEGRA_WIN_FLAG_BLEND_PREMULT) + return BLEND(NOKEY, PREMULT, 0xff, 0xff); + else + return BLEND(NOKEY, FIX, 0xff, 0xff); +} + +static u32 blend_2win(int idx, unsigned long behind_mask, u32* flags, int xy) +{ + int other; + + for (other = 0; other < DC_N_WINDOWS; other++) { + if (other != idx && (xy-- == 0)) + break; + } + if (BIT(other) & behind_mask) + return blend_topwin(flags[idx]); + else if (flags[other]) + return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); + else + return BLEND(NOKEY, FIX, 0x00, 0x00); +} + +static u32 blend_3win(int idx, unsigned long behind_mask, u32* flags) +{ + unsigned long infront_mask; + int first; + + infront_mask = ~(behind_mask | BIT(idx)); + infront_mask &= (BIT(DC_N_WINDOWS) - 1); + first = ffs(infront_mask) - 1; + + if (!infront_mask) + return blend_topwin(flags[idx]); + else if (behind_mask && first != -1 && flags[first]) + return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); + else + return BLEND(NOKEY, FIX, 0x0, 0x0); +} + +static void tegra_dc_set_blending(struct tegra_dc *dc, + struct tegra_dc_blend *blend) +{ + unsigned long mask = BIT(DC_N_WINDOWS) - 1; + + while (mask) { + int idx = get_topmost_window(blend->z, &mask); + + tegra_dc_writel(dc, WINDOW_A_SELECT << idx, + DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), + DC_WIN_BLEND_NOKEY); + tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), + DC_WIN_BLEND_1WIN); + tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0), + DC_WIN_BLEND_2WIN_X); + tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1), + DC_WIN_BLEND_2WIN_Y); + tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags), + DC_WIN_BLEND_3WIN_XY); + } +} + +/* does not support syncing windows on multiple dcs in one call */ +int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n) +{ + int ret; + if (n < 1 || n > DC_N_WINDOWS) + return -EINVAL; + + if (!windows[0]->dc->enabled) + return -EFAULT; + +#ifdef CONFIG_TEGRA_SIMULATION_PLATFORM + /* Don't want to timeout on simulator */ + ret = wait_event_interruptible(windows[0]->dc->wq, + tegra_dc_windows_are_clean(windows, n)); +#else + trace_printk("%s:Before wait_event_interruptible_timeout\n", + windows[0]->dc->ndev->name); + ret = wait_event_interruptible_timeout(windows[0]->dc->wq, + tegra_dc_windows_are_clean(windows, n), + HZ); + trace_printk("%s:After wait_event_interruptible_timeout\n", + windows[0]->dc->ndev->name); +#endif + return ret; +} +EXPORT_SYMBOL(tegra_dc_sync_windows); + +static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int, + bool v, unsigned Bpp) +{ + /* + * min(round((prescaled_size_in_pixels - 1) * 0x1000 / + * (post_scaled_size_in_pixels - 1)), MAX) + * Where the value of MAX is as follows: + * For V_DDA_INCREMENT: 15.0 (0xF000) + * For H_DDA_INCREMENT: 4.0 (0x4000) for 4 Bytes/pix formats. + * 8.0 (0x8000) for 2 Bytes/pix formats. + */ + + fixed20_12 out = dfixed_init(out_int); + u32 dda_inc; + int max; + + if (v) { + max = 15; + } else { + switch (Bpp) { + default: + WARN_ON_ONCE(1); + /* fallthrough */ + case 4: + max = 4; + break; + case 2: + max = 8; + break; + } + } + + out.full = max_t(u32, out.full - dfixed_const(1), dfixed_const(1)); + in.full -= dfixed_const(1); + + dda_inc = dfixed_div(in, out); + + dda_inc = min_t(u32, dda_inc, dfixed_const(max)); + + return dda_inc; +} + +static inline u32 compute_initial_dda(fixed20_12 in) +{ + return dfixed_frac(in); +} + +/* does not support updating windows on multiple dcs in one call */ +int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) +{ + struct tegra_dc *dc; + unsigned long update_mask = GENERAL_ACT_REQ; + unsigned long val; + bool update_blend = false; + int i; + + dc = windows[0]->dc; + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { + /* Acquire one_shot_lock to avoid race condition between + * cancellation of old delayed work and schedule of new + * delayed work. */ + mutex_lock(&dc->one_shot_lock); + cancel_delayed_work_sync(&dc->one_shot_work); + } + mutex_lock(&dc->lock); + + if (!dc->enabled) { + mutex_unlock(&dc->lock); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + mutex_unlock(&dc->one_shot_lock); + return -EFAULT; + } + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + + if (no_vsync) + tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, + DC_CMD_STATE_ACCESS); + else + tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, + DC_CMD_STATE_ACCESS); + + for (i = 0; i < n; i++) { + struct tegra_dc_win *win = windows[i]; + unsigned h_dda; + unsigned v_dda; + fixed20_12 h_offset, v_offset; + bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0; + bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0; + bool yuv = tegra_dc_is_yuv(win->fmt); + bool yuvp = tegra_dc_is_yuv_planar(win->fmt); + unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8; + /* Bytes per pixel of bandwidth, used for dda_inc calculation */ + unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1); + const bool filter_h = win_use_h_filter(dc, win); + const bool filter_v = win_use_v_filter(dc, win); + + if (win->z != dc->blend.z[win->idx]) { + dc->blend.z[win->idx] = win->z; + update_blend = true; + } + if ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) != + dc->blend.flags[win->idx]) { + dc->blend.flags[win->idx] = + win->flags & TEGRA_WIN_BLEND_FLAGS_MASK; + update_blend = true; + } + + tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx, + DC_CMD_DISPLAY_WINDOW_HEADER); + + if (!no_vsync) + update_mask |= WIN_A_ACT_REQ << win->idx; + + if (!WIN_IS_ENABLED(win)) { + dc->windows[i].dirty = 1; + tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS); + continue; + } + + tegra_dc_writel(dc, win->fmt & 0x1f, DC_WIN_COLOR_DEPTH); + tegra_dc_writel(dc, win->fmt >> 6, DC_WIN_BYTE_SWAP); + + tegra_dc_writel(dc, + V_POSITION(win->out_y) | H_POSITION(win->out_x), + DC_WIN_POSITION); + tegra_dc_writel(dc, + V_SIZE(win->out_h) | H_SIZE(win->out_w), + DC_WIN_SIZE); + + if (tegra_dc_feature_has_scaling(dc, win->idx)) { + tegra_dc_writel(dc, + V_PRESCALED_SIZE(dfixed_trunc(win->h)) | + H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp), + DC_WIN_PRESCALED_SIZE); + + h_dda = compute_dda_inc(win->w, win->out_w, false, + Bpp_bw); + v_dda = compute_dda_inc(win->h, win->out_h, true, + Bpp_bw); + tegra_dc_writel(dc, V_DDA_INC(v_dda) | + H_DDA_INC(h_dda), DC_WIN_DDA_INCREMENT); + h_dda = compute_initial_dda(win->x); + v_dda = compute_initial_dda(win->y); + tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); + tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); + } + + tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); + tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); + tegra_dc_writel(dc, (unsigned long)win->phys_addr, + DC_WINBUF_START_ADDR); + + if (!yuvp) { + tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE); + } else { + tegra_dc_writel(dc, + (unsigned long)win->phys_addr_u, + DC_WINBUF_START_ADDR_U); + tegra_dc_writel(dc, + (unsigned long)win->phys_addr_v, + DC_WINBUF_START_ADDR_V); + tegra_dc_writel(dc, + LINE_STRIDE(win->stride) | + UV_LINE_STRIDE(win->stride_uv), + DC_WIN_LINE_STRIDE); + } + + h_offset = win->x; + if (invert_h) { + h_offset.full += win->w.full - dfixed_const(1); + } + + v_offset = win->y; + if (invert_v) { + v_offset.full += win->h.full - dfixed_const(1); + } + + tegra_dc_writel(dc, dfixed_trunc(h_offset) * Bpp, + DC_WINBUF_ADDR_H_OFFSET); + tegra_dc_writel(dc, dfixed_trunc(v_offset), + DC_WINBUF_ADDR_V_OFFSET); + + if (tegra_dc_feature_has_tiling(dc, win->idx)) { + if (WIN_IS_TILED(win)) + tegra_dc_writel(dc, + DC_WIN_BUFFER_ADDR_MODE_TILE | + DC_WIN_BUFFER_ADDR_MODE_TILE_UV, + DC_WIN_BUFFER_ADDR_MODE); + else + tegra_dc_writel(dc, + DC_WIN_BUFFER_ADDR_MODE_LINEAR | + DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV, + DC_WIN_BUFFER_ADDR_MODE); + } + + val = WIN_ENABLE; + if (yuv) + val |= CSC_ENABLE; + else if (tegra_dc_fmt_bpp(win->fmt) < 24) + val |= COLOR_EXPAND; + + if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) + val |= CP_ENABLE; + + if (filter_h) + val |= H_FILTER_ENABLE; + if (filter_v) + val |= V_FILTER_ENABLE; + + if (invert_h) + val |= H_DIRECTION_DECREMENT; + if (invert_v) + val |= V_DIRECTION_DECREMENT; + + tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + if (win->global_alpha == 255) + tegra_dc_writel(dc, 0, DC_WIN_GLOBAL_ALPHA); + else + tegra_dc_writel(dc, GLOBAL_ALPHA_ENABLE | + win->global_alpha, DC_WIN_GLOBAL_ALPHA); +#endif + + win->dirty = no_vsync ? 0 : 1; + + dev_dbg(&dc->ndev->dev, "%s():idx=%d z=%d x=%d y=%d w=%d h=%d " + "out_x=%u out_y=%u out_w=%u out_h=%u " + "fmt=%d yuvp=%d Bpp=%u filter_h=%d filter_v=%d", + __func__, win->idx, win->z, + dfixed_trunc(win->x), dfixed_trunc(win->y), + dfixed_trunc(win->w), dfixed_trunc(win->h), + win->out_x, win->out_y, win->out_w, win->out_h, + win->fmt, yuvp, Bpp, filter_h, filter_v); + trace_printk("%s:win%u in:%ux%u out:%ux%u fmt=%d\n", + dc->ndev->name, win->idx, dfixed_trunc(win->w), + dfixed_trunc(win->h), win->out_w, win->out_h, win->fmt); + } + + if (update_blend) { + tegra_dc_set_blending(dc, &dc->blend); + for (i = 0; i < DC_N_WINDOWS; i++) { + if (!no_vsync) + dc->windows[i].dirty = 1; + update_mask |= WIN_A_ACT_REQ << i; + } + } + + tegra_dc_set_dynamic_emc(windows, n); + + tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL); + + tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS); + if (!no_vsync) { + set_bit(V_BLANK_FLIP, &dc->vblank_ref_count); + tegra_dc_unmask_interrupt(dc, + FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); + } else { + clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count); + tegra_dc_mask_interrupt(dc, + FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); + } + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + schedule_delayed_work(&dc->one_shot_work, + msecs_to_jiffies(dc->one_shot_delay_ms)); + + /* update EMC clock if calculated bandwidth has changed */ + tegra_dc_program_bandwidth(dc, false); + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + update_mask |= NC_HOST_TRIG; + + tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); + trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask); + + mutex_unlock(&dc->lock); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + mutex_unlock(&dc->one_shot_lock); + + return 0; +} +EXPORT_SYMBOL(tegra_dc_update_windows); + +void tegra_dc_trigger_windows(struct tegra_dc *dc) +{ + u32 val, i; + u32 completed = 0; + u32 dirty = 0; + + val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + for (i = 0; i < DC_N_WINDOWS; i++) { +#ifdef CONFIG_TEGRA_SIMULATION_PLATFORM + /* FIXME: this is not needed when the simulator + clears WIN_x_UPDATE bits as in HW */ + dc->windows[i].dirty = 0; + completed = 1; +#else + if (!(val & (WIN_A_ACT_REQ << i))) { + dc->windows[i].dirty = 0; + completed = 1; + } else { + dirty = 1; + } +#endif + } + + if (!dirty) { + if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)) + tegra_dc_mask_interrupt(dc, FRAME_END_INT); + } + + if (completed) + wake_up(&dc->wq); +} + diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index fc9befdf72c8..cb7525e049e9 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -6,7 +6,7 @@ * Colin Cross <ccross@android.com> * Travis Geiselbrecht <travis@palm.com> * - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -37,14 +37,16 @@ #include <mach/dc.h> #include <mach/fb.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> +#include <linux/console.h> + #include "host/dev.h" #include "nvmap/nvmap.h" #include "dc/dc_priv.h" /* Pad pitch to 16-byte boundary. */ -#define TEGRA_LINEAR_PITCH_ALIGNMENT 16 +#define TEGRA_LINEAR_PITCH_ALIGNMENT 32 struct tegra_fb_info { struct tegra_dc_win *win; @@ -155,8 +157,10 @@ static int tegra_fb_set_par(struct fb_info *info) #else FB_VMODE_STEREO_LEFT_RIGHT); #endif - tegra_dc_set_fb_mode(tegra_fb->win->dc, info->mode, stereo); + /* Reflect the mode change on dc */ + tegra_dc_disable(tegra_fb->win->dc); + tegra_dc_enable(tegra_fb->win->dc); tegra_fb->win->w.full = dfixed_const(info->mode->xres); tegra_fb->win->h.full = dfixed_const(info->mode->yres); @@ -245,51 +249,6 @@ static int tegra_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info) return 0; } -#if defined(CONFIG_FRAMEBUFFER_CONSOLE) -static void tegra_fb_flip_win(struct tegra_fb_info *tegra_fb) -{ - struct tegra_dc_win *win = tegra_fb->win; - struct fb_info *info = tegra_fb->info; - - win->x.full = dfixed_const(0); - win->y.full = dfixed_const(0); - win->w.full = dfixed_const(tegra_fb->xres); - win->h.full = dfixed_const(tegra_fb->yres); - - /* TODO: set to output res dc */ - win->out_x = 0; - win->out_y = 0; - win->out_w = tegra_fb->xres; - win->out_h = tegra_fb->yres; - win->z = 0; - win->phys_addr = info->fix.smem_start + - (info->var.yoffset * info->fix.line_length) + - (info->var.xoffset * (info->var.bits_per_pixel / 8)); - win->virt_addr = info->screen_base; - - win->phys_addr_u = 0; - win->phys_addr_v = 0; - win->stride = info->fix.line_length; - win->stride_uv = 0; - - switch (info->var.bits_per_pixel) { - default: - WARN_ON(1); - /* fall through */ - case 32: - tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8; - break; - case 16: - tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5; - break; - } - win->flags = TEGRA_WIN_FLAG_ENABLED; - - tegra_dc_update_windows(&tegra_fb->win, 1); - tegra_dc_sync_windows(&tegra_fb->win, 1); -} -#endif - static int tegra_fb_blank(int blank, struct fb_info *info) { struct tegra_fb_info *tegra_fb = info->par; @@ -466,13 +425,63 @@ static struct fb_ops tegra_fb_ops = { .fb_ioctl = tegra_fb_ioctl, }; +const struct fb_videomode *tegra_fb_find_best_mode( + struct fb_var_screeninfo *var, + struct list_head *head) +{ + struct list_head *pos; + struct fb_modelist *modelist; + struct fb_videomode *mode, *best = NULL; + int diff = 0; + + list_for_each(pos, head) { + int d; + + modelist = list_entry(pos, struct fb_modelist, list); + mode = &modelist->mode; + + if (mode->xres >= var->xres && mode->yres >= var->yres) { + d = (mode->xres - var->xres) + + (mode->yres - var->yres); + if (diff < d) { + diff = d; + best = mode; + } else if (diff == d && best && + mode->refresh > best->refresh) + best = mode; + } + } + return best; +} + +static int tegra_fb_activate_mode(struct tegra_fb_info *fb_info, + struct fb_var_screeninfo *var) +{ + int err; + struct fb_info *info = fb_info->info; + + var->activate |= FB_ACTIVATE_FORCE; + console_lock(); + info->flags |= FBINFO_MISC_USEREVENT; + err = fb_set_var(info, var); + info->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + if (err) + return err; + return 0; +} + void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, struct fb_monspecs *specs, bool (*mode_filter)(const struct tegra_dc *dc, struct fb_videomode *mode)) { - struct fb_event event; int i; + int ret = 0; + struct fb_event event; + struct fb_info *info = fb_info->info; + const struct fb_videomode *best_mode = NULL; + struct fb_var_screeninfo var = {0,}; mutex_lock(&fb_info->info->lock); fb_destroy_modedb(fb_info->info->monspecs.modedb); @@ -499,19 +508,56 @@ void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, sizeof(fb_info->info->monspecs)); fb_info->info->mode = specs->modedb; + /* Prepare a mode db */ for (i = 0; i < specs->modedb_len; i++) { - if (mode_filter) { - if (mode_filter(fb_info->win->dc, &specs->modedb[i])) - fb_add_videomode(&specs->modedb[i], + if (info->fbops->fb_check_var) { + struct fb_videomode m; + + /* Call mode filter to check mode */ + fb_videomode_to_var(&var, &specs->modedb[i]); + if (!(info->fbops->fb_check_var(&var, info))) { + fb_var_to_videomode(&m, &var); + fb_add_videomode(&m, &fb_info->info->modelist); + } } else { fb_add_videomode(&specs->modedb[i], &fb_info->info->modelist); } } + /* Get the best mode from modedb and apply on fb */ + var.xres = 0; + var.yres = 0; + best_mode = tegra_fb_find_best_mode(&var, &info->modelist); + + /* Update framebuffer with best mode */ + fb_videomode_to_var(&var, best_mode); + + /* TODO: Get proper way of getting rid of a 0 bpp */ + if (!var.bits_per_pixel) + var.bits_per_pixel = 32; + + memcpy(&info->var, &var, sizeof(struct fb_var_screeninfo)); + + ret = tegra_fb_activate_mode(fb_info, &var); + if (ret) + return; + event.info = fb_info->info; + +#ifdef CONFIG_FRAMEBUFFER_CONSOLE +/* Lock the console before sending the noti. Fbconsole + * on HDMI might be using console + */ + console_lock(); +#endif fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE +/* Unlock the console */ + console_unlock(); +#endif + mutex_unlock(&fb_info->info->lock); } @@ -527,6 +573,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, unsigned long fb_size = 0; unsigned long fb_phys = 0; int ret = 0; + unsigned stride; win = tegra_dc_get_window(dc, fb_data->win); if (!win) { @@ -560,6 +607,11 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, tegra_fb->valid = true; } + stride = tegra_dc_get_stride(dc, 0); + if (!stride) /* default to pad the stride to 16-byte boundary. */ + stride = round_up(info->fix.line_length, + TEGRA_LINEAR_PITCH_ALIGNMENT); + info->fbops = &tegra_fb_ops; info->pseudo_palette = pseudo_palette; info->screen_base = fb_base; @@ -574,9 +626,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, info->fix.smem_start = fb_phys; info->fix.smem_len = fb_size; info->fix.line_length = fb_data->xres * fb_data->bits_per_pixel / 8; - /* Pad the stride to 16-byte boundary. */ - info->fix.line_length = round_up(info->fix.line_length, - TEGRA_LINEAR_PITCH_ALIGNMENT); + info->fix.line_length = stride; info->var.xres = fb_data->xres; info->var.yres = fb_data->yres; diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile index 0180885af4d7..6e5e469897b3 100644 --- a/drivers/video/tegra/host/Makefile +++ b/drivers/video/tegra/host/Makefile @@ -1,4 +1,6 @@ GCOV_PROFILE := y +EXTRA_CFLAGS += -Idrivers/video/tegra/host + nvhost-objs = \ nvhost_acm.o \ nvhost_syncpt.o \ @@ -9,15 +11,18 @@ nvhost-objs = \ bus.o \ dev.o \ debug.o \ - bus_client.o + bus_client.o \ + chip_support.o \ + nvhost_memmgr.o obj-$(CONFIG_TEGRA_GRHOST) += mpe/ obj-$(CONFIG_TEGRA_GRHOST) += gr3d/ obj-$(CONFIG_TEGRA_GRHOST) += host1x/ obj-$(CONFIG_TEGRA_GRHOST) += t20/ obj-$(CONFIG_TEGRA_GRHOST) += t30/ -obj-$(CONFIG_TEGRA_GRHOST) += dsi/ obj-$(CONFIG_TEGRA_GRHOST) += gr2d/ obj-$(CONFIG_TEGRA_GRHOST) += isp/ obj-$(CONFIG_TEGRA_GRHOST) += vi/ obj-$(CONFIG_TEGRA_GRHOST) += nvhost.o + +obj-$(CONFIG_TEGRA_GRHOST_USE_NVMAP) += nvmap.o diff --git a/drivers/video/tegra/host/bus.c b/drivers/video/tegra/host/bus.c index 774aac7bd431..758a5ca4ad94 100644 --- a/drivers/video/tegra/host/bus.c +++ b/drivers/video/tegra/host/bus.c @@ -17,11 +17,14 @@ * */ +#include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/nvhost.h> +#include "bus.h" #include "dev.h" +struct nvhost_bus *nvhost_bus_inst; struct nvhost_master *nvhost; struct resource *nvhost_get_resource(struct nvhost_device *dev, @@ -72,12 +75,43 @@ int nvhost_get_irq_byname(struct nvhost_device *dev, const char *name) } EXPORT_SYMBOL_GPL(nvhost_get_irq_byname); +static struct nvhost_device_id *nvhost_bus_match_id(struct nvhost_device *dev, + struct nvhost_device_id *id_table) +{ + while (id_table->name[0]) { + if (strcmp(dev->name, id_table->name) == 0 + && dev->version == id_table->version) + return id_table; + id_table++; + } + return NULL; +} + +static int nvhost_bus_match(struct device *_dev, struct device_driver *drv) +{ + struct nvhost_device *dev = to_nvhost_device(_dev); + struct nvhost_driver *ndrv = to_nvhost_driver(drv); + + /* check if driver support multiple devices through id_table */ + if (ndrv->id_table) + return nvhost_bus_match_id(dev, ndrv->id_table) != NULL; + else /* driver does not support id_table */ + return !strncmp(dev->name, drv->name, strlen(drv->name)); +} + static int nvhost_drv_probe(struct device *_dev) { struct nvhost_driver *drv = to_nvhost_driver(_dev->driver); struct nvhost_device *dev = to_nvhost_device(_dev); - return drv->probe(dev); + if (drv && drv->probe) { + if (drv->id_table) + return drv->probe(dev, nvhost_bus_match_id(dev, drv->id_table)); + else + return drv->probe(dev, NULL); + } + else + return -ENODEV; } static int nvhost_drv_remove(struct device *_dev) @@ -98,7 +132,7 @@ static void nvhost_drv_shutdown(struct device *_dev) int nvhost_driver_register(struct nvhost_driver *drv) { - drv->driver.bus = &nvhost_bus_type; + drv->driver.bus = &nvhost_bus_inst->nvhost_bus_type; if (drv->probe) drv->driver.probe = nvhost_drv_probe; if (drv->remove) @@ -116,6 +150,23 @@ void nvhost_driver_unregister(struct nvhost_driver *drv) } EXPORT_SYMBOL_GPL(nvhost_driver_unregister); +int nvhost_add_devices(struct nvhost_device **devs, int num) +{ + int i, ret = 0; + + for (i = 0; i < num; i++) { + ret = nvhost_device_register(devs[i]); + if (ret) { + while (--i >= 0) + nvhost_device_unregister(devs[i]); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(nvhost_add_devices); + int nvhost_device_register(struct nvhost_device *dev) { int i, ret = 0; @@ -129,7 +180,7 @@ int nvhost_device_register(struct nvhost_device *dev) if (!dev->dev.parent && nvhost && nvhost->dev != dev) dev->dev.parent = &nvhost->dev->dev; - dev->dev.bus = &nvhost_bus_type; + dev->dev.bus = &nvhost_bus_inst->nvhost_bus_type; if (dev->id != -1) dev_set_name(&dev->dev, "%s.%d", dev->name, dev->id); @@ -194,13 +245,6 @@ void nvhost_device_unregister(struct nvhost_device *dev) } EXPORT_SYMBOL_GPL(nvhost_device_unregister); -static int nvhost_bus_match(struct device *_dev, struct device_driver *drv) -{ - struct nvhost_device *dev = to_nvhost_device(_dev); - - return !strncmp(dev->name, drv->name, strlen(drv->name)); -} - #ifdef CONFIG_PM_SLEEP static int nvhost_legacy_suspend(struct device *dev, pm_message_t mesg) @@ -528,13 +572,6 @@ static const struct dev_pm_ops nvhost_dev_pm_ops = { .runtime_idle = nvhost_pm_runtime_idle, }; -struct bus_type nvhost_bus_type = { - .name = "nvhost", - .match = nvhost_bus_match, - .pm = &nvhost_dev_pm_ops, -}; -EXPORT_SYMBOL(nvhost_bus_type); - static int set_parent(struct device *dev, void *data) { struct nvhost_device *ndev = to_nvhost_device(dev); @@ -549,21 +586,44 @@ int nvhost_bus_add_host(struct nvhost_master *host) nvhost = host; /* Assign host1x as parent to all devices in nvhost bus */ - bus_for_each_dev(&nvhost_bus_type, NULL, host, set_parent); + bus_for_each_dev(&nvhost_bus_inst->nvhost_bus_type, NULL, host, set_parent); return 0; } +struct nvhost_bus *nvhost_bus_get(void) +{ + return nvhost_bus_inst; +} int nvhost_bus_init(void) { int err; + struct nvhost_chip_support *chip_ops; pr_info("host1x bus init\n"); - err = bus_register(&nvhost_bus_type); + nvhost_bus_inst = kzalloc(sizeof(*nvhost_bus_inst), GFP_KERNEL); + if (nvhost_bus_inst == NULL) { + pr_err("%s: Cannot allocate nvhost_bus\n", __func__); + return -ENOMEM; + } + + chip_ops = kzalloc(sizeof(*chip_ops), GFP_KERNEL); + if (chip_ops == NULL) { + pr_err("%s: Cannot allocate nvhost_chip_support\n", __func__); + kfree(nvhost_bus_inst); + nvhost_bus_inst = NULL; + return -ENOMEM; + } + + nvhost_bus_inst->nvhost_bus_type.name = "nvhost"; + nvhost_bus_inst->nvhost_bus_type.match = nvhost_bus_match; + nvhost_bus_inst->nvhost_bus_type.pm = &nvhost_dev_pm_ops; + nvhost_bus_inst->nvhost_chip_ops = chip_ops; + + err = bus_register(&nvhost_bus_inst->nvhost_bus_type); return err; } postcore_initcall(nvhost_bus_init); - diff --git a/drivers/video/tegra/host/bus.h b/drivers/video/tegra/host/bus.h new file mode 100644 index 000000000000..99f820335d60 --- /dev/null +++ b/drivers/video/tegra/host/bus.h @@ -0,0 +1,38 @@ +/* + * drivers/video/tegra/host/bus.h + * + * Tegra Graphics Host bus API header + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_BUS_H +#define __NVHOST_BUS_H + +#include <linux/types.h> +#include <linux/device.h> + +#include "chip_support.h" + +struct nvhost_bus { + struct nvhost_chip_support *nvhost_chip_ops; + struct bus_type nvhost_bus_type; +}; + +struct nvhost_bus *nvhost_bus_get(void); + +extern struct nvhost_bus *nvhost_bus_inst; + +#endif diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index b49c26e04f29..0137793b39ee 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -36,7 +36,6 @@ #include <linux/nvhost.h> #include <linux/nvhost_ioctl.h> -#include <mach/nvmap.h> #include <mach/gpufuse.h> #include <mach/hardware.h> #include <mach/iomap.h> @@ -44,11 +43,36 @@ #include "debug.h" #include "bus_client.h" #include "dev.h" +#include "nvhost_memmgr.h" +#include "chip_support.h" +#include "nvhost_acm.h" -void nvhost_read_module_regs(struct nvhost_device *ndev, +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "nvhost_hwctx.h" + +static int validate_reg(struct nvhost_device *ndev, u32 offset, int count) +{ + struct resource *r = nvhost_get_resource(ndev, IORESOURCE_MEM, 0); + int err = 0; + + if (offset + 4 * count > resource_size(r) + || (offset + 4 * count < offset)) + err = -EPERM; + + return err; +} + +int nvhost_read_module_regs(struct nvhost_device *ndev, u32 offset, int count, u32 *values) { void __iomem *p = ndev->aperture + offset; + int err; + + /* verify offset */ + err = validate_reg(ndev, offset, count); + if (err) + return err; nvhost_module_busy(ndev); while (count--) { @@ -57,12 +81,20 @@ void nvhost_read_module_regs(struct nvhost_device *ndev, } rmb(); nvhost_module_idle(ndev); + + return 0; } -void nvhost_write_module_regs(struct nvhost_device *ndev, +int nvhost_write_module_regs(struct nvhost_device *ndev, u32 offset, int count, const u32 *values) { void __iomem *p = ndev->aperture + offset; + int err; + + /* verify offset */ + err = validate_reg(ndev, offset, count); + if (err) + return err; nvhost_module_busy(ndev); while (count--) { @@ -71,6 +103,8 @@ void nvhost_write_module_regs(struct nvhost_device *ndev, } wmb(); nvhost_module_idle(ndev); + + return 0; } struct nvhost_channel_userctx { @@ -79,51 +113,12 @@ struct nvhost_channel_userctx { struct nvhost_submit_hdr_ext hdr; int num_relocshifts; struct nvhost_job *job; - struct nvmap_client *nvmap; + struct mem_mgr *memmgr; u32 timeout; u32 priority; int clientid; }; -/* - * Write cmdbuf to ftrace output. Checks if cmdbuf contents should be output - * and mmaps the cmdbuf contents if required. - */ -static void trace_write_cmdbufs(struct nvhost_job *job) -{ - struct nvmap_handle_ref handle; - void *mem = NULL; - int i = 0; - - for (i = 0; i < job->num_gathers; i++) { - struct nvhost_channel_gather *gather = &job->gathers[i]; - if (nvhost_debug_trace_cmdbuf) { - handle.handle = nvmap_id_to_handle(gather->mem_id); - mem = nvmap_mmap(&handle); - if (IS_ERR_OR_NULL(mem)) - mem = NULL; - }; - - if (mem) { - u32 i; - /* - * Write in batches of 128 as there seems to be a limit - * of how much you can output to ftrace at once. - */ - for (i = 0; i < gather->words; i += TRACE_MAX_LENGTH) { - trace_nvhost_channel_write_cmdbuf_data( - job->ch->dev->name, - gather->mem_id, - min(gather->words - i, - TRACE_MAX_LENGTH), - gather->offset + i * sizeof(u32), - mem); - } - nvmap_munmap(&handle, mem); - } - } -} - static int nvhost_channelrelease(struct inode *inode, struct file *filp) { struct nvhost_channel_userctx *priv = filp->private_data; @@ -141,7 +136,7 @@ static int nvhost_channelrelease(struct inode *inode, struct file *filp) if (priv->job) nvhost_job_put(priv->job); - nvmap_client_put(priv->nvmap); + mem_op().put_mgr(priv->memmgr); kfree(priv); return 0; } @@ -174,11 +169,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp) priv->priority = NVHOST_PRIORITY_MEDIUM; priv->clientid = atomic_add_return(1, &nvhost_get_host(ch->dev)->clientid); - - priv->job = nvhost_job_alloc(ch, priv->hwctx, &priv->hdr, - NULL, priv->priority, priv->clientid); - if (!priv->job) - goto fail; + priv->timeout = CONFIG_TEGRA_GRHOST_DEFAULT_TIMEOUT; return 0; fail: @@ -188,21 +179,24 @@ fail: static int set_submit(struct nvhost_channel_userctx *ctx) { - struct device *device = &ctx->ch->dev->dev; + struct nvhost_device *ndev = ctx->ch->dev; + struct nvhost_master *host = nvhost_get_host(ndev); /* submit should have at least 1 cmdbuf */ - if (!ctx->hdr.num_cmdbufs) + if (!ctx->hdr.num_cmdbufs || + !nvhost_syncpt_is_valid(&host->syncpt, + ctx->hdr.syncpt_id)) return -EIO; - if (!ctx->nvmap) { - dev_err(device, "no nvmap context set\n"); + if (!ctx->memmgr) { + dev_err(&ndev->dev, "no nvmap context set\n"); return -EFAULT; } - ctx->job = nvhost_job_realloc(ctx->job, + ctx->job = nvhost_job_alloc(ctx->ch, ctx->hwctx, &ctx->hdr, - ctx->nvmap, + ctx->memmgr, ctx->priority, ctx->clientid); if (!ctx->job) @@ -221,6 +215,11 @@ static void reset_submit(struct nvhost_channel_userctx *ctx) ctx->hdr.num_relocs = 0; ctx->num_relocshifts = 0; ctx->hdr.num_waitchks = 0; + + if (ctx->job) { + nvhost_job_put(ctx->job); + ctx->job = NULL; + } } static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, @@ -271,17 +270,28 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, cmdbuf.mem, cmdbuf.words, cmdbuf.offset); hdr->num_cmdbufs--; } else if (hdr->num_relocs) { - consumed = sizeof(struct nvhost_reloc); - if (remaining < consumed) + int numrelocs = remaining / sizeof(struct nvhost_reloc); + if (!numrelocs) break; - if (copy_from_user(&job->pinarray[job->num_pins], + numrelocs = min_t(int, numrelocs, priv->hdr.num_relocs); + consumed = numrelocs * sizeof(struct nvhost_reloc); + if (copy_from_user(&job->relocarray[job->num_relocs], buf, consumed)) { err = -EFAULT; break; } - trace_nvhost_channel_write_reloc(chname); - job->num_pins++; - hdr->num_relocs--; + while (numrelocs) { + struct nvhost_reloc *reloc = + &job->relocarray[job->num_relocs]; + trace_nvhost_channel_write_reloc(chname, + reloc->cmdbuf_mem, + reloc->cmdbuf_offset, + reloc->target, + reloc->target_offset); + job->num_relocs++; + hdr->num_relocs--; + numrelocs--; + } } else if (hdr->num_waitchks) { int numwaitchks = (remaining / sizeof(struct nvhost_waitchk)); @@ -302,17 +312,19 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, hdr->num_waitchks -= numwaitchks; } else if (priv->num_relocshifts) { int next_shift = - job->num_pins - priv->num_relocshifts; - consumed = sizeof(struct nvhost_reloc_shift); - if (remaining < consumed) + job->num_relocs - priv->num_relocshifts; + int num = + (remaining / sizeof(struct nvhost_reloc_shift)); + if (!num) break; - if (copy_from_user( - &job->pinarray[next_shift].reloc_shift, + num = min_t(int, num, priv->num_relocshifts); + consumed = num * sizeof(struct nvhost_reloc_shift); + if (copy_from_user(&job->relocshiftarray[next_shift], buf, consumed)) { err = -EFAULT; break; } - priv->num_relocshifts--; + priv->num_relocshifts -= num; } else { err = -EFAULT; break; @@ -335,7 +347,7 @@ static int nvhost_ioctl_channel_flush( struct nvhost_get_param_args *args, int null_kickoff) { - struct device *device = &ctx->ch->dev->dev; + struct nvhost_device *ndev = to_nvhost_device(&ctx->ch->dev->dev); int err; trace_nvhost_ioctl_channel_flush(ctx->ch->dev->name); @@ -345,13 +357,13 @@ static int nvhost_ioctl_channel_flush( ctx->hdr.num_cmdbufs || ctx->hdr.num_waitchks) { reset_submit(ctx); - dev_err(device, "channel submit out of sync\n"); + dev_err(&ndev->dev, "channel submit out of sync\n"); return -EFAULT; } - err = nvhost_job_pin(ctx->job); + err = nvhost_job_pin(ctx->job, &nvhost_get_host(ndev)->syncpt); if (err) { - dev_warn(device, "nvhost_job_pin failed: %d\n", err); + dev_warn(&ndev->dev, "nvhost_job_pin failed: %d\n", err); return err; } @@ -364,23 +376,23 @@ static int nvhost_ioctl_channel_flush( ctx->timeout = nvhost_debug_force_timeout_val; } - trace_write_cmdbufs(ctx->job); - /* context switch if needed, and submit user's gathers to the channel */ err = nvhost_channel_submit(ctx->job); args->value = ctx->job->syncpt_end; if (err) nvhost_job_unpin(ctx->job); + nvhost_job_put(ctx->job); + ctx->job = NULL; + return err; } -static int nvhost_ioctl_channel_read_3d_reg( - struct nvhost_channel_userctx *ctx, +static int nvhost_ioctl_channel_read_3d_reg(struct nvhost_channel_userctx *ctx, struct nvhost_read_3d_reg_args *args) { - BUG_ON(!channel_op(ctx->ch).read3dreg); - return channel_op(ctx->ch).read3dreg(ctx->ch, ctx->hwctx, + BUG_ON(!channel_op().read3dreg); + return channel_op().read3dreg(ctx->ch, ctx->hwctx, args->offset, &args->value); } @@ -393,11 +405,10 @@ static long nvhost_channelctl(struct file *filp, if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) || (_IOC_NR(cmd) == 0) || - (_IOC_NR(cmd) > NVHOST_IOCTL_CHANNEL_LAST)) + (_IOC_NR(cmd) > NVHOST_IOCTL_CHANNEL_LAST) || + (_IOC_SIZE(cmd) > NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE)) return -EFAULT; - BUG_ON(_IOC_SIZE(cmd) > NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE); - if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) return -EFAULT; @@ -460,17 +471,17 @@ static long nvhost_channelctl(struct file *filp, case NVHOST_IOCTL_CHANNEL_SET_NVMAP_FD: { int fd = (int)((struct nvhost_set_nvmap_fd_args *)buf)->fd; - struct nvmap_client *new_client = nvmap_client_get_file(fd); + struct mem_mgr *new_client = mem_op().get_mgr_file(fd); if (IS_ERR(new_client)) { err = PTR_ERR(new_client); break; } - if (priv->nvmap) - nvmap_client_put(priv->nvmap); + if (priv->memmgr) + mem_op().put_mgr(priv->memmgr); - priv->nvmap = new_client; + priv->memmgr = new_client; break; } case NVHOST_IOCTL_CHANNEL_READ_3D_REG: @@ -535,18 +546,23 @@ int nvhost_client_user_init(struct nvhost_device *dev) int err, devno; struct nvhost_channel *ch = dev->channel; + err = alloc_chrdev_region(&devno, 0, 1, IFACE_NAME); + if (err < 0) { + dev_err(&dev->dev, "failed to allocate devno\n"); + goto fail; + } cdev_init(&ch->cdev, &nvhost_channelops); ch->cdev.owner = THIS_MODULE; - devno = MKDEV(nvhost_major, nvhost_minor + dev->index); err = cdev_add(&ch->cdev, devno, 1); if (err < 0) { dev_err(&dev->dev, "failed to add chan %i cdev\n", dev->index); goto fail; } - ch->node = device_create(nvhost_get_host(dev)->nvhost_class, NULL, devno, NULL, + ch->node = device_create(nvhost_get_host(dev)->nvhost_class, + NULL, devno, NULL, IFACE_NAME "-%s", dev->name); if (IS_ERR(ch->node)) { err = PTR_ERR(ch->node); @@ -564,7 +580,11 @@ int nvhost_client_device_init(struct nvhost_device *dev) { int err; struct nvhost_master *nvhost_master = nvhost_get_host(dev); - struct nvhost_channel *ch = &nvhost_master->channels[dev->index]; + struct nvhost_channel *ch; + + ch = nvhost_alloc_channel(dev); + if (ch == NULL) + return -ENODEV; /* store the pointer to this device for channel */ ch->dev = dev; @@ -587,6 +607,7 @@ int nvhost_client_device_init(struct nvhost_device *dev) fail: /* Add clean-up */ + nvhost_free_channel(ch); return err; } diff --git a/drivers/video/tegra/host/bus_client.h b/drivers/video/tegra/host/bus_client.h index adc3a704ea5d..e95ea0bc3401 100644 --- a/drivers/video/tegra/host/bus_client.h +++ b/drivers/video/tegra/host/bus_client.h @@ -24,10 +24,10 @@ #include <linux/types.h> struct nvhost_device; -void nvhost_read_module_regs(struct nvhost_device *ndev, +int nvhost_read_module_regs(struct nvhost_device *ndev, u32 offset, int count, u32 *values); -void nvhost_write_module_regs(struct nvhost_device *ndev, +int nvhost_write_module_regs(struct nvhost_device *ndev, u32 offset, int count, const u32 *values); int nvhost_client_user_init(struct nvhost_device *dev); diff --git a/drivers/video/tegra/host/chip_support.c b/drivers/video/tegra/host/chip_support.c new file mode 100644 index 000000000000..9abb1fa026a4 --- /dev/null +++ b/drivers/video/tegra/host/chip_support.c @@ -0,0 +1,56 @@ +/* + * drivers/video/tegra/host/chip_support.c + * + * Tegra Graphics Host Chip support module + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/errno.h> + +#include <mach/hardware.h> + +#include "bus.h" +#include "chip_support.h" +#include "t20/t20.h" +#include "t30/t30.h" + +struct nvhost_chip_support *nvhost_get_chip_ops(void) +{ + return (nvhost_bus_get())->nvhost_chip_ops; +} + +int nvhost_init_chip_support(struct nvhost_master *host) +{ + int err = 0; + struct nvhost_chip_support *chip_ops; + + chip_ops = nvhost_get_chip_ops(); + + switch (tegra_get_chipid()) { + case TEGRA_CHIPID_TEGRA2: + err = nvhost_init_t20_support(host, chip_ops); + break; + + case TEGRA_CHIPID_TEGRA3: + err = nvhost_init_t30_support(host, chip_ops); + break; + + default: + err = -ENODEV; + } + + return err; +} diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h index 6727e7a69fb4..f5d2811f143f 100644 --- a/drivers/video/tegra/host/chip_support.h +++ b/drivers/video/tegra/host/chip_support.h @@ -21,121 +21,160 @@ #define _NVHOST_CHIP_SUPPORT_H_ #include <linux/types.h> +#include "bus.h" + struct output; -struct nvhost_waitchk; -struct nvhost_userctx_timeout; + struct nvhost_master; +struct nvhost_intr; +struct nvhost_syncpt; +struct nvhost_userctx_timeout; struct nvhost_channel; -struct nvmap_handle; -struct nvmap_client; struct nvhost_hwctx; struct nvhost_cdma; -struct nvhost_intr; +struct nvhost_job; struct push_buffer; struct nvhost_syncpt; -struct nvhost_master; struct dentry; struct nvhost_job; +struct nvhost_intr_syncpt; +struct mem_handle; +struct mem_mgr; +struct nvhost_device; + +struct nvhost_channel_ops { + int (*init)(struct nvhost_channel *, + struct nvhost_master *, + int chid); + int (*submit)(struct nvhost_job *job); + int (*read3dreg)(struct nvhost_channel *channel, + struct nvhost_hwctx *hwctx, + u32 offset, + u32 *value); + int (*save_context)(struct nvhost_channel *channel); + int (*drain_read_fifo)(struct nvhost_channel *ch, + u32 *ptr, unsigned int count, unsigned int *pending); +}; + +struct nvhost_cdma_ops { + void (*start)(struct nvhost_cdma *); + void (*stop)(struct nvhost_cdma *); + void (*kick)(struct nvhost_cdma *); + int (*timeout_init)(struct nvhost_cdma *, + u32 syncpt_id); + void (*timeout_destroy)(struct nvhost_cdma *); + void (*timeout_teardown_begin)(struct nvhost_cdma *); + void (*timeout_teardown_end)(struct nvhost_cdma *, + u32 getptr); + void (*timeout_cpu_incr)(struct nvhost_cdma *, + u32 getptr, + u32 syncpt_incrs, + u32 syncval, + u32 nr_slots, + u32 waitbases); +}; + +struct nvhost_pushbuffer_ops { + void (*reset)(struct push_buffer *); + int (*init)(struct push_buffer *); + void (*destroy)(struct push_buffer *); + void (*push_to)(struct push_buffer *, + struct mem_mgr *, struct mem_handle *, + u32 op1, u32 op2); + void (*pop_from)(struct push_buffer *, + unsigned int slots); + u32 (*space)(struct push_buffer *); + u32 (*putptr)(struct push_buffer *); +}; + +struct nvhost_debug_ops { + void (*debug_init)(struct dentry *de); + void (*show_channel_cdma)(struct nvhost_master *, + struct nvhost_channel *, + struct output *, + int chid); + void (*show_channel_fifo)(struct nvhost_master *, + struct nvhost_channel *, + struct output *, + int chid); + void (*show_mlocks)(struct nvhost_master *m, + struct output *o); + +}; + +struct nvhost_syncpt_ops { + void (*reset)(struct nvhost_syncpt *, u32 id); + void (*reset_wait_base)(struct nvhost_syncpt *, u32 id); + void (*read_wait_base)(struct nvhost_syncpt *, u32 id); + u32 (*update_min)(struct nvhost_syncpt *, u32 id); + void (*cpu_incr)(struct nvhost_syncpt *, u32 id); + int (*patch_wait)(struct nvhost_syncpt *sp, + void *patch_addr); + void (*debug)(struct nvhost_syncpt *); + const char * (*name)(struct nvhost_syncpt *, u32 id); + int (*mutex_try_lock)(struct nvhost_syncpt *, + unsigned int idx); + void (*mutex_unlock)(struct nvhost_syncpt *, + unsigned int idx); +}; + +struct nvhost_intr_ops { + void (*init_host_sync)(struct nvhost_intr *); + void (*set_host_clocks_per_usec)( + struct nvhost_intr *, u32 clocks); + void (*set_syncpt_threshold)( + struct nvhost_intr *, u32 id, u32 thresh); + void (*enable_syncpt_intr)(struct nvhost_intr *, u32 id); + void (*disable_all_syncpt_intrs)(struct nvhost_intr *); + int (*request_host_general_irq)(struct nvhost_intr *); + void (*free_host_general_irq)(struct nvhost_intr *); + int (*request_syncpt_irq)(struct nvhost_intr_syncpt *syncpt); +}; + +struct nvhost_dev_ops { + struct nvhost_channel *(*alloc_nvhost_channel)( + struct nvhost_device *dev); + void (*free_nvhost_channel)(struct nvhost_channel *ch); +}; + +struct nvhost_mem_ops { + struct mem_mgr *(*alloc_mgr)(void); + void (*put_mgr)(struct mem_mgr *); + struct mem_mgr *(*get_mgr)(struct mem_mgr *); + struct mem_mgr *(*get_mgr_file)(int fd); + struct mem_handle *(*alloc)(struct mem_mgr *, + size_t size, size_t align, + int flags); + struct mem_handle *(*get)(struct mem_mgr *, u32 id); + void (*put)(struct mem_mgr *, struct mem_handle *); + phys_addr_t (*pin)(struct mem_mgr *, struct mem_handle *); + void (*unpin)(struct mem_mgr *, struct mem_handle *); + void *(*mmap)(struct mem_handle *); + void (*munmap)(struct mem_handle *, void *); +}; struct nvhost_chip_support { - struct { - int (*init)(struct nvhost_channel *, - struct nvhost_master *, - int chid); - int (*submit)(struct nvhost_job *job); - int (*read3dreg)(struct nvhost_channel *channel, - struct nvhost_hwctx *hwctx, - u32 offset, - u32 *value); - } channel; - - struct { - void (*start)(struct nvhost_cdma *); - void (*stop)(struct nvhost_cdma *); - void (*kick)(struct nvhost_cdma *); - int (*timeout_init)(struct nvhost_cdma *, - u32 syncpt_id); - void (*timeout_destroy)(struct nvhost_cdma *); - void (*timeout_teardown_begin)(struct nvhost_cdma *); - void (*timeout_teardown_end)(struct nvhost_cdma *, - u32 getptr); - void (*timeout_cpu_incr)(struct nvhost_cdma *, - u32 getptr, - u32 syncpt_incrs, - u32 syncval, - u32 nr_slots); - void (*timeout_pb_incr)(struct nvhost_cdma *, - u32 getptr, - u32 syncpt_incrs, - u32 nr_slots, - bool exec_ctxsave); - } cdma; - - struct { - void (*reset)(struct push_buffer *); - int (*init)(struct push_buffer *); - void (*destroy)(struct push_buffer *); - void (*push_to)(struct push_buffer *, - struct nvmap_client *, - struct nvmap_handle *, - u32 op1, u32 op2); - void (*pop_from)(struct push_buffer *, - unsigned int slots); - u32 (*space)(struct push_buffer *); - u32 (*putptr)(struct push_buffer *); - } push_buffer; - - struct { - void (*debug_init)(struct dentry *de); - void (*show_channel_cdma)(struct nvhost_master *, - struct nvhost_channel *, - struct output *, - int chid); - void (*show_channel_fifo)(struct nvhost_master *, - struct nvhost_channel *, - struct output *, - int chid); - void (*show_mlocks)(struct nvhost_master *m, - struct output *o); - - } debug; - - struct { - void (*reset)(struct nvhost_syncpt *, u32 id); - void (*reset_wait_base)(struct nvhost_syncpt *, u32 id); - void (*read_wait_base)(struct nvhost_syncpt *, u32 id); - u32 (*update_min)(struct nvhost_syncpt *, u32 id); - void (*cpu_incr)(struct nvhost_syncpt *, u32 id); - int (*wait_check)(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 waitchk_mask, - struct nvhost_waitchk *wait, - int num_waitchk); - void (*debug)(struct nvhost_syncpt *); - const char * (*name)(struct nvhost_syncpt *, u32 id); - int (*mutex_try_lock)(struct nvhost_syncpt *, - unsigned int idx); - void (*mutex_unlock)(struct nvhost_syncpt *, - unsigned int idx); - } syncpt; - - struct { - void (*init_host_sync)(struct nvhost_intr *); - void (*set_host_clocks_per_usec)( - struct nvhost_intr *, u32 clocks); - void (*set_syncpt_threshold)( - struct nvhost_intr *, u32 id, u32 thresh); - void (*enable_syncpt_intr)(struct nvhost_intr *, u32 id); - void (*disable_all_syncpt_intrs)(struct nvhost_intr *); - int (*request_host_general_irq)(struct nvhost_intr *); - void (*free_host_general_irq)(struct nvhost_intr *); - int (*request_syncpt_irq)(struct nvhost_intr_syncpt *syncpt); - } intr; - - struct { - struct nvhost_device *(*get_nvhost_device)(struct nvhost_master *host, - char *name); - } nvhost_dev; + struct nvhost_channel_ops channel; + struct nvhost_cdma_ops cdma; + struct nvhost_pushbuffer_ops push_buffer; + struct nvhost_debug_ops debug; + struct nvhost_syncpt_ops syncpt; + struct nvhost_intr_ops intr; + struct nvhost_dev_ops nvhost_dev; + struct nvhost_mem_ops mem; }; +struct nvhost_chip_support *nvhost_get_chip_ops(void); + +#define host_device_op() nvhost_get_chip_ops()->nvhost_dev +#define channel_cdma_op() nvhost_get_chip_ops()->cdma +#define channel_op() nvhost_get_chip_ops()->channel +#define syncpt_op() nvhost_get_chip_ops()->syncpt +#define intr_op() nvhost_get_chip_ops()->intr +#define cdma_op() nvhost_get_chip_ops()->cdma +#define cdma_pb_op() nvhost_get_chip_ops()->push_buffer +#define mem_op() (nvhost_get_chip_ops()->mem) + +int nvhost_init_chip_support(struct nvhost_master *); + #endif /* _NVHOST_CHIP_SUPPORT_H_ */ diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c index 91436c903fc6..58f9348b84bd 100644 --- a/drivers/video/tegra/host/debug.c +++ b/drivers/video/tegra/host/debug.c @@ -22,8 +22,12 @@ #include <linux/io.h> +#include "bus.h" #include "dev.h" #include "debug.h" +#include "nvhost_acm.h" +#include "nvhost_channel.h" +#include "chip_support.h" pid_t nvhost_debug_null_kickoff_pid; unsigned int nvhost_debug_trace_cmdbuf; @@ -59,8 +63,8 @@ static int show_channels(struct device *dev, void *data) mutex_lock(&ch->reflock); if (ch->refcount) { mutex_lock(&ch->cdma.lock); - m->op.debug.show_channel_fifo(m, ch, o, nvdev->index); - m->op.debug.show_channel_cdma(m, ch, o, nvdev->index); + nvhost_get_chip_ops()->debug.show_channel_fifo(m, ch, o, nvdev->index); + nvhost_get_chip_ops()->debug.show_channel_cdma(m, ch, o, nvdev->index); mutex_unlock(&ch->cdma.lock); } mutex_unlock(&ch->reflock); @@ -72,19 +76,19 @@ static int show_channels(struct device *dev, void *data) static void show_syncpts(struct nvhost_master *m, struct output *o) { int i; - BUG_ON(!m->op.syncpt.name); + BUG_ON(!nvhost_get_chip_ops()->syncpt.name); nvhost_debug_output(o, "---- syncpts ----\n"); - for (i = 0; i < m->syncpt.nb_pts; i++) { + for (i = 0; i < nvhost_syncpt_nb_pts(&m->syncpt); i++) { u32 max = nvhost_syncpt_read_max(&m->syncpt, i); u32 min = nvhost_syncpt_update_min(&m->syncpt, i); if (!min && !max) continue; nvhost_debug_output(o, "id %d (%s) min %d max %d\n", - i, m->op.syncpt.name(&m->syncpt, i), + i, nvhost_get_chip_ops()->syncpt.name(&m->syncpt, i), min, max); } - for (i = 0; i < m->syncpt.nb_bases; i++) { + for (i = 0; i < nvhost_syncpt_nb_pts(&m->syncpt); i++) { u32 base_val; base_val = nvhost_syncpt_read_wait_base(&m->syncpt, i); if (base_val) @@ -99,16 +103,56 @@ static void show_all(struct nvhost_master *m, struct output *o) { nvhost_module_busy(m->dev); - m->op.debug.show_mlocks(m, o); + nvhost_get_chip_ops()->debug.show_mlocks(m, o); show_syncpts(m, o); nvhost_debug_output(o, "---- channels ----\n"); - bus_for_each_dev(&nvhost_bus_type, NULL, o, show_channels); + bus_for_each_dev(&(nvhost_bus_get())->nvhost_bus_type, NULL, o, + show_channels); nvhost_module_idle(m->dev); } #ifdef CONFIG_DEBUG_FS -static int nvhost_debug_show(struct seq_file *s, void *unused) +static int show_channels_no_fifo(struct device *dev, void *data) +{ + struct nvhost_channel *ch; + struct nvhost_device *nvdev = to_nvhost_device(dev); + struct output *o = data; + struct nvhost_master *m; + + if (nvdev == NULL) + return 0; + + m = nvhost_get_host(nvdev); + ch = nvdev->channel; + if (ch) { + mutex_lock(&ch->reflock); + if (ch->refcount) { + mutex_lock(&ch->cdma.lock); + nvhost_get_chip_ops()->debug.show_channel_cdma(m, + ch, o, nvdev->index); + mutex_unlock(&ch->cdma.lock); + } + mutex_unlock(&ch->reflock); + } + + return 0; +} + +static void show_all_no_fifo(struct nvhost_master *m, struct output *o) +{ + nvhost_module_busy(m->dev); + + nvhost_get_chip_ops()->debug.show_mlocks(m, o); + show_syncpts(m, o); + nvhost_debug_output(o, "---- channels ----\n"); + bus_for_each_dev(&(nvhost_bus_get())->nvhost_bus_type, NULL, o, + show_channels_no_fifo); + + nvhost_module_idle(m->dev); +} + +static int nvhost_debug_show_all(struct seq_file *s, void *unused) { struct output o = { .fn = write_to_seqfile, @@ -117,6 +161,27 @@ static int nvhost_debug_show(struct seq_file *s, void *unused) show_all(s->private, &o); return 0; } +static int nvhost_debug_show(struct seq_file *s, void *unused) +{ + struct output o = { + .fn = write_to_seqfile, + .ctx = s + }; + show_all_no_fifo(s->private, &o); + return 0; +} + +static int nvhost_debug_open_all(struct inode *inode, struct file *file) +{ + return single_open(file, nvhost_debug_show_all, inode->i_private); +} + +static const struct file_operations nvhost_debug_all_fops = { + .open = nvhost_debug_open_all, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; static int nvhost_debug_open(struct inode *inode, struct file *file) { @@ -136,14 +201,16 @@ void nvhost_debug_init(struct nvhost_master *master) debugfs_create_file("status", S_IRUGO, de, master, &nvhost_debug_fops); + debugfs_create_file("status_all", S_IRUGO, de, + master, &nvhost_debug_all_fops); debugfs_create_u32("null_kickoff_pid", S_IRUGO|S_IWUSR, de, &nvhost_debug_null_kickoff_pid); debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de, &nvhost_debug_trace_cmdbuf); - if (master->op.debug.debug_init) - master->op.debug.debug_init(de); + if (nvhost_get_chip_ops()->debug.debug_init) + nvhost_get_chip_ops()->debug.debug_init(de); debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de, &nvhost_debug_force_timeout_pid); diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index 2e1ffeea7729..6200507548f7 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -18,568 +18,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/spinlock.h> -#include <linux/fs.h> -#include <linux/cdev.h> -#include <linux/uaccess.h> -#include <linux/file.h> -#include <linux/clk.h> -#include <linux/hrtimer.h> - -#include "dev.h" #define CREATE_TRACE_POINTS #include <trace/events/nvhost.h> -#include <linux/io.h> - #include <linux/nvhost.h> -#include <linux/nvhost_ioctl.h> -#include <mach/nvmap.h> #include <mach/gpufuse.h> -#include <mach/hardware.h> -#include <mach/iomap.h> - -#include "debug.h" -#include "nvhost_job.h" -#include "t20/t20.h" -#include "t30/t30.h" -#include "bus_client.h" - -#define DRIVER_NAME "host1x" - -int nvhost_major; -int nvhost_minor; - -static unsigned int register_sets; - -struct nvhost_ctrl_userctx { - struct nvhost_master *dev; - u32 *mod_locks; -}; - -static int nvhost_ctrlrelease(struct inode *inode, struct file *filp) -{ - struct nvhost_ctrl_userctx *priv = filp->private_data; - int i; - - trace_nvhost_ctrlrelease(priv->dev->dev->name); - - filp->private_data = NULL; - if (priv->mod_locks[0]) - nvhost_module_idle(priv->dev->dev); - for (i = 1; i < priv->dev->syncpt.nb_mlocks; i++) - if (priv->mod_locks[i]) - nvhost_mutex_unlock(&priv->dev->syncpt, i); - kfree(priv->mod_locks); - kfree(priv); - return 0; -} - -static int nvhost_ctrlopen(struct inode *inode, struct file *filp) -{ - struct nvhost_master *host = container_of(inode->i_cdev, struct nvhost_master, cdev); - struct nvhost_ctrl_userctx *priv; - u32 *mod_locks; - - trace_nvhost_ctrlopen(host->dev->name); - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - mod_locks = kzalloc(sizeof(u32) * host->syncpt.nb_mlocks, GFP_KERNEL); - - if (!(priv && mod_locks)) { - kfree(priv); - kfree(mod_locks); - return -ENOMEM; - } - - priv->dev = host; - priv->mod_locks = mod_locks; - filp->private_data = priv; - return 0; -} - -static int nvhost_ioctl_ctrl_syncpt_read(struct nvhost_ctrl_userctx *ctx, - struct nvhost_ctrl_syncpt_read_args *args) -{ - if (args->id >= ctx->dev->syncpt.nb_pts) - return -EINVAL; - args->value = nvhost_syncpt_read(&ctx->dev->syncpt, args->id); - trace_nvhost_ioctl_ctrl_syncpt_read(args->id, args->value); - return 0; -} - -static int nvhost_ioctl_ctrl_syncpt_incr(struct nvhost_ctrl_userctx *ctx, - struct nvhost_ctrl_syncpt_incr_args *args) -{ - if (args->id >= ctx->dev->syncpt.nb_pts) - return -EINVAL; - trace_nvhost_ioctl_ctrl_syncpt_incr(args->id); - nvhost_syncpt_incr(&ctx->dev->syncpt, args->id); - return 0; -} - -static int nvhost_ioctl_ctrl_syncpt_waitex(struct nvhost_ctrl_userctx *ctx, - struct nvhost_ctrl_syncpt_waitex_args *args) -{ - u32 timeout; - int err; - if (args->id >= ctx->dev->syncpt.nb_pts) - return -EINVAL; - if (args->timeout == NVHOST_NO_TIMEOUT) - timeout = MAX_SCHEDULE_TIMEOUT; - else - timeout = (u32)msecs_to_jiffies(args->timeout); - - err = nvhost_syncpt_wait_timeout(&ctx->dev->syncpt, args->id, - args->thresh, timeout, &args->value); - trace_nvhost_ioctl_ctrl_syncpt_wait(args->id, args->thresh, - args->timeout, args->value, err); - - return err; -} - -static int nvhost_ioctl_ctrl_module_mutex(struct nvhost_ctrl_userctx *ctx, - struct nvhost_ctrl_module_mutex_args *args) -{ - int err = 0; - if (args->id >= ctx->dev->syncpt.nb_mlocks || - args->lock > 1) - return -EINVAL; - - trace_nvhost_ioctl_ctrl_module_mutex(args->lock, args->id); - if (args->lock && !ctx->mod_locks[args->id]) { - if (args->id == 0) - nvhost_module_busy(ctx->dev->dev); - else - err = nvhost_mutex_try_lock(&ctx->dev->syncpt, - args->id); - if (!err) - ctx->mod_locks[args->id] = 1; - } else if (!args->lock && ctx->mod_locks[args->id]) { - if (args->id == 0) - nvhost_module_idle(ctx->dev->dev); - else - nvhost_mutex_unlock(&ctx->dev->syncpt, args->id); - ctx->mod_locks[args->id] = 0; - } - return err; -} - -static struct nvhost_device *get_ndev_by_moduleid(struct nvhost_master *host, - u32 id) -{ - int i; - - for (i = 0; i < host->nb_channels; i++) { - struct nvhost_device *ndev = host->channels[i].dev; - - /* display and dsi do not use channel for register programming. - * so their channels do not have device instance. - * hence skip such channels from here. */ - if (ndev == NULL) - continue; - - if (id == ndev->moduleid) - return ndev; - } - return NULL; -} - -static int nvhost_ioctl_ctrl_module_regrdwr(struct nvhost_ctrl_userctx *ctx, - struct nvhost_ctrl_module_regrdwr_args *args) -{ - u32 num_offsets = args->num_offsets; - u32 *offsets = args->offsets; - u32 *values = args->values; - u32 vals[64]; - struct nvhost_device *ndev; - - trace_nvhost_ioctl_ctrl_module_regrdwr(args->id, - args->num_offsets, args->write); - /* Check that there is something to read and that block size is - * u32 aligned */ - if (num_offsets == 0 || args->block_size & 3) - return -EINVAL; - - ndev = get_ndev_by_moduleid(ctx->dev, args->id); - if (!ndev) - return -EINVAL; - - while (num_offsets--) { - int remaining = args->block_size >> 2; - u32 offs; - if (get_user(offs, offsets)) - return -EFAULT; - offsets++; - while (remaining) { - int batch = min(remaining, 64); - if (args->write) { - if (copy_from_user(vals, values, - batch*sizeof(u32))) - return -EFAULT; - nvhost_write_module_regs(ndev, - offs, batch, vals); - } else { - nvhost_read_module_regs(ndev, - offs, batch, vals); - if (copy_to_user(values, vals, - batch*sizeof(u32))) - return -EFAULT; - } - remaining -= batch; - offs += batch; - values += batch; - } - } - - return 0; -} - -static int nvhost_ioctl_ctrl_get_version(struct nvhost_ctrl_userctx *ctx, - struct nvhost_get_param_args *args) -{ - args->value = NVHOST_SUBMIT_VERSION_MAX_SUPPORTED; - return 0; -} - -static long nvhost_ctrlctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct nvhost_ctrl_userctx *priv = filp->private_data; - u8 buf[NVHOST_IOCTL_CTRL_MAX_ARG_SIZE]; - int err = 0; - - if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) || - (_IOC_NR(cmd) == 0) || - (_IOC_NR(cmd) > NVHOST_IOCTL_CTRL_LAST)) - return -EFAULT; - - BUG_ON(_IOC_SIZE(cmd) > NVHOST_IOCTL_CTRL_MAX_ARG_SIZE); - - if (_IOC_DIR(cmd) & _IOC_WRITE) { - if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) - return -EFAULT; - } - - switch (cmd) { - case NVHOST_IOCTL_CTRL_SYNCPT_READ: - err = nvhost_ioctl_ctrl_syncpt_read(priv, (void *)buf); - break; - case NVHOST_IOCTL_CTRL_SYNCPT_INCR: - err = nvhost_ioctl_ctrl_syncpt_incr(priv, (void *)buf); - break; - case NVHOST_IOCTL_CTRL_SYNCPT_WAIT: - err = nvhost_ioctl_ctrl_syncpt_waitex(priv, (void *)buf); - break; - case NVHOST_IOCTL_CTRL_MODULE_MUTEX: - err = nvhost_ioctl_ctrl_module_mutex(priv, (void *)buf); - break; - case NVHOST_IOCTL_CTRL_MODULE_REGRDWR: - err = nvhost_ioctl_ctrl_module_regrdwr(priv, (void *)buf); - break; - case NVHOST_IOCTL_CTRL_SYNCPT_WAITEX: - err = nvhost_ioctl_ctrl_syncpt_waitex(priv, (void *)buf); - break; - case NVHOST_IOCTL_CTRL_GET_VERSION: - err = nvhost_ioctl_ctrl_get_version(priv, (void *)buf); - break; - default: - err = -ENOTTY; - break; - } - - if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) - err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd)); - - return err; -} - -static const struct file_operations nvhost_ctrlops = { - .owner = THIS_MODULE, - .release = nvhost_ctrlrelease, - .open = nvhost_ctrlopen, - .unlocked_ioctl = nvhost_ctrlctl -}; - -static void power_on_host(struct nvhost_device *dev) -{ - struct nvhost_master *host = nvhost_get_drvdata(dev); - nvhost_syncpt_reset(&host->syncpt); - nvhost_intr_start(&host->intr, clk_get_rate(dev->clk[0])); -} - -static int power_off_host(struct nvhost_device *dev) -{ - struct nvhost_master *host = nvhost_get_drvdata(dev); - nvhost_syncpt_save(&host->syncpt); - nvhost_intr_stop(&host->intr); - return 0; -} - -static int __devinit nvhost_user_init(struct nvhost_master *host) -{ - int err, devno; - - host->nvhost_class = class_create(THIS_MODULE, IFACE_NAME); - if (IS_ERR(host->nvhost_class)) { - err = PTR_ERR(host->nvhost_class); - dev_err(&host->dev->dev, "failed to create class\n"); - goto fail; - } - - err = alloc_chrdev_region(&devno, nvhost_minor, - host->nb_channels + 1, IFACE_NAME); - nvhost_major = MAJOR(devno); - if (err < 0) { - dev_err(&host->dev->dev, "failed to reserve chrdev region\n"); - goto fail; - } - - cdev_init(&host->cdev, &nvhost_ctrlops); - host->cdev.owner = THIS_MODULE; - devno = MKDEV(nvhost_major, nvhost_minor + host->nb_channels); - err = cdev_add(&host->cdev, devno, 1); - if (err < 0) - goto fail; - host->ctrl = device_create(host->nvhost_class, NULL, devno, NULL, - IFACE_NAME "-ctrl"); - if (IS_ERR(host->ctrl)) { - err = PTR_ERR(host->ctrl); - dev_err(&host->dev->dev, "failed to create ctrl device\n"); - goto fail; - } - - return 0; -fail: - return err; -} - -struct nvhost_device *nvhost_get_device(char *name) -{ - BUG_ON(!host_device_op(nvhost).get_nvhost_device); - return host_device_op(nvhost).get_nvhost_device(nvhost, name); -} - -static void nvhost_remove_chip_support(struct nvhost_master *host) -{ - kfree(host->channels); - host->channels = 0; - - kfree(host->intr.syncpt); - host->intr.syncpt = 0; -} - -static int __devinit nvhost_init_chip_support(struct nvhost_master *host) -{ - int err; - switch (tegra_get_chipid()) { - case TEGRA_CHIPID_TEGRA2: - err = nvhost_init_t20_support(host); - break; - - case TEGRA_CHIPID_TEGRA3: - err = nvhost_init_t30_support(host); - break; - default: - return -ENODEV; - } - - if (err) - return err; - - /* allocate items sized in chip specific support init */ - host->channels = kzalloc(sizeof(struct nvhost_channel) * - host->nb_channels, GFP_KERNEL); - - host->intr.syncpt = kzalloc(sizeof(struct nvhost_intr_syncpt) * - host->syncpt.nb_pts, GFP_KERNEL); - - if (!(host->channels && host->intr.syncpt)) { - /* frees happen in the support removal phase */ - return -ENOMEM; - } - - return 0; -} - -static struct resource nvhost_resources[] = { - { - .start = TEGRA_HOST1X_BASE, - .end = TEGRA_HOST1X_BASE + TEGRA_HOST1X_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_SYNCPT_THRESH_BASE, - .end = INT_SYNCPT_THRESH_BASE + INT_SYNCPT_THRESH_NR - 1, - .flags = IORESOURCE_IRQ, - }, - { - .start = INT_HOST1X_MPCORE_GENERAL, - .end = INT_HOST1X_MPCORE_GENERAL, - .flags = IORESOURCE_IRQ, - }, -}; - -struct nvhost_device tegra_grhost_device = { - .name = DRIVER_NAME, - .id = -1, - .resource = nvhost_resources, - .num_resources = ARRAY_SIZE(nvhost_resources), - .finalize_poweron = power_on_host, - .prepare_poweroff = power_off_host, - .clocks = {{"host1x", UINT_MAX}, {} }, - NVHOST_MODULE_NO_POWERGATE_IDS, -}; - -static int __devinit nvhost_probe(struct nvhost_device *dev) -{ - struct nvhost_master *host; - struct resource *regs, *intr0, *intr1; - int i, err; - - regs = nvhost_get_resource(dev, IORESOURCE_MEM, 0); - intr0 = nvhost_get_resource(dev, IORESOURCE_IRQ, 0); - intr1 = nvhost_get_resource(dev, IORESOURCE_IRQ, 1); - - if (!regs || !intr0 || !intr1) { - dev_err(&dev->dev, "missing required platform resources\n"); - return -ENXIO; - } - - host = kzalloc(sizeof(*host), GFP_KERNEL); - if (!host) - return -ENOMEM; - - host->nvmap = nvmap_create_client(nvmap_dev, "nvhost"); - if (!host->nvmap) { - dev_err(&dev->dev, "unable to create nvmap client\n"); - err = -EIO; - goto fail; - } - - host->reg_mem = request_mem_region(regs->start, - resource_size(regs), dev->name); - if (!host->reg_mem) { - dev_err(&dev->dev, "failed to get host register memory\n"); - err = -ENXIO; - goto fail; - } - - host->aperture = ioremap(regs->start, resource_size(regs)); - if (!host->aperture) { - dev_err(&dev->dev, "failed to remap host registers\n"); - err = -ENXIO; - goto fail; - } - - err = nvhost_init_chip_support(host); - if (err) { - dev_err(&dev->dev, "failed to init chip support\n"); - goto fail; - } - - /* Register host1x device as bus master */ - host->dev = dev; - - /* Give pointer to host1x via driver */ - nvhost_set_drvdata(dev, host); - - nvhost_bus_add_host(host); - - err = nvhost_syncpt_init(&tegra_grhost_device, &host->syncpt); - if (err) - goto fail; - - err = nvhost_intr_init(&host->intr, intr1->start, intr0->start); - if (err) - goto fail; - - err = nvhost_user_init(host); - if (err) - goto fail; - - err = nvhost_module_init(&tegra_grhost_device); - if (err) - goto fail; - - for (i = 0; i < host->dev->num_clks; i++) - clk_enable(host->dev->clk[i]); - nvhost_syncpt_reset(&host->syncpt); - for (i = 0; i < host->dev->num_clks; i++) - clk_disable(host->dev->clk[0]); - - nvhost_debug_init(host); - - dev_info(&dev->dev, "initialized\n"); - return 0; - -fail: - nvhost_remove_chip_support(host); - if (host->nvmap) - nvmap_client_put(host->nvmap); - kfree(host); - return err; -} - -static int __exit nvhost_remove(struct nvhost_device *dev) -{ - struct nvhost_master *host = nvhost_get_drvdata(dev); - nvhost_intr_deinit(&host->intr); - nvhost_syncpt_deinit(&host->syncpt); - nvhost_remove_chip_support(host); - return 0; -} - -static int nvhost_suspend(struct nvhost_device *dev, pm_message_t state) -{ - struct nvhost_master *host = nvhost_get_drvdata(dev); - int ret = 0; - - ret = nvhost_module_suspend(host->dev); - dev_info(&dev->dev, "suspend status: %d\n", ret); - - return ret; -} - -static int nvhost_resume(struct nvhost_device *dev) -{ - dev_info(&dev->dev, "resuming\n"); - return 0; -} - -static struct nvhost_driver nvhost_driver = { - .probe = nvhost_probe, - .remove = __exit_p(nvhost_remove), - .suspend = nvhost_suspend, - .resume = nvhost_resume, - .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME - } -}; - -static int __init nvhost_mod_init(void) -{ - register_sets = tegra_gpu_register_sets(); - return nvhost_driver_register(&nvhost_driver); -} - -static void __exit nvhost_mod_exit(void) -{ - nvhost_driver_unregister(&nvhost_driver); -} - -/* host1x master device needs nvmap to be instantiated first. - * nvmap is instantiated via fs_initcall. - * Hence instantiate host1x master device using rootfs_initcall - * which is one level after fs_initcall. */ -rootfs_initcall(nvhost_mod_init); -module_exit(nvhost_mod_exit); - -module_param_call(register_sets, NULL, param_get_uint, ®ister_sets, 0444); -MODULE_PARM_DESC(register_sets, "Number of register sets"); MODULE_AUTHOR("NVIDIA"); MODULE_DESCRIPTION("Graphics host driver for Tegra products"); diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h index 74d7e16fc272..53ec2de13aa1 100644 --- a/drivers/video/tegra/host/dev.h +++ b/drivers/video/tegra/host/dev.h @@ -1,9 +1,7 @@ /* * drivers/video/tegra/host/dev.h * - * Tegra Graphics Host Driver Entrypoint - * - * Copyright (c) 2010-2012, NVIDIA Corporation. + * Copyright (c) 2012, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,51 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __NVHOST_DEV_H -#define __NVHOST_DEV_H - -#include "nvhost_acm.h" -#include "nvhost_syncpt.h" -#include "nvhost_intr.h" -#include "nvhost_channel.h" -#include "chip_support.h" - -#define TRACE_MAX_LENGTH 128U -#define IFACE_NAME "nvhost" - -extern int nvhost_major; -extern int nvhost_minor; - -struct nvhost_hwctx; - -struct nvhost_master { - void __iomem *aperture; - void __iomem *sync_aperture; - struct resource *reg_mem; - struct class *nvhost_class; - struct cdev cdev; - struct device *ctrl; - struct nvhost_syncpt syncpt; - struct nvmap_client *nvmap; - struct nvhost_intr intr; - struct nvhost_device *dev; - struct nvhost_channel *channels; - u32 nb_channels; - - struct nvhost_chip_support op; - - atomic_t clientid; -}; - -extern struct nvhost_master *nvhost; - -void nvhost_debug_init(struct nvhost_master *master); -void nvhost_debug_dump(struct nvhost_master *master); - -#define host_device_op(host) (host->op.nvhost_dev) - -struct nvhost_device *nvhost_get_device(char *name); +#ifndef NVHOST_DEV_H +#define NVHOST_DEV_H -extern pid_t nvhost_debug_null_kickoff_pid; +#include "host1x/host1x.h" #endif diff --git a/drivers/video/tegra/host/dsi/Makefile b/drivers/video/tegra/host/dsi/Makefile deleted file mode 100644 index eb94d3ec4928..000000000000 --- a/drivers/video/tegra/host/dsi/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -GCOV_PROFILE := y -EXTRA_CFLAGS += -Idrivers/video/tegra/host - -nvhost-dsi-objs = \ - dsi.o - -obj-$(CONFIG_TEGRA_GRHOST) += nvhost-dsi.o diff --git a/drivers/video/tegra/host/dsi/dsi.c b/drivers/video/tegra/host/dsi/dsi.c deleted file mode 100644 index 0e49f591574d..000000000000 --- a/drivers/video/tegra/host/dsi/dsi.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * drivers/video/tegra/host/dsi/dsi.c - * - * Tegra Graphics DSI - * - * Copyright (c) 2012, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "dev.h" -#include "bus_client.h" - -static int dsi_probe(struct nvhost_device *dev) -{ - return nvhost_client_device_init(dev); -} - -static int __exit dsi_remove(struct nvhost_device *dev) -{ - /* Add clean-up */ - return 0; -} - -static int dsi_suspend(struct nvhost_device *dev, pm_message_t state) -{ - return nvhost_client_device_suspend(dev); -} - -static int dsi_resume(struct nvhost_device *dev) -{ - dev_info(&dev->dev, "resuming\n"); - return 0; -} - -struct nvhost_device *dsi_device; - -static struct nvhost_driver dsi_driver = { - .probe = dsi_probe, - .remove = __exit_p(dsi_remove), -#ifdef CONFIG_PM - .suspend = dsi_suspend, - .resume = dsi_resume, -#endif - .driver = { - .owner = THIS_MODULE, - .name = "dsi", - } -}; - -static int __init dsi_init(void) -{ - int err; - - dsi_device = nvhost_get_device("dsi"); - if (!dsi_device) - return -ENXIO; - - err = nvhost_device_register(dsi_device); - if (err) - return err; - - return nvhost_driver_register(&dsi_driver); -} - -static void __exit dsi_exit(void) -{ - nvhost_driver_unregister(&dsi_driver); -} - -module_init(dsi_init); -module_exit(dsi_exit); diff --git a/drivers/video/tegra/host/gr2d/gr2d.c b/drivers/video/tegra/host/gr2d/gr2d.c index f88eb72e0a40..56752eba5951 100644 --- a/drivers/video/tegra/host/gr2d/gr2d.c +++ b/drivers/video/tegra/host/gr2d/gr2d.c @@ -21,7 +21,8 @@ #include "dev.h" #include "bus_client.h" -static int __devinit gr2d_probe(struct nvhost_device *dev) +static int __devinit gr2d_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { return nvhost_client_device_init(dev); } @@ -32,6 +33,7 @@ static int __exit gr2d_remove(struct nvhost_device *dev) return 0; } +#ifdef CONFIG_PM static int gr2d_suspend(struct nvhost_device *dev, pm_message_t state) { return nvhost_client_device_suspend(dev); @@ -42,8 +44,7 @@ static int gr2d_resume(struct nvhost_device *dev) dev_info(&dev->dev, "resuming\n"); return 0; } - -struct nvhost_device *gr2d_device; +#endif static struct nvhost_driver gr2d_driver = { .probe = gr2d_probe, @@ -60,16 +61,6 @@ static struct nvhost_driver gr2d_driver = { static int __init gr2d_init(void) { - int err; - - gr2d_device = nvhost_get_device("gr2d"); - if (!gr2d_device) - return -ENXIO; - - err = nvhost_device_register(gr2d_device); - if (err) - return err; - return nvhost_driver_register(&gr2d_driver); } diff --git a/drivers/video/tegra/host/gr3d/gr3d.c b/drivers/video/tegra/host/gr3d/gr3d.c index f387d54e585e..715468131d9e 100644 --- a/drivers/video/tegra/host/gr3d/gr3d.c +++ b/drivers/video/tegra/host/gr3d/gr3d.c @@ -18,27 +18,27 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <mach/nvmap.h> #include <linux/slab.h> +#include <mach/gpufuse.h> #include "t20/t20.h" -#include "host1x/host1x_channel.h" -#include "host1x/host1x_hardware.h" -#include "host1x/host1x_syncpt.h" +#include "host1x/host1x01_hardware.h" #include "nvhost_hwctx.h" #include "dev.h" #include "gr3d.h" +#include "gr3d_t20.h" +#include "gr3d_t30.h" +#include "scale3d.h" #include "bus_client.h" - -#ifndef TEGRA_POWERGATE_3D1 -#define TEGRA_POWERGATE_3D1 -1 -#endif +#include "nvhost_channel.h" +#include "nvhost_memmgr.h" +#include "chip_support.h" void nvhost_3dctx_restore_begin(struct host1x_hwctx_handler *p, u32 *ptr) { /* set class to host */ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INCR_SYNCPT_BASE, 1); + host1x_uclass_incr_syncpt_base_r(), 1); /* increment sync point base */ ptr[1] = nvhost_class_host_incr_syncpt_base(p->waitbase, p->restore_incrs); @@ -64,28 +64,27 @@ void nvhost_3dctx_restore_end(struct host1x_hwctx_handler *p, u32 *ptr) { /* syncpt increment to track restore gather. */ ptr[0] = nvhost_opcode_imm_incr_syncpt( - NV_SYNCPT_OP_DONE, p->syncpt); + host1x_uclass_incr_syncpt_cond_op_done_v(), p->syncpt); } /*** ctx3d ***/ - struct host1x_hwctx *nvhost_3dctx_alloc_common(struct host1x_hwctx_handler *p, struct nvhost_channel *ch, bool map_restore) { - struct nvmap_client *nvmap = nvhost_get_host(ch->dev)->nvmap; + struct mem_mgr *memmgr = nvhost_get_host(ch->dev)->memmgr; struct host1x_hwctx *ctx; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; - ctx->restore = nvmap_alloc(nvmap, p->restore_size * 4, 32, - map_restore ? NVMAP_HANDLE_WRITE_COMBINE - : NVMAP_HANDLE_UNCACHEABLE, 0); + ctx->restore = mem_op().alloc(memmgr, p->restore_size * 4, 32, + map_restore ? mem_mgr_flag_write_combine + : mem_mgr_flag_uncacheable); if (IS_ERR_OR_NULL(ctx->restore)) goto fail; if (map_restore) { - ctx->restore_virt = nvmap_mmap(ctx->restore); + ctx->restore_virt = mem_op().mmap(ctx->restore); if (!ctx->restore_virt) goto fail; } else @@ -98,7 +97,7 @@ struct host1x_hwctx *nvhost_3dctx_alloc_common(struct host1x_hwctx_handler *p, ctx->save_incrs = p->save_incrs; ctx->save_thresh = p->save_thresh; ctx->save_slots = p->save_slots; - ctx->restore_phys = nvmap_pin(nvmap, ctx->restore); + ctx->restore_phys = mem_op().pin(memmgr, ctx->restore); if (IS_ERR_VALUE(ctx->restore_phys)) goto fail; @@ -108,10 +107,10 @@ struct host1x_hwctx *nvhost_3dctx_alloc_common(struct host1x_hwctx_handler *p, fail: if (map_restore && ctx->restore_virt) { - nvmap_munmap(ctx->restore, ctx->restore_virt); + mem_op().munmap(ctx->restore, ctx->restore_virt); ctx->restore_virt = NULL; } - nvmap_free(nvmap, ctx->restore); + mem_op().put(memmgr, ctx->restore); ctx->restore = NULL; kfree(ctx); return NULL; @@ -126,16 +125,15 @@ void nvhost_3dctx_free(struct kref *ref) { struct nvhost_hwctx *nctx = container_of(ref, struct nvhost_hwctx, ref); struct host1x_hwctx *ctx = to_host1x_hwctx(nctx); - struct nvmap_client *nvmap = - nvhost_get_host(nctx->channel->dev)->nvmap; + struct mem_mgr *memmgr = nvhost_get_host(nctx->channel->dev)->memmgr; if (ctx->restore_virt) { - nvmap_munmap(ctx->restore, ctx->restore_virt); + mem_op().munmap(ctx->restore, ctx->restore_virt); ctx->restore_virt = NULL; } - nvmap_unpin(nvmap, ctx->restore); + mem_op().unpin(memmgr, ctx->restore); ctx->restore_phys = 0; - nvmap_free(nvmap, ctx->restore); + mem_op().put(memmgr, ctx->restore); ctx->restore = NULL; kfree(ctx); } @@ -147,11 +145,74 @@ void nvhost_3dctx_put(struct nvhost_hwctx *ctx) int nvhost_gr3d_prepare_power_off(struct nvhost_device *dev) { - return host1x_save_context(dev, NVSYNCPT_3D); + return nvhost_channel_save_context(dev->channel); } -static int __devinit gr3d_probe(struct nvhost_device *dev) +enum gr3d_ip_ver { + gr3d_01 = 1, + gr3d_02, +}; + +struct gr3d_desc { + void (*finalize_poweron)(struct nvhost_device *dev); + void (*busy)(struct nvhost_device *); + void (*idle)(struct nvhost_device *); + void (*suspend_ndev)(struct nvhost_device *); + void (*init)(struct nvhost_device *dev); + void (*deinit)(struct nvhost_device *dev); + int (*prepare_poweroff)(struct nvhost_device *dev); + struct nvhost_hwctx_handler *(*alloc_hwctx_handler)(u32 syncpt, + u32 waitbase, struct nvhost_channel *ch); +}; + +static const struct gr3d_desc gr3d[] = { + [gr3d_01] = { + .finalize_poweron = NULL, + .busy = NULL, + .idle = NULL, + .suspend_ndev = NULL, + .init = NULL, + .deinit = NULL, + .prepare_poweroff = nvhost_gr3d_prepare_power_off, + .alloc_hwctx_handler = nvhost_gr3d_t20_ctxhandler_init, + }, + [gr3d_02] = { + .finalize_poweron = NULL, + .busy = nvhost_scale3d_notify_busy, + .idle = nvhost_scale3d_notify_idle, + .suspend_ndev = nvhost_scale3d_suspend, + .init = nvhost_scale3d_init, + .deinit = nvhost_scale3d_deinit, + .prepare_poweroff = nvhost_gr3d_prepare_power_off, + .alloc_hwctx_handler = nvhost_gr3d_t30_ctxhandler_init, + }, +}; + +static struct nvhost_device_id gr3d_id[] = { + { "gr3d", gr3d_01 }, + { "gr3d", gr3d_02 }, + { }, +}; + +MODULE_DEVICE_TABLE(nvhost, gr3d_id); + +static int __devinit gr3d_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { + int index = 0; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + + index = id_table->version; + + drv->finalize_poweron = gr3d[index].finalize_poweron; + drv->busy = gr3d[index].busy; + drv->idle = gr3d[index].idle; + drv->suspend_ndev = gr3d[index].suspend_ndev; + drv->init = gr3d[index].init; + drv->deinit = gr3d[index].deinit; + drv->prepare_poweroff = gr3d[index].prepare_poweroff; + drv->alloc_hwctx_handler = gr3d[index].alloc_hwctx_handler; + return nvhost_client_device_init(dev); } @@ -161,6 +222,7 @@ static int __exit gr3d_remove(struct nvhost_device *dev) return 0; } +#ifdef CONFIG_PM static int gr3d_suspend(struct nvhost_device *dev, pm_message_t state) { return nvhost_client_device_suspend(dev); @@ -171,8 +233,7 @@ static int gr3d_resume(struct nvhost_device *dev) dev_info(&dev->dev, "resuming\n"); return 0; } - -struct nvhost_device *gr3d_device; +#endif static struct nvhost_driver gr3d_driver = { .probe = gr3d_probe, @@ -184,21 +245,12 @@ static struct nvhost_driver gr3d_driver = { .driver = { .owner = THIS_MODULE, .name = "gr3d", - } + }, + .id_table = gr3d_id, }; static int __init gr3d_init(void) { - int err; - - gr3d_device = nvhost_get_device("gr3d"); - if (!gr3d_device) - return -ENXIO; - - err = nvhost_device_register(gr3d_device); - if (err) - return err; - return nvhost_driver_register(&gr3d_driver); } diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c index 9ca990f89077..b6e3896fe50c 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t20.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c @@ -19,11 +19,12 @@ */ #include "nvhost_hwctx.h" -#include "dev.h" -#include "host1x/host1x_channel.h" -#include "host1x/host1x_hardware.h" -#include "host1x/host1x_syncpt.h" +#include "nvhost_channel.h" +#include "host1x/host1x.h" +#include "host1x/host1x01_hardware.h" #include "gr3d.h" +#include "chip_support.h" +#include "nvhost_memmgr.h" #include <linux/slab.h> @@ -136,8 +137,9 @@ static void save_push_v0(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) struct host1x_hwctx_handler *p = host1x_hwctx_handler(ctx); nvhost_cdma_push_gather(cdma, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, + nvhost_get_host(nctx->channel->dev)->memmgr, + p->save_buf, + 0, nvhost_opcode_gather(p->save_size), p->save_phys); } @@ -146,24 +148,26 @@ static void __init save_begin_v0(struct host1x_hwctx_handler *h, u32 *ptr) { /* 3d: when done, increment syncpt to base+1 */ ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0); - ptr[1] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, + ptr[1] = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), h->syncpt); /* incr 1 */ /* host: wait for syncpt base+1 */ ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1); + host1x_uclass_wait_syncpt_base_r(), 1); ptr[3] = nvhost_class_host_wait_syncpt_base(h->syncpt, h->waitbase, 1); /* host: signal context read thread to start reading */ - ptr[4] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE, + ptr[4] = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_immediate_v(), h->syncpt); /* incr 2 */ } static void __init save_direct_v0(u32 *ptr, u32 start_reg, u32 count) { - ptr[0] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDOFF, 1); + ptr[0] = nvhost_opcode_nonincr(host1x_uclass_indoff_r(), 1); ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, start_reg, true); - ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); + ptr[2] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count); } static void __init save_indirect_v0(u32 *ptr, u32 offset_reg, u32 offset, @@ -173,28 +177,28 @@ static void __init save_indirect_v0(u32 *ptr, u32 offset_reg, u32 offset, offset_reg, 1); ptr[1] = offset; ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INDOFF, 1); + host1x_uclass_indoff_r(), 1); ptr[3] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, data_reg, false); - ptr[4] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); + ptr[4] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count); } static void __init save_end_v0(struct host1x_hwctx_handler *h, u32 *ptr) { /* Wait for context read service to finish (cpu incr 3) */ - ptr[0] = nvhost_opcode_nonincr(NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1); + ptr[0] = nvhost_opcode_nonincr(host1x_uclass_wait_syncpt_base_r(), 1); ptr[1] = nvhost_class_host_wait_syncpt_base(h->syncpt, h->waitbase, h->save_incrs); /* Advance syncpoint base */ - ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1); - ptr[3] = nvhost_class_host_incr_syncpt_base(NVWAITBASE_3D, + ptr[2] = nvhost_opcode_nonincr(host1x_uclass_incr_syncpt_base_r(), 1); + ptr[3] = nvhost_class_host_incr_syncpt_base(h->waitbase, h->save_incrs); /* set class back to the unit */ ptr[4] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0); } static u32 *save_regs_v0(u32 *ptr, unsigned int *pending, - void __iomem *chan_regs, + struct nvhost_channel *ch, const struct hwctx_reginfo *regs, unsigned int nr_regs) { @@ -212,7 +216,7 @@ static u32 *save_regs_v0(u32 *ptr, unsigned int *pending, ptr += RESTORE_INDIRECT_SIZE; break; } - drain_result = host1x_drain_read_fifo(chan_regs, + drain_result = nvhost_channel_drain_read_fifo(ch, ptr, count, pending); BUG_ON(drain_result < 0); ptr += count; @@ -338,7 +342,7 @@ static void ctx3d_save_service(struct nvhost_hwctx *nctx) u32 *ptr = (u32 *)ctx->restore_virt + RESTORE_BEGIN_SIZE; unsigned int pending = 0; - ptr = save_regs_v0(ptr, &pending, nctx->channel->aperture, + ptr = save_regs_v0(ptr, &pending, nctx->channel, ctxsave_regs_3d_global, ARRAY_SIZE(ctxsave_regs_3d_global)); @@ -351,22 +355,22 @@ struct nvhost_hwctx_handler *nvhost_gr3d_t20_ctxhandler_init( u32 syncpt, u32 waitbase, struct nvhost_channel *ch) { - struct nvmap_client *nvmap; + struct mem_mgr *memmgr; u32 *save_ptr; struct host1x_hwctx_handler *p; p = kmalloc(sizeof(*p), GFP_KERNEL); if (!p) return NULL; - nvmap = nvhost_get_host(ch->dev)->nvmap; + memmgr = nvhost_get_host(ch->dev)->memmgr; p->syncpt = syncpt; p->waitbase = waitbase; setup_save(p, NULL); - p->save_buf = nvmap_alloc(nvmap, p->save_size * sizeof(u32), 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); + p->save_buf = mem_op().alloc(memmgr, p->save_size * sizeof(u32), 32, + mem_mgr_flag_write_combine); if (IS_ERR(p->save_buf)) { p->save_buf = NULL; return NULL; @@ -374,14 +378,14 @@ struct nvhost_hwctx_handler *nvhost_gr3d_t20_ctxhandler_init( p->save_slots = 1; - save_ptr = nvmap_mmap(p->save_buf); + save_ptr = mem_op().mmap(p->save_buf); if (!save_ptr) { - nvmap_free(nvmap, p->save_buf); + mem_op().put(memmgr, p->save_buf); p->save_buf = NULL; return NULL; } - p->save_phys = nvmap_pin(nvmap, p->save_buf); + p->save_phys = mem_op().pin(memmgr, p->save_buf); setup_save(p, save_ptr); diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.h b/drivers/video/tegra/host/gr3d/gr3d_t20.h index 5fe6d50d0c30..e6fb8fdf8aba 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t20.h +++ b/drivers/video/tegra/host/gr3d/gr3d_t20.h @@ -21,7 +21,10 @@ #ifndef __NVHOST_GR3D_GR3D_T20_H #define __NVHOST_GR3D_GR3D_T20_H +#include <linux/types.h> + struct nvhost_hwctx_handler; +struct nvhost_channel; struct nvhost_hwctx_handler *nvhost_gr3d_t20_ctxhandler_init( u32 syncpt, u32 waitbase, diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c index ab4d04f89ab2..c35fea2f3ac2 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t30.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c @@ -19,18 +19,18 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" +#include "nvhost_cdma.h" #include "dev.h" -#include "host1x/host1x_hardware.h" -#include "host1x/host1x_syncpt.h" +#include "host1x/host1x01_hardware.h" #include "gr3d.h" +#include "chip_support.h" +#include "nvhost_memmgr.h" #include <mach/gpufuse.h> #include <mach/hardware.h> #include <linux/slab.h> -/* 99 > 2, which makes kernel panic if register set is incorrect */ -static int register_sets = 99; - static const struct hwctx_reginfo ctxsave_regs_3d_global[] = { HWCTX_REGINFO(0xe00, 4, DIRECT), HWCTX_REGINFO(0xe05, 30, DIRECT), @@ -112,28 +112,29 @@ static void save_push_v1(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) /* wait for 3d idle */ nvhost_cdma_push(cdma, nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0), - nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, - p->syncpt)); + nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), + p->syncpt)); nvhost_cdma_push(cdma, nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1), + host1x_uclass_wait_syncpt_base_r(), 1), nvhost_class_host_wait_syncpt_base(p->syncpt, p->waitbase, 1)); /* back to 3d */ nvhost_cdma_push(cdma, nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0), NVHOST_OPCODE_NOOP); + /* set register set 0 and 1 register read memory output addresses, and send their reads to memory */ - if (register_sets == 2) { - nvhost_cdma_push(cdma, - nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, 2), - nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, - 1)); - nvhost_cdma_push(cdma, - nvhost_opcode_nonincr(0x904, 1), - ctx->restore_phys + restore_set1_offset * 4); - } + + nvhost_cdma_push(cdma, + nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, 2), + nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, 1)); + nvhost_cdma_push(cdma, + nvhost_opcode_nonincr(0x904, 1), + ctx->restore_phys + restore_set1_offset * 4); + nvhost_cdma_push(cdma, nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, 1), nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, 1)); @@ -142,8 +143,9 @@ static void save_push_v1(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) ctx->restore_phys); /* gather the save buffer */ nvhost_cdma_push_gather(cdma, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, + nvhost_get_host(nctx->channel->dev)->memmgr, + p->save_buf, + 0, nvhost_opcode_gather(p->save_size), p->save_phys); } @@ -163,11 +165,11 @@ static void __init save_direct_v1(u32 *ptr, u32 start_reg, u32 count) nvhost_3dctx_restore_direct(ptr + 1, start_reg, count); ptr += RESTORE_DIRECT_SIZE; ptr[1] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INDOFF, 1); + host1x_uclass_indoff_r(), 1); ptr[2] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, start_reg, true); /* TODO could do this in the setclass if count < 6 */ - ptr[3] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); + ptr[3] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count); } static void __init save_indirect_v1(u32 *ptr, u32 offset_reg, u32 offset, @@ -181,10 +183,10 @@ static void __init save_indirect_v1(u32 *ptr, u32 offset_reg, u32 offset, ptr += RESTORE_INDIRECT_SIZE; ptr[2] = nvhost_opcode_imm(offset_reg, offset); ptr[3] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INDOFF, 1); + host1x_uclass_indoff_r(), 1); ptr[4] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, data_reg, false); - ptr[5] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); + ptr[5] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count); } static void __init save_end_v1(struct host1x_hwctx_handler *p, u32 *ptr) @@ -196,15 +198,16 @@ static void __init save_end_v1(struct host1x_hwctx_handler *p, u32 *ptr) ptr += RESTORE_END_SIZE; /* reset to dual reg if necessary */ ptr[1] = nvhost_opcode_imm(AR3D_GSHIM_WRITE_MASK, - (1 << register_sets) - 1); + (1 << 2) - 1); /* op_done syncpt incr to flush FDC */ - ptr[2] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, p->syncpt); + ptr[2] = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), p->syncpt); /* host wait for that syncpt incr, and advance the wait base */ ptr[3] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_WAIT_SYNCPT_BASE, + host1x_uclass_wait_syncpt_base_r(), nvhost_mask2( - NV_CLASS_HOST_WAIT_SYNCPT_BASE, - NV_CLASS_HOST_INCR_SYNCPT_BASE)); + host1x_uclass_wait_syncpt_base_r(), + host1x_uclass_incr_syncpt_base_r())); ptr[4] = nvhost_class_host_wait_syncpt_base(p->syncpt, p->waitbase, p->save_incrs - 1); ptr[5] = nvhost_class_host_incr_syncpt_base(p->waitbase, @@ -311,16 +314,13 @@ static void __init setup_save(struct host1x_hwctx_handler *p, u32 *ptr) }; int save_end_size = SAVE_END_V1_SIZE; - BUG_ON(register_sets > 2); - if (info.ptr) { save_begin_v1(p, info.ptr); info.ptr += SAVE_BEGIN_V1_SIZE; } /* read from set0, write cmds through set0, restore to set0 and 1 */ - if (register_sets == 2) - switch_gpu(&info, 0, 1, 3); + switch_gpu(&info, 0, 1, 3); /* save regs that are common to both sets */ setup_save_regs(&info, @@ -328,28 +328,26 @@ static void __init setup_save(struct host1x_hwctx_handler *p, u32 *ptr) ARRAY_SIZE(ctxsave_regs_3d_global)); /* read from set 0, write cmds through set0, restore to set0 */ - if (register_sets == 2) - switch_gpu(&info, 0, 1, 1); + switch_gpu(&info, 0, 1, 1); /* save set 0 specific regs */ setup_save_regs(&info, ctxsave_regs_3d_perset, ARRAY_SIZE(ctxsave_regs_3d_perset)); - if (register_sets == 2) { - /* read from set1, write cmds through set1, restore to set1 */ - switch_gpu(&info, 1, 2, 2); - /* note offset at which set 1 restore starts */ - restore_set1_offset = info.restore_count; - /* save set 1 specific regs */ - setup_save_regs(&info, - ctxsave_regs_3d_perset, - ARRAY_SIZE(ctxsave_regs_3d_perset)); - } + + /* read from set1, write cmds through set1, restore to set1 */ + switch_gpu(&info, 1, 2, 2); + /* note offset at which set 1 restore starts */ + restore_set1_offset = info.restore_count; + /* save set 1 specific regs */ + setup_save_regs(&info, + ctxsave_regs_3d_perset, + ARRAY_SIZE(ctxsave_regs_3d_perset)); + /* read from set0, write cmds through set1, restore to set0 and 1 */ - if (register_sets == 2) - switch_gpu(&info, 0, 2, 3); + switch_gpu(&info, 0, 2, 3); if (info.ptr) { save_end_v1(p, info.ptr); @@ -384,7 +382,7 @@ struct nvhost_hwctx_handler *nvhost_gr3d_t30_ctxhandler_init( u32 syncpt, u32 waitbase, struct nvhost_channel *ch) { - struct nvmap_client *nvmap; + struct mem_mgr *memmgr; u32 *save_ptr; struct host1x_hwctx_handler *p; @@ -392,38 +390,35 @@ struct nvhost_hwctx_handler *nvhost_gr3d_t30_ctxhandler_init( if (!p) return NULL; - nvmap = nvhost_get_host(ch->dev)->nvmap; - - register_sets = tegra_gpu_register_sets(); - BUG_ON(register_sets == 0 || register_sets > 2); + memmgr = nvhost_get_host(ch->dev)->memmgr; p->syncpt = syncpt; p->waitbase = waitbase; setup_save(p, NULL); - p->save_buf = nvmap_alloc(nvmap, p->save_size * 4, 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); + p->save_buf = mem_op().alloc(memmgr, p->save_size * 4, 32, + mem_mgr_flag_write_combine); if (IS_ERR(p->save_buf)) { p->save_buf = NULL; return NULL; } - p->save_slots = 6; - if (register_sets == 2) - p->save_slots += 2; + p->save_slots = 8; - save_ptr = nvmap_mmap(p->save_buf); + save_ptr = mem_op().mmap(p->save_buf); if (!save_ptr) { - nvmap_free(nvmap, p->save_buf); + mem_op().put(memmgr, p->save_buf); p->save_buf = NULL; return NULL; } - p->save_phys = nvmap_pin(nvmap, p->save_buf); + p->save_phys = mem_op().pin(memmgr, p->save_buf); setup_save(p, save_ptr); + mem_op().munmap(p->save_buf, save_ptr); + p->h.alloc = ctx3d_alloc_v1; p->h.save_push = save_push_v1; p->h.save_service = NULL; diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.h b/drivers/video/tegra/host/gr3d/gr3d_t30.h index d1b787e14b44..94d5dc0f353b 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t30.h +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.h @@ -21,7 +21,10 @@ #ifndef __NVHOST_GR3D_GR3D_T30_H #define __NVHOST_GR3D_GR3D_T30_H +#include <linux/types.h> + struct nvhost_hwctx_handler; +struct nvhost_channel; struct nvhost_hwctx_handler *nvhost_gr3d_t30_ctxhandler_init( u32 syncpt, u32 waitbase, diff --git a/drivers/video/tegra/host/host1x/Makefile b/drivers/video/tegra/host/host1x/Makefile index c3214ffe147b..76664945e12b 100644 --- a/drivers/video/tegra/host/host1x/Makefile +++ b/drivers/video/tegra/host/host1x/Makefile @@ -3,10 +3,6 @@ GCOV_PROFILE := y EXTRA_CFLAGS += -Idrivers/video/tegra/host nvhost-host1x-objs = \ - host1x_syncpt.o \ - host1x_channel.o \ - host1x_intr.o \ - host1x_cdma.o \ - host1x_debug.o + host1x.o obj-$(CONFIG_TEGRA_GRHOST) += nvhost-host1x.o diff --git a/drivers/video/tegra/host/host1x/host1x.c b/drivers/video/tegra/host/host1x/host1x.c new file mode 100644 index 000000000000..33ebc1ff5d22 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x.c @@ -0,0 +1,537 @@ +/* + * drivers/video/tegra/host/dev.c + * + * Tegra Graphics Host Driver Entrypoint + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/file.h> +#include <linux/clk.h> + +#include "dev.h" +#include "bus.h" +#include <trace/events/nvhost.h> + +#include <linux/nvhost.h> +#include <linux/nvhost_ioctl.h> + +#include "debug.h" +#include "bus_client.h" +#include "nvhost_acm.h" +#include "nvhost_channel.h" +#include "nvhost_job.h" + +#define DRIVER_NAME "host1x" + +struct nvhost_ctrl_userctx { + struct nvhost_master *dev; + u32 *mod_locks; +}; + +static int nvhost_ctrlrelease(struct inode *inode, struct file *filp) +{ + struct nvhost_ctrl_userctx *priv = filp->private_data; + int i; + + trace_nvhost_ctrlrelease(priv->dev->dev->name); + + filp->private_data = NULL; + if (priv->mod_locks[0]) + nvhost_module_idle(priv->dev->dev); + for (i = 1; i < nvhost_syncpt_nb_mlocks(&priv->dev->syncpt); i++) + if (priv->mod_locks[i]) + nvhost_mutex_unlock(&priv->dev->syncpt, i); + kfree(priv->mod_locks); + kfree(priv); + return 0; +} + +static int nvhost_ctrlopen(struct inode *inode, struct file *filp) +{ + struct nvhost_master *host = + container_of(inode->i_cdev, struct nvhost_master, cdev); + struct nvhost_ctrl_userctx *priv; + u32 *mod_locks; + + trace_nvhost_ctrlopen(host->dev->name); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + mod_locks = kzalloc(sizeof(u32) + * nvhost_syncpt_nb_mlocks(&host->syncpt), + GFP_KERNEL); + + if (!(priv && mod_locks)) { + kfree(priv); + kfree(mod_locks); + return -ENOMEM; + } + + priv->dev = host; + priv->mod_locks = mod_locks; + filp->private_data = priv; + return 0; +} + +static int nvhost_ioctl_ctrl_syncpt_read(struct nvhost_ctrl_userctx *ctx, + struct nvhost_ctrl_syncpt_read_args *args) +{ + if (args->id >= nvhost_syncpt_nb_pts(&ctx->dev->syncpt)) + return -EINVAL; + args->value = nvhost_syncpt_read(&ctx->dev->syncpt, args->id); + trace_nvhost_ioctl_ctrl_syncpt_read(args->id, args->value); + return 0; +} + +static int nvhost_ioctl_ctrl_syncpt_incr(struct nvhost_ctrl_userctx *ctx, + struct nvhost_ctrl_syncpt_incr_args *args) +{ + if (args->id >= nvhost_syncpt_nb_pts(&ctx->dev->syncpt)) + return -EINVAL; + trace_nvhost_ioctl_ctrl_syncpt_incr(args->id); + nvhost_syncpt_incr(&ctx->dev->syncpt, args->id); + return 0; +} + +static int nvhost_ioctl_ctrl_syncpt_waitex(struct nvhost_ctrl_userctx *ctx, + struct nvhost_ctrl_syncpt_waitex_args *args) +{ + u32 timeout; + int err; + if (args->id >= nvhost_syncpt_nb_pts(&ctx->dev->syncpt)) + return -EINVAL; + if (args->timeout == NVHOST_NO_TIMEOUT) + timeout = MAX_SCHEDULE_TIMEOUT; + else + timeout = (u32)msecs_to_jiffies(args->timeout); + + err = nvhost_syncpt_wait_timeout(&ctx->dev->syncpt, args->id, + args->thresh, timeout, &args->value); + trace_nvhost_ioctl_ctrl_syncpt_wait(args->id, args->thresh, + args->timeout, args->value, err); + + return err; +} + +static int nvhost_ioctl_ctrl_module_mutex(struct nvhost_ctrl_userctx *ctx, + struct nvhost_ctrl_module_mutex_args *args) +{ + int err = 0; + if (args->id >= nvhost_syncpt_nb_mlocks(&ctx->dev->syncpt) || + args->lock > 1) + return -EINVAL; + + trace_nvhost_ioctl_ctrl_module_mutex(args->lock, args->id); + if (args->lock && !ctx->mod_locks[args->id]) { + if (args->id == 0) + nvhost_module_busy(ctx->dev->dev); + else + err = nvhost_mutex_try_lock(&ctx->dev->syncpt, + args->id); + if (!err) + ctx->mod_locks[args->id] = 1; + } else if (!args->lock && ctx->mod_locks[args->id]) { + if (args->id == 0) + nvhost_module_idle(ctx->dev->dev); + else + nvhost_mutex_unlock(&ctx->dev->syncpt, args->id); + ctx->mod_locks[args->id] = 0; + } + return err; +} + +static int match_by_moduleid(struct device *dev, void *data) +{ + struct nvhost_device *ndev = to_nvhost_device(dev); + u32 id = (u32)data; + + return id == ndev->moduleid; +} + +static struct nvhost_device *get_ndev_by_moduleid(struct nvhost_master *host, + u32 id) +{ + struct device *dev = bus_find_device(&nvhost_bus_inst->nvhost_bus_type, + NULL, (void *)id, match_by_moduleid); + + return dev ? to_nvhost_device(dev) : NULL; +} + +static int nvhost_ioctl_ctrl_module_regrdwr(struct nvhost_ctrl_userctx *ctx, + struct nvhost_ctrl_module_regrdwr_args *args) +{ + u32 num_offsets = args->num_offsets; + u32 *offsets = args->offsets; + u32 *values = args->values; + u32 vals[64]; + struct nvhost_device *ndev; + + trace_nvhost_ioctl_ctrl_module_regrdwr(args->id, + args->num_offsets, args->write); + /* Check that there is something to read and that block size is + * u32 aligned */ + if (num_offsets == 0 || args->block_size & 3) + return -EINVAL; + + ndev = get_ndev_by_moduleid(ctx->dev, args->id); + if (!ndev) + return -EINVAL; + + while (num_offsets--) { + int err; + int remaining = args->block_size >> 2; + u32 offs; + if (get_user(offs, offsets)) + return -EFAULT; + offsets++; + while (remaining) { + int batch = min(remaining, 64); + if (args->write) { + if (copy_from_user(vals, values, + batch*sizeof(u32))) + return -EFAULT; + err = nvhost_write_module_regs(ndev, + offs, batch, vals); + if (err) + return err; + } else { + err = nvhost_read_module_regs(ndev, + offs, batch, vals); + if (err) + return err; + if (copy_to_user(values, vals, + batch*sizeof(u32))) + return -EFAULT; + } + remaining -= batch; + offs += batch*sizeof(u32); + values += batch; + } + } + + return 0; +} + +static int nvhost_ioctl_ctrl_get_version(struct nvhost_ctrl_userctx *ctx, + struct nvhost_get_param_args *args) +{ + args->value = NVHOST_SUBMIT_VERSION_MAX_SUPPORTED; + return 0; +} + +static long nvhost_ctrlctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct nvhost_ctrl_userctx *priv = filp->private_data; + u8 buf[NVHOST_IOCTL_CTRL_MAX_ARG_SIZE]; + int err = 0; + + if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) || + (_IOC_NR(cmd) == 0) || + (_IOC_NR(cmd) > NVHOST_IOCTL_CTRL_LAST) || + (_IOC_SIZE(cmd) > NVHOST_IOCTL_CTRL_MAX_ARG_SIZE)) + return -EFAULT; + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + } + + switch (cmd) { + case NVHOST_IOCTL_CTRL_SYNCPT_READ: + err = nvhost_ioctl_ctrl_syncpt_read(priv, (void *)buf); + break; + case NVHOST_IOCTL_CTRL_SYNCPT_INCR: + err = nvhost_ioctl_ctrl_syncpt_incr(priv, (void *)buf); + break; + case NVHOST_IOCTL_CTRL_SYNCPT_WAIT: + err = nvhost_ioctl_ctrl_syncpt_waitex(priv, (void *)buf); + break; + case NVHOST_IOCTL_CTRL_MODULE_MUTEX: + err = nvhost_ioctl_ctrl_module_mutex(priv, (void *)buf); + break; + case NVHOST_IOCTL_CTRL_MODULE_REGRDWR: + err = nvhost_ioctl_ctrl_module_regrdwr(priv, (void *)buf); + break; + case NVHOST_IOCTL_CTRL_SYNCPT_WAITEX: + err = nvhost_ioctl_ctrl_syncpt_waitex(priv, (void *)buf); + break; + case NVHOST_IOCTL_CTRL_GET_VERSION: + err = nvhost_ioctl_ctrl_get_version(priv, (void *)buf); + break; + default: + err = -ENOTTY; + break; + } + + if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) + err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd)); + + return err; +} + +static const struct file_operations nvhost_ctrlops = { + .owner = THIS_MODULE, + .release = nvhost_ctrlrelease, + .open = nvhost_ctrlopen, + .unlocked_ioctl = nvhost_ctrlctl +}; + +static void power_on_host(struct nvhost_device *dev) +{ + struct nvhost_master *host = nvhost_get_drvdata(dev); + nvhost_syncpt_reset(&host->syncpt); + nvhost_intr_start(&host->intr, clk_get_rate(dev->clk[0])); +} + +static int power_off_host(struct nvhost_device *dev) +{ + struct nvhost_master *host = nvhost_get_drvdata(dev); + nvhost_syncpt_save(&host->syncpt); + nvhost_intr_stop(&host->intr); + return 0; +} + +static int __devinit nvhost_user_init(struct nvhost_master *host) +{ + int err, devno; + + host->nvhost_class = class_create(THIS_MODULE, IFACE_NAME); + if (IS_ERR(host->nvhost_class)) { + err = PTR_ERR(host->nvhost_class); + dev_err(&host->dev->dev, "failed to create class\n"); + goto fail; + } + + err = alloc_chrdev_region(&devno, 0, 1, IFACE_NAME); + if (err < 0) { + dev_err(&host->dev->dev, "failed to reserve chrdev region\n"); + goto fail; + } + + cdev_init(&host->cdev, &nvhost_ctrlops); + host->cdev.owner = THIS_MODULE; + err = cdev_add(&host->cdev, devno, 1); + if (err < 0) + goto fail; + host->ctrl = device_create(host->nvhost_class, NULL, devno, NULL, + IFACE_NAME "-ctrl"); + if (IS_ERR(host->ctrl)) { + err = PTR_ERR(host->ctrl); + dev_err(&host->dev->dev, "failed to create ctrl device\n"); + goto fail; + } + + return 0; +fail: + return err; +} + +struct nvhost_channel *nvhost_alloc_channel(struct nvhost_device *dev) +{ + BUG_ON(!host_device_op().alloc_nvhost_channel); + return host_device_op().alloc_nvhost_channel(dev); +} + +void nvhost_free_channel(struct nvhost_channel *ch) +{ + BUG_ON(!host_device_op().free_nvhost_channel); + host_device_op().free_nvhost_channel(ch); +} + +static void nvhost_free_resources(struct nvhost_master *host) +{ + kfree(host->intr.syncpt); + host->intr.syncpt = 0; +} + +static int __devinit nvhost_alloc_resources(struct nvhost_master *host) +{ + int err; + + err = nvhost_init_chip_support(host); + if (err) + return err; + + host->intr.syncpt = kzalloc(sizeof(struct nvhost_intr_syncpt) * + nvhost_syncpt_nb_pts(&host->syncpt), + GFP_KERNEL); + + if (!host->intr.syncpt) { + /* frees happen in the support removal phase */ + return -ENOMEM; + } + + return 0; +} + +static int __devinit nvhost_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) +{ + struct nvhost_master *host; + struct resource *regs, *intr0, *intr1; + int i, err; + + regs = nvhost_get_resource(dev, IORESOURCE_MEM, 0); + intr0 = nvhost_get_resource(dev, IORESOURCE_IRQ, 0); + intr1 = nvhost_get_resource(dev, IORESOURCE_IRQ, 1); + + if (!regs || !intr0 || !intr1) { + dev_err(&dev->dev, "missing required platform resources\n"); + return -ENXIO; + } + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + /* Register host1x device as bus master */ + host->dev = dev; + + /* Copy host1x parameters */ + memcpy(&host->info, dev->dev.platform_data, + sizeof(struct host1x_device_info)); + + host->reg_mem = request_mem_region(regs->start, + resource_size(regs), dev->name); + if (!host->reg_mem) { + dev_err(&dev->dev, "failed to get host register memory\n"); + err = -ENXIO; + goto fail; + } + + host->aperture = ioremap(regs->start, resource_size(regs)); + if (!host->aperture) { + dev_err(&dev->dev, "failed to remap host registers\n"); + err = -ENXIO; + goto fail; + } + + err = nvhost_alloc_resources(host); + if (err) { + dev_err(&dev->dev, "failed to init chip support\n"); + goto fail; + } + + host->memmgr = mem_op().alloc_mgr(); + if (!host->memmgr) { + dev_err(&dev->dev, "unable to create nvmap client\n"); + err = -EIO; + goto fail; + } + + /* Register host1x device as bus master */ + host->dev = dev; + + /* Give pointer to host1x via driver */ + nvhost_set_drvdata(dev, host); + + nvhost_bus_add_host(host); + + err = nvhost_syncpt_init(dev, &host->syncpt); + if (err) + goto fail; + + err = nvhost_intr_init(&host->intr, intr1->start, intr0->start); + if (err) + goto fail; + + err = nvhost_user_init(host); + if (err) + goto fail; + + err = nvhost_module_init(dev); + if (err) + goto fail; + + for (i = 0; i < host->dev->num_clks; i++) + clk_enable(host->dev->clk[i]); + nvhost_syncpt_reset(&host->syncpt); + for (i = 0; i < host->dev->num_clks; i++) + clk_disable(host->dev->clk[0]); + + nvhost_debug_init(host); + + dev_info(&dev->dev, "initialized\n"); + return 0; + +fail: + nvhost_free_resources(host); + if (host->memmgr) + mem_op().put_mgr(host->memmgr); + kfree(host); + return err; +} + +static int __exit nvhost_remove(struct nvhost_device *dev) +{ + struct nvhost_master *host = nvhost_get_drvdata(dev); + nvhost_intr_deinit(&host->intr); + nvhost_syncpt_deinit(&host->syncpt); + nvhost_free_resources(host); + return 0; +} + +static int nvhost_suspend(struct nvhost_device *dev, pm_message_t state) +{ + struct nvhost_master *host = nvhost_get_drvdata(dev); + int ret = 0; + + ret = nvhost_module_suspend(host->dev); + dev_info(&dev->dev, "suspend status: %d\n", ret); + + return ret; +} + +static int nvhost_resume(struct nvhost_device *dev) +{ + dev_info(&dev->dev, "resuming\n"); + return 0; +} + +static struct nvhost_driver nvhost_driver = { + .probe = nvhost_probe, + .remove = __exit_p(nvhost_remove), + .suspend = nvhost_suspend, + .resume = nvhost_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME + }, + .finalize_poweron = power_on_host, + .prepare_poweroff = power_off_host, +}; + +static int __init nvhost_mod_init(void) +{ + return nvhost_driver_register(&nvhost_driver); +} + +static void __exit nvhost_mod_exit(void) +{ + nvhost_driver_unregister(&nvhost_driver); +} + +/* host1x master device needs nvmap to be instantiated first. + * nvmap is instantiated via fs_initcall. + * Hence instantiate host1x master device using rootfs_initcall + * which is one level after fs_initcall. */ +rootfs_initcall(nvhost_mod_init); +module_exit(nvhost_mod_exit); + diff --git a/drivers/video/tegra/host/host1x/host1x.h b/drivers/video/tegra/host/host1x/host1x.h new file mode 100644 index 000000000000..49916b0757d4 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x.h @@ -0,0 +1,78 @@ +/* + * drivers/video/tegra/host/host1x/host1x.h + * + * Tegra Graphics Host Driver Entrypoint + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_HOST1X_H +#define __NVHOST_HOST1X_H + +#include <linux/cdev.h> +#include <linux/nvhost.h> + +#include "nvhost_syncpt.h" +#include "nvhost_intr.h" + +#define TRACE_MAX_LENGTH 128U +#define IFACE_NAME "nvhost" + +struct nvhost_channel; +struct mem_mgr; + +struct host1x_device_info { + int nb_channels; /* host1x: num channels supported */ + int nb_pts; /* host1x: num syncpoints supported */ + int nb_bases; /* host1x: num syncpoints supported */ + u32 client_managed; /* host1x: client managed syncpts */ + int nb_mlocks; /* host1x: number of mlocks */ + const char **syncpt_names; /* names of sync points */ +}; + +struct nvhost_master { + void __iomem *aperture; + void __iomem *sync_aperture; + struct resource *reg_mem; + struct class *nvhost_class; + struct cdev cdev; + struct device *ctrl; + struct nvhost_syncpt syncpt; + struct mem_mgr *memmgr; + struct nvhost_intr intr; + struct nvhost_device *dev; + atomic_t clientid; + + struct host1x_device_info info; +}; + +extern struct nvhost_master *nvhost; + +void nvhost_debug_init(struct nvhost_master *master); +void nvhost_debug_dump(struct nvhost_master *master); + +struct nvhost_channel *nvhost_alloc_channel(struct nvhost_device *dev); +void nvhost_free_channel(struct nvhost_channel *ch); + +extern pid_t nvhost_debug_null_kickoff_pid; + +static inline struct nvhost_master *nvhost_get_host(struct nvhost_device *_dev) +{ + return (_dev->dev.parent) ? \ + ((struct nvhost_master *) dev_get_drvdata(_dev->dev.parent)) : \ + ((struct nvhost_master *) dev_get_drvdata(&(_dev->dev))); +} + +#endif diff --git a/drivers/video/tegra/host/host1x/host1x01_hardware.h b/drivers/video/tegra/host/host1x/host1x01_hardware.h new file mode 100644 index 000000000000..1d30cc74266a --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x01_hardware.h @@ -0,0 +1,170 @@ +/* + * drivers/video/tegra/host/host1x/host1x01_hardware.h + * + * Tegra Graphics Host Register Offsets for T20/T30 + * + * Copyright (c) 2010-2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_HOST1X01_HARDWARE_H +#define __NVHOST_HOST1X01_HARDWARE_H + +#include <linux/types.h> +#include <linux/bitops.h> +#include "hw_host1x01_channel.h" +#include "hw_host1x01_sync.h" +#include "hw_host1x01_uclass.h" + +/* class ids */ +enum { + NV_HOST1X_CLASS_ID = 0x1, + NV_VIDEO_ENCODE_MPEG_CLASS_ID = 0x20, + NV_GRAPHICS_3D_CLASS_ID = 0x60 +}; + + +/* channel registers */ +#define NV_HOST1X_CHANNEL_MAP_SIZE_BYTES 16384 +#define NV_HOST1X_SYNC_MLOCK_NUM 16 + +/* sync registers */ +#define HOST1X_CHANNEL_SYNC_REG_BASE 0x3000 +#define NV_HOST1X_NB_MLOCKS 16 + +static inline u32 nvhost_class_host_wait_syncpt( + unsigned indx, unsigned threshold) +{ + return (indx << 24) | (threshold & 0xffffff); +} + +static inline u32 nvhost_class_host_load_syncpt_base( + unsigned indx, unsigned threshold) +{ + return host1x_uclass_wait_syncpt_indx_f(indx) + | host1x_uclass_wait_syncpt_thresh_f(threshold); +} + +static inline u32 nvhost_class_host_wait_syncpt_base( + unsigned indx, unsigned base_indx, unsigned offset) +{ + return host1x_uclass_wait_syncpt_base_indx_f(indx) + | host1x_uclass_wait_syncpt_base_base_indx_f(base_indx) + | host1x_uclass_wait_syncpt_base_offset_f(offset); +} + +static inline u32 nvhost_class_host_incr_syncpt_base( + unsigned base_indx, unsigned offset) +{ + return host1x_uclass_incr_syncpt_base_base_indx_f(base_indx) + | host1x_uclass_incr_syncpt_base_offset_f(offset); +} + +static inline u32 nvhost_class_host_incr_syncpt( + unsigned cond, unsigned indx) +{ + return host1x_uclass_incr_syncpt_cond_f(cond) + | host1x_uclass_incr_syncpt_indx_f(indx); +} + +enum { + NV_HOST_MODULE_HOST1X = 0, + NV_HOST_MODULE_MPE = 1, + NV_HOST_MODULE_GR3D = 6 +}; + +static inline u32 nvhost_class_host_indoff_reg_write( + unsigned mod_id, unsigned offset, bool auto_inc) +{ + u32 v = host1x_uclass_indoff_indbe_f(0xf) + | host1x_uclass_indoff_indmodid_f(mod_id) + | host1x_uclass_indoff_indroffset_f(offset); + if (auto_inc) + v |= host1x_uclass_indoff_autoinc_f(1); + return v; +} + +static inline u32 nvhost_class_host_indoff_reg_read( + unsigned mod_id, unsigned offset, bool auto_inc) +{ + u32 v = host1x_uclass_indoff_indmodid_f(mod_id) + | host1x_uclass_indoff_indroffset_f(offset) + | host1x_uclass_indoff_rwn_read_v(); + if (auto_inc) + v |= host1x_uclass_indoff_autoinc_f(1); + return v; +} + + +/* cdma opcodes */ +static inline u32 nvhost_opcode_setclass( + unsigned class_id, unsigned offset, unsigned mask) +{ + return (0 << 28) | (offset << 16) | (class_id << 6) | mask; +} + +static inline u32 nvhost_opcode_incr(unsigned offset, unsigned count) +{ + return (1 << 28) | (offset << 16) | count; +} + +static inline u32 nvhost_opcode_nonincr(unsigned offset, unsigned count) +{ + return (2 << 28) | (offset << 16) | count; +} + +static inline u32 nvhost_opcode_mask(unsigned offset, unsigned mask) +{ + return (3 << 28) | (offset << 16) | mask; +} + +static inline u32 nvhost_opcode_imm(unsigned offset, unsigned value) +{ + return (4 << 28) | (offset << 16) | value; +} + +static inline u32 nvhost_opcode_imm_incr_syncpt(unsigned cond, unsigned indx) +{ + return nvhost_opcode_imm(host1x_uclass_incr_syncpt_r(), + nvhost_class_host_incr_syncpt(cond, indx)); +} + +static inline u32 nvhost_opcode_restart(unsigned address) +{ + return (5 << 28) | (address >> 4); +} + +static inline u32 nvhost_opcode_gather(unsigned count) +{ + return (6 << 28) | count; +} + +static inline u32 nvhost_opcode_gather_nonincr(unsigned offset, unsigned count) +{ + return (6 << 28) | (offset << 16) | BIT(15) | count; +} + +static inline u32 nvhost_opcode_gather_incr(unsigned offset, unsigned count) +{ + return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; +} + +#define NVHOST_OPCODE_NOOP nvhost_opcode_nonincr(0, 0) + +static inline u32 nvhost_mask2(unsigned x, unsigned y) +{ + return 1 | (1 << (y - x)); +} + +#endif diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.c b/drivers/video/tegra/host/host1x/host1x_cdma.c index cdd6026718b1..2e7ff5783a37 100644 --- a/drivers/video/tegra/host/host1x/host1x_cdma.c +++ b/drivers/video/tegra/host/host1x/host1x_cdma.c @@ -19,19 +19,21 @@ */ #include <linux/slab.h> +#include "nvhost_acm.h" #include "nvhost_cdma.h" +#include "nvhost_channel.h" #include "dev.h" +#include "chip_support.h" +#include "nvhost_memmgr.h" -#include "host1x_hardware.h" -#include "host1x_syncpt.h" #include "host1x_cdma.h" #include "host1x_hwctx.h" static inline u32 host1x_channel_dmactrl(int stop, int get_rst, int init_get) { - return HOST1X_CREATE(CHANNEL_DMACTRL, DMASTOP, stop) - | HOST1X_CREATE(CHANNEL_DMACTRL, DMAGETRST, get_rst) - | HOST1X_CREATE(CHANNEL_DMACTRL, DMAINITGET, init_get); + return host1x_channel_dmactrl_dmastop_f(stop) + | host1x_channel_dmactrl_dmagetrst_f(get_rst) + | host1x_channel_dmactrl_dmainitget_f(init_get); } static void cdma_timeout_handler(struct work_struct *work); @@ -60,38 +62,38 @@ static void push_buffer_reset(struct push_buffer *pb) static int push_buffer_init(struct push_buffer *pb) { struct nvhost_cdma *cdma = pb_to_cdma(pb); - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); + struct mem_mgr *mgr = cdma_to_memmgr(cdma); pb->mem = NULL; pb->mapped = NULL; pb->phys = 0; - pb->nvmap = NULL; + pb->client_handle = NULL; - BUG_ON(!cdma_pb_op(cdma).reset); - cdma_pb_op(cdma).reset(pb); + BUG_ON(!cdma_pb_op().reset); + cdma_pb_op().reset(pb); /* allocate and map pushbuffer memory */ - pb->mem = nvmap_alloc(nvmap, PUSH_BUFFER_SIZE + 4, 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); + pb->mem = mem_op().alloc(mgr, PUSH_BUFFER_SIZE + 4, 32, + mem_mgr_flag_write_combine); if (IS_ERR_OR_NULL(pb->mem)) { pb->mem = NULL; goto fail; } - pb->mapped = nvmap_mmap(pb->mem); + pb->mapped = mem_op().mmap(pb->mem); if (pb->mapped == NULL) goto fail; /* pin pushbuffer and get physical address */ - pb->phys = nvmap_pin(nvmap, pb->mem); + pb->phys = mem_op().pin(mgr, pb->mem); if (pb->phys >= 0xfffff000) { pb->phys = 0; goto fail; } /* memory for storing nvmap client and handles for each opcode pair */ - pb->nvmap = kzalloc(NVHOST_GATHER_QUEUE_SIZE * - sizeof(struct nvmap_client_handle), + pb->client_handle = kzalloc(NVHOST_GATHER_QUEUE_SIZE * + sizeof(struct mem_mgr_handle), GFP_KERNEL); - if (!pb->nvmap) + if (!pb->client_handle) goto fail; /* put the restart at the end of pushbuffer memory */ @@ -101,7 +103,7 @@ static int push_buffer_init(struct push_buffer *pb) return 0; fail: - cdma_pb_op(cdma).destroy(pb); + cdma_pb_op().destroy(pb); return -ENOMEM; } @@ -111,22 +113,22 @@ fail: static void push_buffer_destroy(struct push_buffer *pb) { struct nvhost_cdma *cdma = pb_to_cdma(pb); - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); + struct mem_mgr *mgr = cdma_to_memmgr(cdma); if (pb->mapped) - nvmap_munmap(pb->mem, pb->mapped); + mem_op().munmap(pb->mem, pb->mapped); if (pb->phys != 0) - nvmap_unpin(nvmap, pb->mem); + mem_op().unpin(mgr, pb->mem); if (pb->mem) - nvmap_free(nvmap, pb->mem); + mem_op().put(mgr, pb->mem); - kfree(pb->nvmap); + kfree(pb->client_handle); pb->mem = NULL; pb->mapped = NULL; pb->phys = 0; - pb->nvmap = 0; + pb->client_handle = 0; } /** @@ -134,8 +136,8 @@ static void push_buffer_destroy(struct push_buffer *pb) * Caller must ensure push buffer is not full */ static void push_buffer_push_to(struct push_buffer *pb, - struct nvmap_client *client, - struct nvmap_handle *handle, u32 op1, u32 op2) + struct mem_mgr *client, struct mem_handle *handle, + u32 op1, u32 op2) { u32 cur = pb->cur; u32 *p = (u32 *)((u32)pb->mapped + cur); @@ -143,8 +145,8 @@ static void push_buffer_push_to(struct push_buffer *pb, BUG_ON(cur == pb->fence); *(p++) = op1; *(p++) = op2; - pb->nvmap[cur_nvmap].client = client; - pb->nvmap[cur_nvmap].handle = handle; + pb->client_handle[cur_nvmap].client = client; + pb->client_handle[cur_nvmap].handle = handle; pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1); } @@ -161,8 +163,7 @@ static void push_buffer_pop_from(struct push_buffer *pb, for (i = 0; i < slots; i++) { int cur_fence_nvmap = (fence_nvmap+i) & (NVHOST_GATHER_QUEUE_SIZE - 1); - struct nvmap_client_handle *h = - &pb->nvmap[cur_fence_nvmap]; + struct mem_mgr_handle *h = &pb->client_handle[cur_fence_nvmap]; h->client = NULL; h->handle = NULL; } @@ -191,96 +192,25 @@ static u32 push_buffer_putptr(struct push_buffer *pb) */ /** - * Init timeout and syncpt incr buffer resources + * Init timeout resources */ static int cdma_timeout_init(struct nvhost_cdma *cdma, u32 syncpt_id) { - struct nvhost_master *dev = cdma_to_dev(cdma); - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); - struct syncpt_buffer *sb = &cdma->syncpt_buffer; - struct nvhost_channel *ch = cdma_to_channel(cdma); - u32 i = 0; - if (syncpt_id == NVSYNCPT_INVALID) return -EINVAL; - /* allocate and map syncpt incr memory */ - sb->mem = nvmap_alloc(nvmap, - (SYNCPT_INCR_BUFFER_SIZE_WORDS * sizeof(u32)), 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); - if (IS_ERR_OR_NULL(sb->mem)) { - sb->mem = NULL; - goto fail; - } - sb->mapped = nvmap_mmap(sb->mem); - if (sb->mapped == NULL) - goto fail; - - /* pin syncpt buffer and get physical address */ - sb->phys = nvmap_pin(nvmap, sb->mem); - if (sb->phys >= 0xfffff000) { - sb->phys = 0; - goto fail; - } - - dev_dbg(&dev->dev->dev, "%s: SYNCPT_INCR buffer at 0x%x\n", - __func__, sb->phys); - - sb->words_per_incr = (syncpt_id == NVSYNCPT_3D) ? 5 : 3; - sb->incr_per_buffer = (SYNCPT_INCR_BUFFER_SIZE_WORDS / - sb->words_per_incr); - - /* init buffer with SETCL and INCR_SYNCPT methods */ - while (i < sb->incr_per_buffer) { - sb->mapped[i++] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - 0, 0); - sb->mapped[i++] = nvhost_opcode_imm_incr_syncpt( - NV_SYNCPT_IMMEDIATE, - syncpt_id); - if (syncpt_id == NVSYNCPT_3D) { - /* also contains base increments */ - sb->mapped[i++] = nvhost_opcode_nonincr( - NV_CLASS_HOST_INCR_SYNCPT_BASE, - 1); - sb->mapped[i++] = nvhost_class_host_incr_syncpt_base( - NVWAITBASE_3D, 1); - } - sb->mapped[i++] = nvhost_opcode_setclass(ch->dev->class, - 0, 0); - } - wmb(); - INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler); cdma->timeout.initialized = true; return 0; -fail: - cdma_op(cdma).timeout_destroy(cdma); - return -ENOMEM; } /** - * Clean up timeout syncpt buffer resources + * Clean up timeout resources */ static void cdma_timeout_destroy(struct nvhost_cdma *cdma) { - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); - struct syncpt_buffer *sb = &cdma->syncpt_buffer; - - if (sb->mapped) - nvmap_munmap(sb->mem, sb->mapped); - - if (sb->phys != 0) - nvmap_unpin(nvmap, sb->mem); - - if (sb->mem) - nvmap_free(nvmap, sb->mem); - - sb->mem = NULL; - sb->mapped = NULL; - sb->phys = 0; - if (cdma->timeout.initialized) cancel_delayed_work(&cdma->timeout.wq); cdma->timeout.initialized = false; @@ -290,7 +220,8 @@ static void cdma_timeout_destroy(struct nvhost_cdma *cdma) * Increment timedout buffer's syncpt via CPU. */ static void cdma_timeout_cpu_incr(struct nvhost_cdma *cdma, u32 getptr, - u32 syncpt_incrs, u32 syncval, u32 nr_slots) + u32 syncpt_incrs, u32 syncval, u32 nr_slots, + u32 waitbases) { struct nvhost_master *dev = cdma_to_dev(cdma); struct push_buffer *pb = &cdma->push_buffer; @@ -303,10 +234,10 @@ static void cdma_timeout_cpu_incr(struct nvhost_cdma *cdma, u32 getptr, nvhost_syncpt_update_min(&dev->syncpt, cdma->timeout.syncpt_id); /* update WAITBASE_3D by same number of incrs */ - if (cdma->timeout.syncpt_id == NVSYNCPT_3D) { + if (waitbases) { void __iomem *p; - p = dev->sync_aperture + HOST1X_SYNC_SYNCPT_BASE_0 + - (NVWAITBASE_3D * sizeof(u32)); + p = dev->sync_aperture + host1x_sync_syncpt_base_0_r() + + (ffs(waitbases) * sizeof(u32)); writel(syncval, p); } @@ -324,73 +255,6 @@ static void cdma_timeout_cpu_incr(struct nvhost_cdma *cdma, u32 getptr, } /** - * This routine is called at the point we transition back into a timed - * ctx. The syncpts are incremented via pushbuffer with a flag indicating - * whether there's a CTXSAVE that should be still executed (for the - * preceding HW ctx). - */ -static void cdma_timeout_pb_incr(struct nvhost_cdma *cdma, u32 getptr, - u32 syncpt_incrs, u32 nr_slots, - bool exec_ctxsave) -{ - struct nvhost_master *dev = cdma_to_dev(cdma); - struct syncpt_buffer *sb = &cdma->syncpt_buffer; - struct push_buffer *pb = &cdma->push_buffer; - struct host1x_hwctx *hwctx = to_host1x_hwctx(cdma->timeout.ctx); - u32 getidx, *p; - - /* should have enough slots to incr to desired count */ - BUG_ON(syncpt_incrs > (nr_slots * sb->incr_per_buffer)); - - getidx = getptr - pb->phys; - if (exec_ctxsave) { - /* don't disrupt the CTXSAVE of a good/non-timed out ctx */ - nr_slots -= hwctx->save_slots; - syncpt_incrs -= hwctx->save_incrs; - - getidx += (hwctx->save_slots * 8); - getidx &= (PUSH_BUFFER_SIZE - 1); - - dev_dbg(&dev->dev->dev, - "%s: exec CTXSAVE of prev ctx (slots %d, incrs %d)\n", - __func__, nr_slots, syncpt_incrs); - } - - while (syncpt_incrs) { - u32 incrs, count; - - /* GATHER count are incrs * number of DWORDs per incr */ - incrs = min(syncpt_incrs, sb->incr_per_buffer); - count = incrs * sb->words_per_incr; - - p = (u32 *)((u32)pb->mapped + getidx); - *(p++) = nvhost_opcode_gather(count); - *(p++) = sb->phys; - - dev_dbg(&dev->dev->dev, - "%s: GATHER at 0x%x, from 0x%x, dcount = %d\n", - __func__, - pb->phys + getidx, sb->phys, - (incrs * sb->words_per_incr)); - - syncpt_incrs -= incrs; - getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); - nr_slots--; - } - - /* NOP remaining slots */ - while (nr_slots--) { - p = (u32 *)((u32)pb->mapped + getidx); - *(p++) = NVHOST_OPCODE_NOOP; - *(p++) = NVHOST_OPCODE_NOOP; - dev_dbg(&dev->dev->dev, "%s: NOP at 0x%x\n", - __func__, pb->phys + getidx); - getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); - } - wmb(); -} - -/** * Start channel DMA */ static void cdma_start(struct nvhost_cdma *cdma) @@ -400,24 +264,24 @@ static void cdma_start(struct nvhost_cdma *cdma) if (cdma->running) return; - BUG_ON(!cdma_pb_op(cdma).putptr); - cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + BUG_ON(!cdma_pb_op().putptr); + cdma->last_put = cdma_pb_op().putptr(&cdma->push_buffer); writel(host1x_channel_dmactrl(true, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); /* set base, put, end pointer (all of memory) */ - writel(0, chan_regs + HOST1X_CHANNEL_DMASTART); - writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT); - writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND); + writel(0, chan_regs + host1x_channel_dmastart_r()); + writel(cdma->last_put, chan_regs + host1x_channel_dmaput_r()); + writel(0xFFFFFFFF, chan_regs + host1x_channel_dmaend_r()); /* reset GET */ writel(host1x_channel_dmactrl(true, true, true), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); /* start the command DMA */ writel(host1x_channel_dmactrl(false, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); cdma->running = true; } @@ -435,36 +299,36 @@ static void cdma_timeout_restart(struct nvhost_cdma *cdma, u32 getptr) if (cdma->running) return; - BUG_ON(!cdma_pb_op(cdma).putptr); - cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + BUG_ON(!cdma_pb_op().putptr); + cdma->last_put = cdma_pb_op().putptr(&cdma->push_buffer); writel(host1x_channel_dmactrl(true, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); /* set base, end pointer (all of memory) */ - writel(0, chan_regs + HOST1X_CHANNEL_DMASTART); - writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND); + writel(0, chan_regs + host1x_channel_dmastart_r()); + writel(0xFFFFFFFF, chan_regs + host1x_channel_dmaend_r()); /* set GET, by loading the value in PUT (then reset GET) */ - writel(getptr, chan_regs + HOST1X_CHANNEL_DMAPUT); + writel(getptr, chan_regs + host1x_channel_dmaput_r()); writel(host1x_channel_dmactrl(true, true, true), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); dev_dbg(&dev->dev->dev, "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", __func__, - readl(chan_regs + HOST1X_CHANNEL_DMAGET), - readl(chan_regs + HOST1X_CHANNEL_DMAPUT), + readl(chan_regs + host1x_channel_dmaget_r()), + readl(chan_regs + host1x_channel_dmaput_r()), cdma->last_put); /* deassert GET reset and set PUT */ writel(host1x_channel_dmactrl(true, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); - writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT); + chan_regs + host1x_channel_dmactrl_r()); + writel(cdma->last_put, chan_regs + host1x_channel_dmaput_r()); /* start the command DMA */ writel(host1x_channel_dmactrl(false, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); cdma->running = true; } @@ -475,14 +339,14 @@ static void cdma_timeout_restart(struct nvhost_cdma *cdma, u32 getptr) static void cdma_kick(struct nvhost_cdma *cdma) { u32 put; - BUG_ON(!cdma_pb_op(cdma).putptr); + BUG_ON(!cdma_pb_op().putptr); - put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + put = cdma_pb_op().putptr(&cdma->push_buffer); if (put != cdma->last_put) { void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; wmb(); - writel(put, chan_regs + HOST1X_CHANNEL_DMAPUT); + writel(put, chan_regs + host1x_channel_dmaput_r()); cdma->last_put = put; } } @@ -495,31 +359,17 @@ static void cdma_stop(struct nvhost_cdma *cdma) if (cdma->running) { nvhost_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); writel(host1x_channel_dmactrl(true, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); + chan_regs + host1x_channel_dmactrl_r()); cdma->running = false; } mutex_unlock(&cdma->lock); } /** - * Retrieve the op pair at a slot offset from a DMA address - */ -void cdma_peek(struct nvhost_cdma *cdma, - u32 dmaget, int slot, u32 *out) -{ - u32 offset = dmaget - cdma->push_buffer.phys; - u32 *p = cdma->push_buffer.mapped; - - offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; - out[0] = p[offset]; - out[1] = p[offset + 1]; -} - -/** * Stops both channel's command processor and CDMA immediately. * Also, tears down the channel and resets corresponding module. */ -void cdma_timeout_teardown_begin(struct nvhost_cdma *cdma) +static void cdma_timeout_teardown_begin(struct nvhost_cdma *cdma) { struct nvhost_master *dev = cdma_to_dev(cdma); struct nvhost_channel *ch = cdma_to_channel(cdma); @@ -530,28 +380,28 @@ void cdma_timeout_teardown_begin(struct nvhost_cdma *cdma) dev_dbg(&dev->dev->dev, "begin channel teardown (channel id %d)\n", ch->chid); - cmdproc_stop = readl(dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + cmdproc_stop = readl(dev->sync_aperture + host1x_sync_cmdproc_stop_r()); cmdproc_stop |= BIT(ch->chid); - writel(cmdproc_stop, dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + writel(cmdproc_stop, dev->sync_aperture + host1x_sync_cmdproc_stop_r()); dev_dbg(&dev->dev->dev, "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", __func__, - readl(ch->aperture + HOST1X_CHANNEL_DMAGET), - readl(ch->aperture + HOST1X_CHANNEL_DMAPUT), + readl(ch->aperture + host1x_channel_dmaget_r()), + readl(ch->aperture + host1x_channel_dmaput_r()), cdma->last_put); writel(host1x_channel_dmactrl(true, false, false), - ch->aperture + HOST1X_CHANNEL_DMACTRL); + ch->aperture + host1x_channel_dmactrl_r()); - writel(BIT(ch->chid), dev->sync_aperture + HOST1X_SYNC_CH_TEARDOWN); + writel(BIT(ch->chid), dev->sync_aperture + host1x_sync_ch_teardown_r()); nvhost_module_reset(ch->dev); cdma->running = false; cdma->torndown = true; } -void cdma_timeout_teardown_end(struct nvhost_cdma *cdma, u32 getptr) +static void cdma_timeout_teardown_end(struct nvhost_cdma *cdma, u32 getptr) { struct nvhost_master *dev = cdma_to_dev(cdma); struct nvhost_channel *ch = cdma_to_channel(cdma); @@ -563,9 +413,9 @@ void cdma_timeout_teardown_end(struct nvhost_cdma *cdma, u32 getptr) "end channel teardown (id %d, DMAGET restart = 0x%x)\n", ch->chid, getptr); - cmdproc_stop = readl(dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + cmdproc_stop = readl(dev->sync_aperture + host1x_sync_cmdproc_stop_r()); cmdproc_stop &= ~(BIT(ch->chid)); - writel(cmdproc_stop, dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + writel(cmdproc_stop, dev->sync_aperture + host1x_sync_cmdproc_stop_r()); cdma->torndown = false; cdma_timeout_restart(cdma, getptr); @@ -603,9 +453,9 @@ static void cdma_timeout_handler(struct work_struct *work) } /* stop processing to get a clean snapshot */ - prev_cmdproc = readl(dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + prev_cmdproc = readl(dev->sync_aperture + host1x_sync_cmdproc_stop_r()); cmdproc_stop = prev_cmdproc | BIT(ch->chid); - writel(cmdproc_stop, dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + writel(cmdproc_stop, dev->sync_aperture + host1x_sync_cmdproc_stop_r()); dev_dbg(&dev->dev->dev, "cdma_timeout: cmdproc was 0x%x is 0x%x\n", prev_cmdproc, cmdproc_stop); @@ -620,7 +470,7 @@ static void cdma_timeout_handler(struct work_struct *work) /* restore */ cmdproc_stop = prev_cmdproc & ~(BIT(ch->chid)); writel(cmdproc_stop, - dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); + dev->sync_aperture + host1x_sync_cmdproc_stop_r()); mutex_unlock(&cdma->lock); return; } @@ -629,37 +479,36 @@ static void cdma_timeout_handler(struct work_struct *work) "%s: timeout: %d (%s) ctx 0x%p, HW thresh %d, done %d\n", __func__, cdma->timeout.syncpt_id, - syncpt_op(sp).name(sp, cdma->timeout.syncpt_id), + syncpt_op().name(sp, cdma->timeout.syncpt_id), cdma->timeout.ctx, syncpt_val, cdma->timeout.syncpt_val); /* stop HW, resetting channel/module */ - cdma_op(cdma).timeout_teardown_begin(cdma); + cdma_op().timeout_teardown_begin(cdma); - nvhost_cdma_update_sync_queue(cdma, sp, &dev->dev->dev); + nvhost_cdma_update_sync_queue(cdma, sp, dev->dev); mutex_unlock(&cdma->lock); } -int host1x_init_cdma_support(struct nvhost_master *host) -{ - host->op.cdma.start = cdma_start; - host->op.cdma.stop = cdma_stop; - host->op.cdma.kick = cdma_kick; - - host->op.cdma.timeout_init = cdma_timeout_init; - host->op.cdma.timeout_destroy = cdma_timeout_destroy; - host->op.cdma.timeout_teardown_begin = cdma_timeout_teardown_begin; - host->op.cdma.timeout_teardown_end = cdma_timeout_teardown_end; - host->op.cdma.timeout_cpu_incr = cdma_timeout_cpu_incr; - host->op.cdma.timeout_pb_incr = cdma_timeout_pb_incr; - - host->op.push_buffer.reset = push_buffer_reset; - host->op.push_buffer.init = push_buffer_init; - host->op.push_buffer.destroy = push_buffer_destroy; - host->op.push_buffer.push_to = push_buffer_push_to; - host->op.push_buffer.pop_from = push_buffer_pop_from; - host->op.push_buffer.space = push_buffer_space; - host->op.push_buffer.putptr = push_buffer_putptr; +static const struct nvhost_cdma_ops host1x_cdma_ops = { + .start = cdma_start, + .stop = cdma_stop, + .kick = cdma_kick, + + .timeout_init = cdma_timeout_init, + .timeout_destroy = cdma_timeout_destroy, + .timeout_teardown_begin = cdma_timeout_teardown_begin, + .timeout_teardown_end = cdma_timeout_teardown_end, + .timeout_cpu_incr = cdma_timeout_cpu_incr, +}; + +static const struct nvhost_pushbuffer_ops host1x_pushbuffer_ops = { + .reset = push_buffer_reset, + .init = push_buffer_init, + .destroy = push_buffer_destroy, + .push_to = push_buffer_push_to, + .pop_from = push_buffer_pop_from, + .space = push_buffer_space, + .putptr = push_buffer_putptr, +}; - return 0; -} diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.h b/drivers/video/tegra/host/host1x/host1x_cdma.h index 60909236a7ca..94bfc092c8c9 100644 --- a/drivers/video/tegra/host/host1x/host1x_cdma.h +++ b/drivers/video/tegra/host/host1x/host1x_cdma.h @@ -1,7 +1,7 @@ /* * drivers/video/tegra/host/host1x/host1x_cdma.h * - * Tegra Graphics Host Channel + * Tegra Graphics Host Command DMA * * Copyright (c) 2011-2012, NVIDIA Corporation. * @@ -36,6 +36,4 @@ * and replaces the original timed out contexts GATHER slots */ #define SYNCPT_INCR_BUFFER_SIZE_WORDS (4096 / sizeof(u32)) -int host1x_init_cdma_support(struct nvhost_master *); - #endif diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c index b16a34f416ab..9e9fc25dc966 100644 --- a/drivers/video/tegra/host/host1x/host1x_channel.c +++ b/drivers/video/tegra/host/host1x/host1x_channel.c @@ -20,18 +20,20 @@ #include "nvhost_channel.h" #include "dev.h" +#include "nvhost_acm.h" +#include "nvhost_job.h" #include "nvhost_hwctx.h" #include <trace/events/nvhost.h> #include <linux/slab.h> -#include "host1x_syncpt.h" -#include "host1x_channel.h" -#include "host1x_hardware.h" #include "host1x_hwctx.h" #include "nvhost_intr.h" #define NV_FIFO_READ_TIMEOUT 200000 +static int host1x_drain_read_fifo(struct nvhost_channel *ch, + u32 *ptr, unsigned int count, unsigned int *pending); + static void sync_waitbases(struct nvhost_channel *ch, u32 syncpt_val) { unsigned long waitbase; @@ -40,7 +42,7 @@ static void sync_waitbases(struct nvhost_channel *ch, u32 syncpt_val) waitbase = find_first_bit(&waitbase_mask, BITS_PER_LONG); nvhost_cdma_push(&ch->cdma, nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_LOAD_SYNCPT_BASE, + host1x_uclass_load_syncpt_base_r(), 1), nvhost_class_host_load_syncpt_base(waitbase, syncpt_val)); @@ -140,22 +142,24 @@ static void submit_ctxrestore(struct nvhost_job *job) /* Send restore buffer to channel */ nvhost_cdma_push_gather(&ch->cdma, - host->nvmap, - nvmap_ref_to_handle(ctx->restore), + host->memmgr, + ctx->restore, + 0, nvhost_opcode_gather(ctx->restore_size), ctx->restore_phys); trace_nvhost_channel_context_restore(ch->dev->name, &ctx->hwctx); } -void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) +static void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) { struct nvhost_channel *ch = job->ch; int incr; u32 op_incr; /* push increments that correspond to nulled out commands */ - op_incr = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, + op_incr = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), job->syncpt_id); for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++) nvhost_cdma_push(&ch->cdma, op_incr, op_incr); @@ -168,7 +172,7 @@ void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) nvhost_cdma_push(&ch->cdma, nvhost_opcode_setclass( NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INCR_SYNCPT_BASE, + host1x_uclass_incr_syncpt_base_r(), 1), nvhost_class_host_incr_syncpt_base( waitbase, @@ -176,20 +180,22 @@ void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) } } -void submit_gathers(struct nvhost_job *job) +static void submit_gathers(struct nvhost_job *job) { /* push user gathers */ - int i = 0; - for ( ; i < job->num_gathers; i++) { + int i; + for (i = 0 ; i < job->num_gathers; i++) { u32 op1 = nvhost_opcode_gather(job->gathers[i].words); u32 op2 = job->gathers[i].mem; nvhost_cdma_push_gather(&job->ch->cdma, - job->nvmap, job->unpins[i/2], + job->memmgr, + job->gathers[i].ref, + job->gathers[i].offset, op1, op2); } } -int host1x_channel_submit(struct nvhost_job *job) +static int host1x_channel_submit(struct nvhost_job *job) { struct nvhost_channel *ch = job->ch; struct nvhost_syncpt *sp = &nvhost_get_host(job->ch->dev)->syncpt; @@ -198,6 +204,7 @@ int host1x_channel_submit(struct nvhost_job *job) u32 syncval; int err; void *completed_waiter = NULL, *ctxsave_waiter = NULL; + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); /* Bail out on timed out contexts */ if (job->hwctx && job->hwctx->has_timedout) @@ -205,8 +212,8 @@ int host1x_channel_submit(struct nvhost_job *job) /* Turn on the client module and host1x */ nvhost_module_busy(ch->dev); - if (ch->dev->busy) - ch->dev->busy(ch->dev); + if (drv->busy) + drv->busy(ch->dev); /* before error checks, return current max */ prev_max = job->syncpt_end = @@ -236,22 +243,6 @@ int host1x_channel_submit(struct nvhost_job *job) goto error; } - /* remove stale waits */ - if (job->num_waitchk) { - err = nvhost_syncpt_wait_check(sp, - job->nvmap, - job->waitchk_mask, - job->waitchk, - job->num_waitchk); - if (err) { - dev_warn(&ch->dev->dev, - "nvhost_syncpt_wait_check failed: %d\n", err); - mutex_unlock(&ch->submitlock); - nvhost_module_idle(ch->dev); - goto error; - } - } - /* begin a CDMA submit */ err = nvhost_cdma_begin(&ch->cdma, job); if (err) { @@ -260,6 +251,18 @@ int host1x_channel_submit(struct nvhost_job *job) goto error; } + if (ch->dev->serialize) { + /* Force serialization by inserting a host wait for the + * previous job to finish before this one can commence. */ + nvhost_cdma_push(&ch->cdma, + nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, + host1x_uclass_wait_syncpt_r(), + 1), + nvhost_class_host_wait_syncpt(job->syncpt_id, + nvhost_syncpt_read_max(sp, + job->syncpt_id))); + } + submit_ctxsave(job, ctxsave_waiter, ch->cur_ctx); submit_ctxrestore(job); ch->cur_ctx = job->hwctx; @@ -307,7 +310,7 @@ error: return err; } -int host1x_channel_read_3d_reg( +static int host1x_channel_read_3d_reg( struct nvhost_channel *channel, struct nvhost_hwctx *hwctx, u32 offset, @@ -339,7 +342,7 @@ int host1x_channel_read_3d_reg( job = nvhost_job_alloc(channel, hwctx, NULL, - nvhost_get_host(channel->dev)->nvmap, 0, 0); + nvhost_get_host(channel->dev)->memmgr, 0, 0); if (!job) { err = -ENOMEM; goto done; @@ -396,38 +399,41 @@ int host1x_channel_read_3d_reg( /* Switch to 3D - wait for it to complete what it was doing */ nvhost_cdma_push(&channel->cdma, nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0), - nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, + nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), p->syncpt)); nvhost_cdma_push(&channel->cdma, nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1), + host1x_uclass_wait_syncpt_base_r(), 1), nvhost_class_host_wait_syncpt_base(p->syncpt, p->waitbase, 1)); /* Tell 3D to send register value to FIFO */ nvhost_cdma_push(&channel->cdma, - nvhost_opcode_nonincr(NV_CLASS_HOST_INDOFF, 1), + nvhost_opcode_nonincr(host1x_uclass_indoff_r(), 1), nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, offset, false)); nvhost_cdma_push(&channel->cdma, - nvhost_opcode_imm(NV_CLASS_HOST_INDDATA, 0), + nvhost_opcode_imm(host1x_uclass_inddata_r(), 0), NVHOST_OPCODE_NOOP); /* Increment syncpt to indicate that FIFO can be read */ nvhost_cdma_push(&channel->cdma, - nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE, + nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_immediate_v(), p->syncpt), NVHOST_OPCODE_NOOP); /* Wait for value to be read from FIFO */ nvhost_cdma_push(&channel->cdma, - nvhost_opcode_nonincr(NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1), + nvhost_opcode_nonincr(host1x_uclass_wait_syncpt_base_r(), 1), nvhost_class_host_wait_syncpt_base(p->syncpt, p->waitbase, 3)); /* Indicate submit complete */ nvhost_cdma_push(&channel->cdma, - nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1), + nvhost_opcode_nonincr(host1x_uclass_incr_syncpt_base_r(), 1), nvhost_class_host_incr_syncpt_base(p->waitbase, 4)); nvhost_cdma_push(&channel->cdma, NVHOST_OPCODE_NOOP, - nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE, + nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_immediate_v(), p->syncpt)); /* end CDMA submit */ @@ -467,8 +473,7 @@ int host1x_channel_read_3d_reg( nvhost_intr_put_ref(&nvhost_get_host(channel->dev)->intr, ref); /* Read the register value from FIFO */ - err = host1x_drain_read_fifo(channel->aperture, - value, 1, &pending); + err = host1x_drain_read_fifo(channel, value, 1, &pending); /* Indicate we've read the value */ nvhost_syncpt_cpu_incr(&nvhost_get_host(channel->dev)->syncpt, @@ -492,18 +497,19 @@ done: } -int host1x_drain_read_fifo(void __iomem *chan_regs, +static int host1x_drain_read_fifo(struct nvhost_channel *ch, u32 *ptr, unsigned int count, unsigned int *pending) { unsigned int entries = *pending; unsigned long timeout = jiffies + NV_FIFO_READ_TIMEOUT; + void __iomem *chan_regs = ch->aperture; while (count) { unsigned int num; while (!entries && time_before(jiffies, timeout)) { /* query host for number of entries in fifo */ - entries = HOST1X_VAL(CHANNEL_FIFOSTAT, OUTFENTRIES, - readl(chan_regs + HOST1X_CHANNEL_FIFOSTAT)); + entries = host1x_channel_fifostat_outfentries_v( + readl(chan_regs + host1x_channel_fifostat_r())); if (!entries) cpu_relax(); } @@ -518,25 +524,25 @@ int host1x_drain_read_fifo(void __iomem *chan_regs, while (num & ~0x3) { u32 arr[4]; - arr[0] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); - arr[1] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); - arr[2] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); - arr[3] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); + arr[0] = readl(chan_regs + host1x_channel_inddata_r()); + arr[1] = readl(chan_regs + host1x_channel_inddata_r()); + arr[2] = readl(chan_regs + host1x_channel_inddata_r()); + arr[3] = readl(chan_regs + host1x_channel_inddata_r()); memcpy(ptr, arr, 4*sizeof(u32)); ptr += 4; num -= 4; } while (num--) - *ptr++ = readl(chan_regs + HOST1X_CHANNEL_INDDATA); + *ptr++ = readl(chan_regs + host1x_channel_inddata_r()); } *pending = entries; return 0; } -int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) +static int host1x_save_context(struct nvhost_channel *ch) { - struct nvhost_channel *ch = dev->channel; + struct nvhost_device *dev = ch->dev; struct nvhost_hwctx *hwctx_to_save; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); u32 syncpt_incrs, syncpt_val; @@ -544,6 +550,8 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) void *ref; void *ctx_waiter = NULL, *wakeup_waiter = NULL; struct nvhost_job *job; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + u32 syncpt_id; ctx_waiter = nvhost_intr_alloc_waiter(); wakeup_waiter = nvhost_intr_alloc_waiter(); @@ -552,8 +560,8 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) goto done; } - if (dev->busy) - dev->busy(dev); + if (drv->busy) + drv->busy(dev); mutex_lock(&ch->submitlock); hwctx_to_save = ch->cur_ctx; @@ -564,7 +572,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) job = nvhost_job_alloc(ch, hwctx_to_save, NULL, - nvhost_get_host(ch->dev)->nvmap, 0, 0); + nvhost_get_host(ch->dev)->memmgr, 0, 0); if (IS_ERR_OR_NULL(job)) { err = PTR_ERR(job); mutex_unlock(&ch->submitlock); @@ -574,6 +582,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) hwctx_to_save->valid = true; ch->ctxhandler->get(hwctx_to_save); ch->cur_ctx = NULL; + syncpt_id = to_host1x_hwctx_handler(hwctx_to_save->h)->syncpt; syncpt_incrs = to_host1x_hwctx(hwctx_to_save)->save_incrs; syncpt_val = nvhost_syncpt_incr_max(&nvhost_get_host(ch->dev)->syncpt, @@ -625,3 +634,48 @@ done: kfree(wakeup_waiter); return err; } + +static inline void __iomem *host1x_channel_aperture(void __iomem *p, int ndx) +{ + p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES; + return p; +} + +static inline int host1x_hwctx_handler_init(struct nvhost_channel *ch) +{ + int err = 0; + unsigned long syncpts = ch->dev->syncpts; + unsigned long waitbases = ch->dev->waitbases; + u32 syncpt = find_first_bit(&syncpts, BITS_PER_LONG); + u32 waitbase = find_first_bit(&waitbases, BITS_PER_LONG); + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); + + if (drv->alloc_hwctx_handler) { + ch->ctxhandler = drv->alloc_hwctx_handler(syncpt, + waitbase, ch); + if (!ch->ctxhandler) + err = -ENOMEM; + } + + return err; +} + +static int host1x_channel_init(struct nvhost_channel *ch, + struct nvhost_master *dev, int index) +{ + ch->chid = index; + mutex_init(&ch->reflock); + mutex_init(&ch->submitlock); + + ch->aperture = host1x_channel_aperture(dev->aperture, index); + + return host1x_hwctx_handler_init(ch); +} + +static const struct nvhost_channel_ops host1x_channel_ops = { + .init = host1x_channel_init, + .submit = host1x_channel_submit, + .read3dreg = host1x_channel_read_3d_reg, + .save_context = host1x_save_context, + .drain_read_fifo = host1x_drain_read_fifo, +}; diff --git a/drivers/video/tegra/host/host1x/host1x_channel.h b/drivers/video/tegra/host/host1x/host1x_channel.h deleted file mode 100644 index 4113dbcada20..000000000000 --- a/drivers/video/tegra/host/host1x/host1x_channel.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * drivers/video/tegra/host/host1x/host1x_channel.h - * - * Tegra Graphics Host Channel - * - * Copyright (c) 2011-2012, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __NVHOST_HOST1X_CHANNEL_H -#define __NVHOST_HOST1X_CHANNEL_H - -struct nvhost_job; -struct nvhost_channel; -struct nvhost_hwctx; -struct nvhost_device; - -/* Submit job to a host1x client */ -int host1x_channel_submit(struct nvhost_job *job); - -/* Read 3d register via FIFO */ -int host1x_channel_read_3d_reg( - struct nvhost_channel *channel, - struct nvhost_hwctx *hwctx, - u32 offset, - u32 *value); - -/* Reads words from FIFO */ -int host1x_drain_read_fifo(void __iomem *chan_regs, - u32 *ptr, unsigned int count, unsigned int *pending); - -int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id); - -#endif diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c index 1a1d764bbd63..1c4ed684dd84 100644 --- a/drivers/video/tegra/host/host1x/host1x_debug.c +++ b/drivers/video/tegra/host/host1x/host1x_debug.c @@ -26,10 +26,10 @@ #include "dev.h" #include "debug.h" #include "nvhost_cdma.h" -#include "../../nvmap/nvmap.h" - -#include "host1x_hardware.h" -#include "host1x_cdma.h" +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "chip_support.h" +#include "nvhost_memmgr.h" #define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 @@ -160,49 +160,15 @@ static void show_channel_word(struct output *o, int *state, int *count, } } -static void show_channel_gather(struct output *o, u32 addr, +static void do_show_channel_gather(struct output *o, phys_addr_t phys_addr, - u32 words, struct nvhost_cdma *cdma) + u32 words, struct nvhost_cdma *cdma, + phys_addr_t pin_addr, u32 *map_addr) { -#if defined(CONFIG_TEGRA_NVMAP) /* Map dmaget cursor to corresponding nvmap_handle */ - struct push_buffer *pb = &cdma->push_buffer; - u32 cur = addr - pb->phys; - struct nvmap_client_handle *nvmap = &pb->nvmap[cur/8]; - struct nvmap_handle_ref ref; - u32 *map_addr, offset; - phys_addr_t pin_addr; + u32 offset; int state, count, i; - if ((u32)nvmap->handle == NVHOST_CDMA_PUSH_GATHER_CTXSAVE) { - nvhost_debug_output(o, "[context save]\n"); - return; - } - - if (!nvmap->handle || !nvmap->client - || atomic_read(&nvmap->handle->ref) < 1) { - nvhost_debug_output(o, "[already deallocated]\n"); - return; - } - - /* Create a fake nvmap_handle_ref - nvmap requires it - * but accesses only the first field - nvmap_handle */ - ref.handle = nvmap->handle; - - map_addr = nvmap_mmap(&ref); - if (!map_addr) { - nvhost_debug_output(o, "[could not mmap]\n"); - return; - } - - /* Get base address from nvmap */ - pin_addr = nvmap_pin(nvmap->client, &ref); - if (IS_ERR_VALUE(pin_addr)) { - nvhost_debug_output(o, "[couldn't pin]\n"); - nvmap_munmap(&ref, map_addr); - return; - } - offset = phys_addr - pin_addr; /* * Sometimes we're given different hardware address to the same @@ -220,42 +186,81 @@ static void show_channel_gather(struct output *o, u32 addr, *(map_addr + offset/4 + i), cdma); } - nvmap_unpin(nvmap->client, &ref); - nvmap_munmap(&ref, map_addr); -#endif } -static void show_channel_pair(struct output *o, u32 addr, - u32 w0, u32 w1, struct nvhost_cdma *cdma) +static void show_channel_gather(struct output *o, u32 addr, + phys_addr_t phys_addr, + u32 words, struct nvhost_cdma *cdma) { - int state = NVHOST_DBG_STATE_CMD; - int count; +#if defined(CONFIG_TEGRA_NVMAP) + /* Map dmaget cursor to corresponding nvmap_handle */ + struct push_buffer *pb = &cdma->push_buffer; + u32 cur = addr - pb->phys; + struct mem_mgr_handle *nvmap = &pb->client_handle[cur/8]; + u32 *map_addr, offset; + phys_addr_t pin_addr; - show_channel_word(o, &state, &count, addr, w0, cdma); - show_channel_word(o, &state, &count, addr+4, w1, cdma); -} + if (!nvmap || !nvmap->handle || !nvmap->client) { + nvhost_debug_output(o, "[already deallocated]\n"); + return; + } -/** - * Retrieve the op pair at a slot offset from a DMA address - */ -static void cdma_peek(struct nvhost_cdma *cdma, - u32 dmaget, int slot, u32 *out) -{ - u32 offset = dmaget - cdma->push_buffer.phys; - u32 *p = cdma->push_buffer.mapped; + map_addr = mem_op().mmap(nvmap->handle); + if (!map_addr) { + nvhost_debug_output(o, "[could not mmap]\n"); + return; + } + + /* Get base address from nvmap */ + pin_addr = mem_op().pin(nvmap->client, nvmap->handle); + if (IS_ERR_VALUE(pin_addr)) { + nvhost_debug_output(o, "[couldn't pin]\n"); + mem_op().munmap(nvmap->handle, map_addr); + return; + } - offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; - out[0] = p[offset]; - out[1] = p[offset + 1]; + offset = phys_addr - pin_addr; + do_show_channel_gather(o, phys_addr, words, cdma, + pin_addr, map_addr); + mem_op().unpin(nvmap->client, nvmap->handle); + mem_op().munmap(nvmap->handle, map_addr); +#endif } -u32 previous_oppair(struct nvhost_cdma *cdma, u32 cur) +static void show_channel_gathers(struct output *o, struct nvhost_cdma *cdma) { - u32 pb = cdma->push_buffer.phys; - u32 prev = cur-8; - if (prev < pb) - prev += PUSH_BUFFER_SIZE; - return prev; + struct nvhost_job *job; + + list_for_each_entry(job, &cdma->sync_queue, list) { + int i; + nvhost_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d," + " first_get=%08x, timeout=%d, ctx=%p," + " num_slots=%d, num_handles=%d\n", + job, + job->syncpt_id, + job->syncpt_end, + job->first_get, + job->timeout, + job->hwctx, + job->num_slots, + job->num_unpins); + + for (i = 0; i < job->num_gathers; i++) { + struct nvhost_job_gather *g = &job->gathers[i]; + u32 *mapped = mem_op().mmap(g->ref); + if (!mapped) { + nvhost_debug_output(o, "[could not mmap]\n"); + continue; + } + + nvhost_debug_output(o, " GATHER at %08x, %d words\n", + g->mem, g->words); + + do_show_channel_gather(o, g->mem + g->offset, + g->words, cdma, g->mem, mapped); + mem_op().munmap(g->ref, mapped); + } + } } static void t20_debug_show_channel_cdma(struct nvhost_master *m, @@ -266,19 +271,18 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m, u32 dmaput, dmaget, dmactrl; u32 cbstat, cbread; u32 val, base, baseval; - u32 pbw[2]; - dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT); - dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET); - dmactrl = readl(channel->aperture + HOST1X_CHANNEL_DMACTRL); - cbread = readl(m->sync_aperture + HOST1X_SYNC_CBREAD_x(chid)); - cbstat = readl(m->sync_aperture + HOST1X_SYNC_CBSTAT_x(chid)); + dmaput = readl(channel->aperture + host1x_channel_dmaput_r()); + dmaget = readl(channel->aperture + host1x_channel_dmaget_r()); + dmactrl = readl(channel->aperture + host1x_channel_dmactrl_r()); + cbread = readl(m->sync_aperture + host1x_sync_cbread0_r() + 4 * chid); + cbstat = readl(m->sync_aperture + host1x_sync_cbstat_0_r() + 4 * chid); nvhost_debug_output(o, "%d-%s (%d): ", chid, channel->dev->name, channel->dev->refcount); - if (HOST1X_VAL(CHANNEL_DMACTRL, DMASTOP, dmactrl) + if (host1x_channel_dmactrl_dmastop_v(dmactrl) || !channel->cdma.push_buffer.mapped) { nvhost_debug_output(o, "inactive\n\n"); return; @@ -292,9 +296,8 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m, case 0x00010009: base = (cbread >> 16) & 0xff; - val = readl(m->sync_aperture + - HOST1X_SYNC_SYNCPT_BASE_x(base)); - baseval = HOST1X_VAL(SYNC_SYNCPT_BASE_0, BASE, val); + baseval = readl(m->sync_aperture + + host1x_sync_syncpt_base_0_r() + 4 * base); val = cbread & 0xffff; nvhost_debug_output(o, "waiting on syncpt %d val %d " "(base %d = %d; offset = %d)\n", @@ -305,8 +308,8 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m, default: nvhost_debug_output(o, "active class %02x, offset %04x, val %08x\n", - HOST1X_VAL(SYNC_CBSTAT_0, CBCLASS0, cbstat), - HOST1X_VAL(SYNC_CBSTAT_0, CBOFFSET0, cbstat), + host1x_sync_cbstat_0_cbclass0_v(cbstat), + host1x_sync_cbstat_0_cboffset0_v(cbstat), cbread); break; } @@ -315,13 +318,11 @@ static void t20_debug_show_channel_cdma(struct nvhost_master *m, dmaput, dmaget, dmactrl); nvhost_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); - cdma_peek(cdma, dmaget, -1, pbw); - show_channel_pair(o, previous_oppair(cdma, dmaget), - pbw[0], pbw[1], &channel->cdma); + show_channel_gathers(o, cdma); nvhost_debug_output(o, "\n"); } -void t20_debug_show_channel_fifo(struct nvhost_master *m, +static void t20_debug_show_channel_fifo(struct nvhost_master *m, struct nvhost_channel *ch, struct output *o, int chid) { u32 val, rd_ptr, wr_ptr, start, end; @@ -330,35 +331,35 @@ void t20_debug_show_channel_fifo(struct nvhost_master *m, nvhost_debug_output(o, "%d: fifo:\n", chid); - val = readl(channel->aperture + HOST1X_CHANNEL_FIFOSTAT); + val = readl(channel->aperture + host1x_channel_fifostat_r()); nvhost_debug_output(o, "FIFOSTAT %08x\n", val); - if (HOST1X_VAL(CHANNEL_FIFOSTAT, CFEMPTY, val)) { + if (host1x_channel_fifostat_cfempty_v(val)) { nvhost_debug_output(o, "[empty]\n"); return; } - writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); - writel(HOST1X_CREATE(SYNC_CFPEEK_CTRL, ENA, 1) - | HOST1X_CREATE(SYNC_CFPEEK_CTRL, CHANNR, chid), - m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); + writel(0x0, m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); + writel(host1x_sync_cfpeek_ctrl_cfpeek_ena_f(1) + | host1x_sync_cfpeek_ctrl_cfpeek_channr_f(chid), + m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); - val = readl(m->sync_aperture + HOST1X_SYNC_CFPEEK_PTRS); - rd_ptr = HOST1X_VAL(SYNC_CFPEEK_PTRS, CF_RD_PTR, val); - wr_ptr = HOST1X_VAL(SYNC_CFPEEK_PTRS, CF_WR_PTR, val); + val = readl(m->sync_aperture + host1x_sync_cfpeek_ptrs_r()); + rd_ptr = host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(val); + wr_ptr = host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(val); - val = readl(m->sync_aperture + HOST1X_SYNC_CFx_SETUP(chid)); - start = HOST1X_VAL(SYNC_CF0_SETUP, BASE, val); - end = HOST1X_VAL(SYNC_CF0_SETUP, LIMIT, val); + val = readl(m->sync_aperture + host1x_sync_cf0_setup_r() + 4 * chid); + start = host1x_sync_cf0_setup_cf0_base_v(val); + end = host1x_sync_cf0_setup_cf0_limit_v(val); state = NVHOST_DBG_STATE_CMD; do { - writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); - writel(HOST1X_CREATE(SYNC_CFPEEK_CTRL, ENA, 1) - | HOST1X_CREATE(SYNC_CFPEEK_CTRL, CHANNR, chid) - | HOST1X_CREATE(SYNC_CFPEEK_CTRL, ADDR, rd_ptr), - m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); - val = readl(m->sync_aperture + HOST1X_SYNC_CFPEEK_READ); + writel(0x0, m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); + writel(host1x_sync_cfpeek_ctrl_cfpeek_ena_f(1) + | host1x_sync_cfpeek_ctrl_cfpeek_channr_f(chid) + | host1x_sync_cfpeek_ctrl_cfpeek_addr_f(rd_ptr), + m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); + val = readl(m->sync_aperture + host1x_sync_cfpeek_read_r()); show_channel_word(o, &state, &count, 0, val, NULL); @@ -372,21 +373,24 @@ void t20_debug_show_channel_fifo(struct nvhost_master *m, nvhost_debug_output(o, ", ...])\n"); nvhost_debug_output(o, "\n"); - writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); + writel(0x0, m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); } static void t20_debug_show_mlocks(struct nvhost_master *m, struct output *o) { - u32 __iomem *mlo_regs = m->sync_aperture + HOST1X_SYNC_MLOCK_OWNER_0; + u32 __iomem *mlo_regs = m->sync_aperture + + host1x_sync_mlock_owner_0_r(); int i; nvhost_debug_output(o, "---- mlocks ----\n"); for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) { u32 owner = readl(mlo_regs + i); - if (HOST1X_VAL(SYNC_MLOCK_OWNER_0, CH_OWNS, owner)) + if (host1x_sync_mlock_owner_0_mlock_ch_owns_0_v(owner)) nvhost_debug_output(o, "%d: locked by channel %d\n", - i, HOST1X_VAL(SYNC_MLOCK_OWNER_0, CHID, owner)); - else if (HOST1X_VAL(SYNC_MLOCK_OWNER_0, CPU_OWNS, owner)) + i, + host1x_sync_mlock_owner_0_mlock_owner_chid_0_f( + owner)); + else if (host1x_sync_mlock_owner_0_mlock_cpu_owns_0_v(owner)) nvhost_debug_output(o, "%d: locked by cpu\n", i); else nvhost_debug_output(o, "%d: unlocked\n", i); @@ -394,11 +398,8 @@ static void t20_debug_show_mlocks(struct nvhost_master *m, struct output *o) nvhost_debug_output(o, "\n"); } -int nvhost_init_t20_debug_support(struct nvhost_master *host) -{ - host->op.debug.show_channel_cdma = t20_debug_show_channel_cdma; - host->op.debug.show_channel_fifo = t20_debug_show_channel_fifo; - host->op.debug.show_mlocks = t20_debug_show_mlocks; - - return 0; -} +static const struct nvhost_debug_ops host1x_debug_ops = { + .show_channel_cdma = t20_debug_show_channel_cdma, + .show_channel_fifo = t20_debug_show_channel_fifo, + .show_mlocks = t20_debug_show_mlocks, +}; diff --git a/drivers/video/tegra/host/host1x/host1x_hardware.h b/drivers/video/tegra/host/host1x/host1x_hardware.h deleted file mode 100644 index d13d57523644..000000000000 --- a/drivers/video/tegra/host/host1x/host1x_hardware.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - * drivers/video/tegra/host/host1x/host1x_hardware.h - * - * Tegra Graphics Host Register Offsets - * - * Copyright (c) 2010-2012 NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __NVHOST_HOST1X_HOST1X_HARDWARE_H -#define __NVHOST_HOST1X_HOST1X_HARDWARE_H - -#include <linux/types.h> -#include <linux/bitops.h> - -/* class ids */ -enum { - NV_HOST1X_CLASS_ID = 0x1, - NV_VIDEO_ENCODE_MPEG_CLASS_ID = 0x20, - NV_GRAPHICS_3D_CLASS_ID = 0x60 -}; - - -/* channel registers */ -#define NV_HOST1X_CHANNELS 8 -#define NV_HOST1X_CHANNEL0_BASE 0 -#define NV_HOST1X_CHANNEL_MAP_SIZE_BYTES 16384 -#define NV_HOST1X_SYNC_MLOCK_NUM 16 - -#define HOST1X_VAL(reg, field, regdata) \ - ((regdata >> HOST1X_##reg##_##field##_SHIFT) \ - & HOST1X_##reg##_##field##_MASK) -#define HOST1X_CREATE(reg, field, data) \ - ((data & HOST1X_##reg##_##field##_MASK) \ - << HOST1X_##reg##_##field##_SHIFT) \ - -#define HOST1X_CHANNEL_FIFOSTAT 0x00 -#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_SHIFT 10 -#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_MASK 0x1 -#define HOST1X_CHANNEL_FIFOSTAT_OUTFENTRIES_SHIFT 24 -#define HOST1X_CHANNEL_FIFOSTAT_OUTFENTRIES_MASK 0x1f -#define HOST1X_CHANNEL_INDDATA 0x0c -#define HOST1X_CHANNEL_DMASTART 0x14 -#define HOST1X_CHANNEL_DMAPUT 0x18 -#define HOST1X_CHANNEL_DMAGET 0x1c -#define HOST1X_CHANNEL_DMAEND 0x20 -#define HOST1X_CHANNEL_DMACTRL 0x24 -#define HOST1X_CHANNEL_DMACTRL_DMASTOP_SHIFT 0 -#define HOST1X_CHANNEL_DMACTRL_DMASTOP_MASK 0x1 -#define HOST1X_CHANNEL_DMACTRL_DMAGETRST_SHIFT 1 -#define HOST1X_CHANNEL_DMACTRL_DMAGETRST_MASK 0x1 -#define HOST1X_CHANNEL_DMACTRL_DMAINITGET_SHIFT 2 -#define HOST1X_CHANNEL_DMACTRL_DMAINITGET_MASK 0x1 - -#define HOST1X_CHANNEL_SYNC_REG_BASE 0x3000 - -#define HOST1X_SYNC_INTMASK 0x4 -#define HOST1X_SYNC_INTC0MASK 0x8 -#define HOST1X_SYNC_HINTSTATUS 0x20 -#define HOST1X_SYNC_HINTMASK 0x24 -#define HOST1X_SYNC_HINTSTATUS_EXT 0x28 -#define HOST1X_SYNC_HINTSTATUS_EXT_IP_READ_INT_SHIFT 30 -#define HOST1X_SYNC_HINTSTATUS_EXT_IP_READ_INT_MASK 0x1 -#define HOST1X_SYNC_HINTSTATUS_EXT_IP_WRITE_INT_SHIFT 31 -#define HOST1X_SYNC_HINTSTATUS_EXT_IP_WRITE_INT_MASK 0x1 -#define HOST1X_SYNC_HINTMASK_EXT 0x2c -#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS 0x40 -#define HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS 0x48 -#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE 0x60 -#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0 0x68 -#define HOST1X_SYNC_CF0_SETUP 0x80 -#define HOST1X_SYNC_CF0_SETUP_BASE_SHIFT 0 -#define HOST1X_SYNC_CF0_SETUP_BASE_MASK 0x1ff -#define HOST1X_SYNC_CF0_SETUP_LIMIT_SHIFT 16 -#define HOST1X_SYNC_CF0_SETUP_LIMIT_MASK 0x1ff -#define HOST1X_SYNC_CFx_SETUP(x) (HOST1X_SYNC_CF0_SETUP + (4 * (x))) - -#define HOST1X_SYNC_CMDPROC_STOP 0xac -#define HOST1X_SYNC_CH_TEARDOWN 0xb0 -#define HOST1X_SYNC_USEC_CLK 0x1a4 -#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG 0x1a8 -#define HOST1X_SYNC_IP_BUSY_TIMEOUT 0x1bc -#define HOST1X_SYNC_IP_READ_TIMEOUT_ADDR 0x1c0 -#define HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR 0x1c4 -#define HOST1X_SYNC_MLOCK_0 0x2c0 -#define HOST1X_SYNC_MLOCK_OWNER_0 0x340 -#define HOST1X_SYNC_MLOCK_OWNER_0_CHID_SHIFT 8 -#define HOST1X_SYNC_MLOCK_OWNER_0_CHID_MASK 0xf -#define HOST1X_SYNC_MLOCK_OWNER_0_CPU_OWNS_SHIFT 1 -#define HOST1X_SYNC_MLOCK_OWNER_0_CPU_OWNS_MASK 0x1 -#define HOST1X_SYNC_MLOCK_OWNER_0_CH_OWNS_SHIFT 0 -#define HOST1X_SYNC_MLOCK_OWNER_0_CH_OWNS_MASK 0x1 -#define HOST1X_SYNC_SYNCPT_0 0x400 -#define HOST1X_SYNC_SYNCPT_INT_THRESH_0 0x500 - -#define HOST1X_SYNC_SYNCPT_BASE_0 0x600 -#define HOST1X_SYNC_SYNCPT_BASE_0_BASE_SHIFT 0 -#define HOST1X_SYNC_SYNCPT_BASE_0_BASE_MASK 0xffff -#define HOST1X_SYNC_SYNCPT_BASE_x(x) (HOST1X_SYNC_SYNCPT_BASE_0 + (4 * (x))) - -#define HOST1X_SYNC_SYNCPT_CPU_INCR 0x700 - -#define HOST1X_SYNC_CBREAD_0 0x720 -#define HOST1X_SYNC_CBREAD_x(x) (HOST1X_SYNC_CBREAD_0 + (4 * (x))) -#define HOST1X_SYNC_CFPEEK_CTRL 0x74c -#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_SHIFT 0 -#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_MASK 0x1ff -#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_SHIFT 16 -#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_MASK 0x7 -#define HOST1X_SYNC_CFPEEK_CTRL_ENA_SHIFT 31 -#define HOST1X_SYNC_CFPEEK_CTRL_ENA_MASK 0x1 -#define HOST1X_SYNC_CFPEEK_READ 0x750 -#define HOST1X_SYNC_CFPEEK_PTRS 0x754 -#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_SHIFT 0 -#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_MASK 0x1ff -#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_SHIFT 16 -#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_MASK 0x1ff -#define HOST1X_SYNC_CBSTAT_0 0x758 -#define HOST1X_SYNC_CBSTAT_0_CBOFFSET0_SHIFT 0 -#define HOST1X_SYNC_CBSTAT_0_CBOFFSET0_MASK 0xffff -#define HOST1X_SYNC_CBSTAT_0_CBCLASS0_SHIFT 16 -#define HOST1X_SYNC_CBSTAT_0_CBCLASS0_MASK 0xffff -#define HOST1X_SYNC_CBSTAT_x(x) (HOST1X_SYNC_CBSTAT_0 + (4 * (x))) - -/* sync registers */ -#define NV_HOST1X_SYNCPT_NB_PTS 32 -#define NV_HOST1X_SYNCPT_NB_BASES 8 -#define NV_HOST1X_NB_MLOCKS 16 - -/* host class methods */ -enum { - NV_CLASS_HOST_INCR_SYNCPT = 0x0, - NV_CLASS_HOST_WAIT_SYNCPT = 0x8, - NV_CLASS_HOST_WAIT_SYNCPT_BASE = 0x9, - NV_CLASS_HOST_LOAD_SYNCPT_BASE = 0xb, - NV_CLASS_HOST_INCR_SYNCPT_BASE = 0xc, - NV_CLASS_HOST_INDOFF = 0x2d, - NV_CLASS_HOST_INDDATA = 0x2e -}; -/* sync point conditionals */ -enum { - NV_SYNCPT_IMMEDIATE = 0x0, - NV_SYNCPT_OP_DONE = 0x1, - NV_SYNCPT_RD_DONE = 0x2, - NV_SYNCPT_REG_WR_SAFE = 0x3, -}; - -static inline u32 nvhost_class_host_wait_syncpt( - unsigned indx, unsigned threshold) -{ - return (indx << 24) | (threshold & 0xffffff); -} - -static inline u32 nvhost_class_host_load_syncpt_base( - unsigned indx, unsigned threshold) -{ - return (indx << 24) | (threshold & 0xffffff); -} - -static inline u32 nvhost_class_host_wait_syncpt_base( - unsigned indx, unsigned base_indx, unsigned offset) -{ - return (indx << 24) | (base_indx << 16) | offset; -} - -static inline u32 nvhost_class_host_incr_syncpt_base( - unsigned base_indx, unsigned offset) -{ - return (base_indx << 24) | offset; -} - -static inline u32 nvhost_class_host_incr_syncpt( - unsigned cond, unsigned indx) -{ - return (cond << 8) | indx; -} - -enum { - NV_HOST_MODULE_HOST1X = 0, - NV_HOST_MODULE_MPE = 1, - NV_HOST_MODULE_GR3D = 6 -}; - -static inline u32 nvhost_class_host_indoff_reg_write( - unsigned mod_id, unsigned offset, bool auto_inc) -{ - u32 v = (0xf << 28) | (mod_id << 18) | (offset << 2); - if (auto_inc) - v |= BIT(27); - return v; -} - -static inline u32 nvhost_class_host_indoff_reg_read( - unsigned mod_id, unsigned offset, bool auto_inc) -{ - u32 v = (mod_id << 18) | (offset << 2) | 1; - if (auto_inc) - v |= BIT(27); - return v; -} - - -/* cdma opcodes */ -static inline u32 nvhost_opcode_setclass( - unsigned class_id, unsigned offset, unsigned mask) -{ - return (0 << 28) | (offset << 16) | (class_id << 6) | mask; -} - -static inline u32 nvhost_opcode_incr(unsigned offset, unsigned count) -{ - return (1 << 28) | (offset << 16) | count; -} - -static inline u32 nvhost_opcode_nonincr(unsigned offset, unsigned count) -{ - return (2 << 28) | (offset << 16) | count; -} - -static inline u32 nvhost_opcode_mask(unsigned offset, unsigned mask) -{ - return (3 << 28) | (offset << 16) | mask; -} - -static inline u32 nvhost_opcode_imm(unsigned offset, unsigned value) -{ - return (4 << 28) | (offset << 16) | value; -} - -static inline u32 nvhost_opcode_imm_incr_syncpt(unsigned cond, unsigned indx) -{ - return nvhost_opcode_imm(NV_CLASS_HOST_INCR_SYNCPT, - nvhost_class_host_incr_syncpt(cond, indx)); -} - -static inline u32 nvhost_opcode_restart(unsigned address) -{ - return (5 << 28) | (address >> 4); -} - -static inline u32 nvhost_opcode_gather(unsigned count) -{ - return (6 << 28) | count; -} - -static inline u32 nvhost_opcode_gather_nonincr(unsigned offset, unsigned count) -{ - return (6 << 28) | (offset << 16) | BIT(15) | count; -} - -static inline u32 nvhost_opcode_gather_incr(unsigned offset, unsigned count) -{ - return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; -} - -#define NVHOST_OPCODE_NOOP nvhost_opcode_nonincr(0, 0) - -static inline u32 nvhost_mask2(unsigned x, unsigned y) -{ - return 1 | (1 << (y - x)); -} - -#endif diff --git a/drivers/video/tegra/host/host1x/host1x_hwctx.h b/drivers/video/tegra/host/host1x/host1x_hwctx.h index 7587642d0e14..13f0071d1e33 100644 --- a/drivers/video/tegra/host/host1x/host1x_hwctx.h +++ b/drivers/video/tegra/host/host1x/host1x_hwctx.h @@ -24,6 +24,7 @@ #define __NVHOST_HOST1X_HWCTX_H #include <linux/kref.h> +#include "nvhost_hwctx.h" struct nvhost_hwctx_handler; struct nvhost_channel; @@ -40,7 +41,7 @@ struct host1x_hwctx { u32 save_thresh; u32 save_slots; - struct nvmap_handle_ref *restore; + struct mem_handle *restore; u32 *restore_virt; phys_addr_t restore_phys; u32 restore_size; @@ -54,7 +55,7 @@ struct host1x_hwctx_handler { u32 waitbase; u32 restore_size; u32 restore_incrs; - struct nvmap_handle_ref *save_buf; + struct mem_handle *save_buf; u32 save_incrs; u32 save_thresh; u32 save_slots; diff --git a/drivers/video/tegra/host/host1x/host1x_intr.c b/drivers/video/tegra/host/host1x/host1x_intr.c index 47e984e2943e..62fd07cbb9ba 100644 --- a/drivers/video/tegra/host/host1x/host1x_intr.c +++ b/drivers/video/tegra/host/host1x/host1x_intr.c @@ -3,6 +3,7 @@ * * Tegra Graphics Host Interrupt Management * + * Copyright (C) 2010 Google, Inc. * Copyright (c) 2010-2012, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify it @@ -20,27 +21,86 @@ #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/io.h> +#include <asm/mach/irq.h> #include "nvhost_intr.h" #include "dev.h" -#include "host1x_hardware.h" +/* Spacing between sync registers */ +#define REGISTER_STRIDE 4 /*** HW host sync management ***/ +static void syncpt_thresh_mask(struct irq_data *data) +{ + (void)data; +} + +static void syncpt_thresh_unmask(struct irq_data *data) +{ + (void)data; +} + +static void syncpt_thresh_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct nvhost_master *dev = irq_desc_get_handler_data(desc); + void __iomem *sync_regs = dev->sync_aperture; + unsigned long reg; + int i, id; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + for (i = 0; i < dev->info.nb_pts / BITS_PER_LONG; i++) { + reg = readl(sync_regs + + host1x_sync_syncpt_thresh_cpu0_int_status_r() + + i * REGISTER_STRIDE); + for_each_set_bit(id, ®, BITS_PER_LONG) + generic_handle_irq(id + + dev->intr.host_syncpt_irq_base + + i * BITS_PER_LONG); + } + + chained_irq_exit(chip, desc); +} + +static struct irq_chip syncpt_thresh_irq = { + .name = "syncpt", + .irq_mask = syncpt_thresh_mask, + .irq_unmask = syncpt_thresh_unmask +}; + static void t20_intr_init_host_sync(struct nvhost_intr *intr) { struct nvhost_master *dev = intr_to_dev(intr); void __iomem *sync_regs = dev->sync_aperture; + int i, irq; + + writel(0xffffffffUL, + sync_regs + host1x_sync_syncpt_thresh_int_disable_r()); + writel(0xffffffffUL, + sync_regs + host1x_sync_syncpt_thresh_cpu0_int_status_r()); + + for (i = 0; i < dev->info.nb_pts; i++) { + irq = intr->host_syncpt_irq_base + i; + irq_set_chip_and_handler(irq, &syncpt_thresh_irq, + handle_simple_irq); + irq_set_chip_data(irq, sync_regs); + set_irq_flags(irq, IRQF_VALID); + } + irq_set_chained_handler(INT_HOST1X_MPCORE_SYNCPT, + syncpt_thresh_cascade); + irq_set_handler_data(INT_HOST1X_MPCORE_SYNCPT, dev); /* disable the ip_busy_timeout. this prevents write drops, etc. * there's no real way to recover from a hung client anyway. */ - writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT); + writel(0, sync_regs + host1x_sync_ip_busy_timeout_r()); /* increase the auto-ack timout to the maximum value. 2d will hang * otherwise on ap20. */ - writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG); + writel(0xff, sync_regs + host1x_sync_ctxsw_timeout_cfg_r()); } static void t20_intr_set_host_clocks_per_usec(struct nvhost_intr *intr, u32 cpm) @@ -48,7 +108,7 @@ static void t20_intr_set_host_clocks_per_usec(struct nvhost_intr *intr, u32 cpm) struct nvhost_master *dev = intr_to_dev(intr); void __iomem *sync_regs = dev->sync_aperture; /* write microsecond clock register */ - writel(cpm, sync_regs + HOST1X_SYNC_USEC_CLK); + writel(cpm, sync_regs + host1x_sync_usec_clk_r()); } static void t20_intr_set_syncpt_threshold(struct nvhost_intr *intr, @@ -57,35 +117,46 @@ static void t20_intr_set_syncpt_threshold(struct nvhost_intr *intr, struct nvhost_master *dev = intr_to_dev(intr); void __iomem *sync_regs = dev->sync_aperture; thresh &= 0xffff; - writel(thresh, sync_regs + (HOST1X_SYNC_SYNCPT_INT_THRESH_0 + id * 4)); + writel(thresh, sync_regs + + (host1x_sync_syncpt_int_thresh_0_r() + id * REGISTER_STRIDE)); } static void t20_intr_enable_syncpt_intr(struct nvhost_intr *intr, u32 id) { struct nvhost_master *dev = intr_to_dev(intr); void __iomem *sync_regs = dev->sync_aperture; - writel(BIT(id), sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0); + + writel(BIT_MASK(id), sync_regs + + host1x_sync_syncpt_thresh_int_enable_cpu0_r() + + BIT_WORD(id) * REGISTER_STRIDE); } static void t20_intr_disable_all_syncpt_intrs(struct nvhost_intr *intr) { struct nvhost_master *dev = intr_to_dev(intr); void __iomem *sync_regs = dev->sync_aperture; - /* disable interrupts for both cpu's */ - writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); - - /* clear status for both cpu's */ - writel(0xffffffffu, sync_regs + - HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); - writel(0xffffffffu, sync_regs + - HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS); + u32 reg; + + for (reg = 0; reg <= BIT_WORD(dev->info.nb_pts) * REGISTER_STRIDE; + reg += REGISTER_STRIDE) { + /* disable interrupts for both cpu's */ + writel(0, sync_regs + + host1x_sync_syncpt_thresh_int_disable_r() + + reg); + + /* clear status for both cpu's */ + writel(0xffffffffu, sync_regs + + host1x_sync_syncpt_thresh_cpu0_int_status_r() + reg); + writel(0xffffffffu, sync_regs + + host1x_sync_syncpt_thresh_cpu1_int_status_r() + reg); + } } /** * Sync point threshold interrupt service function * Handles sync point threshold triggers, in interrupt context */ -irqreturn_t t20_intr_syncpt_thresh_isr(int irq, void *dev_id) +static irqreturn_t t20_intr_syncpt_thresh_isr(int irq, void *dev_id) { struct nvhost_intr_syncpt *syncpt = dev_id; unsigned int id = syncpt->id; @@ -93,10 +164,12 @@ irqreturn_t t20_intr_syncpt_thresh_isr(int irq, void *dev_id) void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; - writel(BIT(id), - sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); - writel(BIT(id), - sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); + u32 reg = BIT_WORD(id) * REGISTER_STRIDE; + + writel(BIT_MASK(id), sync_regs + + host1x_sync_syncpt_thresh_int_disable_r() + reg); + writel(BIT_MASK(id), sync_regs + + host1x_sync_syncpt_thresh_cpu0_int_status_r() + reg); return IRQ_WAKE_THREAD; } @@ -113,21 +186,21 @@ static irqreturn_t t20_intr_host1x_isr(int irq, void *dev_id) u32 ext_stat; u32 addr; - stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS); - ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); + stat = readl(sync_regs + host1x_sync_hintstatus_r()); + ext_stat = readl(sync_regs + host1x_sync_hintstatus_ext_r()); - if (HOST1X_VAL(SYNC_HINTSTATUS_EXT, IP_READ_INT, ext_stat)) { - addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR); + if (host1x_sync_hintstatus_ext_ip_read_int_v(ext_stat)) { + addr = readl(sync_regs + host1x_sync_ip_read_timeout_addr_r()); pr_err("Host read timeout at address %x\n", addr); } - if (HOST1X_VAL(SYNC_HINTSTATUS_EXT, IP_WRITE_INT, ext_stat)) { - addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR); + if (host1x_sync_hintstatus_ext_ip_write_int_v(ext_stat)) { + addr = readl(sync_regs + host1x_sync_ip_write_timeout_addr_r()); pr_err("Host write timeout at address %x\n", addr); } - writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); - writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS); + writel(ext_stat, sync_regs + host1x_sync_hintstatus_ext_r()); + writel(stat, sync_regs + host1x_sync_hintstatus_r()); return IRQ_HANDLED; } @@ -140,11 +213,11 @@ static int t20_intr_request_host_general_irq(struct nvhost_intr *intr) return 0; /* master disable for general (not syncpt) host interrupts */ - writel(0, sync_regs + HOST1X_SYNC_INTMASK); + writel(0, sync_regs + host1x_sync_intmask_r()); /* clear status & extstatus */ - writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); - writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS); + writel(0xfffffffful, sync_regs + host1x_sync_hintstatus_ext_r()); + writel(0xfffffffful, sync_regs + host1x_sync_hintstatus_r()); err = request_irq(intr->host_general_irq, t20_intr_host1x_isr, 0, "host_status", intr); @@ -152,16 +225,16 @@ static int t20_intr_request_host_general_irq(struct nvhost_intr *intr) return err; /* enable extra interrupt sources IP_READ_INT and IP_WRITE_INT */ - writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT); + writel(BIT(30) | BIT(31), sync_regs + host1x_sync_hintmask_ext_r()); /* enable extra interrupt sources */ - writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK); + writel(BIT(31), sync_regs + host1x_sync_hintmask_r()); /* enable host module interrupt to CPU0 */ - writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK); + writel(BIT(0), sync_regs + host1x_sync_intc0mask_r()); /* master enable for general (not syncpt) host interrupts */ - writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK); + writel(BIT(0), sync_regs + host1x_sync_intmask_r()); intr->host_general_irq_requested = true; @@ -174,7 +247,7 @@ static void t20_intr_free_host_general_irq(struct nvhost_intr *intr) void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; /* master disable for general (not syncpt) host interrupts */ - writel(0, sync_regs + HOST1X_SYNC_INTMASK); + writel(0, sync_regs + host1x_sync_intmask_r()); free_irq(intr->host_general_irq, intr); intr->host_general_irq_requested = false; @@ -198,21 +271,13 @@ static int t20_request_syncpt_irq(struct nvhost_intr_syncpt *syncpt) return 0; } -int nvhost_init_t20_intr_support(struct nvhost_master *host) -{ - host->op.intr.init_host_sync = t20_intr_init_host_sync; - host->op.intr.set_host_clocks_per_usec = - t20_intr_set_host_clocks_per_usec; - host->op.intr.set_syncpt_threshold = t20_intr_set_syncpt_threshold; - host->op.intr.enable_syncpt_intr = t20_intr_enable_syncpt_intr; - host->op.intr.disable_all_syncpt_intrs = - t20_intr_disable_all_syncpt_intrs; - host->op.intr.request_host_general_irq = - t20_intr_request_host_general_irq; - host->op.intr.free_host_general_irq = - t20_intr_free_host_general_irq; - host->op.intr.request_syncpt_irq = - t20_request_syncpt_irq; - - return 0; -} +static const struct nvhost_intr_ops host1x_intr_ops = { + .init_host_sync = t20_intr_init_host_sync, + .set_host_clocks_per_usec = t20_intr_set_host_clocks_per_usec, + .set_syncpt_threshold = t20_intr_set_syncpt_threshold, + .enable_syncpt_intr = t20_intr_enable_syncpt_intr, + .disable_all_syncpt_intrs = t20_intr_disable_all_syncpt_intrs, + .request_host_general_irq = t20_intr_request_host_general_irq, + .free_host_general_irq = t20_intr_free_host_general_irq, + .request_syncpt_irq = t20_request_syncpt_irq, +}; diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c index b431fa350638..8cca9dbbbc08 100644 --- a/drivers/video/tegra/host/host1x/host1x_syncpt.c +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c @@ -19,10 +19,12 @@ */ #include <linux/nvhost_ioctl.h> +#include <linux/io.h> +#include <trace/events/nvhost.h> #include "nvhost_syncpt.h" -#include "dev.h" -#include "host1x_syncpt.h" -#include "host1x_hardware.h" +#include "nvhost_acm.h" +#include "host1x.h" +#include "chip_support.h" /** * Write the current syncpoint value back to hw. @@ -31,7 +33,7 @@ static void t20_syncpt_reset(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); int min = nvhost_syncpt_read_min(sp, id); - writel(min, dev->sync_aperture + (HOST1X_SYNC_SYNCPT_0 + id * 4)); + writel(min, dev->sync_aperture + (host1x_sync_syncpt_0_r() + id * 4)); } /** @@ -41,7 +43,7 @@ static void t20_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); writel(sp->base_val[id], - dev->sync_aperture + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); + dev->sync_aperture + (host1x_sync_syncpt_base_0_r() + id * 4)); } /** @@ -51,7 +53,7 @@ static void t20_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); sp->base_val[id] = readl(dev->sync_aperture + - (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); + (host1x_sync_syncpt_base_0_r() + id * 4)); } /** @@ -66,7 +68,7 @@ static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) do { old = nvhost_syncpt_read_min(sp, id); - live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4)); + live = readl(sync_regs + (host1x_sync_syncpt_0_r() + id * 4)); } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); if (!nvhost_syncpt_check_max(sp, id, live)) @@ -87,114 +89,55 @@ static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); + u32 reg_offset = id / 32; + BUG_ON(!nvhost_module_powered(dev->dev)); - if (!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)) { + if (!nvhost_syncpt_client_managed(sp, id) + && nvhost_syncpt_min_eq_max(sp, id)) { dev_err(&syncpt_to_dev(sp)->dev->dev, "Trying to increment syncpoint id %d beyond max\n", id); nvhost_debug_dump(syncpt_to_dev(sp)); return; } - writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR); + writel(BIT_MASK(id), dev->sync_aperture + + host1x_sync_syncpt_cpu_incr_r() + reg_offset * 4); wmb(); } -/* check for old WAITs to be removed (avoiding a wrap) */ -static int t20_syncpt_wait_check(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 waitchk_mask, - struct nvhost_waitchk *wait, - int num_waitchk) +/* remove a wait pointed to by patch_addr */ +static int host1x_syncpt_patch_wait(struct nvhost_syncpt *sp, + void *patch_addr) { - u32 idx; - int err = 0; - - /* get current syncpt values */ - for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) { - if (BIT(idx) & waitchk_mask) - nvhost_syncpt_update_min(sp, idx); - } - - BUG_ON(!wait && !num_waitchk); - - /* compare syncpt vs wait threshold */ - while (num_waitchk) { - u32 override; - - BUG_ON(wait->syncpt_id >= NV_HOST1X_SYNCPT_NB_PTS); - if (nvhost_syncpt_is_expired(sp, - wait->syncpt_id, wait->thresh)) { - /* - * NULL an already satisfied WAIT_SYNCPT host method, - * by patching its args in the command stream. The - * method data is changed to reference a reserved - * (never given out or incr) NVSYNCPT_GRAPHICS_HOST - * syncpt with a matching threshold value of 0, so - * is guaranteed to be popped by the host HW. - */ - dev_dbg(&syncpt_to_dev(sp)->dev->dev, - "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", - wait->syncpt_id, - syncpt_op(sp).name(sp, wait->syncpt_id), - wait->thresh, - nvhost_syncpt_read_min(sp, wait->syncpt_id)); - - /* patch the wait */ - override = nvhost_class_host_wait_syncpt( - NVSYNCPT_GRAPHICS_HOST, 0); - err = nvmap_patch_word(nvmap, - (struct nvmap_handle *)wait->mem, - wait->offset, override); - if (err) - break; - } - - wait++; - num_waitchk--; - } - return err; + u32 override = nvhost_class_host_wait_syncpt( + NVSYNCPT_GRAPHICS_HOST, 0); + __raw_writel(override, patch_addr); + return 0; } -static const char *s_syncpt_names[32] = { - "gfx_host", - "", "", "", "", "", "", "", - "disp0_a", "disp1_a", "avp_0", - "csi_vi_0", "csi_vi_1", - "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4", - "2d_0", "2d_1", - "disp0_b", "disp1_b", - "3d", - "mpe", - "disp0_c", "disp1_c", - "vblank0", "vblank1", - "mpe_ebm_eof", "mpe_wr_safe", - "2d_tinyblt", - "dsi" -}; - -static const char *t20_syncpt_name(struct nvhost_syncpt *s, u32 id) +static const char *t20_syncpt_name(struct nvhost_syncpt *sp, u32 id) { - BUG_ON(id >= ARRAY_SIZE(s_syncpt_names)); - return s_syncpt_names[id]; + struct host1x_device_info *info = &syncpt_to_dev(sp)->info; + return (id >= info->nb_pts) ? NULL : info->syncpt_names[id]; } static void t20_syncpt_debug(struct nvhost_syncpt *sp) { u32 i; - for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) { + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { u32 max = nvhost_syncpt_read_max(sp, i); u32 min = nvhost_syncpt_update_min(sp, i); if (!max && !min) continue; dev_info(&syncpt_to_dev(sp)->dev->dev, "id %d (%s) min %d max %d\n", - i, syncpt_op(sp).name(sp, i), + i, syncpt_op().name(sp, i), min, max); } - for (i = 0; i < NV_HOST1X_SYNCPT_NB_BASES; i++) { + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) { u32 base_val; t20_syncpt_read_wait_base(sp, i); base_val = sp->base_val[i]; @@ -212,7 +155,7 @@ static int syncpt_mutex_try_lock(struct nvhost_syncpt *sp, void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture; /* mlock registers returns 0 when the lock is aquired. * writing 0 clears the lock. */ - return !!readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); + return !!readl(sync_regs + (host1x_sync_mlock_0_r() + idx * 4)); } static void syncpt_mutex_unlock(struct nvhost_syncpt *sp, @@ -220,31 +163,18 @@ static void syncpt_mutex_unlock(struct nvhost_syncpt *sp, { void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture; - writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); + writel(0, sync_regs + (host1x_sync_mlock_0_r() + idx * 4)); } -int host1x_init_syncpt_support(struct nvhost_master *host) -{ - - host->sync_aperture = host->aperture + - (NV_HOST1X_CHANNEL0_BASE + - HOST1X_CHANNEL_SYNC_REG_BASE); - - host->op.syncpt.reset = t20_syncpt_reset; - host->op.syncpt.reset_wait_base = t20_syncpt_reset_wait_base; - host->op.syncpt.read_wait_base = t20_syncpt_read_wait_base; - host->op.syncpt.update_min = t20_syncpt_update_min; - host->op.syncpt.cpu_incr = t20_syncpt_cpu_incr; - host->op.syncpt.wait_check = t20_syncpt_wait_check; - host->op.syncpt.debug = t20_syncpt_debug; - host->op.syncpt.name = t20_syncpt_name; - host->op.syncpt.mutex_try_lock = syncpt_mutex_try_lock; - host->op.syncpt.mutex_unlock = syncpt_mutex_unlock; - - host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS; - host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES; - host->syncpt.client_managed = NVSYNCPTS_CLIENT_MANAGED; - host->syncpt.nb_mlocks = NV_HOST1X_SYNC_MLOCK_NUM; - - return 0; -} +static const struct nvhost_syncpt_ops host1x_syncpt_ops = { + .reset = t20_syncpt_reset, + .reset_wait_base = t20_syncpt_reset_wait_base, + .read_wait_base = t20_syncpt_read_wait_base, + .update_min = t20_syncpt_update_min, + .cpu_incr = t20_syncpt_cpu_incr, + .patch_wait = host1x_syncpt_patch_wait, + .debug = t20_syncpt_debug, + .name = t20_syncpt_name, + .mutex_try_lock = syncpt_mutex_try_lock, + .mutex_unlock = syncpt_mutex_unlock, +}; diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.h b/drivers/video/tegra/host/host1x/host1x_syncpt.h index 0d263dc92ed5..a971db8b1d94 100644 --- a/drivers/video/tegra/host/host1x/host1x_syncpt.h +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.h @@ -21,9 +21,12 @@ #ifndef __NVHOST_HOST1X_HOST1X_SYNCPT_H #define __NVHOST_HOST1X_HOST1X_SYNCPT_H -#define NVSYNCPT_DISP0_A (8) -#define NVSYNCPT_DISP1_A (9) -#define NVSYNCPT_AVP_0 (10) +/* FIXME: + * Sync point ids are now split into 2 files. + * 1 if this one and other is in include/linux/nvhost.h + * So if someone decides to add new sync point in future + * please check both the header files + */ #define NVSYNCPT_CSI_VI_0 (11) #define NVSYNCPT_CSI_VI_1 (12) #define NVSYNCPT_VI_ISP_0 (13) @@ -33,23 +36,10 @@ #define NVSYNCPT_VI_ISP_4 (17) #define NVSYNCPT_2D_0 (18) #define NVSYNCPT_2D_1 (19) -#define NVSYNCPT_DISP0_B (20) -#define NVSYNCPT_DISP1_B (21) #define NVSYNCPT_3D (22) #define NVSYNCPT_MPE (23) -#define NVSYNCPT_DISP0_C (24) -#define NVSYNCPT_DISP1_C (25) -#define NVSYNCPT_VBLANK0 (26) -#define NVSYNCPT_VBLANK1 (27) #define NVSYNCPT_MPE_EBM_EOF (28) #define NVSYNCPT_MPE_WR_SAFE (29) -#define NVSYNCPT_DSI (31) - - -/*#define NVSYNCPT_2D_CHANNEL2_0 (20) */ -/*#define NVSYNCPT_2D_CHANNEL2_1 (21) */ -/*#define NVSYNCPT_2D_TINYBLT_WAR (30)*/ -/*#define NVSYNCPT_2D_TINYBLT_RESTORE_CLASS_ID (30)*/ /* sync points that are wholly managed by the client */ #define NVSYNCPTS_CLIENT_MANAGED ( \ @@ -64,14 +54,9 @@ BIT(NVSYNCPT_MPE_EBM_EOF) | BIT(NVSYNCPT_MPE_WR_SAFE) | \ BIT(NVSYNCPT_2D_1) | BIT(NVSYNCPT_AVP_0)) - #define NVWAITBASE_2D_0 (1) #define NVWAITBASE_2D_1 (2) #define NVWAITBASE_3D (3) #define NVWAITBASE_MPE (4) -struct nvhost_master; -int host1x_init_syncpt(struct nvhost_master *host); -int host1x_init_syncpt_support(struct nvhost_master *host); - #endif diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_channel.h b/drivers/video/tegra/host/host1x/hw_host1x01_channel.h new file mode 100644 index 000000000000..ca2f9a0778cd --- /dev/null +++ b/drivers/video/tegra/host/host1x/hw_host1x01_channel.h @@ -0,0 +1,182 @@ +/* + * drivers/video/tegra/host/host1x/hw_host1x_channel_host1x.h + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + /* + * Function naming determines intended use: + * + * <x>_r(void) : Returns the offset for register <x>. + * + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. + * + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. + * + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field <y> of register <x>. This value + * can be |'d with others to produce a full register value for + * register <x>. + * + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This + * value can be ~'d and then &'d to clear the value of field <y> for + * register <x>. + * + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted + * to place it at field <y> of register <x>. This value can be |'d + * with others to produce a full register value for <x>. + * + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register + * <x> value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field <y> of register <x>. + * + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for + * field <y> of register <x>. This value is suitable for direct + * comparison with unshifted values appropriate for use in field <y> + * of register <x>. + */ + +#ifndef __hw_host1x_channel_host1x_h__ +#define __hw_host1x_channel_host1x_h__ +/*This file is autogenerated. Do not edit. */ + +static inline u32 host1x_channel_fifostat_r(void) +{ + return 0x0; +} +static inline u32 host1x_channel_fifostat_cfempty_s(void) +{ + return 1; +} +static inline u32 host1x_channel_fifostat_cfempty_f(u32 v) +{ + return (v & 0x1) << 10; +} +static inline u32 host1x_channel_fifostat_cfempty_m(void) +{ + return 0x1 << 10; +} +static inline u32 host1x_channel_fifostat_cfempty_v(u32 r) +{ + return (r >> 10) & 0x1; +} +static inline u32 host1x_channel_fifostat_cfempty_notempty_v(void) +{ + return 0; +} +static inline u32 host1x_channel_fifostat_cfempty_empty_v(void) +{ + return 1; +} +static inline u32 host1x_channel_fifostat_outfentries_s(void) +{ + return 5; +} +static inline u32 host1x_channel_fifostat_outfentries_f(u32 v) +{ + return (v & 0x1f) << 24; +} +static inline u32 host1x_channel_fifostat_outfentries_m(void) +{ + return 0x1f << 24; +} +static inline u32 host1x_channel_fifostat_outfentries_v(u32 r) +{ + return (r >> 24) & 0x1f; +} +static inline u32 host1x_channel_inddata_r(void) +{ + return 0xc; +} +static inline u32 host1x_channel_dmastart_r(void) +{ + return 0x14; +} +static inline u32 host1x_channel_dmaput_r(void) +{ + return 0x18; +} +static inline u32 host1x_channel_dmaget_r(void) +{ + return 0x1c; +} +static inline u32 host1x_channel_dmaend_r(void) +{ + return 0x20; +} +static inline u32 host1x_channel_dmactrl_r(void) +{ + return 0x24; +} +static inline u32 host1x_channel_dmactrl_dmastop_s(void) +{ + return 1; +} +static inline u32 host1x_channel_dmactrl_dmastop_f(u32 v) +{ + return (v & 0x1) << 0; +} +static inline u32 host1x_channel_dmactrl_dmastop_m(void) +{ + return 0x1 << 0; +} +static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r) +{ + return (r >> 0) & 0x1; +} +static inline u32 host1x_channel_dmactrl_dmastop_run_v(void) +{ + return 0; +} +static inline u32 host1x_channel_dmactrl_dmastop_stop_v(void) +{ + return 1; +} +static inline u32 host1x_channel_dmactrl_dmagetrst_s(void) +{ + return 1; +} +static inline u32 host1x_channel_dmactrl_dmagetrst_f(u32 v) +{ + return (v & 0x1) << 1; +} +static inline u32 host1x_channel_dmactrl_dmagetrst_m(void) +{ + return 0x1 << 1; +} +static inline u32 host1x_channel_dmactrl_dmagetrst_v(u32 r) +{ + return (r >> 1) & 0x1; +} +static inline u32 host1x_channel_dmactrl_dmainitget_s(void) +{ + return 1; +} +static inline u32 host1x_channel_dmactrl_dmainitget_f(u32 v) +{ + return (v & 0x1) << 2; +} +static inline u32 host1x_channel_dmactrl_dmainitget_m(void) +{ + return 0x1 << 2; +} +static inline u32 host1x_channel_dmactrl_dmainitget_v(u32 r) +{ + return (r >> 2) & 0x1; +} + +#endif /* __hw_host1x_channel_host1x_h__ */ diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_sync.h b/drivers/video/tegra/host/host1x/hw_host1x01_sync.h new file mode 100644 index 000000000000..67f0cbfb85b9 --- /dev/null +++ b/drivers/video/tegra/host/host1x/hw_host1x01_sync.h @@ -0,0 +1,398 @@ +/* + * drivers/video/tegra/host/host1x/hw_host1x_sync_host1x.h + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + /* + * Function naming determines intended use: + * + * <x>_r(void) : Returns the offset for register <x>. + * + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. + * + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. + * + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field <y> of register <x>. This value + * can be |'d with others to produce a full register value for + * register <x>. + * + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This + * value can be ~'d and then &'d to clear the value of field <y> for + * register <x>. + * + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted + * to place it at field <y> of register <x>. This value can be |'d + * with others to produce a full register value for <x>. + * + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register + * <x> value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field <y> of register <x>. + * + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for + * field <y> of register <x>. This value is suitable for direct + * comparison with unshifted values appropriate for use in field <y> + * of register <x>. + */ + +#ifndef __hw_host1x_sync_host1x_h__ +#define __hw_host1x_sync_host1x_h__ +/*This file is autogenerated. Do not edit. */ + +static inline u32 host1x_sync_intmask_r(void) +{ + return 0x4; +} +static inline u32 host1x_sync_intc0mask_r(void) +{ + return 0x8; +} +static inline u32 host1x_sync_hintstatus_r(void) +{ + return 0x20; +} +static inline u32 host1x_sync_hintmask_r(void) +{ + return 0x24; +} +static inline u32 host1x_sync_hintstatus_ext_r(void) +{ + return 0x28; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_s(void) +{ + return 1; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_f(u32 v) +{ + return (v & 0x1) << 30; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_m(void) +{ + return 0x1 << 30; +} +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_v(u32 r) +{ + return (r >> 30) & 0x1; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_s(void) +{ + return 1; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_f(u32 v) +{ + return (v & 0x1) << 31; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_m(void) +{ + return 0x1 << 31; +} +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_v(u32 r) +{ + return (r >> 31) & 0x1; +} +static inline u32 host1x_sync_hintmask_ext_r(void) +{ + return 0x2c; +} +static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(void) +{ + return 0x40; +} +static inline u32 host1x_sync_syncpt_thresh_cpu1_int_status_r(void) +{ + return 0x48; +} +static inline u32 host1x_sync_syncpt_thresh_int_disable_r(void) +{ + return 0x60; +} +static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(void) +{ + return 0x68; +} +static inline u32 host1x_sync_cf0_setup_r(void) +{ + return 0x80; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_f(u32 v) +{ + return (v & 0x1ff) << 0; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_m(void) +{ + return 0x1ff << 0; +} +static inline u32 host1x_sync_cf0_setup_cf0_base_v(u32 r) +{ + return (r >> 0) & 0x1ff; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_f(u32 v) +{ + return (v & 0x1ff) << 16; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_m(void) +{ + return 0x1ff << 16; +} +static inline u32 host1x_sync_cf0_setup_cf0_limit_v(u32 r) +{ + return (r >> 16) & 0x1ff; +} +static inline u32 host1x_sync_cmdproc_stop_r(void) +{ + return 0xac; +} +static inline u32 host1x_sync_ch_teardown_r(void) +{ + return 0xb0; +} +static inline u32 host1x_sync_usec_clk_r(void) +{ + return 0x1a4; +} +static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void) +{ + return 0x1a8; +} +static inline u32 host1x_sync_ip_busy_timeout_r(void) +{ + return 0x1bc; +} +static inline u32 host1x_sync_ip_read_timeout_addr_r(void) +{ + return 0x1c0; +} +static inline u32 host1x_sync_ip_write_timeout_addr_r(void) +{ + return 0x1c4; +} +static inline u32 host1x_sync_mlock_0_r(void) +{ + return 0x2c0; +} +static inline u32 host1x_sync_mlock_owner_0_r(void) +{ + return 0x340; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_s(void) +{ + return 4; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_f(u32 v) +{ + return (v & 0xf) << 8; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_m(void) +{ + return 0xf << 8; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_v(u32 r) +{ + return (r >> 8) & 0xf; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_s(void) +{ + return 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_f(u32 v) +{ + return (v & 0x1) << 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_m(void) +{ + return 0x1 << 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_v(u32 r) +{ + return (r >> 1) & 0x1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_s(void) +{ + return 1; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_f(u32 v) +{ + return (v & 0x1) << 0; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_m(void) +{ + return 0x1 << 0; +} +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_v(u32 r) +{ + return (r >> 0) & 0x1; +} +static inline u32 host1x_sync_syncpt_0_r(void) +{ + return 0x400; +} +static inline u32 host1x_sync_syncpt_int_thresh_0_r(void) +{ + return 0x500; +} +static inline u32 host1x_sync_syncpt_base_0_r(void) +{ + return 0x600; +} +static inline u32 host1x_sync_syncpt_cpu_incr_r(void) +{ + return 0x700; +} +static inline u32 host1x_sync_cbread0_r(void) +{ + return 0x720; +} +static inline u32 host1x_sync_cfpeek_ctrl_r(void) +{ + return 0x74c; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_f(u32 v) +{ + return (v & 0x1ff) << 0; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_m(void) +{ + return 0x1ff << 0; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_v(u32 r) +{ + return (r >> 0) & 0x1ff; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_s(void) +{ + return 3; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_f(u32 v) +{ + return (v & 0x7) << 16; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_m(void) +{ + return 0x7 << 16; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_v(u32 r) +{ + return (r >> 16) & 0x7; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_s(void) +{ + return 1; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_f(u32 v) +{ + return (v & 0x1) << 31; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_m(void) +{ + return 0x1 << 31; +} +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_v(u32 r) +{ + return (r >> 31) & 0x1; +} +static inline u32 host1x_sync_cfpeek_read_r(void) +{ + return 0x750; +} +static inline u32 host1x_sync_cfpeek_ptrs_r(void) +{ + return 0x754; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_f(u32 v) +{ + return (v & 0x1ff) << 0; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_m(void) +{ + return 0x1ff << 0; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r) +{ + return (r >> 0) & 0x1ff; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_s(void) +{ + return 9; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_f(u32 v) +{ + return (v & 0x1ff) << 16; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_m(void) +{ + return 0x1ff << 16; +} +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r) +{ + return (r >> 16) & 0x1ff; +} +static inline u32 host1x_sync_cbstat_0_r(void) +{ + return 0x758; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_s(void) +{ + return 16; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_f(u32 v) +{ + return (v & 0xffff) << 0; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_m(void) +{ + return 0xffff << 0; +} +static inline u32 host1x_sync_cbstat_0_cboffset0_v(u32 r) +{ + return (r >> 0) & 0xffff; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_s(void) +{ + return 10; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_f(u32 v) +{ + return (v & 0x3ff) << 16; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_m(void) +{ + return 0x3ff << 16; +} +static inline u32 host1x_sync_cbstat_0_cbclass0_v(u32 r) +{ + return (r >> 16) & 0x3ff; +} + +#endif /* __hw_host1x_sync_host1x_h__ */ diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_uclass.h b/drivers/video/tegra/host/host1x/hw_host1x01_uclass.h new file mode 100644 index 000000000000..ed6e4b706ab9 --- /dev/null +++ b/drivers/video/tegra/host/host1x/hw_host1x01_uclass.h @@ -0,0 +1,474 @@ +/* + * drivers/video/tegra/host/host1x/hw_host1x_uclass_host1x.h + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + /* + * Function naming determines intended use: + * + * <x>_r(void) : Returns the offset for register <x>. + * + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. + * + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. + * + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field <y> of register <x>. This value + * can be |'d with others to produce a full register value for + * register <x>. + * + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This + * value can be ~'d and then &'d to clear the value of field <y> for + * register <x>. + * + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted + * to place it at field <y> of register <x>. This value can be |'d + * with others to produce a full register value for <x>. + * + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register + * <x> value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field <y> of register <x>. + * + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for + * field <y> of register <x>. This value is suitable for direct + * comparison with unshifted values appropriate for use in field <y> + * of register <x>. + */ + +#ifndef __hw_host1x_uclass_host1x_h__ +#define __hw_host1x_uclass_host1x_h__ +/*This file is autogenerated. Do not edit. */ + +static inline u32 host1x_uclass_incr_syncpt_r(void) +{ + return 0x0; +} +static inline u32 host1x_uclass_incr_syncpt_cond_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v) +{ + return (v & 0xff) << 8; +} +static inline u32 host1x_uclass_incr_syncpt_cond_m(void) +{ + return 0xff << 8; +} +static inline u32 host1x_uclass_incr_syncpt_cond_v(u32 r) +{ + return (r >> 8) & 0xff; +} +static inline u32 host1x_uclass_incr_syncpt_cond_immediate_v(void) +{ + return 0; +} +static inline u32 host1x_uclass_incr_syncpt_cond_op_done_v(void) +{ + return 1; +} +static inline u32 host1x_uclass_incr_syncpt_cond_rd_done_v(void) +{ + return 2; +} +static inline u32 host1x_uclass_incr_syncpt_cond_reg_wr_safe_v(void) +{ + return 3; +} +static inline u32 host1x_uclass_incr_syncpt_indx_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v) +{ + return (v & 0xff) << 0; +} +static inline u32 host1x_uclass_incr_syncpt_indx_m(void) +{ + return 0xff << 0; +} +static inline u32 host1x_uclass_incr_syncpt_indx_v(u32 r) +{ + return (r >> 0) & 0xff; +} +static inline u32 host1x_uclass_wait_syncpt_r(void) +{ + return 0x8; +} +static inline u32 host1x_uclass_wait_syncpt_indx_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +static inline u32 host1x_uclass_wait_syncpt_indx_m(void) +{ + return 0xff << 24; +} +static inline u32 host1x_uclass_wait_syncpt_indx_v(u32 r) +{ + return (r >> 24) & 0xff; +} +static inline u32 host1x_uclass_wait_syncpt_thresh_s(void) +{ + return 24; +} +static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v) +{ + return (v & 0xffffff) << 0; +} +static inline u32 host1x_uclass_wait_syncpt_thresh_m(void) +{ + return 0xffffff << 0; +} +static inline u32 host1x_uclass_wait_syncpt_thresh_v(u32 r) +{ + return (r >> 0) & 0xffffff; +} +static inline u32 host1x_uclass_wait_syncpt_base_r(void) +{ + return 0x9; +} +static inline u32 host1x_uclass_wait_syncpt_base_indx_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +static inline u32 host1x_uclass_wait_syncpt_base_indx_m(void) +{ + return 0xff << 24; +} +static inline u32 host1x_uclass_wait_syncpt_base_indx_v(u32 r) +{ + return (r >> 24) & 0xff; +} +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v) +{ + return (v & 0xff) << 16; +} +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_m(void) +{ + return 0xff << 16; +} +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_v(u32 r) +{ + return (r >> 16) & 0xff; +} +static inline u32 host1x_uclass_wait_syncpt_base_offset_s(void) +{ + return 16; +} +static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v) +{ + return (v & 0xffff) << 0; +} +static inline u32 host1x_uclass_wait_syncpt_base_offset_m(void) +{ + return 0xffff << 0; +} +static inline u32 host1x_uclass_wait_syncpt_base_offset_v(u32 r) +{ + return (r >> 0) & 0xffff; +} +static inline u32 host1x_uclass_load_syncpt_base_r(void) +{ + return 0xb; +} +static inline u32 host1x_uclass_load_syncpt_base_base_indx_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +static inline u32 host1x_uclass_load_syncpt_base_base_indx_m(void) +{ + return 0xff << 24; +} +static inline u32 host1x_uclass_load_syncpt_base_base_indx_v(u32 r) +{ + return (r >> 24) & 0xff; +} +static inline u32 host1x_uclass_load_syncpt_base_value_s(void) +{ + return 24; +} +static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v) +{ + return (v & 0xffffff) << 0; +} +static inline u32 host1x_uclass_load_syncpt_base_value_m(void) +{ + return 0xffffff << 0; +} +static inline u32 host1x_uclass_load_syncpt_base_value_v(u32 r) +{ + return (r >> 0) & 0xffffff; +} +static inline u32 host1x_uclass_incr_syncpt_base_r(void) +{ + return 0xc; +} +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_m(void) +{ + return 0xff << 24; +} +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_v(u32 r) +{ + return (r >> 24) & 0xff; +} +static inline u32 host1x_uclass_incr_syncpt_base_offset_s(void) +{ + return 24; +} +static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v) +{ + return (v & 0xffffff) << 0; +} +static inline u32 host1x_uclass_incr_syncpt_base_offset_m(void) +{ + return 0xffffff << 0; +} +static inline u32 host1x_uclass_incr_syncpt_base_offset_v(u32 r) +{ + return (r >> 0) & 0xffffff; +} +static inline u32 host1x_uclass_indoff_r(void) +{ + return 0x2d; +} +static inline u32 host1x_uclass_indoff_indbe_s(void) +{ + return 4; +} +static inline u32 host1x_uclass_indoff_indbe_f(u32 v) +{ + return (v & 0xf) << 28; +} +static inline u32 host1x_uclass_indoff_indbe_m(void) +{ + return 0xf << 28; +} +static inline u32 host1x_uclass_indoff_indbe_v(u32 r) +{ + return (r >> 28) & 0xf; +} +static inline u32 host1x_uclass_indoff_autoinc_s(void) +{ + return 1; +} +static inline u32 host1x_uclass_indoff_autoinc_f(u32 v) +{ + return (v & 0x1) << 27; +} +static inline u32 host1x_uclass_indoff_autoinc_m(void) +{ + return 0x1 << 27; +} +static inline u32 host1x_uclass_indoff_autoinc_v(u32 r) +{ + return (r >> 27) & 0x1; +} +static inline u32 host1x_uclass_indoff_spool_s(void) +{ + return 1; +} +static inline u32 host1x_uclass_indoff_spool_f(u32 v) +{ + return (v & 0x1) << 26; +} +static inline u32 host1x_uclass_indoff_spool_m(void) +{ + return 0x1 << 26; +} +static inline u32 host1x_uclass_indoff_spool_v(u32 r) +{ + return (r >> 26) & 0x1; +} +static inline u32 host1x_uclass_indoff_indoffset_s(void) +{ + return 24; +} +static inline u32 host1x_uclass_indoff_indoffset_f(u32 v) +{ + return (v & 0xffffff) << 2; +} +static inline u32 host1x_uclass_indoff_indoffset_m(void) +{ + return 0xffffff << 2; +} +static inline u32 host1x_uclass_indoff_indoffset_v(u32 r) +{ + return (r >> 2) & 0xffffff; +} +static inline u32 host1x_uclass_indoff_indmodid_s(void) +{ + return 8; +} +static inline u32 host1x_uclass_indoff_indmodid_f(u32 v) +{ + return (v & 0xff) << 18; +} +static inline u32 host1x_uclass_indoff_indmodid_m(void) +{ + return 0xff << 18; +} +static inline u32 host1x_uclass_indoff_indmodid_v(u32 r) +{ + return (r >> 18) & 0xff; +} +static inline u32 host1x_uclass_indoff_indmodid_host1x_v(void) +{ + return 0; +} +static inline u32 host1x_uclass_indoff_indmodid_mpe_v(void) +{ + return 1; +} +static inline u32 host1x_uclass_indoff_indmodid_vi_v(void) +{ + return 2; +} +static inline u32 host1x_uclass_indoff_indmodid_epp_v(void) +{ + return 3; +} +static inline u32 host1x_uclass_indoff_indmodid_isp_v(void) +{ + return 4; +} +static inline u32 host1x_uclass_indoff_indmodid_gr2d_v(void) +{ + return 5; +} +static inline u32 host1x_uclass_indoff_indmodid_gr3d_v(void) +{ + return 6; +} +static inline u32 host1x_uclass_indoff_indmodid_display_v(void) +{ + return 8; +} +static inline u32 host1x_uclass_indoff_indmodid_tvo_v(void) +{ + return 11; +} +static inline u32 host1x_uclass_indoff_indmodid_displayb_v(void) +{ + return 9; +} +static inline u32 host1x_uclass_indoff_indmodid_dsi_v(void) +{ + return 12; +} +static inline u32 host1x_uclass_indoff_indmodid_hdmi_v(void) +{ + return 10; +} +static inline u32 host1x_uclass_indoff_indmodid_dsib_v(void) +{ + return 16; +} +static inline u32 host1x_uclass_indoff_indroffset_s(void) +{ + return 16; +} +static inline u32 host1x_uclass_indoff_indroffset_f(u32 v) +{ + return (v & 0xffff) << 2; +} +static inline u32 host1x_uclass_indoff_indroffset_m(void) +{ + return 0xffff << 2; +} +static inline u32 host1x_uclass_indoff_indroffset_v(u32 r) +{ + return (r >> 2) & 0xffff; +} +static inline u32 host1x_uclass_indoff_acctype_s(void) +{ + return 1; +} +static inline u32 host1x_uclass_indoff_acctype_f(u32 v) +{ + return (v & 0x1) << 1; +} +static inline u32 host1x_uclass_indoff_acctype_m(void) +{ + return 0x1 << 1; +} +static inline u32 host1x_uclass_indoff_acctype_v(u32 r) +{ + return (r >> 1) & 0x1; +} +static inline u32 host1x_uclass_indoff_acctype_reg_v(void) +{ + return 0; +} +static inline u32 host1x_uclass_indoff_acctype_fb_v(void) +{ + return 1; +} +static inline u32 host1x_uclass_indoff_rwn_s(void) +{ + return 1; +} +static inline u32 host1x_uclass_indoff_rwn_f(u32 v) +{ + return (v & 0x1) << 0; +} +static inline u32 host1x_uclass_indoff_rwn_m(void) +{ + return 0x1 << 0; +} +static inline u32 host1x_uclass_indoff_rwn_v(u32 r) +{ + return (r >> 0) & 0x1; +} +static inline u32 host1x_uclass_indoff_rwn_write_v(void) +{ + return 0; +} +static inline u32 host1x_uclass_indoff_rwn_read_v(void) +{ + return 1; +} +static inline u32 host1x_uclass_inddata_r(void) +{ + return 0x2e; +} + +#endif /* __hw_host1x_uclass_host1x_h__ */ diff --git a/drivers/video/tegra/host/isp/isp.c b/drivers/video/tegra/host/isp/isp.c index 9044d40b8574..0a3cc3b03578 100644 --- a/drivers/video/tegra/host/isp/isp.c +++ b/drivers/video/tegra/host/isp/isp.c @@ -18,14 +18,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/resource.h> - -#include <mach/iomap.h> - #include "dev.h" #include "bus_client.h" -static int __devinit isp_probe(struct nvhost_device *dev) +static int __devinit isp_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { int err = 0; @@ -42,6 +39,7 @@ static int __exit isp_remove(struct nvhost_device *dev) return 0; } +#ifdef CONFIG_PM static int isp_suspend(struct nvhost_device *dev, pm_message_t state) { return nvhost_client_device_suspend(dev); @@ -52,15 +50,7 @@ static int isp_resume(struct nvhost_device *dev) dev_info(&dev->dev, "resuming\n"); return 0; } - -static struct resource isp_resources = { - .name = "regs", - .start = TEGRA_ISP_BASE, - .end = TEGRA_ISP_BASE + TEGRA_ISP_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -struct nvhost_device *isp_device; +#endif static struct nvhost_driver isp_driver = { .probe = isp_probe, @@ -77,18 +67,6 @@ static struct nvhost_driver isp_driver = { static int __init isp_init(void) { - int err; - - isp_device = nvhost_get_device("isp"); - if (!isp_device) - return -ENXIO; - - isp_device->resource = &isp_resources; - isp_device->num_resources = 1; - err = nvhost_device_register(isp_device); - if (err) - return err; - return nvhost_driver_register(&isp_driver); } diff --git a/drivers/video/tegra/host/mpe/mpe.c b/drivers/video/tegra/host/mpe/mpe.c index 36d1d6f26682..c738700469c6 100644 --- a/drivers/video/tegra/host/mpe/mpe.c +++ b/drivers/video/tegra/host/mpe/mpe.c @@ -19,17 +19,15 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" #include "dev.h" -#include "host1x/host1x_hardware.h" -#include "host1x/host1x_channel.h" -#include "host1x/host1x_syncpt.h" +#include "host1x/host1x01_hardware.h" #include "host1x/host1x_hwctx.h" #include "t20/t20.h" +#include "chip_support.h" +#include "nvhost_memmgr.h" #include <linux/slab.h> -#include <linux/resource.h> - -#include <mach/iomap.h> #include "bus_client.h" @@ -140,7 +138,7 @@ static void restore_begin(struct host1x_hwctx_handler *h, u32 *ptr) { /* set class to host */ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INCR_SYNCPT_BASE, 1); + host1x_uclass_incr_syncpt_base_r(), 1); /* increment sync point base */ ptr[1] = nvhost_class_host_incr_syncpt_base(h->waitbase, 1); /* set class to MPE */ @@ -159,7 +157,8 @@ static void restore_ram(u32 *ptr, unsigned words, static void restore_end(struct host1x_hwctx_handler *h, u32 *ptr) { /* syncpt increment to track restore gather. */ - ptr[0] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, + ptr[0] = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), h->syncpt); } #define RESTORE_END_SIZE 1 @@ -217,23 +216,26 @@ static void __init save_begin(struct host1x_hwctx_handler *h, u32 *ptr) { /* MPE: when done, increment syncpt to base+1 */ ptr[0] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID, 0, 0); - ptr[1] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, h->syncpt); + ptr[1] = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_op_done_v(), h->syncpt); /* host: wait for syncpt base+1 */ ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1); + host1x_uclass_wait_syncpt_base_r(), 1); ptr[3] = nvhost_class_host_wait_syncpt_base(h->syncpt, h->waitbase, 1); /* host: signal context read thread to start reading */ - ptr[4] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE, h->syncpt); + ptr[4] = nvhost_opcode_imm_incr_syncpt( + host1x_uclass_incr_syncpt_cond_immediate_v(), + h->syncpt); } #define SAVE_BEGIN_SIZE 5 static void __init save_direct(u32 *ptr, u32 start_reg, u32 count) { ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INDOFF, 1); + host1x_uclass_indoff_r(), 1); ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_MPE, start_reg, true); - ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count); + ptr[2] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count); } #define SAVE_DIRECT_SIZE 3 @@ -248,10 +250,10 @@ static void __init save_set_ram_cmd(u32 *ptr, u32 cmd_reg, u32 count) static void __init save_read_ram_data_nasty(u32 *ptr, u32 data_reg) { ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INDOFF, 1); + host1x_uclass_indoff_r(), 1); ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_MPE, data_reg, false); - ptr[2] = nvhost_opcode_imm(NV_CLASS_HOST_INDDATA, 0); + ptr[2] = nvhost_opcode_imm(host1x_uclass_inddata_r(), 0); /* write junk data to avoid 'cached problem with register memory' */ ptr[3] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID, data_reg, 1); @@ -263,10 +265,10 @@ static void __init save_end(struct host1x_hwctx_handler *h, u32 *ptr) { /* Wait for context read service to finish (cpu incr 3) */ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1); + host1x_uclass_wait_syncpt_base_r(), 1); ptr[1] = nvhost_class_host_wait_syncpt_base(h->syncpt, h->waitbase, 3); /* Advance syncpoint base */ - ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1); + ptr[2] = nvhost_opcode_nonincr(host1x_uclass_incr_syncpt_base_r(), 1); ptr[3] = nvhost_class_host_incr_syncpt_base(h->waitbase, 3); /* set class back to the unit */ ptr[4] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID, 0, 0); @@ -392,7 +394,7 @@ static u32 *save_regs(u32 *ptr, unsigned int *pending, u32 count = regs->count; ++ptr; /* restore incr */ if (regs->type == HWCTX_REGINFO_NORMAL) { - host1x_drain_read_fifo(channel->aperture, + nvhost_channel_drain_read_fifo(channel, ptr, count, pending); ptr += count; } else { @@ -401,8 +403,8 @@ static u32 *save_regs(u32 *ptr, unsigned int *pending, BUG_ON(msi->out_pos >= NR_WRITEBACKS); word = msi->out[msi->out_pos++]; } else { - host1x_drain_read_fifo(channel->aperture, - &word, 1, pending); + nvhost_channel_drain_read_fifo(channel, + &word, 1, pending); if (regs->type == HWCTX_REGINFO_STASH) { BUG_ON(msi->in_pos >= NR_STASHES); msi->in[msi->in_pos++] = word; @@ -422,7 +424,7 @@ static u32 *save_ram(u32 *ptr, unsigned int *pending, { int err = 0; ptr += RESTORE_RAM_SIZE; - err = host1x_drain_read_fifo(channel->aperture, ptr, words, pending); + err = nvhost_channel_drain_read_fifo(channel, ptr, words, pending); WARN_ON(err); return ptr + words; } @@ -432,23 +434,23 @@ static u32 *save_ram(u32 *ptr, unsigned int *pending, static struct nvhost_hwctx *ctxmpe_alloc(struct nvhost_hwctx_handler *h, struct nvhost_channel *ch) { - struct nvmap_client *nvmap = nvhost_get_host(ch->dev)->nvmap; + struct mem_mgr *memmgr = nvhost_get_host(ch->dev)->memmgr; struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h); struct host1x_hwctx *ctx; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; - ctx->restore = nvmap_alloc(nvmap, restore_size * 4, 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); + ctx->restore = mem_op().alloc(memmgr, restore_size * 4, 32, + mem_mgr_flag_write_combine); if (IS_ERR_OR_NULL(ctx->restore)) { kfree(ctx); return NULL; } - ctx->restore_virt = nvmap_mmap(ctx->restore); + ctx->restore_virt = mem_op().mmap(ctx->restore); if (!ctx->restore_virt) { - nvmap_free(nvmap, ctx->restore); + mem_op().put(memmgr, ctx->restore); kfree(ctx); return NULL; } @@ -459,7 +461,8 @@ static struct nvhost_hwctx *ctxmpe_alloc(struct nvhost_hwctx_handler *h, ctx->hwctx.valid = false; ctx->save_incrs = 3; ctx->save_thresh = 2; - ctx->restore_phys = nvmap_pin(nvmap, ctx->restore); + ctx->save_slots = p->save_slots; + ctx->restore_phys = mem_op().pin(memmgr, ctx->restore); ctx->restore_size = restore_size; ctx->restore_incrs = 1; @@ -477,13 +480,12 @@ static void ctxmpe_free(struct kref *ref) { struct nvhost_hwctx *nctx = container_of(ref, struct nvhost_hwctx, ref); struct host1x_hwctx *ctx = to_host1x_hwctx(nctx); - struct nvmap_client *nvmap = - nvhost_get_host(nctx->channel->dev)->nvmap; + struct mem_mgr *memmgr = nvhost_get_host(nctx->channel->dev)->memmgr; if (ctx->restore_virt) - nvmap_munmap(ctx->restore, ctx->restore_virt); - nvmap_unpin(nvmap, ctx->restore); - nvmap_free(nvmap, ctx->restore); + mem_op().munmap(ctx->restore, ctx->restore_virt); + mem_op().unpin(memmgr, ctx->restore); + mem_op().put(memmgr, ctx->restore); kfree(ctx); } @@ -497,7 +499,10 @@ static void ctxmpe_save_push(struct nvhost_hwctx *nctx, { struct host1x_hwctx *ctx = to_host1x_hwctx(nctx); struct host1x_hwctx_handler *h = host1x_hwctx_handler(ctx); - nvhost_cdma_push(cdma, + nvhost_cdma_push_gather(cdma, + nvhost_get_host(nctx->channel->dev)->memmgr, + h->save_buf, + 0, nvhost_opcode_gather(h->save_size), h->save_phys); } @@ -528,11 +533,10 @@ static void ctxmpe_save_service(struct nvhost_hwctx *nctx) h->syncpt); } -struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init( - u32 syncpt, u32 waitbase, - struct nvhost_channel *ch) +struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init(u32 syncpt, + u32 waitbase, struct nvhost_channel *ch) { - struct nvmap_client *nvmap; + struct mem_mgr *memmgr; u32 *save_ptr; struct host1x_hwctx_handler *p; @@ -540,28 +544,29 @@ struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init( if (!p) return NULL; - nvmap = nvhost_get_host(ch->dev)->nvmap; + memmgr = nvhost_get_host(ch->dev)->memmgr; p->syncpt = syncpt; p->waitbase = waitbase; setup_save(p, NULL); - p->save_buf = nvmap_alloc(nvmap, p->save_size * 4, 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); + p->save_buf = mem_op().alloc(memmgr, p->save_size * 4, 32, + mem_mgr_flag_write_combine); if (IS_ERR(p->save_buf)) { p->save_buf = NULL; return NULL; } - save_ptr = nvmap_mmap(p->save_buf); + save_ptr = mem_op().mmap(p->save_buf); if (!save_ptr) { - nvmap_free(nvmap, p->save_buf); + mem_op().put(memmgr, p->save_buf); p->save_buf = NULL; return NULL; } - p->save_phys = nvmap_pin(nvmap, p->save_buf); + p->save_phys = mem_op().pin(memmgr, p->save_buf); + p->save_slots = 1; setup_save(p, save_ptr); @@ -576,12 +581,50 @@ struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init( int nvhost_mpe_prepare_power_off(struct nvhost_device *dev) { - return host1x_save_context(dev, NVSYNCPT_MPE); + return nvhost_channel_save_context(dev->channel); } -static int __devinit mpe_probe(struct nvhost_device *dev) +enum mpe_ip_ver { + mpe_01 = 1, + mpe_02, +}; + +struct mpe_desc { + int (*prepare_poweroff)(struct nvhost_device *dev); + struct nvhost_hwctx_handler *(*alloc_hwctx_handler)(u32 syncpt, + u32 waitbase, struct nvhost_channel *ch); +}; + +static const struct mpe_desc mpe[] = { + [mpe_01] = { + .prepare_poweroff = nvhost_mpe_prepare_power_off, + .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, + }, + [mpe_02] = { + .prepare_poweroff = nvhost_mpe_prepare_power_off, + .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, + }, +}; + +static struct nvhost_device_id mpe_id[] = { + { "mpe", mpe_01 }, + { "mpe", mpe_02 }, + { }, +}; + +MODULE_DEVICE_TABLE(nvhost, mpe_id); + +static int __devinit mpe_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { int err = 0; + int index = 0; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + + index = id_table->version; + + drv->prepare_poweroff = mpe[index].prepare_poweroff; + drv->alloc_hwctx_handler = mpe[index].alloc_hwctx_handler; err = nvhost_client_device_get_resources(dev); if (err) @@ -596,6 +639,7 @@ static int __exit mpe_remove(struct nvhost_device *dev) return 0; } +#ifdef CONFIG_PM static int mpe_suspend(struct nvhost_device *dev, pm_message_t state) { return nvhost_client_device_suspend(dev); @@ -606,15 +650,7 @@ static int mpe_resume(struct nvhost_device *dev) dev_info(&dev->dev, "resuming\n"); return 0; } - -static struct resource mpe_resources = { - .name = "regs", - .start = TEGRA_MPE_BASE, - .end = TEGRA_MPE_BASE + TEGRA_MPE_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -struct nvhost_device *mpe_device; +#endif static struct nvhost_driver mpe_driver = { .probe = mpe_probe, @@ -626,24 +662,12 @@ static struct nvhost_driver mpe_driver = { .driver = { .owner = THIS_MODULE, .name = "mpe", - } + }, + .id_table = mpe_id, }; static int __init mpe_init(void) { - int err; - - mpe_device = nvhost_get_device("mpe"); - if (!mpe_device) - return -ENXIO; - - /* use ARRAY_SIZE macro if resources are more than 1 */ - mpe_device->resource = &mpe_resources; - mpe_device->num_resources = 1; - err = nvhost_device_register(mpe_device); - if (err) - return err; - return nvhost_driver_register(&mpe_driver); } diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c index 015b7c4dbf66..06005c423a21 100644 --- a/drivers/video/tegra/host/nvhost_acm.c +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -3,7 +3,7 @@ * * Tegra Graphics Host Automatic Clock Management * - * Copyright (c) 2010-2012, NVIDIA Corporation. + * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -31,9 +31,9 @@ #include <mach/clk.h> #include <mach/hardware.h> -#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ) -#define POWERGATE_DELAY 10 -#define MAX_DEVID_LENGTH 16 +#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ) +#define POWERGATE_DELAY 10 +#define MAX_DEVID_LENGTH 16 DEFINE_MUTEX(client_list_lock); @@ -120,9 +120,12 @@ static void to_state_clockgated_locked(struct nvhost_device *dev) static void to_state_running_locked(struct nvhost_device *dev) { + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); int prev_state = dev->powerstate; + if (dev->powerstate == NVHOST_POWER_STATE_POWERGATED) to_state_clockgated_locked(dev); + if (dev->powerstate == NVHOST_POWER_STATE_CLOCKGATED) { int i; @@ -131,12 +134,16 @@ static void to_state_running_locked(struct nvhost_device *dev) for (i = 0; i < dev->num_clks; i++) { int err = clk_enable(dev->clk[i]); - BUG_ON(err); + if (err) { + dev_err(&dev->dev, "Cannot turn on clock %s", + dev->clocks[i].name); + return; + } } if (prev_state == NVHOST_POWER_STATE_POWERGATED - && dev->finalize_poweron) - dev->finalize_poweron(dev); + && drv->finalize_poweron) + drv->finalize_poweron(dev); } dev->powerstate = NVHOST_POWER_STATE_RUNNING; } @@ -148,12 +155,13 @@ static void to_state_running_locked(struct nvhost_device *dev) static int to_state_powergated_locked(struct nvhost_device *dev) { int err = 0; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); - if (dev->prepare_poweroff + if (drv->prepare_poweroff && dev->powerstate != NVHOST_POWER_STATE_POWERGATED) { /* Clock needs to be on in prepare_poweroff */ to_state_running_locked(dev); - err = dev->prepare_poweroff(dev); + err = drv->prepare_poweroff(dev); if (err) return err; } @@ -185,8 +193,10 @@ static void schedule_clockgating_locked(struct nvhost_device *dev) void nvhost_module_busy(struct nvhost_device *dev) { - if (dev->busy) - dev->busy(dev); + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + + if (drv->busy) + drv->busy(dev); mutex_lock(&dev->lock); cancel_delayed_work(&dev->powerstate_down); @@ -223,9 +233,9 @@ static void powerstate_down_handler(struct work_struct *work) mutex_unlock(&dev->lock); } - void nvhost_module_idle_mult(struct nvhost_device *dev, int refs) { + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); bool kick = false; mutex_lock(&dev->lock); @@ -240,8 +250,8 @@ void nvhost_module_idle_mult(struct nvhost_device *dev, int refs) if (kick) { wake_up(&dev->idle_wq); - if (dev->idle) - dev->idle(dev); + if (drv->idle) + drv->idle(dev); } } @@ -349,9 +359,103 @@ void nvhost_module_remove_client(struct nvhost_device *dev, void *priv) mutex_unlock(&client_list_lock); } +static ssize_t refcount_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, \ + power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]); + struct nvhost_device *dev = power_attribute->ndev; + + mutex_lock(&dev->lock); + ret = sprintf(buf, "%d\n", dev->refcount); + mutex_unlock(&dev->lock); + + return ret; +} + +static ssize_t powergate_delay_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int powergate_delay = 0, ret = 0; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, \ + power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]); + struct nvhost_device *dev = power_attribute->ndev; + + if (!dev->can_powergate) { + dev_info(&dev->dev, "does not support power-gating\n"); + return count; + } + + mutex_lock(&dev->lock); + ret = sscanf(buf, "%d", &powergate_delay); + if (ret == 1 && powergate_delay >= 0) + dev->powergate_delay = powergate_delay; + else + dev_err(&dev->dev, "Invalid powergate delay\n"); + mutex_unlock(&dev->lock); + + return count; +} + +static ssize_t powergate_delay_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, \ + power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]); + struct nvhost_device *dev = power_attribute->ndev; + + mutex_lock(&dev->lock); + ret = sprintf(buf, "%d\n", dev->powergate_delay); + mutex_unlock(&dev->lock); + + return ret; +} + +static ssize_t clockgate_delay_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int clockgate_delay = 0, ret = 0; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, \ + power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]); + struct nvhost_device *dev = power_attribute->ndev; + + mutex_lock(&dev->lock); + ret = sscanf(buf, "%d", &clockgate_delay); + if (ret == 1 && clockgate_delay >= 0) + dev->clockgate_delay = clockgate_delay; + else + dev_err(&dev->dev, "Invalid clockgate delay\n"); + mutex_unlock(&dev->lock); + + return count; +} + +static ssize_t clockgate_delay_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret; + struct nvhost_device_power_attr *power_attribute = + container_of(attr, struct nvhost_device_power_attr, \ + power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]); + struct nvhost_device *dev = power_attribute->ndev; + + mutex_lock(&dev->lock); + ret = sprintf(buf, "%d\n", dev->clockgate_delay); + mutex_unlock(&dev->lock); + + return ret; +} + int nvhost_module_init(struct nvhost_device *dev) { - int i = 0; + int i = 0, err = 0; + struct kobj_attribute *attr = NULL; /* initialize clocks to known state */ INIT_LIST_HEAD(&dev->client_list); @@ -362,7 +466,11 @@ int nvhost_module_init(struct nvhost_device *dev) snprintf(devname, MAX_DEVID_LENGTH, "tegra_%s", dev->name); c = clk_get_sys(devname, dev->clocks[i].name); - BUG_ON(IS_ERR_OR_NULL(c)); + if (IS_ERR_OR_NULL(c)) { + dev_err(&dev->dev, "Cannot get clock %s\n", + dev->clocks[i].name); + continue; + } rate = clk_round_rate(c, rate); clk_enable(c); @@ -388,7 +496,71 @@ int nvhost_module_init(struct nvhost_device *dev) dev->powerstate = NVHOST_POWER_STATE_CLOCKGATED; } + /* Init the power sysfs attributes for this device */ + dev->power_attrib = kzalloc(sizeof(struct nvhost_device_power_attr), + GFP_KERNEL); + if (!dev->power_attrib) { + dev_err(&dev->dev, "Unable to allocate sysfs attributes\n"); + return -ENOMEM; + } + dev->power_attrib->ndev = dev; + + dev->power_kobj = kobject_create_and_add("acm", &dev->dev.kobj); + if (!dev->power_kobj) { + dev_err(&dev->dev, "Could not add dir 'power'\n"); + err = -EIO; + goto fail_attrib_alloc; + } + + attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]; + attr->attr.name = "clockgate_delay"; + attr->attr.mode = S_IWUSR | S_IRUGO; + attr->show = clockgate_delay_show; + attr->store = clockgate_delay_store; + if (sysfs_create_file(dev->power_kobj, &attr->attr)) { + dev_err(&dev->dev, "Could not create sysfs attribute clockgate_delay\n"); + err = -EIO; + goto fail_clockdelay; + } + + attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]; + attr->attr.name = "powergate_delay"; + attr->attr.mode = S_IWUSR | S_IRUGO; + attr->show = powergate_delay_show; + attr->store = powergate_delay_store; + if (sysfs_create_file(dev->power_kobj, &attr->attr)) { + dev_err(&dev->dev, "Could not create sysfs attribute powergate_delay\n"); + err = -EIO; + goto fail_powergatedelay; + } + + attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]; + attr->attr.name = "refcount"; + attr->attr.mode = S_IRUGO; + attr->show = refcount_show; + if (sysfs_create_file(dev->power_kobj, &attr->attr)) { + dev_err(&dev->dev, "Could not create sysfs attribute refcount\n"); + err = -EIO; + goto fail_refcount; + } + return 0; + +fail_refcount: + attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]; + sysfs_remove_file(dev->power_kobj, &attr->attr); + +fail_powergatedelay: + attr = &dev->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]; + sysfs_remove_file(dev->power_kobj, &attr->attr); + +fail_clockdelay: + kobject_put(dev->power_kobj); + +fail_attrib_alloc: + kfree(dev->power_attrib); + + return err; } static int is_module_idle(struct nvhost_device *dev) @@ -403,6 +575,7 @@ static int is_module_idle(struct nvhost_device *dev) int nvhost_module_suspend(struct nvhost_device *dev) { int ret; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); ret = wait_event_timeout(dev->idle_wq, is_module_idle(dev), ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT); @@ -417,8 +590,8 @@ int nvhost_module_suspend(struct nvhost_device *dev) to_state_powergated_locked(dev); mutex_unlock(&dev->lock); - if (dev->suspend) - dev->suspend(dev); + if (drv->suspend_ndev) + drv->suspend_ndev(dev); return 0; } @@ -426,9 +599,10 @@ int nvhost_module_suspend(struct nvhost_device *dev) void nvhost_module_deinit(struct nvhost_device *dev) { int i; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); - if (dev->deinit) - dev->deinit(dev); + if (drv->deinit) + drv->deinit(dev); nvhost_module_suspend(dev); for (i = 0; i < dev->num_clks; i++) @@ -436,3 +610,18 @@ void nvhost_module_deinit(struct nvhost_device *dev) dev->powerstate = NVHOST_POWER_STATE_DEINIT; } +/* public host1x power management APIs */ +bool nvhost_module_powered_ext(struct nvhost_device *dev) +{ + return nvhost_module_powered(dev); +} + +void nvhost_module_busy_ext(struct nvhost_device *dev) +{ + nvhost_module_busy(dev); +} + +void nvhost_module_idle_ext(struct nvhost_device *dev) +{ + nvhost_module_idle(dev); +} diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h index d2562e9a369f..a5894dcfc0b2 100644 --- a/drivers/video/tegra/host/nvhost_acm.h +++ b/drivers/video/tegra/host/nvhost_acm.h @@ -45,7 +45,6 @@ int nvhost_module_get_rate(struct nvhost_device *dev, int nvhost_module_set_rate(struct nvhost_device *dev, void *priv, unsigned long rate, int index); - static inline bool nvhost_module_powered(struct nvhost_device *dev) { return dev->powerstate == NVHOST_POWER_STATE_RUNNING; @@ -56,5 +55,4 @@ static inline void nvhost_module_idle(struct nvhost_device *dev) nvhost_module_idle_mult(dev, 1); } - #endif diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index 775d761e65c9..dae3b7e6182d 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -19,7 +19,13 @@ */ #include "nvhost_cdma.h" +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "nvhost_hwctx.h" #include "dev.h" +#include "debug.h" +#include "nvhost_memmgr.h" +#include "chip_support.h" #include <asm/cacheflush.h> #include <linux/slab.h> @@ -49,6 +55,18 @@ static void add_to_sync_queue(struct nvhost_cdma *cdma, job->num_slots = nr_slots; nvhost_job_get(job); list_add_tail(&job->list, &cdma->sync_queue); + + switch (job->priority) { + case NVHOST_PRIORITY_HIGH: + cdma->high_prio_count++; + break; + case NVHOST_PRIORITY_MEDIUM: + cdma->med_prio_count++; + break; + case NVHOST_PRIORITY_LOW: + cdma->low_prio_count++; + break; + } } /** @@ -65,8 +83,8 @@ static unsigned int cdma_status_locked(struct nvhost_cdma *cdma, return list_empty(&cdma->sync_queue) ? 1 : 0; case CDMA_EVENT_PUSH_BUFFER_SPACE: { struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).space); - return cdma_pb_op(cdma).space(pb); + BUG_ON(!cdma_pb_op().space); + return cdma_pb_op().space(pb); } default: return 0; @@ -92,7 +110,13 @@ unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, trace_nvhost_wait_cdma(cdma_to_channel(cdma)->dev->name, event); - BUG_ON(cdma->event != CDMA_EVENT_NONE); + /* If somebody has managed to already start waiting, yield */ + if (cdma->event != CDMA_EVENT_NONE) { + mutex_unlock(&cdma->lock); + schedule(); + mutex_lock(&cdma->lock); + continue; + } cdma->event = event; mutex_unlock(&cdma->lock); @@ -153,7 +177,9 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) struct nvhost_syncpt *sp = &dev->syncpt; struct nvhost_job *job, *n; - BUG_ON(!cdma->running); + /* If CDMA is stopped, queue is cleared and we can return */ + if (!cdma->running) + return; /* * Walk the sync queue, reading the sync point registers as necessary, @@ -181,13 +207,26 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) /* Pop push buffer slots */ if (job->num_slots) { struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).pop_from); - cdma_pb_op(cdma).pop_from(pb, job->num_slots); + BUG_ON(!cdma_pb_op().pop_from); + cdma_pb_op().pop_from(pb, job->num_slots); if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE) signal = true; } list_del(&job->list); + + switch (job->priority) { + case NVHOST_PRIORITY_HIGH: + cdma->high_prio_count--; + break; + case NVHOST_PRIORITY_MEDIUM: + cdma->med_prio_count--; + break; + case NVHOST_PRIORITY_LOW: + cdma->low_prio_count--; + break; + } + nvhost_job_put(job); } @@ -203,17 +242,16 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) } void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, - struct nvhost_syncpt *syncpt, struct device *dev) + struct nvhost_syncpt *syncpt, struct nvhost_device *dev) { u32 get_restart; u32 syncpt_incrs; - bool exec_ctxsave; struct nvhost_job *job = NULL; u32 syncpt_val; syncpt_val = nvhost_syncpt_update_min(syncpt, cdma->timeout.syncpt_id); - dev_dbg(dev, + dev_dbg(&dev->dev, "%s: starting cleanup (thresh %d)\n", __func__, syncpt_val); @@ -224,7 +262,7 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, * where a syncpt incr happens just prior/during the teardown. */ - dev_dbg(dev, + dev_dbg(&dev->dev, "%s: skip completed buffers still in sync_queue\n", __func__); @@ -232,7 +270,7 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, if (syncpt_val < job->syncpt_end) break; - nvhost_job_dump(dev, job); + nvhost_job_dump(&dev->dev, job); } /* @@ -250,7 +288,7 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, * properly for this buffer and resources are freed. */ - dev_dbg(dev, + dev_dbg(&dev->dev, "%s: perform CPU incr on pending same ctx buffers\n", __func__); @@ -268,61 +306,27 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, job->timeout = 0; syncpt_incrs = job->syncpt_end - syncpt_val; - dev_dbg(dev, + dev_dbg(&dev->dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs); - nvhost_job_dump(dev, job); + nvhost_job_dump(&dev->dev, job); /* safe to use CPU to incr syncpts */ - cdma_op(cdma).timeout_cpu_incr(cdma, + cdma_op().timeout_cpu_incr(cdma, job->first_get, syncpt_incrs, job->syncpt_end, - job->num_slots); + job->num_slots, + dev->waitbases); syncpt_val += syncpt_incrs; } - dev_dbg(dev, - "%s: GPU incr blocked interleaved ctx buffers\n", - __func__); - - exec_ctxsave = false; - - /* setup GPU increments */ - list_for_each_entry_from(job, &cdma->sync_queue, list) { - /* same context, increment in the pushbuffer */ - if (job->clientid == cdma->timeout.clientid) { - /* won't need a timeout when replayed */ - job->timeout = 0; - - /* update buffer's syncpts in the pushbuffer */ - cdma_op(cdma).timeout_pb_incr(cdma, - job->first_get, - job->syncpt_incrs, - job->num_slots, - exec_ctxsave); - - exec_ctxsave = false; - } else { - dev_dbg(dev, - "%s: switch to a different userctx\n", - __func__); - /* - * If previous context was the timed out context - * then clear its CTXSAVE in this slot. - */ - exec_ctxsave = true; - } - - nvhost_job_dump(dev, job); - } - - dev_dbg(dev, + dev_dbg(&dev->dev, "%s: finished sync_queue modification\n", __func__); /* roll back DMAGET and start up channel again */ - cdma_op(cdma).timeout_teardown_end(cdma, get_restart); + cdma_op().timeout_teardown_end(cdma, get_restart); if (cdma->timeout.ctx) cdma->timeout.ctx->has_timedout = true; @@ -335,7 +339,7 @@ int nvhost_cdma_init(struct nvhost_cdma *cdma) { int err; struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).init); + BUG_ON(!cdma_pb_op().init); mutex_init(&cdma->lock); sema_init(&cdma->sem, 0); @@ -345,7 +349,7 @@ int nvhost_cdma_init(struct nvhost_cdma *cdma) cdma->running = false; cdma->torndown = false; - err = cdma_pb_op(cdma).init(pb); + err = cdma_pb_op().init(pb); if (err) return err; return 0; @@ -358,10 +362,10 @@ void nvhost_cdma_deinit(struct nvhost_cdma *cdma) { struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).destroy); + BUG_ON(!cdma_pb_op().destroy); BUG_ON(cdma->running); - cdma_pb_op(cdma).destroy(pb); - cdma_op(cdma).timeout_destroy(cdma); + cdma_pb_op().destroy(pb); + cdma_op().timeout_destroy(cdma); } /** @@ -375,8 +379,8 @@ int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job) /* init state on first submit with timeout value */ if (!cdma->timeout.initialized) { int err; - BUG_ON(!cdma_op(cdma).timeout_init); - err = cdma_op(cdma).timeout_init(cdma, + BUG_ON(!cdma_op().timeout_init); + err = cdma_op().timeout_init(cdma, job->syncpt_id); if (err) { mutex_unlock(&cdma->lock); @@ -385,22 +389,56 @@ int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job) } } if (!cdma->running) { - BUG_ON(!cdma_op(cdma).start); - cdma_op(cdma).start(cdma); + BUG_ON(!cdma_op().start); + cdma_op().start(cdma); } cdma->slots_free = 0; cdma->slots_used = 0; - cdma->first_get = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + cdma->first_get = cdma_pb_op().putptr(&cdma->push_buffer); return 0; } +static void trace_write_gather(struct nvhost_cdma *cdma, + struct mem_handle *ref, + u32 offset, u32 words) +{ + void *mem = NULL; + + if (nvhost_debug_trace_cmdbuf) { + mem = mem_op().mmap(ref); + if (IS_ERR_OR_NULL(mem)) + mem = NULL; + }; + + if (mem) { + u32 i; + /* + * Write in batches of 128 as there seems to be a limit + * of how much you can output to ftrace at once. + */ + for (i = 0; i < words; i += TRACE_MAX_LENGTH) { + trace_nvhost_cdma_push_gather( + cdma_to_channel(cdma)->dev->name, + (u32)ref, + min(words - i, TRACE_MAX_LENGTH), + offset + i * sizeof(u32), + mem); + } + mem_op().munmap(ref, mem); + } +} + /** * Push two words into a push buffer slot * Blocks as necessary if the push buffer is full. */ void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) { - nvhost_cdma_push_gather(cdma, NULL, NULL, op1, op2); + if (nvhost_debug_trace_cmdbuf) + trace_nvhost_cdma_push(cdma_to_channel(cdma)->dev->name, + op1, op2); + + nvhost_cdma_push_gather(cdma, NULL, NULL, 0, op1, op2); } /** @@ -408,21 +446,26 @@ void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) * Blocks as necessary if the push buffer is full. */ void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, - struct nvmap_client *client, - struct nvmap_handle *handle, u32 op1, u32 op2) + struct mem_mgr *client, struct mem_handle *handle, + u32 offset, u32 op1, u32 op2) { u32 slots_free = cdma->slots_free; struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).push_to); - BUG_ON(!cdma_op(cdma).kick); + + BUG_ON(!cdma_pb_op().push_to); + BUG_ON(!cdma_op().kick); + + if (handle) + trace_write_gather(cdma, handle, offset, op1 & 0xffff); + if (slots_free == 0) { - cdma_op(cdma).kick(cdma); + cdma_op().kick(cdma); slots_free = nvhost_cdma_wait_locked(cdma, CDMA_EVENT_PUSH_BUFFER_SPACE); } cdma->slots_free = slots_free - 1; cdma->slots_used++; - cdma_pb_op(cdma).push_to(pb, client, handle, op1, op2); + cdma_pb_op().push_to(pb, client, handle, op1, op2); } /** @@ -436,8 +479,8 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, { bool was_idle = list_empty(&cdma->sync_queue); - BUG_ON(!cdma_op(cdma).kick); - cdma_op(cdma).kick(cdma); + BUG_ON(!cdma_op().kick); + cdma_op().kick(cdma); BUG_ON(job->syncpt_id == NVSYNCPT_INVALID); @@ -450,6 +493,12 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, if (job->timeout && was_idle) cdma_start_timer_locked(cdma, job); + trace_nvhost_cdma_end(job->ch->dev->name, + job->priority, + job->ch->cdma.high_prio_count, + job->ch->cdma.med_prio_count, + job->ch->cdma.low_prio_count); + mutex_unlock(&cdma->lock); } @@ -474,6 +523,8 @@ int nvhost_cdma_flush(struct nvhost_cdma *cdma, int timeout) unsigned int space, err = 0; unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout); + trace_nvhost_cdma_flush(cdma_to_channel(cdma)->dev->name, timeout); + /* * Wait for at most timeout ms. Recalculate timeout at each iteration * to better keep within given timeout. diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index 9cb9b8277254..a9522c5f6326 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -25,14 +25,13 @@ #include <linux/semaphore.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> #include <linux/list.h> -#include "nvhost_acm.h" - struct nvhost_syncpt; struct nvhost_userctx_timeout; struct nvhost_job; +struct mem_mgr; +struct mem_handle; /* * cdma @@ -48,27 +47,13 @@ struct nvhost_job; * update - call to update sync queue and push buffer, unpin memory */ -struct nvmap_client_handle { - struct nvmap_client *client; - struct nvmap_handle *handle; -}; - struct push_buffer { - struct nvmap_handle_ref *mem; /* handle to pushbuffer memory */ + struct mem_handle *mem; /* handle to pushbuffer memory */ u32 *mapped; /* mapped pushbuffer memory */ u32 phys; /* physical address of pushbuffer */ u32 fence; /* index we've written */ u32 cur; /* index to write to */ - struct nvmap_client_handle *nvmap; - /* nvmap handle for each opcode pair */ -}; - -struct syncpt_buffer { - struct nvmap_handle_ref *mem; /* handle to pushbuffer memory */ - u32 *mapped; /* mapped gather buffer (at channel offset */ - u32 phys; /* physical address (at channel offset) */ - u32 incr_per_buffer; /* max # of incrs per GATHER */ - u32 words_per_incr; /* # of DWORDS in buffer to incr a syncpt */ + struct mem_mgr_handle *client_handle; /* handle for each opcode pair */ }; struct buffer_timeout { @@ -97,29 +82,28 @@ struct nvhost_cdma { unsigned int first_get; /* DMAGET value, where submit begins */ unsigned int last_put; /* last value written to DMAPUT */ struct push_buffer push_buffer; /* channel's push buffer */ - struct syncpt_buffer syncpt_buffer; /* syncpt incr buffer */ struct list_head sync_queue; /* job queue */ struct buffer_timeout timeout; /* channel's timeout state/wq */ bool running; bool torndown; + int high_prio_count; + int med_prio_count; + int low_prio_count; }; #define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma) #define cdma_to_dev(cdma) nvhost_get_host(cdma_to_channel(cdma)->dev) -#define cdma_op(cdma) (cdma_to_dev(cdma)->op.cdma) -#define cdma_to_nvmap(cdma) ((cdma_to_dev(cdma))->nvmap) +#define cdma_to_memmgr(cdma) ((cdma_to_dev(cdma))->memmgr) #define pb_to_cdma(pb) container_of(pb, struct nvhost_cdma, push_buffer) -#define cdma_pb_op(cdma) (cdma_to_dev(cdma)->op.push_buffer) int nvhost_cdma_init(struct nvhost_cdma *cdma); void nvhost_cdma_deinit(struct nvhost_cdma *cdma); void nvhost_cdma_stop(struct nvhost_cdma *cdma); int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job); void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2); -#define NVHOST_CDMA_PUSH_GATHER_CTXSAVE 0xffffffff void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, - struct nvmap_client *client, - struct nvmap_handle *handle, u32 op1, u32 op2); + struct mem_mgr *client, + struct mem_handle *handle, u32 offset, u32 op1, u32 op2); void nvhost_cdma_end(struct nvhost_cdma *cdma, struct nvhost_job *job); void nvhost_cdma_update(struct nvhost_cdma *cdma); @@ -129,5 +113,5 @@ void nvhost_cdma_peek(struct nvhost_cdma *cdma, unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, enum cdma_event event); void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, - struct nvhost_syncpt *syncpt, struct device *dev); + struct nvhost_syncpt *syncpt, struct nvhost_device *dev); #endif diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c index afbac6fe4c4e..fd309ee9917b 100644 --- a/drivers/video/tegra/host/nvhost_channel.c +++ b/drivers/video/tegra/host/nvhost_channel.c @@ -20,13 +20,14 @@ #include "nvhost_channel.h" #include "dev.h" +#include "nvhost_acm.h" #include "nvhost_job.h" +#include "chip_support.h" + #include <trace/events/nvhost.h> #include <linux/nvhost_ioctl.h> #include <linux/slab.h> -#include <linux/platform_device.h> - #define NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT 50 int nvhost_channel_init(struct nvhost_channel *ch, @@ -36,7 +37,7 @@ int nvhost_channel_init(struct nvhost_channel *ch, struct nvhost_device *ndev; /* Link nvhost_device to nvhost_channel */ - err = host_channel_op(dev).init(ch, dev, index); + err = channel_op().init(ch, dev, index); if (err < 0) { dev_err(&dev->dev->dev, "failed to init channel %d\n", index); @@ -50,23 +51,41 @@ int nvhost_channel_init(struct nvhost_channel *ch, int nvhost_channel_submit(struct nvhost_job *job) { - /* Low priority submits wait until sync queue is empty. Ignores result - * from nvhost_cdma_flush, as we submit either when push buffer is - * empty or when we reach the timeout. */ - if (job->priority < NVHOST_PRIORITY_MEDIUM) + /* + * Check if queue has higher priority jobs running. If so, wait until + * queue is empty. Ignores result from nvhost_cdma_flush, as we submit + * either when push buffer is empty or when we reach the timeout. + */ + int higher_count = 0; + + switch (job->priority) { + case NVHOST_PRIORITY_HIGH: + higher_count = 0; + break; + case NVHOST_PRIORITY_MEDIUM: + higher_count = job->ch->cdma.high_prio_count; + break; + case NVHOST_PRIORITY_LOW: + higher_count = job->ch->cdma.high_prio_count + + job->ch->cdma.med_prio_count; + break; + } + if (higher_count > 0) (void)nvhost_cdma_flush(&job->ch->cdma, NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT); - return channel_op(job->ch).submit(job); + return channel_op().submit(job); } struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) { int err = 0; + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); + mutex_lock(&ch->reflock); if (ch->refcount == 0) { - if (ch->dev->init) - ch->dev->init(ch->dev); + if (drv->init) + drv->init(ch->dev); err = nvhost_cdma_init(&ch->cdma); } else if (ch->dev->exclusive) { err = -EBUSY; @@ -85,7 +104,7 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx) { - BUG_ON(!channel_cdma_op(ch).stop); + BUG_ON(!channel_cdma_op().stop); if (ctx) { mutex_lock(&ch->submitlock); @@ -100,7 +119,7 @@ void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx) mutex_lock(&ch->reflock); if (ch->refcount == 1) { - channel_cdma_op(ch).stop(&ch->cdma); + channel_cdma_op().stop(&ch->cdma); nvhost_cdma_deinit(&ch->cdma); nvhost_module_suspend(ch->dev); } @@ -113,14 +132,57 @@ int nvhost_channel_suspend(struct nvhost_channel *ch) int ret = 0; mutex_lock(&ch->reflock); - BUG_ON(!channel_cdma_op(ch).stop); + BUG_ON(!channel_cdma_op().stop); if (ch->refcount) { ret = nvhost_module_suspend(ch->dev); if (!ret) - channel_cdma_op(ch).stop(&ch->cdma); + channel_cdma_op().stop(&ch->cdma); } mutex_unlock(&ch->reflock); return ret; } + +struct nvhost_channel *nvhost_alloc_channel_internal(int chindex, + int max_channels, int *current_channel_count) +{ + struct nvhost_channel *ch = NULL; + + if ( (chindex > max_channels) || + ( (*current_channel_count + 1) > max_channels) ) + return NULL; + else { + ch = kzalloc(sizeof(*ch), GFP_KERNEL); + if (ch == NULL) + return NULL; + else { + (*current_channel_count)++; + return ch; + } + } +} + +void nvhost_free_channel_internal(struct nvhost_channel *ch, + int *current_channel_count) +{ + kfree(ch); + (*current_channel_count)--; +} + +int nvhost_channel_save_context(struct nvhost_channel *ch) +{ + struct nvhost_hwctx *cur_ctx = ch->cur_ctx; + int err = 0; + if (cur_ctx) + err = channel_op().save_context(ch); + + return err; + +} + +int nvhost_channel_drain_read_fifo(struct nvhost_channel *ch, + u32 *ptr, unsigned int count, unsigned int *pending) +{ + return channel_op().drain_read_fifo(ch, ptr, count, pending); +} diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h index 7b946c8ee853..d7f096db1ffa 100644 --- a/drivers/video/tegra/host/nvhost_channel.h +++ b/drivers/video/tegra/host/nvhost_channel.h @@ -21,29 +21,19 @@ #ifndef __NVHOST_CHANNEL_H #define __NVHOST_CHANNEL_H -#include "nvhost_cdma.h" -#include "nvhost_acm.h" -#include "nvhost_hwctx.h" -#include "nvhost_job.h" - #include <linux/cdev.h> #include <linux/io.h> +#include "nvhost_cdma.h" -#define NVHOST_MAX_WAIT_CHECKS 256 -#define NVHOST_MAX_GATHERS 512 -#define NVHOST_MAX_HANDLES 1280 -#define NVHOST_MAX_POWERGATE_IDS 2 +#define NVHOST_MAX_WAIT_CHECKS 256 +#define NVHOST_MAX_GATHERS 512 +#define NVHOST_MAX_HANDLES 1280 +#define NVHOST_MAX_POWERGATE_IDS 2 struct nvhost_master; -struct nvhost_waitchk; struct nvhost_device; - -struct nvhost_channel_gather { - u32 words; - phys_addr_t mem; - u32 mem_id; - int offset; -}; +struct nvhost_channel; +struct nvhost_hwctx; struct nvhost_channel { int refcount; @@ -60,8 +50,7 @@ struct nvhost_channel { struct nvhost_cdma cdma; }; -int nvhost_channel_init( - struct nvhost_channel *ch, +int nvhost_channel_init(struct nvhost_channel *ch, struct nvhost_master *dev, int index); int nvhost_channel_submit(struct nvhost_job *job); @@ -70,17 +59,19 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch); void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx); int nvhost_channel_suspend(struct nvhost_channel *ch); -#define channel_cdma_op(ch) (nvhost_get_host(ch->dev)->op.cdma) -#define channel_op(ch) (nvhost_get_host(ch->dev)->op.channel) -#define host_channel_op(host) (host->op.channel) - -int nvhost_channel_drain_read_fifo(void __iomem *chan_regs, +int nvhost_channel_drain_read_fifo(struct nvhost_channel *ch, u32 *ptr, unsigned int count, unsigned int *pending); -int nvhost_channel_read_3d_reg( - struct nvhost_channel *channel, +int nvhost_channel_read_3d_reg(struct nvhost_channel *channel, struct nvhost_hwctx *hwctx, - u32 offset, - u32 *value); + u32 offset, u32 *value); + +struct nvhost_channel *nvhost_alloc_channel_internal(int chindex, + int max_channels, int *current_channel_count); + +void nvhost_free_channel_internal(struct nvhost_channel *ch, + int *current_channel_count); + +int nvhost_channel_save_context(struct nvhost_channel *ch); #endif diff --git a/drivers/video/tegra/host/nvhost_hwctx.h b/drivers/video/tegra/host/nvhost_hwctx.h index 02a3976f01ce..47bc3d408fde 100644 --- a/drivers/video/tegra/host/nvhost_hwctx.h +++ b/drivers/video/tegra/host/nvhost_hwctx.h @@ -25,7 +25,6 @@ #include <linux/kref.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> struct nvhost_channel; struct nvhost_cdma; diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c index 7c4bdc7bafb6..38a04f151e87 100644 --- a/drivers/video/tegra/host/nvhost_intr.c +++ b/drivers/video/tegra/host/nvhost_intr.c @@ -20,14 +20,14 @@ #include "nvhost_intr.h" #include "dev.h" +#include "nvhost_acm.h" #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/irq.h> #include <trace/events/nvhost.h> - - - - +#include "nvhost_channel.h" +#include "nvhost_hwctx.h" +#include "chip_support.h" /*** Wait list management ***/ @@ -116,11 +116,11 @@ void reset_threshold_interrupt(struct nvhost_intr *intr, { u32 thresh = list_first_entry(head, struct nvhost_waitlist, list)->thresh; - BUG_ON(!(intr_op(intr).set_syncpt_threshold && - intr_op(intr).enable_syncpt_intr)); + BUG_ON(!(intr_op().set_syncpt_threshold && + intr_op().enable_syncpt_intr)); - intr_op(intr).set_syncpt_threshold(intr, id, thresh); - intr_op(intr).enable_syncpt_intr(intr, id); + intr_op().set_syncpt_threshold(intr, id, thresh); + intr_op().enable_syncpt_intr(intr, id); } @@ -129,12 +129,16 @@ static void action_submit_complete(struct nvhost_waitlist *waiter) struct nvhost_channel *channel = waiter->data; int nr_completed = waiter->count; + nvhost_cdma_update(&channel->cdma); + nvhost_module_idle_mult(channel->dev, nr_completed); + /* Add nr_completed to trace */ trace_nvhost_channel_submit_complete(channel->dev->name, - nr_completed, waiter->thresh); + nr_completed, waiter->thresh, + channel->cdma.high_prio_count, + channel->cdma.med_prio_count, + channel->cdma.low_prio_count); - nvhost_cdma_update(&channel->cdma); - nvhost_module_idle_mult(channel->dev, nr_completed); } static void action_ctxsave(struct nvhost_waitlist *waiter) @@ -264,8 +268,8 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, BUG_ON(waiter == NULL); - BUG_ON(!(intr_op(intr).set_syncpt_threshold && - intr_op(intr).enable_syncpt_intr)); + BUG_ON(!(intr_op().set_syncpt_threshold && + intr_op().enable_syncpt_intr)); /* initialize a new waiter */ INIT_LIST_HEAD(&waiter->list); @@ -278,7 +282,6 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, waiter->data = data; waiter->count = 1; - BUG_ON(id >= intr_to_dev(intr)->syncpt.nb_pts); syncpt = intr->syncpt + id; spin_lock(&syncpt->lock); @@ -288,8 +291,8 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, spin_unlock(&syncpt->lock); mutex_lock(&intr->mutex); - BUG_ON(!(intr_op(intr).request_syncpt_irq)); - err = intr_op(intr).request_syncpt_irq(syncpt); + BUG_ON(!(intr_op().request_syncpt_irq)); + err = intr_op().request_syncpt_irq(syncpt); mutex_unlock(&intr->mutex); if (err) { @@ -304,11 +307,11 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, if (add_waiter_to_queue(waiter, &syncpt->wait_head)) { /* added at head of list - new threshold value */ - intr_op(intr).set_syncpt_threshold(intr, id, thresh); + intr_op().set_syncpt_threshold(intr, id, thresh); /* added as first waiter - enable interrupt */ if (queue_was_empty) - intr_op(intr).enable_syncpt_intr(intr, id); + intr_op().enable_syncpt_intr(intr, id); } spin_unlock(&syncpt->lock); @@ -342,13 +345,15 @@ int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync) { unsigned int id; struct nvhost_intr_syncpt *syncpt; - struct nvhost_master *host = - container_of(intr, struct nvhost_master, intr); - u32 nb_pts = host->syncpt.nb_pts; + struct nvhost_master *host = intr_to_dev(intr); + u32 nb_pts = nvhost_syncpt_nb_pts(&host->syncpt); mutex_init(&intr->mutex); + intr->host_syncpt_irq_base = irq_sync; + intr_op().init_host_sync(intr); intr->host_general_irq = irq_gen; intr->host_general_irq_requested = false; + intr_op().request_host_general_irq(intr); for (id = 0, syncpt = intr->syncpt; id < nb_pts; @@ -374,17 +379,17 @@ void nvhost_intr_deinit(struct nvhost_intr *intr) void nvhost_intr_start(struct nvhost_intr *intr, u32 hz) { - BUG_ON(!(intr_op(intr).init_host_sync && - intr_op(intr).set_host_clocks_per_usec && - intr_op(intr).request_host_general_irq)); + BUG_ON(!(intr_op().init_host_sync && + intr_op().set_host_clocks_per_usec && + intr_op().request_host_general_irq)); mutex_lock(&intr->mutex); - intr_op(intr).init_host_sync(intr); - intr_op(intr).set_host_clocks_per_usec(intr, + intr_op().init_host_sync(intr); + intr_op().set_host_clocks_per_usec(intr, (hz + 1000000 - 1)/1000000); - intr_op(intr).request_host_general_irq(intr); + intr_op().request_host_general_irq(intr); mutex_unlock(&intr->mutex); } @@ -393,14 +398,14 @@ void nvhost_intr_stop(struct nvhost_intr *intr) { unsigned int id; struct nvhost_intr_syncpt *syncpt; - u32 nb_pts = intr_to_dev(intr)->syncpt.nb_pts; + u32 nb_pts = nvhost_syncpt_nb_pts(&intr_to_dev(intr)->syncpt); - BUG_ON(!(intr_op(intr).disable_all_syncpt_intrs && - intr_op(intr).free_host_general_irq)); + BUG_ON(!(intr_op().disable_all_syncpt_intrs && + intr_op().free_host_general_irq)); mutex_lock(&intr->mutex); - intr_op(intr).disable_all_syncpt_intrs(intr); + intr_op().disable_all_syncpt_intrs(intr); for (id = 0, syncpt = intr->syncpt; id < nb_pts; @@ -422,7 +427,7 @@ void nvhost_intr_stop(struct nvhost_intr *intr) free_syncpt_irq(syncpt); } - intr_op(intr).free_host_general_irq(intr); + intr_op().free_host_general_irq(intr); mutex_unlock(&intr->mutex); } diff --git a/drivers/video/tegra/host/nvhost_intr.h b/drivers/video/tegra/host/nvhost_intr.h index 26ab04ebd4ab..cf0b6b9e8934 100644 --- a/drivers/video/tegra/host/nvhost_intr.h +++ b/drivers/video/tegra/host/nvhost_intr.h @@ -71,10 +71,10 @@ struct nvhost_intr { struct nvhost_intr_syncpt *syncpt; struct mutex mutex; int host_general_irq; + int host_syncpt_irq_base; bool host_general_irq_requested; }; #define intr_to_dev(x) container_of(x, struct nvhost_master, intr) -#define intr_op(intr) (intr_to_dev(intr)->op.intr) #define intr_syncpt_to_intr(is) (is->intr) /** diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index a4f0cfc44212..f93d7df1a552 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -22,138 +22,58 @@ #include <linux/kref.h> #include <linux/err.h> #include <linux/vmalloc.h> -#include <mach/nvmap.h> +#include <trace/events/nvhost.h> #include "nvhost_channel.h" #include "nvhost_job.h" +#include "nvhost_hwctx.h" +#include "nvhost_syncpt.h" #include "dev.h" +#include "nvhost_memmgr.h" +#include "chip_support.h" /* Magic to use to fill freed handle slots */ #define BAD_MAGIC 0xdeadbeef static int job_size(struct nvhost_submit_hdr_ext *hdr) { - int num_pins = hdr ? (hdr->num_relocs + hdr->num_cmdbufs)*2 : 0; + int num_relocs = hdr ? hdr->num_relocs : 0; int num_waitchks = hdr ? hdr->num_waitchks : 0; + int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; + int num_unpins = num_cmdbufs + num_relocs; return sizeof(struct nvhost_job) - + num_pins * sizeof(struct nvmap_pinarray_elem) - + num_pins * sizeof(struct nvmap_handle *) - + num_waitchks * sizeof(struct nvhost_waitchk); -} - -static int gather_size(int num_cmdbufs) -{ - return num_cmdbufs * sizeof(struct nvhost_channel_gather); -} - -static void free_gathers(struct nvhost_job *job) -{ - if (job->gathers) { - nvmap_munmap(job->gather_mem, job->gathers); - job->gathers = NULL; - } - if (job->gather_mem) { - nvmap_free(job->nvmap, job->gather_mem); - job->gather_mem = NULL; - } -} - -static int alloc_gathers(struct nvhost_job *job, - int num_cmdbufs) -{ - int err = 0; - - job->gather_mem = NULL; - job->gathers = NULL; - job->gather_mem_size = 0; - - if (num_cmdbufs) { - /* Allocate memory */ - job->gather_mem = nvmap_alloc(job->nvmap, - gather_size(num_cmdbufs), - 32, NVMAP_HANDLE_CACHEABLE, 0); - if (IS_ERR_OR_NULL(job->gather_mem)) { - err = job->gather_mem ? PTR_ERR(job->gather_mem) : -ENOMEM; - job->gather_mem = NULL; - goto error; - } - job->gather_mem_size = gather_size(num_cmdbufs); - - /* Map memory to kernel */ - job->gathers = nvmap_mmap(job->gather_mem); - if (IS_ERR_OR_NULL(job->gathers)) { - err = job->gathers ? PTR_ERR(job->gathers) : -ENOMEM; - job->gathers = NULL; - goto error; - } - } - - return 0; - -error: - free_gathers(job); - return err; -} - -static int realloc_gathers(struct nvhost_job *oldjob, - struct nvhost_job *newjob, - int num_cmdbufs) -{ - int err = 0; - - /* Check if we can reuse gather buffer */ - if (oldjob->gather_mem_size < gather_size(num_cmdbufs) - || oldjob->nvmap != newjob->nvmap) { - free_gathers(oldjob); - err = alloc_gathers(newjob, num_cmdbufs); - } else { - newjob->gather_mem = oldjob->gather_mem; - newjob->gathers = oldjob->gathers; - newjob->gather_mem_size = oldjob->gather_mem_size; - - oldjob->gather_mem = NULL; - oldjob->gathers = NULL; - oldjob->gather_mem_size = 0; - } - return err; + + num_relocs * sizeof(struct nvhost_reloc) + + num_relocs * sizeof(struct nvhost_reloc_shift) + + num_unpins * sizeof(struct mem_handle *) + + num_waitchks * sizeof(struct nvhost_waitchk) + + num_cmdbufs * sizeof(struct nvhost_job_gather); } static void init_fields(struct nvhost_job *job, struct nvhost_submit_hdr_ext *hdr, int priority, int clientid) { - int num_pins = hdr ? (hdr->num_relocs + hdr->num_cmdbufs)*2 : 0; + int num_relocs = hdr ? hdr->num_relocs : 0; int num_waitchks = hdr ? hdr->num_waitchks : 0; + int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; + int num_unpins = num_cmdbufs + num_relocs; void *mem = job; /* First init state to zero */ - job->num_gathers = 0; - job->num_pins = 0; - job->num_unpins = 0; - job->num_waitchk = 0; - job->waitchk_mask = 0; - job->syncpt_id = 0; - job->syncpt_incrs = 0; - job->syncpt_end = 0; job->priority = priority; job->clientid = clientid; - job->null_kickoff = false; - job->first_get = 0; - job->num_slots = 0; /* Redistribute memory to the structs */ mem += sizeof(struct nvhost_job); - if (num_pins) { - job->pinarray = mem; - mem += num_pins * sizeof(struct nvmap_pinarray_elem); - job->unpins = mem; - mem += num_pins * sizeof(struct nvmap_handle *); - } else { - job->pinarray = NULL; - job->unpins = NULL; - } - + job->relocarray = num_relocs ? mem : NULL; + mem += num_relocs * sizeof(struct nvhost_reloc); + job->relocshiftarray = num_relocs ? mem : NULL; + mem += num_relocs * sizeof(struct nvhost_reloc_shift); + job->unpins = num_unpins ? mem : NULL; + mem += num_unpins * sizeof(struct mem_handle *); job->waitchk = num_waitchks ? mem : NULL; + mem += num_waitchks * sizeof(struct nvhost_waitchk); + job->gathers = num_cmdbufs ? mem : NULL; /* Copy information from header */ if (hdr) { @@ -166,13 +86,11 @@ static void init_fields(struct nvhost_job *job, struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, struct nvhost_hwctx *hwctx, struct nvhost_submit_hdr_ext *hdr, - struct nvmap_client *nvmap, + struct mem_mgr *memmgr, int priority, int clientid) { struct nvhost_job *job = NULL; - int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; - int err = 0; job = vzalloc(job_size(hdr)); if (!job) @@ -183,11 +101,7 @@ struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, job->hwctx = hwctx; if (hwctx) hwctx->h->get(hwctx); - job->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL; - - err = alloc_gathers(job, num_cmdbufs); - if (err) - goto error; + job->memmgr = memmgr ? mem_op().get_mgr(memmgr) : NULL; init_fields(job, hdr, priority, clientid); @@ -199,46 +113,6 @@ error: return NULL; } -struct nvhost_job *nvhost_job_realloc( - struct nvhost_job *oldjob, - struct nvhost_hwctx *hwctx, - struct nvhost_submit_hdr_ext *hdr, - struct nvmap_client *nvmap, - int priority, int clientid) -{ - struct nvhost_job *newjob = NULL; - int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0; - int err = 0; - - newjob = vzalloc(job_size(hdr)); - if (!newjob) - goto error; - kref_init(&newjob->ref); - newjob->ch = oldjob->ch; - newjob->hwctx = hwctx; - if (hwctx) - newjob->hwctx->h->get(newjob->hwctx); - newjob->timeout = oldjob->timeout; - newjob->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL; - - err = realloc_gathers(oldjob, newjob, num_cmdbufs); - if (err) - goto error; - - nvhost_job_put(oldjob); - - init_fields(newjob, hdr, priority, clientid); - - return newjob; - -error: - if (newjob) - nvhost_job_put(newjob); - if (oldjob) - nvhost_job_put(oldjob); - return NULL; -} - void nvhost_job_get(struct nvhost_job *job) { kref_get(&job->ref); @@ -252,12 +126,8 @@ static void job_free(struct kref *ref) job->hwctxref->h->put(job->hwctxref); if (job->hwctx) job->hwctx->h->put(job->hwctx); - if (job->gathers) - nvmap_munmap(job->gather_mem, job->gathers); - if (job->gather_mem) - nvmap_free(job->nvmap, job->gather_mem); - if (job->nvmap) - nvmap_client_put(job->nvmap); + if (job->memmgr) + mem_op().put_mgr(job->memmgr); vfree(job); } @@ -279,42 +149,176 @@ void nvhost_job_put(struct nvhost_job *job) void nvhost_job_add_gather(struct nvhost_job *job, u32 mem_id, u32 words, u32 offset) { - struct nvmap_pinarray_elem *pin; - struct nvhost_channel_gather *cur_gather = + struct nvhost_job_gather *cur_gather = &job->gathers[job->num_gathers]; - pin = &job->pinarray[job->num_pins++]; - pin->patch_mem = (u32)nvmap_ref_to_handle(job->gather_mem); - pin->patch_offset = (void *)&(cur_gather->mem) - (void *)job->gathers; - pin->pin_mem = nvmap_convert_handle_u2k(mem_id); - pin->pin_offset = offset; cur_gather->words = words; cur_gather->mem_id = mem_id; cur_gather->offset = offset; job->num_gathers += 1; } -int nvhost_job_pin(struct nvhost_job *job) +static int do_relocs(struct nvhost_job *job, u32 cmdbuf_mem, void *cmdbuf_addr) +{ + phys_addr_t target_phys = -EINVAL; + int i; + u32 mem_id = 0; + struct mem_handle *target_ref = NULL; + + /* pin & patch the relocs for one gather */ + for (i = 0; i < job->num_relocs; i++) { + struct nvhost_reloc *reloc = &job->relocarray[i]; + struct nvhost_reloc_shift *shift = &job->relocshiftarray[i]; + + /* skip all other gathers */ + if (cmdbuf_mem != reloc->cmdbuf_mem) + continue; + + /* check if pin-mem is same as previous */ + if (reloc->target != mem_id) { + target_ref = mem_op().get(job->memmgr, reloc->target); + if (IS_ERR(target_ref)) + return PTR_ERR(target_ref); + + target_phys = mem_op().pin(job->memmgr, target_ref); + if (IS_ERR((void *)target_phys)) { + mem_op().put(job->memmgr, target_ref); + return target_phys; + } + + mem_id = reloc->target; + job->unpins[job->num_unpins++] = target_ref; + } + + __raw_writel( + (target_phys + reloc->target_offset) >> shift->shift, + (cmdbuf_addr + reloc->cmdbuf_offset)); + + /* Different gathers might have same mem_id. This ensures we + * perform reloc only once per gather memid. */ + reloc->cmdbuf_mem = 0; + } + + return 0; +} + +/* + * Check driver supplied waitchk structs for syncpt thresholds + * that have already been satisfied and NULL the comparison (to + * avoid a wrap condition in the HW). + */ +static int do_waitchks(struct nvhost_job *job, struct nvhost_syncpt *sp, + u32 patch_mem, void *patch_addr) { - int err = 0; + int i; + + /* compare syncpt vs wait threshold */ + for (i = 0; i < job->num_waitchk; i++) { + struct nvhost_waitchk *wait = &job->waitchk[i]; + + /* skip all other gathers */ + if (patch_mem != wait->mem) + continue; + + trace_nvhost_syncpt_wait_check(wait->mem, wait->offset, + wait->syncpt_id, wait->thresh, + nvhost_syncpt_read(sp, wait->syncpt_id)); + if (nvhost_syncpt_is_expired(sp, + wait->syncpt_id, wait->thresh)) { + /* + * NULL an already satisfied WAIT_SYNCPT host method, + * by patching its args in the command stream. The + * method data is changed to reference a reserved + * (never given out or incr) NVSYNCPT_GRAPHICS_HOST + * syncpt with a matching threshold value of 0, so + * is guaranteed to be popped by the host HW. + */ + dev_dbg(&syncpt_to_dev(sp)->dev->dev, + "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", + wait->syncpt_id, + syncpt_op().name(sp, wait->syncpt_id), + wait->thresh, + nvhost_syncpt_read_min(sp, wait->syncpt_id)); + + /* patch the wait */ + nvhost_syncpt_patch_wait(sp, + (patch_addr + wait->offset)); + } + + wait->mem = 0; + } + return 0; +} - /* pin mem handles and patch physical addresses */ - job->num_unpins = nvmap_pin_array(job->nvmap, - nvmap_ref_to_handle(job->gather_mem), - job->pinarray, job->num_pins, - job->unpins); - if (job->num_unpins < 0) - err = job->num_unpins; +int nvhost_job_pin(struct nvhost_job *job, struct nvhost_syncpt *sp) +{ + int err = 0, i = 0; + phys_addr_t gather_phys = 0; + void *gather_addr = NULL; + unsigned long waitchk_mask = job->waitchk_mask; + + /* get current syncpt values for waitchk */ + for_each_set_bit(i, &waitchk_mask, sizeof(job->waitchk_mask)) + nvhost_syncpt_update_min(sp, i); + + /* pin gathers */ + for (i = 0; i < job->num_gathers; i++) { + struct nvhost_job_gather *g = &job->gathers[i]; + + /* process each gather mem only once */ + if (!g->ref) { + g->ref = mem_op().get(job->memmgr, + job->gathers[i].mem_id); + if (IS_ERR(g->ref)) { + err = PTR_ERR(g->ref); + g->ref = NULL; + break; + } + + gather_phys = mem_op().pin(job->memmgr, g->ref); + if (IS_ERR((void *)gather_phys)) { + mem_op().put(job->memmgr, g->ref); + err = gather_phys; + break; + } + + /* store the gather ref into unpin array */ + job->unpins[job->num_unpins++] = g->ref; + + gather_addr = mem_op().mmap(g->ref); + if (!gather_addr) { + err = -ENOMEM; + break; + } + + err = do_relocs(job, g->mem_id, gather_addr); + if (!err) + err = do_waitchks(job, sp, + g->mem_id, gather_addr); + mem_op().munmap(g->ref, gather_addr); + + if (err) + break; + } + g->mem = gather_phys + g->offset; + } + wmb(); return err; } void nvhost_job_unpin(struct nvhost_job *job) { - nvmap_unpin_handles(job->nvmap, job->unpins, - job->num_unpins); + int i; + + for (i = 0; i < job->num_unpins; i++) { + mem_op().unpin(job->memmgr, job->unpins[i]); + mem_op().put(job->memmgr, job->unpins[i]); + } + memset(job->unpins, BAD_MAGIC, - job->num_unpins * sizeof(struct nvmap_handle *)); + job->num_unpins * sizeof(struct mem_handle *)); + job->num_unpins = 0; } /** diff --git a/drivers/video/tegra/host/nvhost_job.h b/drivers/video/tegra/host/nvhost_job.h index ad9d1af60da1..3b444579c543 100644 --- a/drivers/video/tegra/host/nvhost_job.h +++ b/drivers/video/tegra/host/nvhost_job.h @@ -25,9 +25,16 @@ struct nvhost_channel; struct nvhost_hwctx; -struct nvmap_client; struct nvhost_waitchk; -struct nvmap_handle; +struct nvhost_syncpt; + +struct nvhost_job_gather { + u32 words; + phys_addr_t mem; + u32 mem_id; + int offset; + struct mem_handle *ref; +}; /* * Each submit is tracked as a nvhost_job. @@ -47,13 +54,11 @@ struct nvhost_job { int clientid; /* Nvmap to be used for pinning & unpinning memory */ - struct nvmap_client *nvmap; + struct mem_mgr *memmgr; /* Gathers and their memory */ - struct nvmap_handle_ref *gather_mem; - struct nvhost_channel_gather *gathers; + struct nvhost_job_gather *gathers; int num_gathers; - int gather_mem_size; /* Wait checks to be processed at submit time */ struct nvhost_waitchk *waitchk; @@ -61,9 +66,10 @@ struct nvhost_job { u32 waitchk_mask; /* Array of handles to be pinned & unpinned */ - struct nvmap_pinarray_elem *pinarray; - int num_pins; - struct nvmap_handle **unpins; + struct nvhost_reloc *relocarray; + struct nvhost_reloc_shift *relocshiftarray; + int num_relocs; + struct mem_handle **unpins; int num_unpins; /* Sync point id, number of increments and end related to the submit */ @@ -95,18 +101,7 @@ struct nvhost_job { struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, struct nvhost_hwctx *hwctx, struct nvhost_submit_hdr_ext *hdr, - struct nvmap_client *nvmap, - int priority, int clientid); - -/* - * Allocate memory for a job. Just enough memory will be allocated to - * accomodate the submit announced in submit header. Gather memory from - * oldjob will be reused, and nvhost_job_put() will be called to it. - */ -struct nvhost_job *nvhost_job_realloc(struct nvhost_job *oldjob, - struct nvhost_hwctx *hwctx, - struct nvhost_submit_hdr_ext *hdr, - struct nvmap_client *nvmap, + struct mem_mgr *memmgr, int priority, int clientid); /* @@ -134,8 +129,11 @@ void nvhost_job_put(struct nvhost_job *job); * Pin memory related to job. This handles relocation of addresses to the * host1x address space. Handles both the gather memory and any other memory * referred to from the gather buffers. + * + * Handles also patching out host waits that would wait for an expired sync + * point value. */ -int nvhost_job_pin(struct nvhost_job *job); +int nvhost_job_pin(struct nvhost_job *job, struct nvhost_syncpt *sp); /* * Unpin memory related to job. diff --git a/drivers/video/tegra/host/nvhost_memmgr.c b/drivers/video/tegra/host/nvhost_memmgr.c new file mode 100644 index 000000000000..f530c2e63006 --- /dev/null +++ b/drivers/video/tegra/host/nvhost_memmgr.c @@ -0,0 +1,34 @@ +/* + * drivers/video/tegra/host/nvhost_memmgr.c + * + * Tegra Graphics Host Memory Management Abstraction + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/err.h> + +#include "nvhost_memmgr.h" +#include "nvmap.h" + +int nvhost_memmgr_init(struct nvhost_chip_support *chip) +{ +#ifdef CONFIG_TEGRA_GRHOST_USE_NVMAP + return nvhost_init_nvmap_support(chip); +#endif + BUG_ON(!"No memory manager selected"); + return -ENODEV; +} diff --git a/drivers/video/tegra/host/nvhost_memmgr.h b/drivers/video/tegra/host/nvhost_memmgr.h new file mode 100644 index 000000000000..d61379b6ff55 --- /dev/null +++ b/drivers/video/tegra/host/nvhost_memmgr.h @@ -0,0 +1,38 @@ +/* + * drivers/video/tegra/host/nvhost_memmgr.h + * + * Tegra Graphics Host Memory Management Abstraction header + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _NVHOST_MEM_MGR_H_ +#define _NVHOST_MEM_MGR_H_ + +struct nvhost_chip_support; + +enum mem_mgr_flag { + mem_mgr_flag_uncacheable = 0, + mem_mgr_flag_write_combine = 1, +}; + +struct mem_mgr_handle { + struct mem_mgr *client; + struct mem_handle *handle; +}; + +int nvhost_memmgr_init(struct nvhost_chip_support *chip); + +#endif diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index 13ad0fc3a3cf..9fa7d0652c1f 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -21,10 +21,12 @@ #include <linux/nvhost_ioctl.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <trace/events/nvhost.h> #include "nvhost_syncpt.h" +#include "nvhost_acm.h" #include "dev.h" +#include "chip_support.h" -#define MAX_STUCK_CHECK_COUNT 15 #define MAX_SYNCPT_LENGTH 5 /* Name of sysfs node for min and max value */ static const char *min_name = "min"; @@ -36,12 +38,12 @@ static const char *max_name = "max"; void nvhost_syncpt_reset(struct nvhost_syncpt *sp) { u32 i; - BUG_ON(!(syncpt_op(sp).reset && syncpt_op(sp).reset_wait_base)); + BUG_ON(!(syncpt_op().reset && syncpt_op().reset_wait_base)); - for (i = 0; i < sp->nb_pts; i++) - syncpt_op(sp).reset(sp, i); - for (i = 0; i < sp->nb_bases; i++) - syncpt_op(sp).reset_wait_base(sp, i); + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) + syncpt_op().reset(sp, i); + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) + syncpt_op().reset_wait_base(sp, i); wmb(); } @@ -51,17 +53,17 @@ void nvhost_syncpt_reset(struct nvhost_syncpt *sp) void nvhost_syncpt_save(struct nvhost_syncpt *sp) { u32 i; - BUG_ON(!(syncpt_op(sp).update_min && syncpt_op(sp).read_wait_base)); + BUG_ON(!(syncpt_op().update_min && syncpt_op().read_wait_base)); - for (i = 0; i < sp->nb_pts; i++) { - if (client_managed(i)) - syncpt_op(sp).update_min(sp, i); + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { + if (nvhost_syncpt_client_managed(sp, i)) + syncpt_op().update_min(sp, i); else BUG_ON(!nvhost_syncpt_min_eq_max(sp, i)); } - for (i = 0; i < sp->nb_bases; i++) - syncpt_op(sp).read_wait_base(sp, i); + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) + syncpt_op().read_wait_base(sp, i); } /** @@ -69,9 +71,14 @@ void nvhost_syncpt_save(struct nvhost_syncpt *sp) */ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) { - BUG_ON(!syncpt_op(sp).update_min); + u32 val; + + BUG_ON(!syncpt_op().update_min); + + val = syncpt_op().update_min(sp, id); + trace_nvhost_syncpt_update_min(id, val); - return syncpt_op(sp).update_min(sp, id); + return val; } /** @@ -80,9 +87,9 @@ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) { u32 val; - BUG_ON(!syncpt_op(sp).update_min); + BUG_ON(!syncpt_op().update_min); nvhost_module_busy(syncpt_to_dev(sp)->dev); - val = syncpt_op(sp).update_min(sp, id); + val = syncpt_op().update_min(sp, id); nvhost_module_idle(syncpt_to_dev(sp)->dev); return val; } @@ -93,9 +100,9 @@ u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) u32 nvhost_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) { u32 val; - BUG_ON(!syncpt_op(sp).read_wait_base); + BUG_ON(!syncpt_op().read_wait_base); nvhost_module_busy(syncpt_to_dev(sp)->dev); - syncpt_op(sp).read_wait_base(sp, id); + syncpt_op().read_wait_base(sp, id); val = sp->base_val[id]; nvhost_module_idle(syncpt_to_dev(sp)->dev); return val; @@ -107,8 +114,8 @@ u32 nvhost_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) */ void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) { - BUG_ON(!syncpt_op(sp).cpu_incr); - syncpt_op(sp).cpu_incr(sp, id); + BUG_ON(!syncpt_op().cpu_incr); + syncpt_op().cpu_incr(sp, id); } /** @@ -116,7 +123,7 @@ void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) */ void nvhost_syncpt_incr(struct nvhost_syncpt *sp, u32 id) { - if (client_managed(id)) + if (nvhost_syncpt_client_managed(sp, id)) nvhost_syncpt_incr_max(sp, id, 1); nvhost_module_busy(syncpt_to_dev(sp)->dev); nvhost_syncpt_cpu_incr(sp, id); @@ -124,6 +131,19 @@ void nvhost_syncpt_incr(struct nvhost_syncpt *sp, u32 id) } /** + * Updated sync point form hardware, and returns true if syncpoint is expired, + * false if we may need to wait + */ +static bool syncpt_update_min_is_expired( + struct nvhost_syncpt *sp, + u32 id, + u32 thresh) +{ + syncpt_op().update_min(sp, id); + return nvhost_syncpt_is_expired(sp, id, thresh); +} + +/** * Main entrypoint for syncpoint value waits. */ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, @@ -149,7 +169,7 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, nvhost_module_busy(syncpt_to_dev(sp)->dev); /* try to read from register */ - val = syncpt_op(sp).update_min(sp, id); + val = syncpt_op().update_min(sp, id); if (nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = val; @@ -184,9 +204,9 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, while (timeout) { u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout); int remain = wait_event_interruptible_timeout(wq, - nvhost_syncpt_is_expired(sp, id, thresh), + syncpt_update_min_is_expired(sp, id, thresh), check); - if (remain > 0) { + if (remain > 0 || nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = nvhost_syncpt_read_min(sp, id); err = 0; @@ -198,20 +218,19 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, } if (timeout != NVHOST_NO_TIMEOUT) timeout -= check; - if (timeout) { + if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) { dev_warn(&syncpt_to_dev(sp)->dev->dev, "%s: syncpoint id %d (%s) stuck waiting %d, timeout=%d\n", - current->comm, id, syncpt_op(sp).name(sp, id), + current->comm, id, syncpt_op().name(sp, id), thresh, timeout); - syncpt_op(sp).debug(sp); - if (check_count > MAX_STUCK_CHECK_COUNT) { + syncpt_op().debug(sp); + if (check_count == MAX_STUCK_CHECK_COUNT) { if (low_timeout) { dev_warn(&syncpt_to_dev(sp)->dev->dev, "is timeout %d too low?\n", low_timeout); } nvhost_debug_dump(syncpt_to_dev(sp)); - BUG(); } check_count++; } @@ -279,7 +298,7 @@ bool nvhost_syncpt_is_expired( * If future valueis zero, we have a client managed sync point. In that * case we do a direct comparison. */ - if (!client_managed(id)) + if (!nvhost_syncpt_client_managed(sp, id)) return future_val - thresh >= current_val - thresh; else return (s32)(current_val - thresh) >= 0; @@ -287,7 +306,7 @@ bool nvhost_syncpt_is_expired( void nvhost_syncpt_debug(struct nvhost_syncpt *sp) { - syncpt_op(sp).debug(sp); + syncpt_op().debug(sp); } int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx) @@ -296,7 +315,7 @@ int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx) u32 reg; nvhost_module_busy(host->dev); - reg = syncpt_op(sp).mutex_try_lock(sp, idx); + reg = syncpt_op().mutex_try_lock(sp, idx); if (reg) { nvhost_module_idle(host->dev); return -EBUSY; @@ -307,20 +326,15 @@ int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx) void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx) { - syncpt_op(sp).mutex_unlock(sp, idx); + syncpt_op().mutex_unlock(sp, idx); nvhost_module_idle(syncpt_to_dev(sp)->dev); atomic_dec(&sp->lock_counts[idx]); } -/* check for old WAITs to be removed (avoiding a wrap) */ -int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 waitchk_mask, - struct nvhost_waitchk *wait, - int num_waitchk) +/* remove a wait pointed to by patch_addr */ +int nvhost_syncpt_patch_wait(struct nvhost_syncpt *sp, void *patch_addr) { - return syncpt_op(sp).wait_check(sp, nvmap, - waitchk_mask, wait, num_waitchk); + return syncpt_op().patch_wait(sp, patch_addr); } /* Displays the current value of the sync point via sysfs */ @@ -354,10 +368,15 @@ int nvhost_syncpt_init(struct nvhost_device *dev, int err = 0; /* Allocate structs for min, max and base values */ - sp->min_val = kzalloc(sizeof(atomic_t) * sp->nb_pts, GFP_KERNEL); - sp->max_val = kzalloc(sizeof(atomic_t) * sp->nb_pts, GFP_KERNEL); - sp->base_val = kzalloc(sizeof(u32) * sp->nb_bases, GFP_KERNEL); - sp->lock_counts = kzalloc(sizeof(atomic_t) * sp->nb_mlocks, GFP_KERNEL); + sp->min_val = kzalloc(sizeof(atomic_t) * nvhost_syncpt_nb_pts(sp), + GFP_KERNEL); + sp->max_val = kzalloc(sizeof(atomic_t) * nvhost_syncpt_nb_pts(sp), + GFP_KERNEL); + sp->base_val = kzalloc(sizeof(u32) * nvhost_syncpt_nb_bases(sp), + GFP_KERNEL); + sp->lock_counts = + kzalloc(sizeof(atomic_t) * nvhost_syncpt_nb_mlocks(sp), + GFP_KERNEL); if (!(sp->min_val && sp->max_val && sp->base_val && sp->lock_counts)) { /* frees happen in the deinit */ @@ -372,15 +391,15 @@ int nvhost_syncpt_init(struct nvhost_device *dev, } /* Allocate two attributes for each sync point: min and max */ - sp->syncpt_attrs = kzalloc(sizeof(*sp->syncpt_attrs) * sp->nb_pts * 2, - GFP_KERNEL); + sp->syncpt_attrs = kzalloc(sizeof(*sp->syncpt_attrs) + * nvhost_syncpt_nb_pts(sp) * 2, GFP_KERNEL); if (!sp->syncpt_attrs) { err = -ENOMEM; goto fail; } /* Fill in the attributes */ - for (i = 0; i < sp->nb_pts; i++) { + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { char name[MAX_SYNCPT_LENGTH]; struct kobject *kobj; struct nvhost_syncpt_attr *min = &sp->syncpt_attrs[i*2]; @@ -441,3 +460,49 @@ void nvhost_syncpt_deinit(struct nvhost_syncpt *sp) kfree(sp->syncpt_attrs); sp->syncpt_attrs = NULL; } + +int nvhost_syncpt_client_managed(struct nvhost_syncpt *sp, u32 id) +{ + return BIT(id) & syncpt_to_dev(sp)->info.client_managed; +} + +int nvhost_syncpt_nb_pts(struct nvhost_syncpt *sp) +{ + return syncpt_to_dev(sp)->info.nb_pts; +} + +int nvhost_syncpt_nb_bases(struct nvhost_syncpt *sp) +{ + return syncpt_to_dev(sp)->info.nb_bases; +} + +int nvhost_syncpt_nb_mlocks(struct nvhost_syncpt *sp) +{ + return syncpt_to_dev(sp)->info.nb_mlocks; +} + +/* public sync point API */ +u32 nvhost_syncpt_incr_max_ext(struct nvhost_device *dev, u32 id, u32 incrs) +{ + struct nvhost_syncpt *sp = &(nvhost_get_host(dev)->syncpt); + return nvhost_syncpt_incr_max(sp, id, incrs); +} + +void nvhost_syncpt_cpu_incr_ext(struct nvhost_device *dev, u32 id) +{ + struct nvhost_syncpt *sp = &(nvhost_get_host(dev)->syncpt); + nvhost_syncpt_cpu_incr(sp, id); +} + +u32 nvhost_syncpt_read_ext(struct nvhost_device *dev, u32 id) +{ + struct nvhost_syncpt *sp = &(nvhost_get_host(dev)->syncpt); + return nvhost_syncpt_read(sp, id); +} + +int nvhost_syncpt_wait_timeout_ext(struct nvhost_device *dev, u32 id, u32 thresh, + u32 timeout, u32 *value) +{ + struct nvhost_syncpt *sp = &(nvhost_get_host(dev)->syncpt); + return nvhost_syncpt_wait_timeout(sp, id, thresh, timeout, value); +} diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h index b71cb3e6287e..9ee4f3a8d49d 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.h +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -24,15 +24,10 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> #include <linux/atomic.h> -struct nvhost_syncpt; -struct nvhost_waitchk; - /* host managed and invalid syncpt id */ #define NVSYNCPT_GRAPHICS_HOST (0) -#define NVSYNCPT_INVALID (-1) /* Attribute struct for sysfs min and max attributes */ struct nvhost_syncpt_attr { @@ -46,22 +41,17 @@ struct nvhost_syncpt { atomic_t *min_val; atomic_t *max_val; u32 *base_val; - u32 nb_pts; - u32 nb_bases; - u32 client_managed; atomic_t *lock_counts; - u32 nb_mlocks; + const char **syncpt_names; struct nvhost_syncpt_attr *syncpt_attrs; }; int nvhost_syncpt_init(struct nvhost_device *, struct nvhost_syncpt *); void nvhost_syncpt_deinit(struct nvhost_syncpt *); -#define client_managed(id) (BIT(id) & sp->client_managed) #define syncpt_to_dev(sp) container_of(sp, struct nvhost_master, syncpt) -#define syncpt_op(sp) (syncpt_to_dev(sp)->op.syncpt) -#define SYNCPT_CHECK_PERIOD (2*HZ) - +#define SYNCPT_CHECK_PERIOD (2 * HZ) +#define MAX_STUCK_CHECK_COUNT 15 /** * Updates the value sent to hardware. @@ -95,11 +85,16 @@ static inline u32 nvhost_syncpt_read_min(struct nvhost_syncpt *sp, u32 id) return (u32)atomic_read(&sp->min_val[id]); } +int nvhost_syncpt_client_managed(struct nvhost_syncpt *sp, u32 id); +int nvhost_syncpt_nb_pts(struct nvhost_syncpt *sp); +int nvhost_syncpt_nb_bases(struct nvhost_syncpt *sp); +int nvhost_syncpt_nb_mlocks(struct nvhost_syncpt *sp); + static inline bool nvhost_syncpt_check_max(struct nvhost_syncpt *sp, u32 id, u32 real) { u32 max; - if (client_managed(id)) + if (nvhost_syncpt_client_managed(sp, id)) return true; max = nvhost_syncpt_read_max(sp, id); return (s32)(max - real) >= 0; @@ -140,25 +135,15 @@ static inline int nvhost_syncpt_wait(struct nvhost_syncpt *sp, u32 id, u32 thres MAX_SCHEDULE_TIMEOUT, NULL); } -/* - * Check driver supplied waitchk structs for syncpt thresholds - * that have already been satisfied and NULL the comparison (to - * avoid a wrap condition in the HW). - * - * @param: sp - global shadowed syncpt struct - * @param: nvmap - needed to access command buffer - * @param: mask - bit mask of syncpt IDs referenced in WAITs - * @param: wait - start of filled in array of waitchk structs - * @param: waitend - end ptr (one beyond last valid waitchk) - */ -int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, - struct nvmap_client *nvmap, - u32 mask, - struct nvhost_waitchk *wait, - int num_waitchk); +int nvhost_syncpt_patch_wait(struct nvhost_syncpt *sp, void *patch_addr); void nvhost_syncpt_debug(struct nvhost_syncpt *sp); +static inline int nvhost_syncpt_is_valid(struct nvhost_syncpt *sp, u32 id) +{ + return id != NVSYNCPT_INVALID && id < nvhost_syncpt_nb_pts(sp); +} + int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx); void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx); diff --git a/drivers/video/tegra/host/nvmap.c b/drivers/video/tegra/host/nvmap.c new file mode 100644 index 000000000000..fd82f40c59ff --- /dev/null +++ b/drivers/video/tegra/host/nvmap.c @@ -0,0 +1,100 @@ +/* + * drivers/video/tegra/host/nvmap.c + * + * Tegra Graphics Host Nvmap support + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "chip_support.h" +#include <linux/nvmap.h> + +struct mem_mgr *nvhost_nvmap_alloc_mgr(void) +{ + return (struct mem_mgr *)nvmap_create_client(nvmap_dev, "nvhost"); +} + +void nvhost_nvmap_put_mgr(struct mem_mgr *mgr) +{ + nvmap_client_put((struct nvmap_client *)mgr); +} + +struct mem_mgr *nvhost_nvmap_get_mgr(struct mem_mgr *mgr) +{ + return (struct mem_mgr *)nvmap_client_get((struct nvmap_client *)mgr); +} + +struct mem_mgr *nvhost_nvmap_get_mgr_file(int fd) +{ + return (struct mem_mgr *)nvmap_client_get_file(fd); +} + +struct mem_handle *nvhost_nvmap_alloc(struct mem_mgr *mgr, + size_t size, size_t align, int flags) +{ + return (struct mem_handle *)nvmap_alloc((struct nvmap_client *)mgr, + size, align, flags, 0); +} + +void nvhost_nvmap_put(struct mem_mgr *mgr, struct mem_handle *handle) +{ + return nvmap_free((struct nvmap_client *)mgr, + (struct nvmap_handle_ref *)handle); +} + +phys_addr_t nvhost_nvmap_pin(struct mem_mgr *mgr, struct mem_handle *handle) +{ + return nvmap_pin((struct nvmap_client *)mgr, + (struct nvmap_handle_ref *)handle); +} + +void nvhost_nvmap_unpin(struct mem_mgr *mgr, struct mem_handle *handle) +{ + return nvmap_unpin((struct nvmap_client *)mgr, + (struct nvmap_handle_ref *)handle); +} + +void *nvhost_nvmap_mmap(struct mem_handle *handle) +{ + return nvmap_mmap((struct nvmap_handle_ref *)handle); +} + +void nvhost_nvmap_munmap(struct mem_handle *handle, void *addr) +{ + nvmap_munmap((struct nvmap_handle_ref *)handle, addr); +} + +struct mem_handle *nvhost_nvmap_get(struct mem_mgr *mgr, u32 id) +{ + return (struct mem_handle *) + nvmap_duplicate_handle_id((struct nvmap_client *)mgr, id); +} + +int nvhost_init_nvmap_support(struct nvhost_chip_support *chip) +{ + chip->mem.alloc_mgr = nvhost_nvmap_alloc_mgr; + chip->mem.put_mgr = nvhost_nvmap_put_mgr; + chip->mem.get_mgr = nvhost_nvmap_get_mgr; + chip->mem.get_mgr_file = nvhost_nvmap_get_mgr_file; + chip->mem.alloc = nvhost_nvmap_alloc; + chip->mem.put = nvhost_nvmap_put; + chip->mem.get = nvhost_nvmap_get; + chip->mem.pin = nvhost_nvmap_pin; + chip->mem.unpin = nvhost_nvmap_unpin; + chip->mem.mmap = nvhost_nvmap_mmap; + chip->mem.munmap = nvhost_nvmap_munmap; + + return 0; +} diff --git a/drivers/video/tegra/host/nvmap.h b/drivers/video/tegra/host/nvmap.h new file mode 100644 index 000000000000..90f64d44f434 --- /dev/null +++ b/drivers/video/tegra/host/nvmap.h @@ -0,0 +1,27 @@ +/* + * drivers/video/tegra/host/nvmap.h + * + * Tegra Graphics Host nvmap memory manager + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_NVMAP_H +#define __NVHOST_NVMAP_H + +struct nvhost_chip_support; +int nvhost_init_nvmap_support(struct nvhost_chip_support *op); + +#endif diff --git a/drivers/video/tegra/host/t20/t20.c b/drivers/video/tegra/host/t20/t20.c index 24ddedc842e4..71ed7334e714 100644 --- a/drivers/video/tegra/host/t20/t20.c +++ b/drivers/video/tegra/host/t20/t20.c @@ -18,18 +18,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/slab.h> +#include <linux/nvhost_ioctl.h> #include <mach/powergate.h> -#include "dev.h" +#include <mach/iomap.h> #include "t20.h" -#include "host1x/host1x_channel.h" -#include "host1x/host1x_syncpt.h" -#include "host1x/host1x_hardware.h" -#include "host1x/host1x_cdma.h" -#include "gr3d/gr3d.h" #include "gr3d/gr3d_t20.h" #include "mpe/mpe.h" -#include "nvhost_hwctx.h" +#include "host1x/host1x.h" +#include "nvhost_channel.h" +#include "nvhost_memmgr.h" +#include "host1x/host1x01_hardware.h" +#include "host1x/host1x_syncpt.h" +#include "chip_support.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) @@ -41,15 +41,67 @@ #define NVMODMUTEX_VI (8) #define NVMODMUTEX_DSI (9) -#define NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1) +static int t20_num_alloc_channels = 0; -struct nvhost_device t20_devices[] = { -{ - /* channel 0 */ +static struct resource tegra_host1x01_resources[] = { + { + .start = TEGRA_HOST1X_BASE, + .end = TEGRA_HOST1X_BASE + TEGRA_HOST1X_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SYNCPT_THRESH_BASE, + .end = INT_SYNCPT_THRESH_BASE + INT_SYNCPT_THRESH_NR - 1, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_HOST1X_MPCORE_GENERAL, + .end = INT_HOST1X_MPCORE_GENERAL, + .flags = IORESOURCE_IRQ, + }, +}; + +static const char *s_syncpt_names[32] = { + "gfx_host", + "", "", "", "", "", "", "", + "disp0_a", "disp1_a", "avp_0", + "csi_vi_0", "csi_vi_1", + "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4", + "2d_0", "2d_1", + "disp0_b", "disp1_b", + "3d", + "mpe", + "disp0_c", "disp1_c", + "vblank0", "vblank1", + "mpe_ebm_eof", "mpe_wr_safe", + "2d_tinyblt", + "dsi" +}; + +static struct host1x_device_info host1x01_info = { + .nb_channels = 8, + .nb_pts = 32, + .nb_mlocks = 16, + .nb_bases = 8, + .syncpt_names = s_syncpt_names, + .client_managed = NVSYNCPTS_CLIENT_MANAGED, +}; + +static struct nvhost_device tegra_host1x01_device = { + .dev = {.platform_data = &host1x01_info}, + .name = "host1x", + .id = -1, + .resource = tegra_host1x01_resources, + .num_resources = ARRAY_SIZE(tegra_host1x01_resources), + .clocks = {{"host1x", UINT_MAX}, {} }, + NVHOST_MODULE_NO_POWERGATE_IDS, +}; + +static struct nvhost_device tegra_display01_device = { .name = "display", .id = -1, .index = 0, - .syncpts = BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | + .syncpts = BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | BIT(NVSYNCPT_DISP0_B) | BIT(NVSYNCPT_DISP1_B) | BIT(NVSYNCPT_DISP0_C) | BIT(NVSYNCPT_DISP1_C) | BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1), @@ -57,25 +109,24 @@ struct nvhost_device t20_devices[] = { NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_NONE, -}, -{ - /* channel 1 */ +}; + +static struct nvhost_device tegra_gr3d01_device = { .name = "gr3d", + .version = 1, .id = -1, .index = 1, .syncpts = BIT(NVSYNCPT_3D), .waitbases = BIT(NVWAITBASE_3D), .modulemutexes = BIT(NVMODMUTEX_3D), .class = NV_GRAPHICS_3D_CLASS_ID, - .prepare_poweroff = nvhost_gr3d_prepare_power_off, - .alloc_hwctx_handler = nvhost_gr3d_t20_ctxhandler_init, - .clocks = {{"gr3d", UINT_MAX}, {"emc", UINT_MAX}, {} }, + .clocks = {{"gr3d", UINT_MAX}, {"emc", UINT_MAX}, {} }, .powergate_ids = {TEGRA_POWERGATE_3D, -1}, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_NONE, -}, -{ - /* channel 2 */ +}; + +static struct nvhost_device tegra_gr2d01_device = { .name = "gr2d", .id = -1, .index = 2, @@ -83,26 +134,49 @@ struct nvhost_device t20_devices[] = { .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1), .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) | BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B), - .clocks = { {"gr2d", UINT_MAX}, + .clocks = { {"gr2d", UINT_MAX}, {"epp", UINT_MAX}, {"emc", UINT_MAX} }, NVHOST_MODULE_NO_POWERGATE_IDS, .clockgate_delay = 0, .moduleid = NVHOST_MODULE_NONE, -}, -{ - /* channel 3 */ + .serialize = true, +}; + +static struct resource isp_resources_t20[] = { + { + .name = "regs", + .start = TEGRA_ISP_BASE, + .end = TEGRA_ISP_BASE + TEGRA_ISP_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct nvhost_device tegra_isp01_device = { .name = "isp", .id = -1, + .resource = isp_resources_t20, + .num_resources = ARRAY_SIZE(isp_resources_t20), .index = 3, .syncpts = 0, NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_ISP, -}, -{ - /* channel 4 */ +}; + +static struct resource vi_resources[] = { + { + .name = "regs", + .start = TEGRA_VI_BASE, + .end = TEGRA_VI_BASE + TEGRA_VI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct nvhost_device tegra_vi01_device = { .name = "vi", + .resource = vi_resources, + .num_resources = ARRAY_SIZE(vi_resources), .id = -1, .index = 4, .syncpts = BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | @@ -114,11 +188,23 @@ struct nvhost_device t20_devices[] = { NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_VI, -}, -{ - /* channel 5 */ +}; + +static struct resource tegra_mpe01_resources[] = { + { + .name = "regs", + .start = TEGRA_MPE_BASE, + .end = TEGRA_MPE_BASE + TEGRA_MPE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct nvhost_device tegra_mpe01_device = { .name = "mpe", + .version = 1, .id = -1, + .resource = tegra_mpe01_resources, + .num_resources = ARRAY_SIZE(tegra_mpe01_resources), .index = 5, .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) | BIT(NVSYNCPT_MPE_WR_SAFE), @@ -126,16 +212,14 @@ struct nvhost_device t20_devices[] = { .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID, .waitbasesync = true, .keepalive = true, - .prepare_poweroff = nvhost_mpe_prepare_power_off, - .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, .clocks = { {"mpe", UINT_MAX}, {"emc", UINT_MAX} }, .powergate_ids = {TEGRA_POWERGATE_MPE, -1}, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_MPE, -}, -{ - /* channel 6 */ +}; + +static struct nvhost_device tegra_dsi01_device = { .name = "dsi", .id = -1, .index = 6, @@ -144,90 +228,61 @@ struct nvhost_device t20_devices[] = { NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_NONE, -} }; +}; +static struct nvhost_device *t20_devices[] = { + &tegra_host1x01_device, + &tegra_display01_device, + &tegra_gr3d01_device, + &tegra_gr2d01_device, + &tegra_isp01_device, + &tegra_vi01_device, + &tegra_mpe01_device, + &tegra_dsi01_device, +}; -static inline void __iomem *t20_channel_aperture(void __iomem *p, int ndx) +int tegra2_register_host1x_devices(void) { - p += NV_HOST1X_CHANNEL0_BASE; - p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES; - return p; + return nvhost_add_devices(t20_devices, ARRAY_SIZE(t20_devices)); } -static inline int t20_nvhost_hwctx_handler_init(struct nvhost_channel *ch) +static void t20_free_nvhost_channel(struct nvhost_channel *ch) { - int err = 0; - unsigned long syncpts = ch->dev->syncpts; - unsigned long waitbases = ch->dev->waitbases; - u32 syncpt = find_first_bit(&syncpts, BITS_PER_LONG); - u32 waitbase = find_first_bit(&waitbases, BITS_PER_LONG); - - if (ch->dev->alloc_hwctx_handler) { - ch->ctxhandler = ch->dev->alloc_hwctx_handler(syncpt, - waitbase, ch); - if (!ch->ctxhandler) - err = -ENOMEM; - } - - return err; + nvhost_free_channel_internal(ch, &t20_num_alloc_channels); } -static int t20_channel_init(struct nvhost_channel *ch, - struct nvhost_master *dev, int index) +static struct nvhost_channel *t20_alloc_nvhost_channel( + struct nvhost_device *dev) { - ch->chid = index; - mutex_init(&ch->reflock); - mutex_init(&ch->submitlock); - - ch->aperture = t20_channel_aperture(dev->aperture, index); - - return t20_nvhost_hwctx_handler_init(ch); + return nvhost_alloc_channel_internal(dev->index, + nvhost_get_host(dev)->info.nb_channels, + &t20_num_alloc_channels); } -int nvhost_init_t20_channel_support(struct nvhost_master *host) -{ - host->nb_channels = NVHOST_NUMCHANNELS; - - host->op.channel.init = t20_channel_init; - host->op.channel.submit = host1x_channel_submit; - host->op.channel.read3dreg = host1x_channel_read_3d_reg; +#include "host1x/host1x_channel.c" +#include "host1x/host1x_cdma.c" +#include "host1x/host1x_debug.c" +#include "host1x/host1x_syncpt.c" +#include "host1x/host1x_intr.c" - return 0; -} - -struct nvhost_device *t20_get_nvhost_device(struct nvhost_master *host, - char *name) -{ - int i; - - for (i = 0; i < host->nb_channels; i++) { - if (strcmp(t20_devices[i].name, name) == 0) - return &t20_devices[i]; - } - - return NULL; -} - -int nvhost_init_t20_support(struct nvhost_master *host) +int nvhost_init_t20_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { int err; - /* don't worry about cleaning up on failure... "remove" does it. */ - err = nvhost_init_t20_channel_support(host); - if (err) - return err; - err = host1x_init_cdma_support(host); - if (err) - return err; - err = nvhost_init_t20_debug_support(host); + op->channel = host1x_channel_ops; + op->cdma = host1x_cdma_ops; + op->push_buffer = host1x_pushbuffer_ops; + op->debug = host1x_debug_ops; + host->sync_aperture = host->aperture + HOST1X_CHANNEL_SYNC_REG_BASE; + op->syncpt = host1x_syncpt_ops; + op->intr = host1x_intr_ops; + err = nvhost_memmgr_init(op); if (err) return err; - err = host1x_init_syncpt_support(host); - if (err) - return err; - err = nvhost_init_t20_intr_support(host); - if (err) - return err; - host->op.nvhost_dev.get_nvhost_device = t20_get_nvhost_device; + + op->nvhost_dev.alloc_nvhost_channel = t20_alloc_nvhost_channel; + op->nvhost_dev.free_nvhost_channel = t20_free_nvhost_channel; + return 0; } diff --git a/drivers/video/tegra/host/t20/t20.h b/drivers/video/tegra/host/t20/t20.h index 93555a55b589..729f9d8e85e4 100644 --- a/drivers/video/tegra/host/t20/t20.h +++ b/drivers/video/tegra/host/t20/t20.h @@ -21,13 +21,9 @@ #define _NVHOST_T20_H_ struct nvhost_master; -struct nvhost_module; +struct nvhost_chip_support; -int nvhost_init_t20_channel_support(struct nvhost_master *); -int nvhost_init_t20_debug_support(struct nvhost_master *); -int nvhost_init_t20_syncpt_support(struct nvhost_master *); -int nvhost_init_t20_intr_support(struct nvhost_master *); -int nvhost_init_t20_support(struct nvhost_master *host); -int nvhost_t20_save_context(struct nvhost_module *mod, u32 syncpt_id); +int nvhost_init_t20_support(struct nvhost_master *, + struct nvhost_chip_support *); #endif /* _NVHOST_T20_H_ */ diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c index fb69d4e0f57a..0c8d626a4d67 100644 --- a/drivers/video/tegra/host/t30/t30.c +++ b/drivers/video/tegra/host/t30/t30.c @@ -18,20 +18,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/mutex.h> +#include <linux/nvhost_ioctl.h> #include <mach/powergate.h> #include <mach/iomap.h> -#include "dev.h" #include "t20/t20.h" #include "t30.h" -#include "gr3d/gr3d.h" -#include "mpe/mpe.h" #include "gr3d/gr3d_t30.h" #include "gr3d/scale3d.h" -#include "host1x/host1x_hardware.h" -#include "host1x/host1x_cdma.h" -#include "host1x/host1x_syncpt.h" +#include "mpe/mpe.h" +#include "host1x/host1x.h" +#include "host1x/host1x01_hardware.h" #include "chip_support.h" +#include "nvhost_channel.h" +#include "nvhost_memmgr.h" +#include "host1x/host1x_syncpt.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) @@ -43,15 +43,67 @@ #define NVMODMUTEX_VI (8) #define NVMODMUTEX_DSI (9) -#define NVHOST_CHANNEL_BASE 0 +static int t30_num_alloc_channels = 0; -struct nvhost_device t30_devices[] = { -{ - /* channel 0 */ +static struct resource tegra_host1x01_resources[] = { + { + .start = TEGRA_HOST1X_BASE, + .end = TEGRA_HOST1X_BASE + TEGRA_HOST1X_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SYNCPT_THRESH_BASE, + .end = INT_SYNCPT_THRESH_BASE + INT_SYNCPT_THRESH_NR - 1, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_HOST1X_MPCORE_GENERAL, + .end = INT_HOST1X_MPCORE_GENERAL, + .flags = IORESOURCE_IRQ, + }, +}; + +static const char *s_syncpt_names[32] = { + "gfx_host", + "", "", "", "", "", "", "", + "disp0_a", "disp1_a", "avp_0", + "csi_vi_0", "csi_vi_1", + "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4", + "2d_0", "2d_1", + "disp0_b", "disp1_b", + "3d", + "mpe", + "disp0_c", "disp1_c", + "vblank0", "vblank1", + "mpe_ebm_eof", "mpe_wr_safe", + "2d_tinyblt", + "dsi" +}; + +static struct host1x_device_info host1x01_info = { + .nb_channels = 8, + .nb_pts = 32, + .nb_mlocks = 16, + .nb_bases = 8, + .syncpt_names = s_syncpt_names, + .client_managed = NVSYNCPTS_CLIENT_MANAGED, +}; + +static struct nvhost_device tegra_host1x01_device = { + .dev = {.platform_data = &host1x01_info}, + .name = "host1x", + .id = -1, + .resource = tegra_host1x01_resources, + .num_resources = ARRAY_SIZE(tegra_host1x01_resources), + .clocks = {{"host1x", UINT_MAX}, {} }, + NVHOST_MODULE_NO_POWERGATE_IDS, +}; + +static struct nvhost_device tegra_display01_device = { .name = "display", .id = -1, .index = 0, - .syncpts = BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | + .syncpts = BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | BIT(NVSYNCPT_DISP0_B) | BIT(NVSYNCPT_DISP1_B) | BIT(NVSYNCPT_DISP0_C) | BIT(NVSYNCPT_DISP1_C) | BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1), @@ -59,23 +111,17 @@ struct nvhost_device t30_devices[] = { NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_NONE, -}, -{ - /* channel 1 */ +}; + +static struct nvhost_device tegra_gr3d02_device = { .name = "gr3d", + .version = 2, .id = -1, .index = 1, - .syncpts = BIT(NVSYNCPT_3D), + .syncpts = BIT(NVSYNCPT_3D), .waitbases = BIT(NVWAITBASE_3D), .modulemutexes = BIT(NVMODMUTEX_3D), .class = NV_GRAPHICS_3D_CLASS_ID, - .prepare_poweroff = nvhost_gr3d_prepare_power_off, - .busy = nvhost_scale3d_notify_busy, - .idle = nvhost_scale3d_notify_idle, - .init = nvhost_scale3d_init, - .deinit = nvhost_scale3d_deinit, - .suspend = nvhost_scale3d_suspend, - .alloc_hwctx_handler = nvhost_gr3d_t30_ctxhandler_init, .clocks = { {"gr3d", UINT_MAX}, {"gr3d2", UINT_MAX}, {"emc", UINT_MAX} }, @@ -86,9 +132,9 @@ struct nvhost_device t30_devices[] = { .powerup_reset = true, .powergate_delay = 250, .moduleid = NVHOST_MODULE_NONE, -}, -{ - /* channel 2 */ +}; + +static struct nvhost_device tegra_gr2d02_device = { .name = "gr2d", .id = -1, .index = 2, @@ -96,29 +142,56 @@ struct nvhost_device t30_devices[] = { .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1), .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) | BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B), - .clocks = { {"gr2d", UINT_MAX}, - {"epp", 0}, - {"emc", 300000000} }, + .clocks = { {"gr2d", UINT_MAX}, + {"epp", 0}, + {"emc", 300000000} }, NVHOST_MODULE_NO_POWERGATE_IDS, .clockgate_delay = 0, .moduleid = NVHOST_MODULE_NONE, -}, -{ - /* channel 3 */ + .serialize = true, +}; + +static struct resource isp_resources_t20[] = { + { + .name = "regs", + .start = TEGRA_ISP_BASE, + .end = TEGRA_ISP_BASE + TEGRA_ISP_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct nvhost_device tegra_isp01_device = { .name = "isp", .id = -1, + .resource = isp_resources_t20, + .num_resources = ARRAY_SIZE(isp_resources_t20), .index = 3, - .syncpts = 0, + .syncpts = BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) | + BIT(NVSYNCPT_VI_ISP_4), + .clocks = { {"epp", 0} + }, + .keepalive = true, NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_ISP, -}, -{ - /* channel 4 */ +}; + +static struct resource vi_resources[] = { + { + .name = "regs", + .start = TEGRA_VI_BASE, + .end = TEGRA_VI_BASE + TEGRA_VI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct nvhost_device tegra_vi01_device = { .name = "vi", + .resource = vi_resources, + .num_resources = ARRAY_SIZE(vi_resources), .id = -1, .index = 4, - .syncpts = BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | + .syncpts = BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | BIT(NVSYNCPT_VI_ISP_0) | BIT(NVSYNCPT_VI_ISP_1) | BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) | BIT(NVSYNCPT_VI_ISP_4), @@ -127,11 +200,23 @@ struct nvhost_device t30_devices[] = { NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_VI, -}, -{ - /* channel 5 */ +}; + +static struct resource tegra_mpe01_resources[] = { + { + .name = "regs", + .start = TEGRA_MPE_BASE, + .end = TEGRA_MPE_BASE + TEGRA_MPE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct nvhost_device tegra_mpe02_device = { .name = "mpe", + .version = 2, .id = -1, + .resource = tegra_mpe01_resources, + .num_resources = ARRAY_SIZE(tegra_mpe01_resources), .index = 5, .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) | BIT(NVSYNCPT_MPE_WR_SAFE), @@ -139,18 +224,16 @@ struct nvhost_device t30_devices[] = { .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID, .waitbasesync = true, .keepalive = true, - .prepare_poweroff = nvhost_mpe_prepare_power_off, - .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, - .clocks = { {"mpe", UINT_MAX}, + .clocks = { {"mpe", UINT_MAX}, {"emc", UINT_MAX} }, .powergate_ids = {TEGRA_POWERGATE_MPE, -1}, NVHOST_DEFAULT_CLOCKGATE_DELAY, .can_powergate = true, .powergate_delay = 100, .moduleid = NVHOST_MODULE_MPE, -}, -{ - /* channel 6 */ +}; + +static struct nvhost_device tegra_dsi01_device = { .name = "dsi", .id = -1, .index = 6, @@ -159,94 +242,62 @@ struct nvhost_device t30_devices[] = { NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, .moduleid = NVHOST_MODULE_NONE, -} }; +}; -static inline int t30_nvhost_hwctx_handler_init(struct nvhost_channel *ch) -{ - int err = 0; - unsigned long syncpts = ch->dev->syncpts; - unsigned long waitbases = ch->dev->waitbases; - u32 syncpt = find_first_bit(&syncpts, BITS_PER_LONG); - u32 waitbase = find_first_bit(&waitbases, BITS_PER_LONG); - - if (ch->dev->alloc_hwctx_handler) { - ch->ctxhandler = ch->dev->alloc_hwctx_handler(syncpt, - waitbase, ch); - if (!ch->ctxhandler) - err = -ENOMEM; - } +static struct nvhost_device *t30_devices[] = { + &tegra_host1x01_device, + &tegra_display01_device, + &tegra_gr3d02_device, + &tegra_gr2d02_device, + &tegra_isp01_device, + &tegra_vi01_device, + &tegra_mpe02_device, + &tegra_dsi01_device, +}; - return err; -} - -static inline void __iomem *t30_channel_aperture(void __iomem *p, int ndx) +int tegra3_register_host1x_devices(void) { - ndx += NVHOST_CHANNEL_BASE; - p += NV_HOST1X_CHANNEL0_BASE; - p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES; - return p; + return nvhost_add_devices(t30_devices, ARRAY_SIZE(t30_devices)); } -static int t30_channel_init(struct nvhost_channel *ch, - struct nvhost_master *dev, int index) +static void t30_free_nvhost_channel(struct nvhost_channel *ch) { - ch->chid = index; - mutex_init(&ch->reflock); - mutex_init(&ch->submitlock); - - ch->aperture = t30_channel_aperture(dev->aperture, index); - - return t30_nvhost_hwctx_handler_init(ch); + nvhost_free_channel_internal(ch, &t30_num_alloc_channels); } -int nvhost_init_t30_channel_support(struct nvhost_master *host) +static struct nvhost_channel *t30_alloc_nvhost_channel( + struct nvhost_device *dev) { - int result = nvhost_init_t20_channel_support(host); - host->op.channel.init = t30_channel_init; - - return result; + return nvhost_alloc_channel_internal(dev->index, + nvhost_get_host(dev)->info.nb_channels, + &t30_num_alloc_channels); } -int nvhost_init_t30_debug_support(struct nvhost_master *host) -{ - nvhost_init_t20_debug_support(host); - host->op.debug.debug_init = nvhost_scale3d_debug_init; - return 0; -} +#include "host1x/host1x_channel.c" +#include "host1x/host1x_cdma.c" +#include "host1x/host1x_debug.c" +#include "host1x/host1x_syncpt.c" +#include "host1x/host1x_intr.c" -struct nvhost_device *t30_get_nvhost_device(struct nvhost_master *host, - char *name) -{ - int i; - - for (i = 0; i < host->nb_channels; i++) { - if (strcmp(t30_devices[i].name, name) == 0) - return &t30_devices[i]; - } - - return NULL; -} - -int nvhost_init_t30_support(struct nvhost_master *host) +int nvhost_init_t30_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { int err; - /* don't worry about cleaning up on failure... "remove" does it. */ - err = nvhost_init_t30_channel_support(host); - if (err) - return err; - err = host1x_init_cdma_support(host); - if (err) - return err; - err = nvhost_init_t30_debug_support(host); + op->channel = host1x_channel_ops; + op->cdma = host1x_cdma_ops; + op->push_buffer = host1x_pushbuffer_ops; + op->debug = host1x_debug_ops; + op->debug.debug_init = nvhost_scale3d_debug_init; + host->sync_aperture = host->aperture + HOST1X_CHANNEL_SYNC_REG_BASE; + op->syncpt = host1x_syncpt_ops; + op->intr = host1x_intr_ops; + err = nvhost_memmgr_init(op); if (err) return err; - err = host1x_init_syncpt_support(host); - if (err) - return err; - err = nvhost_init_t20_intr_support(host); - if (err) - return err; - host->op.nvhost_dev.get_nvhost_device = t30_get_nvhost_device; + + op->nvhost_dev.alloc_nvhost_channel = t30_alloc_nvhost_channel; + op->nvhost_dev.free_nvhost_channel = t30_free_nvhost_channel; + return 0; } diff --git a/drivers/video/tegra/host/t30/t30.h b/drivers/video/tegra/host/t30/t30.h index 0446dbd19b39..80838a5e287c 100644 --- a/drivers/video/tegra/host/t30/t30.h +++ b/drivers/video/tegra/host/t30/t30.h @@ -21,9 +21,9 @@ #define _NVHOST_T30_H_ struct nvhost_master; +struct nvhost_chip_support; -int nvhost_init_t30_channel_support(struct nvhost_master *); -int nvhost_init_t30_debug_support(struct nvhost_master *); -int nvhost_init_t30_support(struct nvhost_master *host); +int nvhost_init_t30_support(struct nvhost_master *host, + struct nvhost_chip_support *); #endif /* _NVHOST_T30_H_ */ diff --git a/drivers/video/tegra/host/vi/vi.c b/drivers/video/tegra/host/vi/vi.c index a6f902a1ac7b..ee801c91efa5 100644 --- a/drivers/video/tegra/host/vi/vi.c +++ b/drivers/video/tegra/host/vi/vi.c @@ -18,14 +18,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/resource.h> - -#include <mach/iomap.h> - #include "dev.h" #include "bus_client.h" -static int __devinit vi_probe(struct nvhost_device *dev) +static int __devinit vi_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { int err = 0; @@ -42,6 +39,7 @@ static int __exit vi_remove(struct nvhost_device *dev) return 0; } +#ifdef CONFIG_PM static int vi_suspend(struct nvhost_device *dev, pm_message_t state) { return nvhost_client_device_suspend(dev); @@ -52,15 +50,7 @@ static int vi_resume(struct nvhost_device *dev) dev_info(&dev->dev, "resuming\n"); return 0; } - -static struct resource vi_resources = { - .name = "regs", - .start = TEGRA_VI_BASE, - .end = TEGRA_VI_BASE + TEGRA_VI_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -struct nvhost_device *vi_device; +#endif static struct nvhost_driver vi_driver = { .probe = vi_probe, @@ -77,18 +67,6 @@ static struct nvhost_driver vi_driver = { static int __init vi_init(void) { - int err; - - vi_device = nvhost_get_device("vi"); - if (!vi_device) - return -ENXIO; - - vi_device->resource = &vi_resources; - vi_device->num_resources = 1; - err = nvhost_device_register(vi_device); - if (err) - return err; - return nvhost_driver_register(&vi_driver); } diff --git a/drivers/video/tegra/nvmap/Makefile b/drivers/video/tegra/nvmap/Makefile index 95d7f68836af..3da03af2b1eb 100644 --- a/drivers/video/tegra/nvmap/Makefile +++ b/drivers/video/tegra/nvmap/Makefile @@ -4,4 +4,5 @@ obj-y += nvmap_dev.o obj-y += nvmap_handle.o obj-y += nvmap_heap.o obj-y += nvmap_ioctl.o +obj-${CONFIG_IOMMU_API} += nvmap_iommu.o obj-${CONFIG_NVMAP_RECLAIM_UNPINNED_VM} += nvmap_mru.o
\ No newline at end of file diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index b4b6241618db..b7fd695d04ee 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -32,7 +32,7 @@ #include <asm/tlbflush.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap.h" #include "nvmap_mru.h" @@ -352,225 +352,6 @@ static phys_addr_t handle_phys(struct nvmap_handle *h) return addr; } -/* stores the physical address (+offset) of each handle relocation entry - * into its output location. see nvmap_pin_array for more details. - * - * each entry in arr (i.e., each relocation request) specifies two handles: - * the handle to pin (pin), and the handle where the address of pin should be - * written (patch). in pseudocode, this loop basically looks like: - * - * for (i = 0; i < nr; i++) { - * (pin, pin_offset, patch, patch_offset) = arr[i]; - * patch[patch_offset] = address_of(pin) + pin_offset; - * } - */ -static int nvmap_reloc_pin_array(struct nvmap_client *client, - const struct nvmap_pinarray_elem *arr, - int nr, struct nvmap_handle *gather) -{ - struct nvmap_handle *last_patch = NULL; - unsigned int last_pfn = 0; - pte_t **pte; - void *addr; - int i; - - pte = nvmap_alloc_pte(client->dev, &addr); - if (IS_ERR(pte)) - return PTR_ERR(pte); - - for (i = 0; i < nr; i++) { - struct nvmap_handle *patch; - struct nvmap_handle *pin; - phys_addr_t reloc_addr; - phys_addr_t phys; - unsigned int pfn; - - /* all of the handles are validated and get'ted prior to - * calling this function, so casting is safe here */ - pin = (struct nvmap_handle *)arr[i].pin_mem; - - if (arr[i].patch_mem == (unsigned long)last_patch) { - patch = last_patch; - } else if (arr[i].patch_mem == (unsigned long)gather) { - patch = gather; - } else { - if (last_patch) - nvmap_handle_put(last_patch); - - patch = nvmap_get_handle_id(client, arr[i].patch_mem); - if (!patch) { - nvmap_free_pte(client->dev, pte); - return -EPERM; - } - last_patch = patch; - } - - if (patch->heap_pgalloc) { - unsigned int page = arr[i].patch_offset >> PAGE_SHIFT; - phys = page_to_phys(patch->pgalloc.pages[page]); - phys += (arr[i].patch_offset & ~PAGE_MASK); - } else { - phys = patch->carveout->base + arr[i].patch_offset; - } - - pfn = __phys_to_pfn(phys); - if (pfn != last_pfn) { - pgprot_t prot = nvmap_pgprot(patch, pgprot_kernel); - phys_addr_t kaddr = (phys_addr_t)addr; - set_pte_at(&init_mm, kaddr, *pte, pfn_pte(pfn, prot)); - flush_tlb_kernel_page(kaddr); - last_pfn = pfn; - } - - reloc_addr = handle_phys(pin) + arr[i].pin_offset; - reloc_addr >>= arr[i].reloc_shift; - __raw_writel(reloc_addr, addr + (phys & ~PAGE_MASK)); - } - - nvmap_free_pte(client->dev, pte); - - if (last_patch) - nvmap_handle_put(last_patch); - - wmb(); - - return 0; -} - -static int nvmap_validate_get_pin_array(struct nvmap_client *client, - const struct nvmap_pinarray_elem *arr, - int nr, struct nvmap_handle **h) -{ - int i; - int ret = 0; - int count = 0; - - nvmap_ref_lock(client); - - for (i = 0; i < nr; i++) { - struct nvmap_handle_ref *ref; - - if (need_resched()) { - nvmap_ref_unlock(client); - schedule(); - nvmap_ref_lock(client); - } - - ref = _nvmap_validate_id_locked(client, arr[i].pin_mem); - - if (!ref) - nvmap_warn(client, "falied to validate id\n"); - else if (!ref->handle) - nvmap_warn(client, "id had no associated handle\n"); - else if (!ref->handle->alloc) - nvmap_warn(client, "handle had no allocation\n"); - - if (!ref || !ref->handle || !ref->handle->alloc) { - ret = -EPERM; - break; - } - - /* a handle may be referenced multiple times in arr, but - * it will only be pinned once; this ensures that the - * minimum number of sync-queue slots in the host driver - * are dedicated to storing unpin lists, which allows - * for greater parallelism between the CPU and graphics - * processor */ - if (ref->handle->flags & NVMAP_HANDLE_VISITED) - continue; - - ref->handle->flags |= NVMAP_HANDLE_VISITED; - - h[count] = nvmap_handle_get(ref->handle); - BUG_ON(!h[count]); - count++; - } - - nvmap_ref_unlock(client); - - if (ret) { - for (i = 0; i < count; i++) { - h[i]->flags &= ~NVMAP_HANDLE_VISITED; - nvmap_handle_put(h[i]); - } - } - - return ret ?: count; -} - -/* a typical mechanism host1x clients use for using the Tegra graphics - * processor is to build a command buffer which contains relocatable - * memory handle commands, and rely on the kernel to convert these in-place - * to addresses which are understood by the GPU hardware. - * - * this is implemented by having clients provide a sideband array - * of relocatable handles (+ offsets) and the location in the command - * buffer handle to patch with the GPU address when the client submits - * its command buffer to the host1x driver. - * - * the host driver also uses this relocation mechanism internally to - * relocate the client's (unpinned) command buffers into host-addressable - * memory. - * - * @client: nvmap_client which should be used for validation; should be - * owned by the process which is submitting command buffers - * @gather: special handle for relocated command buffer outputs used - * internally by the host driver. if this handle is encountered - * as an output handle in the relocation array, it is assumed - * to be a known-good output and is not validated. - * @arr: array of ((relocatable handle, offset), (output handle, offset)) - * tuples. - * @nr: number of entries in arr - * @unique_arr: list of nvmap_handle objects which were pinned by - * nvmap_pin_array. must be unpinned by the caller after the - * command buffers referenced in gather have completed. - */ -int nvmap_pin_array(struct nvmap_client *client, struct nvmap_handle *gather, - const struct nvmap_pinarray_elem *arr, int nr, - struct nvmap_handle **unique_arr) -{ - int count = 0; - int ret = 0; - int i; - - if (mutex_lock_interruptible(&client->share->pin_lock)) { - nvmap_warn(client, "%s interrupted when acquiring pin lock\n", - current->group_leader->comm); - return -EINTR; - } - - count = nvmap_validate_get_pin_array(client, arr, nr, unique_arr); - if (count < 0) { - mutex_unlock(&client->share->pin_lock); - nvmap_warn(client, "failed to validate pin array\n"); - return count; - } - - for (i = 0; i < count; i++) - unique_arr[i]->flags &= ~NVMAP_HANDLE_VISITED; - - ret = wait_pin_array_locked(client, unique_arr, count); - - mutex_unlock(&client->share->pin_lock); - - if (!ret) - ret = nvmap_reloc_pin_array(client, arr, nr, gather); - - if (WARN_ON(ret)) { - for (i = 0; i < count; i++) - nvmap_handle_put(unique_arr[i]); - return ret; - } else { - for (i = 0; i < count; i++) { - if (unique_arr[i]->heap_pgalloc && - unique_arr[i]->pgalloc.dirty) - map_iovmm_area(unique_arr[i]); - } - } - - return count; -} - phys_addr_t nvmap_pin(struct nvmap_client *client, struct nvmap_handle_ref *ref) { @@ -820,52 +601,3 @@ void nvmap_free(struct nvmap_client *client, struct nvmap_handle_ref *r) nvmap_free_handle_id(client, nvmap_ref_to_id(r)); } - -/* - * create a mapping to the user's buffer and write it - * (uses similar logic from nvmap_reloc_pin_array to map the cmdbuf) - */ -int nvmap_patch_word(struct nvmap_client *client, - struct nvmap_handle *patch, - u32 patch_offset, u32 patch_value) -{ - phys_addr_t phys; - unsigned long kaddr; - unsigned int pfn; - void *addr; - pte_t **pte; - pgprot_t prot; - - if (patch_offset >= patch->size) { - nvmap_warn(client, "read/write outside of handle\n"); - return -EFAULT; - } - - pte = nvmap_alloc_pte(client->dev, &addr); - if (IS_ERR(pte)) - return PTR_ERR(pte); - - /* derive physaddr of cmdbuf WAIT to patch */ - if (patch->heap_pgalloc) { - unsigned int page = patch_offset >> PAGE_SHIFT; - phys = page_to_phys(patch->pgalloc.pages[page]); - phys += (patch_offset & ~PAGE_MASK); - } else { - phys = patch->carveout->base + patch_offset; - } - - pfn = __phys_to_pfn(phys); - prot = nvmap_pgprot(patch, pgprot_kernel); - kaddr = (unsigned long)addr; - - /* write PTE, so addr points to cmdbuf PFN */ - set_pte_at(&init_mm, kaddr, *pte, pfn_pte(pfn, prot)); - flush_tlb_kernel_page(kaddr); - - /* write patch_value to addr + page offset */ - __raw_writel(patch_value, addr + (phys & ~PAGE_MASK)); - - nvmap_free_pte(client->dev, pte); - wmb(); - return 0; -} diff --git a/drivers/video/tegra/nvmap/nvmap.h b/drivers/video/tegra/nvmap/nvmap.h index 44a0d86b6039..25403f5e7098 100644 --- a/drivers/video/tegra/nvmap/nvmap.h +++ b/drivers/video/tegra/nvmap/nvmap.h @@ -30,13 +30,15 @@ #include <linux/sched.h> #include <linux/wait.h> #include <linux/atomic.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap_heap.h" struct nvmap_device; struct page; struct tegra_iovmm_area; +void _nvmap_handle_free(struct nvmap_handle *h); + #if defined(CONFIG_TEGRA_NVMAP) #define nvmap_err(_client, _fmt, ...) \ dev_err(nvmap_client_to_device(_client), \ @@ -86,7 +88,7 @@ struct nvmap_handle { struct mutex lock; }; -#define NVMAP_DEFAULT_PAGE_POOL_SIZE 8192 +#ifdef CONFIG_NVMAP_PAGE_POOLS #define NVMAP_UC_POOL NVMAP_HANDLE_UNCACHEABLE #define NVMAP_WC_POOL NVMAP_HANDLE_WRITE_COMBINE #define NVMAP_IWB_POOL NVMAP_HANDLE_INNER_CACHEABLE @@ -103,11 +105,13 @@ struct nvmap_page_pool { }; int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags); +#endif struct nvmap_share { struct tegra_iovmm_client *iovmm; wait_queue_head_t pin_wait; struct mutex pin_lock; +#ifdef CONFIG_NVMAP_PAGE_POOLS union { struct nvmap_page_pool pools[NVMAP_NUM_POOLS]; struct { @@ -117,6 +121,7 @@ struct nvmap_share { struct nvmap_page_pool wb_pool; }; }; +#endif #ifdef CONFIG_NVMAP_RECLAIM_UNPINNED_VM struct mutex mru_lock; struct list_head *mru_lists; @@ -159,7 +164,46 @@ static inline void nvmap_ref_unlock(struct nvmap_client *priv) { mutex_unlock(&priv->ref_lock); } -#endif /* CONFIG_TEGRA_NVMAP */ + +static inline struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h) +{ + if (unlikely(atomic_inc_return(&h->ref) <= 1)) { + pr_err("%s: %s getting a freed handle\n", + __func__, current->group_leader->comm); + if (atomic_read(&h->ref) <= 0) + return NULL; + } + return h; +} + +static inline void nvmap_handle_put(struct nvmap_handle *h) +{ + int cnt = atomic_dec_return(&h->ref); + + if (WARN_ON(cnt < 0)) { + pr_err("%s: %s put to negative references\n", + __func__, current->comm); + } else if (cnt == 0) + _nvmap_handle_free(h); +} + +static inline pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot) +{ + if (h->flags == NVMAP_HANDLE_UNCACHEABLE) + return pgprot_noncached(prot); + else if (h->flags == NVMAP_HANDLE_WRITE_COMBINE) + return pgprot_writecombine(prot); + else if (h->flags == NVMAP_HANDLE_INNER_CACHEABLE) + return pgprot_inner_writeback(prot); + return prot; +} + +#else /* CONFIG_TEGRA_NVMAP */ +struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h); +void nvmap_handle_put(struct nvmap_handle *h); +pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot); + +#endif /* !CONFIG_TEGRA_NVMAP */ struct device *nvmap_client_to_device(struct nvmap_client *client); @@ -201,9 +245,6 @@ struct nvmap_handle *nvmap_get_handle_id(struct nvmap_client *client, struct nvmap_handle_ref *nvmap_create_handle(struct nvmap_client *client, size_t size); -struct nvmap_handle_ref *nvmap_duplicate_handle_id(struct nvmap_client *client, - unsigned long id); - int nvmap_alloc_handle_id(struct nvmap_client *client, unsigned long id, unsigned int heap_mask, size_t align, unsigned int flags); @@ -216,51 +257,10 @@ int nvmap_pin_ids(struct nvmap_client *client, void nvmap_unpin_ids(struct nvmap_client *priv, unsigned int nr, const unsigned long *ids); -void _nvmap_handle_free(struct nvmap_handle *h); - int nvmap_handle_remove(struct nvmap_device *dev, struct nvmap_handle *h); void nvmap_handle_add(struct nvmap_device *dev, struct nvmap_handle *h); -#if defined(CONFIG_TEGRA_NVMAP) -static inline struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h) -{ - if (unlikely(atomic_inc_return(&h->ref) <= 1)) { - pr_err("%s: %s getting a freed handle\n", - __func__, current->group_leader->comm); - if (atomic_read(&h->ref) <= 0) - return NULL; - } - return h; -} - -static inline void nvmap_handle_put(struct nvmap_handle *h) -{ - int cnt = atomic_dec_return(&h->ref); - - if (WARN_ON(cnt < 0)) { - pr_err("%s: %s put to negative references\n", - __func__, current->comm); - } else if (cnt == 0) - _nvmap_handle_free(h); -} - -static inline pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot) -{ - if (h->flags == NVMAP_HANDLE_UNCACHEABLE) - return pgprot_noncached(prot); - else if (h->flags == NVMAP_HANDLE_WRITE_COMBINE) - return pgprot_writecombine(prot); - else if (h->flags == NVMAP_HANDLE_INNER_CACHEABLE) - return pgprot_inner_writeback(prot); - return prot; -} -#else /* CONFIG_TEGRA_NVMAP */ -struct nvmap_handle *nvmap_handle_get(struct nvmap_handle *h); -void nvmap_handle_put(struct nvmap_handle *h); -pgprot_t nvmap_pgprot(struct nvmap_handle *h, pgprot_t prot); -#endif /* !CONFIG_TEGRA_NVMAP */ - int is_nvmap_vma(struct vm_area_struct *vma); struct nvmap_handle_ref *nvmap_alloc_iovm(struct nvmap_client *client, @@ -268,4 +268,4 @@ struct nvmap_handle_ref *nvmap_alloc_iovm(struct nvmap_client *client, void nvmap_free_iovm(struct nvmap_client *client, struct nvmap_handle_ref *r); -#endif +#endif /* __VIDEO_TEGRA_NVMAP_NVMAP_H */ diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c index f84f38c93aad..98b0bcc18ba5 100644 --- a/drivers/video/tegra/nvmap/nvmap_dev.c +++ b/drivers/video/tegra/nvmap/nvmap_dev.c @@ -34,12 +34,15 @@ #include <linux/spinlock.h> #include <linux/uaccess.h> #include <linux/vmalloc.h> +#include <linux/nvmap.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> + +#define CREATE_TRACE_POINTS +#include <trace/events/nvmap.h> #include "nvmap.h" #include "nvmap_ioctl.h" @@ -761,6 +764,7 @@ static int nvmap_open(struct inode *inode, struct file *filp) priv = nvmap_create_client(dev, "user"); if (!priv) return -ENOMEM; + trace_nvmap_open(priv); priv->super = (filp->f_op == &nvmap_super_fops); @@ -772,6 +776,7 @@ static int nvmap_open(struct inode *inode, struct file *filp) static int nvmap_release(struct inode *inode, struct file *filp) { + trace_nvmap_release(filp->private_data); nvmap_client_put(filp->private_data); return 0; } @@ -973,20 +978,17 @@ static void client_stringify(struct nvmap_client *client, struct seq_file *s) } static void allocations_stringify(struct nvmap_client *client, - struct seq_file *s) + struct seq_file *s, bool iovmm) { - unsigned long base = 0; struct rb_node *n = rb_first(&client->handle_refs); for (; n != NULL; n = rb_next(n)) { struct nvmap_handle_ref *ref = rb_entry(n, struct nvmap_handle_ref, node); struct nvmap_handle *handle = ref->handle; - if (handle->alloc && !handle->heap_pgalloc) { - seq_printf(s, "%-18s %-18s %8lx %10u %8x\n", "", "", - (unsigned long)(handle->carveout->base), - handle->size, handle->userflags); - } else if (handle->alloc && handle->heap_pgalloc) { + if (handle->alloc && handle->heap_pgalloc == iovmm) { + unsigned long base = iovmm ? 0: + (unsigned long)(handle->carveout->base); seq_printf(s, "%-18s %-18s %8lx %10u %8x\n", "", "", base, handle->size, handle->userflags); } @@ -1010,7 +1012,7 @@ static int nvmap_debug_allocations_show(struct seq_file *s, void *unused) get_client_from_carveout_commit(node, commit); client_stringify(client, s); seq_printf(s, " %10u\n", commit->commit); - allocations_stringify(client, s); + allocations_stringify(client, s, false); seq_printf(s, "\n"); total += commit->commit; } @@ -1111,14 +1113,14 @@ static int nvmap_debug_iovmm_allocations_show(struct seq_file *s, void *unused) struct nvmap_device *dev = s->private; spin_lock_irqsave(&dev->clients_lock, flags); - seq_printf(s, "%-18s %18s %8s %10s\n", "CLIENT", "PROCESS", "PID", - "SIZE"); + seq_printf(s, "%-18s %18s %8s %10s %8s\n", "CLIENT", "PROCESS", "PID", + "SIZE", "FLAGS"); seq_printf(s, "%-18s %18s %8s %10s\n", "", "", "BASE", "SIZE"); list_for_each_entry(client, &dev->clients, list) { client_stringify(client, s); seq_printf(s, " %10u\n", atomic_read(&client->iovm_commit)); - allocations_stringify(client, s); + allocations_stringify(client, s, true); seq_printf(s, "\n"); total += atomic_read(&client->iovm_commit); } @@ -1182,13 +1184,15 @@ static int nvmap_probe(struct platform_device *pdev) init_waitqueue_head(&dev->iovmm_master.pin_wait); mutex_init(&dev->iovmm_master.pin_lock); +#ifdef CONFIG_NVMAP_PAGE_POOLS for (i = 0; i < NVMAP_NUM_POOLS; i++) nvmap_page_pool_init(&dev->iovmm_master.pools[i], i); +#endif dev->iovmm_master.iovmm = - tegra_iovmm_alloc_client(dev_name(&pdev->dev), NULL, + tegra_iovmm_alloc_client(&pdev->dev, NULL, &(dev->dev_user)); -#ifdef CONFIG_TEGRA_IOVMM +#if defined(CONFIG_TEGRA_IOVMM) || defined(CONFIG_IOMMU_API) if (!dev->iovmm_master.iovmm) { e = PTR_ERR(dev->iovmm_master.iovmm); dev_err(&pdev->dev, "couldn't create iovmm client\n"); @@ -1311,6 +1315,7 @@ static int nvmap_probe(struct platform_device *pdev) dev, &debug_iovmm_clients_fops); debugfs_create_file("allocations", 0664, iovmm_root, dev, &debug_iovmm_allocations_fops); +#ifdef CONFIG_NVMAP_PAGE_POOLS for (i = 0; i < NVMAP_NUM_POOLS; i++) { char name[40]; char *memtype_string[] = {"uc", "wc", @@ -1321,6 +1326,7 @@ static int nvmap_probe(struct platform_device *pdev) iovmm_root, &dev->iovmm_master.pools[i].npages); } +#endif } } diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c index 539b7ce9801f..05046ed8ba79 100644 --- a/drivers/video/tegra/nvmap/nvmap_handle.c +++ b/drivers/video/tegra/nvmap/nvmap_handle.c @@ -30,30 +30,21 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/fs.h> +#include <linux/shrinker.h> +#include <linux/moduleparam.h> +#include <linux/nvmap.h> #include <asm/cacheflush.h> #include <asm/outercache.h> #include <asm/pgtable.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> - -#include <linux/vmstat.h> -#include <linux/swap.h> -#include <linux/shrinker.h> -#include <linux/moduleparam.h> +#include <trace/events/nvmap.h> #include "nvmap.h" #include "nvmap_mru.h" #include "nvmap_common.h" -#define PRINT_CARVEOUT_CONVERSION 0 -#if PRINT_CARVEOUT_CONVERSION -#define PR_INFO pr_info -#else -#define PR_INFO(...) -#endif - #define NVMAP_SECURE_HEAPS (NVMAP_HEAP_CARVEOUT_IRAM | NVMAP_HEAP_IOVMM | \ NVMAP_HEAP_CARVEOUT_VPR) #ifdef CONFIG_NVMAP_HIGHMEM_ONLY @@ -66,6 +57,9 @@ * preserve kmalloc space, if the array of pages exceeds PAGELIST_VMALLOC_MIN, * the array is allocated using vmalloc. */ #define PAGELIST_VMALLOC_MIN (PAGE_SIZE * 2) + +#ifdef CONFIG_NVMAP_PAGE_POOLS + #define NVMAP_TEST_PAGE_POOL_SHRINKER 1 static bool enable_pp = 1; static int pool_size[NVMAP_NUM_POOLS]; @@ -377,6 +371,7 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) int i; static int reg = 1; struct sysinfo info; + int highmem_pages = 0; typedef int (*set_pages_array) (struct page **pages, int addrinarray); set_pages_array s_cpa[] = { set_pages_array_uc, @@ -395,14 +390,16 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) return 0; si_meminfo(&info); - if (!pool_size[flags]) { + if (!pool_size[flags] && !CONFIG_NVMAP_PAGE_POOL_SIZE) /* Use 3/8th of total ram for page pools. * 1/8th for uc, 1/8th for wc and 1/8th for iwb. */ pool->max_pages = info.totalram >> 3; - } + else + pool->max_pages = CONFIG_NVMAP_PAGE_POOL_SIZE; + if (pool->max_pages <= 0 || pool->max_pages >= info.totalram) - pool->max_pages = NVMAP_DEFAULT_PAGE_POOL_SIZE; + goto fail; pool_size[flags] = pool->max_pages; pr_info("nvmap %s page pool size=%d pages", s_memtype_str[flags], pool->max_pages); @@ -425,7 +422,14 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) __free_page(page); goto do_cpa; } + if (PageHighMem(page)) + highmem_pages++; } + si_meminfo(&info); + pr_info("nvmap pool = %s, highmem=%d, pool_size=%d," + "totalram=%lu, freeram=%lu, totalhigh=%lu, freehigh=%lu", + s_memtype_str[flags], highmem_pages, pool->max_pages, + info.totalram, info.freeram, info.totalhigh, info.freehigh); do_cpa: (*s_cpa[flags])(pool->page_array, pool->npages); nvmap_page_pool_unlock(pool); @@ -436,6 +440,7 @@ fail: vfree(pool->page_array); return -ENOMEM; } +#endif static inline void *altalloc(size_t len) { @@ -460,7 +465,9 @@ void _nvmap_handle_free(struct nvmap_handle *h) { struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); unsigned int i, nr_page, page_index = 0; +#ifdef CONFIG_NVMAP_PAGE_POOLS struct nvmap_page_pool *pool = NULL; +#endif if (nvmap_handle_remove(h->dev, h) != 0) return; @@ -481,6 +488,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) nvmap_mru_remove(share, h); +#ifdef CONFIG_NVMAP_PAGE_POOLS if (h->flags < NVMAP_NUM_POOLS) pool = &share->pools[h->flags]; @@ -490,6 +498,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) break; page_index++; } +#endif if (page_index == nr_page) goto skip_attr_restore; @@ -538,12 +547,14 @@ static int handle_page_alloc(struct nvmap_client *client, struct nvmap_handle *h, bool contiguous) { size_t size = PAGE_ALIGN(h->size); - struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); unsigned int nr_page = size >> PAGE_SHIFT; pgprot_t prot; unsigned int i = 0, page_index = 0; struct page **pages; +#ifdef CONFIG_NVMAP_PAGE_POOLS struct nvmap_page_pool *pool = NULL; + struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); +#endif pages = altalloc(nr_page * sizeof(*pages)); if (!pages) @@ -562,6 +573,7 @@ static int handle_page_alloc(struct nvmap_client *client, pages[i] = nth_page(page, i); } else { +#ifdef CONFIG_NVMAP_PAGE_POOLS if (h->flags < NVMAP_NUM_POOLS) pool = &share->pools[h->flags]; @@ -572,7 +584,7 @@ static int handle_page_alloc(struct nvmap_client *client, break; page_index++; } - +#endif for (; i < nr_page; i++) { pages[i] = nvmap_alloc_pages_exact(GFP_NVMAP, PAGE_SIZE); @@ -625,36 +637,19 @@ fail: static void alloc_handle(struct nvmap_client *client, struct nvmap_handle *h, unsigned int type) { + unsigned int carveout_mask = NVMAP_HEAP_CARVEOUT_MASK; + unsigned int iovmm_mask = NVMAP_HEAP_IOVMM; + BUG_ON(type & (type - 1)); #ifdef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM -#define __NVMAP_HEAP_CARVEOUT (NVMAP_HEAP_CARVEOUT_IRAM | NVMAP_HEAP_CARVEOUT_VPR) -#define __NVMAP_HEAP_IOVMM (NVMAP_HEAP_IOVMM | NVMAP_HEAP_CARVEOUT_GENERIC) - if (type & NVMAP_HEAP_CARVEOUT_GENERIC) { -#ifdef CONFIG_NVMAP_ALLOW_SYSMEM - if (h->size <= PAGE_SIZE) { - PR_INFO("###CARVEOUT CONVERTED TO SYSMEM " - "0x%x bytes %s(%d)###\n", - h->size, current->comm, current->pid); - goto sysheap; - } -#endif - PR_INFO("###CARVEOUT CONVERTED TO IOVM " - "0x%x bytes %s(%d)###\n", - h->size, current->comm, current->pid); - } -#else -#define __NVMAP_HEAP_CARVEOUT NVMAP_HEAP_CARVEOUT_MASK -#define __NVMAP_HEAP_IOVMM NVMAP_HEAP_IOVMM + /* Convert generic carveout requests to iovmm requests. */ + carveout_mask &= ~NVMAP_HEAP_CARVEOUT_GENERIC; + iovmm_mask |= NVMAP_HEAP_CARVEOUT_GENERIC; #endif - if (type & __NVMAP_HEAP_CARVEOUT) { + if (type & carveout_mask) { struct nvmap_heap_block *b; -#ifdef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM - PR_INFO("###IRAM REQUEST RETAINED " - "0x%x bytes %s(%d)###\n", - h->size, current->comm, current->pid); -#endif /* Protect handle from relocation */ nvmap_usecount_inc(h); @@ -668,7 +663,7 @@ static void alloc_handle(struct nvmap_client *client, } nvmap_usecount_dec(h); - } else if (type & __NVMAP_HEAP_IOVMM) { + } else if (type & iovmm_mask) { size_t reserved = PAGE_ALIGN(h->size); int commit = 0; int ret; @@ -692,10 +687,6 @@ static void alloc_handle(struct nvmap_client *client, } } else if (type & NVMAP_HEAP_SYSMEM) { -#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM) && \ - defined(CONFIG_NVMAP_ALLOW_SYSMEM) -sysheap: -#endif if (handle_page_alloc(client, h, true) == 0) { BUG_ON(!h->pgalloc.contig); h->heap_pgalloc = true; @@ -730,10 +721,6 @@ static const unsigned int heap_policy_large[] = { 0, }; -/* Do not override single page policy if there is not much space to -avoid invoking system oom killer. */ -#define NVMAP_SMALL_POLICY_SYSMEM_THRESHOLD 50000000 - int nvmap_alloc_handle_id(struct nvmap_client *client, unsigned long id, unsigned int heap_mask, size_t align, unsigned int flags) @@ -751,6 +738,7 @@ int nvmap_alloc_handle_id(struct nvmap_client *client, if (h->alloc) goto out; + trace_nvmap_alloc_handle_id(client, id, heap_mask, align, flags); h->userflags = flags; nr_page = ((h->size + PAGE_SIZE - 1) >> PAGE_SHIFT); h->secure = !!(flags & NVMAP_HANDLE_SECURE); @@ -758,32 +746,22 @@ int nvmap_alloc_handle_id(struct nvmap_client *client, h->align = max_t(size_t, align, L1_CACHE_BYTES); #ifndef CONFIG_TEGRA_IOVMM + /* convert iovmm requests to generic carveout. */ if (heap_mask & NVMAP_HEAP_IOVMM) { - heap_mask &= NVMAP_HEAP_IOVMM; - heap_mask |= NVMAP_HEAP_CARVEOUT_GENERIC; + heap_mask = (heap_mask & ~NVMAP_HEAP_IOVMM) | + NVMAP_HEAP_CARVEOUT_GENERIC; } #endif -#ifndef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM #ifdef CONFIG_NVMAP_ALLOW_SYSMEM /* Allow single pages allocations in system memory to save * carveout space and avoid extra iovm mappings */ if (nr_page == 1) { - if (heap_mask & NVMAP_HEAP_IOVMM) + if (heap_mask & + (NVMAP_HEAP_IOVMM | NVMAP_HEAP_CARVEOUT_GENERIC)) heap_mask |= NVMAP_HEAP_SYSMEM; - else if (heap_mask & NVMAP_HEAP_CARVEOUT_GENERIC) { - /* Calculate size of free physical pages - * managed by kernel */ - unsigned long freeMem = - (global_page_state(NR_FREE_PAGES) + - global_page_state(NR_FILE_PAGES) - - total_swapcache_pages) << PAGE_SHIFT; - - if (freeMem > NVMAP_SMALL_POLICY_SYSMEM_THRESHOLD) - heap_mask |= NVMAP_HEAP_SYSMEM; - } } #endif - +#ifndef CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM /* This restriction is deprecated as alignments greater than PAGE_SIZE are now correctly handled, but it is retained for AP20 compatibility. */ @@ -843,6 +821,7 @@ void nvmap_free_handle_id(struct nvmap_client *client, unsigned long id) return; } + trace_nvmap_free_handle_id(client, id); BUG_ON(!ref->handle); h = ref->handle; @@ -943,6 +922,7 @@ struct nvmap_handle_ref *nvmap_create_handle(struct nvmap_client *client, ref->handle = h; atomic_set(&ref->pin, 0); add_handle_ref(client, ref); + trace_nvmap_create_handle(client, h, size, ref); return ref; } @@ -1016,5 +996,6 @@ struct nvmap_handle_ref *nvmap_duplicate_handle_id(struct nvmap_client *client, ref->handle = h; atomic_set(&ref->pin, 0); add_handle_ref(client, ref); + trace_nvmap_duplicate_handle_id(client, id, ref); return ref; } diff --git a/drivers/video/tegra/nvmap/nvmap_heap.c b/drivers/video/tegra/nvmap/nvmap_heap.c index 7474f31534ff..a6fe78c42f87 100644 --- a/drivers/video/tegra/nvmap/nvmap_heap.c +++ b/drivers/video/tegra/nvmap/nvmap_heap.c @@ -28,7 +28,7 @@ #include <linux/slab.h> #include <linux/err.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap.h" #include "nvmap_heap.h" #include "nvmap_common.h" diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.c b/drivers/video/tegra/nvmap/nvmap_ioctl.c index 58bc71d50469..44f00d2951a0 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.c +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.c @@ -25,13 +25,14 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/nvmap.h> #include <asm/cacheflush.h> #include <asm/outercache.h> #include <asm/tlbflush.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> +#include <trace/events/nvmap.h> #include "nvmap_ioctl.h" #include "nvmap.h" @@ -83,6 +84,7 @@ int nvmap_ioctl_pinop(struct file *filp, bool is_pin, void __user *arg) on_stack[0] = (unsigned long)op.handles; } + trace_nvmap_ioctl_pinop(filp->private_data, is_pin, op.count, refs); if (is_pin) err = nvmap_pin_ids(filp->private_data, op.count, refs); else @@ -234,6 +236,8 @@ int nvmap_map_into_caller_ptr(struct file *filp, void __user *arg) if (!h) return -EPERM; + trace_nvmap_map_into_caller_ptr(client, h, op.offset, + op.length, op.flags); down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, op.addr); @@ -409,6 +413,9 @@ int nvmap_ioctl_rw_handle(struct file *filp, int is_read, void __user* arg) nvmap_usecount_inc(h); + trace_nvmap_ioctl_rw_handle(client, h, is_read, op.offset, + op.addr, op.hmem_stride, + op.user_stride, op.elem_size, op.count); copied = rw_handle(client, h, is_read, op.offset, (unsigned long)op.addr, op.hmem_stride, op.user_stride, op.elem_size, op.count); @@ -540,6 +547,7 @@ static bool fast_cache_maint(struct nvmap_client *client, struct nvmap_handle *h { int ret = false; +#if defined(CONFIG_NVMAP_CACHE_MAINT_BY_SET_WAYS) if ((op == NVMAP_CACHE_OP_INV) || ((end - start) < FLUSH_CLEAN_BY_SET_WAY_THRESHOLD)) goto out; @@ -559,6 +567,7 @@ static bool fast_cache_maint(struct nvmap_client *client, struct nvmap_handle *h } ret = true; out: +#endif return ret; } @@ -580,6 +589,7 @@ static int cache_maint(struct nvmap_client *client, struct nvmap_handle *h, goto out; } + trace_cache_maint(client, h, start, end, op); wmb(); if (h->flags == NVMAP_HANDLE_UNCACHEABLE || h->flags == NVMAP_HANDLE_WRITE_COMBINE || start == end) diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.h b/drivers/video/tegra/nvmap/nvmap_ioctl.h index 54627683ccdb..300ce9b9a6ea 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.h +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.h @@ -27,7 +27,7 @@ #ifdef __KERNEL__ #include <linux/file.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #endif enum { diff --git a/drivers/video/tegra/nvmap/nvmap_iommu.c b/drivers/video/tegra/nvmap/nvmap_iommu.c new file mode 100644 index 000000000000..ca868032db3f --- /dev/null +++ b/drivers/video/tegra/nvmap/nvmap_iommu.c @@ -0,0 +1,97 @@ +/* + * IOMMU backend support for NVMAP + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/slab.h> +#include <mach/iomap.h> +#include <mach/iovmm.h> + +#include "nvmap.h" + +struct tegra_iovmm_area *tegra_iommu_create_vm(struct device *dev, + dma_addr_t req, size_t size, pgprot_t prot) +{ + struct tegra_iovmm_area *area; + dma_addr_t iova; + + area = kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + + if (!req) + req = DMA_ANON_ADDR; + + iova = arm_iommu_alloc_iova_at(dev, req, size); + if (iova == DMA_ERROR_CODE) + goto err_out; + area->iovm_start = iova; + area->iovm_length = size; + area->pgprot = prot; + area->dev = dev; + return area; + +err_out: + kfree(area); + return NULL; +} + +void tegra_iommu_free_vm(struct tegra_iovmm_area *area) +{ + int i; + size_t count = area->iovm_length >> PAGE_SHIFT; + + for (i = 0; i < count; i++) { + dma_addr_t iova; + + iova = area->iovm_start + i * PAGE_SIZE; + dma_unmap_page(area->dev, iova, PAGE_SIZE, DMA_NONE); + } + kfree(area); +} + +struct tegra_iovmm_client *tegra_iommu_alloc_client(struct device *dev) +{ + struct dma_iommu_mapping *map; + struct tegra_iovmm_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + map = arm_iommu_create_mapping(&platform_bus_type, + TEGRA_IOMMU_BASE, TEGRA_IOMMU_SIZE, 0); + if (IS_ERR(map)) + goto err_map; + + if (arm_iommu_attach_device(dev, map)) + goto err_attach; + client->dev = dev; + return client; + +err_attach: + arm_iommu_release_mapping(map); +err_map: + kfree(client); + return NULL; +} + +void tegra_iommu_free_client(struct tegra_iovmm_client *client) +{ + arm_iommu_release_mapping(client->dev->archdata.mapping); + kfree(client); +} diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index 4fa31b25a361..0d8373efd3f1 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -3,7 +3,7 @@ * * watchdog driver for NVIDIA tegra internal watchdog * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2012, NVIDIA Corporation. * * based on drivers/watchdog/softdog.c and drivers/watchdog/omap_wdt.c * @@ -22,6 +22,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <linux/kernel.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -34,15 +35,20 @@ #include <linux/spinlock.h> #include <linux/uaccess.h> #include <linux/watchdog.h> +#ifdef CONFIG_TEGRA_FIQ_DEBUGGER +#include <mach/irqs.h> +#endif /* minimum and maximum watchdog trigger periods, in seconds */ #define MIN_WDT_PERIOD 5 #define MAX_WDT_PERIOD 1000 +/* Assign Timer 7 to Timer 10 for WDT0 to WDT3, respectively */ +#define TMR_SRC_START 7 enum tegra_wdt_status { WDT_DISABLED = 1 << 0, WDT_ENABLED = 1 << 1, - WDT_IOCTL_ENABBLED_AT_PROBE = 1 << 2, + WDT_ENABLED_AT_PROBE = 1 << 2, }; struct tegra_wdt { @@ -50,15 +56,17 @@ struct tegra_wdt { struct notifier_block notifier; struct resource *res_src; struct resource *res_wdt; + struct resource *res_int_base; unsigned long users; void __iomem *wdt_source; void __iomem *wdt_timer; + void __iomem *int_base; int irq; + int tmrsrc; int timeout; int status; }; -static struct platform_device *tegra_wdt_dev; /* * For spinlock lockup detection to work, the heartbeat should be 2*lockup * for cases where the spinlock disabled irqs. @@ -117,39 +125,41 @@ static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id) #define TIMER_PCR 0x4 #define TIMER_PCR_INTR (1 << 30) #define WDT_CFG (0) - #define WDT_CFG_TMR_SRC (0 << 0) /* for TMR10. */ #define WDT_CFG_PERIOD (1 << 4) #define WDT_CFG_INT_EN (1 << 12) + #define WDT_CFG_FIQ_INT_EN (1 << 13) #define WDT_CFG_SYS_RST_EN (1 << 14) #define WDT_CFG_PMC2CAR_RST_EN (1 << 15) +#define WDT_STATUS (4) + #define WDT_INTR_STAT (1 << 1) #define WDT_CMD (8) #define WDT_CMD_START_COUNTER (1 << 0) #define WDT_CMD_DISABLE_COUNTER (1 << 1) #define WDT_UNLOCK (0xC) #define WDT_UNLOCK_PATTERN (0xC45A << 0) +#define ICTLR_IEP_CLASS 0x2C +#define MAX_NR_CPU_WDT 0x4 -static void tegra_wdt_set_timeout(struct tegra_wdt *wdt, int sec) -{ - u32 ptv; - - ptv = readl(wdt->wdt_timer + TIMER_PTV); - - wdt->timeout = clamp(sec, MIN_WDT_PERIOD, MAX_WDT_PERIOD); - if (ptv & TIMER_EN) { - /* since the watchdog reset occurs when a fourth interrupt - * is asserted before the first is processed, program the - * timer period to one-fourth of the watchdog period */ - ptv = (wdt->timeout * 1000000ul) / 4; - ptv |= (TIMER_EN | TIMER_PERIODIC); - writel(ptv, wdt->wdt_timer + TIMER_PTV); - } -} +struct tegra_wdt *tegra_wdt[MAX_NR_CPU_WDT]; static inline void tegra_wdt_ping(struct tegra_wdt *wdt) { writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD); } +#ifdef CONFIG_TEGRA_FIQ_DEBUGGER +static void tegra_wdt_int_priority(struct tegra_wdt *wdt) +{ + unsigned val = 0; + + if (!wdt->int_base) + return; + val = readl(wdt->int_base + ICTLR_IEP_CLASS); + val &= ~(1 << (INT_WDT_CPU & 31)); + writel(val, wdt->int_base + ICTLR_IEP_CLASS); +} +#endif + static void tegra_wdt_enable(struct tegra_wdt *wdt) { u32 val; @@ -159,8 +169,17 @@ static void tegra_wdt_enable(struct tegra_wdt *wdt) val |= (TIMER_EN | TIMER_PERIODIC); writel(val, wdt->wdt_timer + TIMER_PTV); - val = WDT_CFG_TMR_SRC | WDT_CFG_PERIOD | /*WDT_CFG_INT_EN |*/ + /* Interrupt handler is not required for user space + * WDT accesses, since the caller is responsible to ping the + * WDT to reset the counter before expiration, through ioctls. + * SYS_RST_EN doesnt work as there is no external reset + * from Tegra. + */ + val = wdt->tmrsrc | WDT_CFG_PERIOD | /*WDT_CFG_INT_EN |*/ /*WDT_CFG_SYS_RST_EN |*/ WDT_CFG_PMC2CAR_RST_EN; +#ifdef CONFIG_TEGRA_FIQ_DEBUGGER + val |= WDT_CFG_FIQ_INT_EN; +#endif writel(val, wdt->wdt_source + WDT_CFG); writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD); } @@ -175,9 +194,17 @@ static void tegra_wdt_disable(struct tegra_wdt *wdt) static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id) { - struct tegra_wdt *wdt = dev_id; + unsigned i, status; + + for (i = 0; i < MAX_NR_CPU_WDT; i++) { + if (tegra_wdt[i] == NULL) + continue; + status = readl(tegra_wdt[i]->wdt_source + WDT_STATUS); + if ((tegra_wdt[i]->status & WDT_ENABLED) && + (status & WDT_INTR_STAT)) + tegra_wdt_ping(tegra_wdt[i]); + } - tegra_wdt_ping(wdt); return IRQ_HANDLED; } #endif @@ -194,7 +221,9 @@ static int tegra_wdt_notify(struct notifier_block *this, static int tegra_wdt_open(struct inode *inode, struct file *file) { - struct tegra_wdt *wdt = platform_get_drvdata(tegra_wdt_dev); + struct miscdevice *mdev = file->private_data; + struct tegra_wdt *wdt = container_of(mdev, struct tegra_wdt, + miscdev); if (test_and_set_bit(1, &wdt->users)) return -EBUSY; @@ -301,30 +330,39 @@ static const struct file_operations tegra_wdt_fops = { static int tegra_wdt_probe(struct platform_device *pdev) { - struct resource *res_src, *res_wdt, *res_irq; + struct resource *res_src, *res_wdt, *res_irq, *res_int_base; struct tegra_wdt *wdt; u32 src; int ret = 0; + u32 val = 0; - if (pdev->id != -1) { - dev_err(&pdev->dev, "only id -1 supported\n"); + if (pdev->id < -1 && pdev->id > 3) { + dev_err(&pdev->dev, "only IDs 3:0 supported\n"); return -ENODEV; } - if (tegra_wdt_dev != NULL) { - dev_err(&pdev->dev, "watchdog already registered\n"); - return -EIO; - } - res_src = platform_get_resource(pdev, IORESOURCE_MEM, 0); res_wdt = platform_get_resource(pdev, IORESOURCE_MEM, 1); res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res_src || !res_wdt || !res_irq) { + if (!res_src || !res_wdt || (!pdev->id && !res_irq)) { dev_err(&pdev->dev, "incorrect resources\n"); return -ENOENT; } +#ifdef CONFIG_TEGRA_FIQ_DEBUGGER + res_int_base = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!pdev->id && !res_int_base) { + dev_err(&pdev->dev, "FIQ_DBG: INT base not defined\n"); + return -ENOENT; + } +#endif + + if (pdev->id == -1 && !res_irq) { + dev_err(&pdev->dev, "incorrect irq\n"); + return -ENOENT; + } + wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); if (!wdt) { dev_err(&pdev->dev, "out of memory\n"); @@ -333,8 +371,20 @@ static int tegra_wdt_probe(struct platform_device *pdev) wdt->irq = -1; wdt->miscdev.parent = &pdev->dev; - wdt->miscdev.minor = WATCHDOG_MINOR; - wdt->miscdev.name = "watchdog"; + if (pdev->id == -1) { + wdt->miscdev.minor = WATCHDOG_MINOR; + wdt->miscdev.name = "watchdog"; + } else { + wdt->miscdev.minor = MISC_DYNAMIC_MINOR; + if (pdev->id == 0) + wdt->miscdev.name = "watchdog0"; + else if (pdev->id == 1) + wdt->miscdev.name = "watchdog1"; + else if (pdev->id == 2) + wdt->miscdev.name = "watchdog2"; + else if (pdev->id == 3) + wdt->miscdev.name = "watchdog3"; + } wdt->miscdev.fops = &tegra_wdt_fops; wdt->notifier.notifier_call = tegra_wdt_notify; @@ -352,6 +402,8 @@ static int tegra_wdt_probe(struct platform_device *pdev) wdt->wdt_source = ioremap(res_src->start, resource_size(res_src)); wdt->wdt_timer = ioremap(res_wdt->start, resource_size(res_wdt)); + /* tmrsrc will be used to set WDT_CFG */ + wdt->tmrsrc = (TMR_SRC_START + pdev->id) % 10; if (!wdt->wdt_source || !wdt->wdt_timer) { dev_err(&pdev->dev, "unable to map registers\n"); ret = -ENOMEM; @@ -365,16 +417,38 @@ static int tegra_wdt_probe(struct platform_device *pdev) tegra_wdt_disable(wdt); writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR); - ret = request_irq(res_irq->start, tegra_wdt_interrupt, IRQF_DISABLED, - dev_name(&pdev->dev), wdt); - if (ret) { - dev_err(&pdev->dev, "unable to configure IRQ\n"); - goto fail; + if (res_irq != NULL) { +#ifdef CONFIG_TEGRA_FIQ_DEBUGGER + /* FIQ debugger enables FIQ priority for INT_WDT_CPU. + * But that will disable IRQ on WDT expiration. + * Reset the priority back to IRQ on INT_WDT_CPU so + * that tegra_wdt_interrupt gets its chance to restart the + * counter before expiration. + */ + res_int_base = request_mem_region(res_int_base->start, + resource_size(res_int_base), + pdev->name); + if (!res_int_base) + goto fail; + wdt->int_base = ioremap(res_int_base->start, + resource_size(res_int_base)); + if (!wdt->int_base) + goto fail; + tegra_wdt_int_priority(wdt); +#endif + ret = request_irq(res_irq->start, tegra_wdt_interrupt, + IRQF_DISABLED, dev_name(&pdev->dev), wdt); + if (ret) { + dev_err(&pdev->dev, "unable to configure IRQ\n"); + goto fail; + } + wdt->irq = res_irq->start; } - wdt->irq = res_irq->start; wdt->res_src = res_src; wdt->res_wdt = res_wdt; + wdt->res_int_base = res_int_base; + wdt->status = WDT_DISABLED; ret = register_reboot_notifier(&wdt->notifier); if (ret) { @@ -390,14 +464,23 @@ static int tegra_wdt_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, wdt); - tegra_wdt_dev = pdev; + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC #ifdef CONFIG_TEGRA_WATCHDOG_ENABLE_ON_PROBE - wdt->status = WDT_ENABLED | WDT_ENABLED_AT_PROBE; - wdt->timeout = heartbeat; - tegra_wdt_enable(wdt); -#else - wdt->status = WDT_DISABLED; + /* Init and enable watchdog on WDT0 with timer 8 during probe */ + if (!(pdev->id)) { + wdt->status = WDT_ENABLED | WDT_ENABLED_AT_PROBE; + wdt->timeout = heartbeat; + tegra_wdt_enable(wdt); + val = readl(wdt->wdt_source + WDT_CFG); + val |= WDT_CFG_INT_EN; + writel(val, wdt->wdt_source + WDT_CFG); + pr_info("WDT heartbeat enabled on probe\n"); + } +#endif + tegra_wdt[pdev->id] = wdt; #endif + pr_info("%s done\n", __func__); return 0; fail: if (wdt->irq != -1) @@ -406,10 +489,15 @@ fail: iounmap(wdt->wdt_source); if (wdt->wdt_timer) iounmap(wdt->wdt_timer); + if (wdt->int_base) + iounmap(wdt->int_base); if (res_src) release_mem_region(res_src->start, resource_size(res_src)); if (res_wdt) release_mem_region(res_wdt->start, resource_size(res_wdt)); + if (res_int_base) + release_mem_region(res_int_base->start, + resource_size(res_int_base)); kfree(wdt); return ret; } @@ -422,11 +510,17 @@ static int tegra_wdt_remove(struct platform_device *pdev) unregister_reboot_notifier(&wdt->notifier); misc_deregister(&wdt->miscdev); - free_irq(wdt->irq, wdt); + if (wdt->irq != -1) + free_irq(wdt->irq, wdt); iounmap(wdt->wdt_source); iounmap(wdt->wdt_timer); + if (wdt->int_base) + iounmap(wdt->int_base); release_mem_region(wdt->res_src->start, resource_size(wdt->res_src)); release_mem_region(wdt->res_wdt->start, resource_size(wdt->res_wdt)); + if (wdt->res_int_base) + release_mem_region(wdt->res_int_base->start, + resource_size(wdt->res_int_base)); kfree(wdt); platform_set_drvdata(pdev, NULL); return 0; @@ -448,6 +542,16 @@ static int tegra_wdt_resume(struct platform_device *pdev) if (wdt->status & WDT_ENABLED) tegra_wdt_enable(wdt); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* Enable interrupt for WDT3 heartbeat watchdog */ + if (wdt->status & WDT_ENABLED_AT_PROBE) { + u32 val = 0; + val = readl(wdt->wdt_source + WDT_CFG); + val |= WDT_CFG_INT_EN; + writel(val, wdt->wdt_source + WDT_CFG); + pr_info("WDT heartbeat enabled on probe\n"); + } +#endif return 0; } #endif @@ -488,4 +592,3 @@ MODULE_PARM_DESC(heartbeat, MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS("platform:tegra_wdt"); - |