diff options
author | Nitin Kumbhar <nkumbhar@nvidia.com> | 2011-01-18 14:52:31 +0530 |
---|---|---|
committer | Nitin Kumbhar <nkumbhar@nvidia.com> | 2011-01-18 14:52:31 +0530 |
commit | 754deb500cac53edcff2165ca4edb8c8be896d2c (patch) | |
tree | 71356f7bf39e87b5d261ab69c159cb8a114831eb /drivers | |
parent | 66e0ed154cfb916b83e3f0074f7cd197effaab39 (diff) | |
parent | 5edc1199f320e0d9226fe8b73711c6a171ce78de (diff) |
merging android-tegra-2.6.36 into git-master/linux-2.6/android-tegra-2.6.36
Conflicts:
drivers/net/wireless/bcm4329/Makefile
Change-Id: I31ce81e09c6f18d6966a5cffebc533453bce02d8
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/crypto/tegra-aes.c | 92 | ||||
-rw-r--r-- | drivers/net/wireless/bcm4329/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/bcm4329/dhd_sdio.c | 60 | ||||
-rw-r--r-- | drivers/net/wireless/bcm4329/include/epivers.h | 10 | ||||
-rwxr-xr-x | drivers/rtc/alarm.c | 11 | ||||
-rw-r--r-- | drivers/usb/gadget/f_mtp.c | 6 | ||||
-rw-r--r-- | drivers/video/tegra/Kconfig | 9 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 70 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 3 | ||||
-rw-r--r-- | drivers/video/tegra/fb.c | 44 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_acm.c | 20 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_acm.h | 2 | ||||
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap_dev.c | 160 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 6 | ||||
-rw-r--r-- | drivers/watchdog/tegra_wdt.c | 10 |
15 files changed, 375 insertions, 130 deletions
diff --git a/drivers/crypto/tegra-aes.c b/drivers/crypto/tegra-aes.c index d94046a4bb95..f7dd72964f0c 100644 --- a/drivers/crypto/tegra-aes.c +++ b/drivers/crypto/tegra-aes.c @@ -32,7 +32,6 @@ #include <linux/mutex.h> #include <linux/interrupt.h> #include <linux/completion.h> -#include <linux/delay.h> #include <linux/workqueue.h> #include <mach/arb_sema.h> @@ -198,7 +197,8 @@ static DEFINE_SPINLOCK(list_lock); static DEFINE_MUTEX(aes_lock); static void aes_workqueue_handler(struct work_struct *work); -static DECLARE_WORK(aes_wq, aes_workqueue_handler); +static DECLARE_WORK(aes_work, aes_workqueue_handler); +static struct workqueue_struct *aes_wq; extern unsigned long long tegra_chip_uid(void); @@ -222,11 +222,6 @@ static int aes_hw_init(struct tegra_aes_dev *dd) return ret; } - tegra_periph_reset_assert(dd->iclk); - udelay(50); - tegra_periph_reset_deassert(dd->iclk); - udelay(50); - ret = clk_enable(dd->iclk); if (ret < 0) { dev_err(dd->dev, "%s: iclock enable fail(%d)\n", __func__, ret); @@ -234,10 +229,24 @@ static int aes_hw_init(struct tegra_aes_dev *dd) return ret; } + ret = clk_set_rate(dd->iclk, 240000000); + if (ret) { + dev_err(dd->dev, "%s: iclk set_rate fail(%d)\n", __func__, ret); + clk_disable(dd->iclk); + clk_disable(dd->pclk); + return ret; + } + aes_writel(dd, 0x33, INT_ENB); return ret; } +static void aes_hw_deinit(struct tegra_aes_dev *dd) +{ + clk_disable(dd->iclk); + clk_disable(dd->pclk); +} + static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr, int nblocks, int mode, bool upd_iv) { @@ -245,12 +254,6 @@ static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr, int qlen = 0, i, eng_busy, icq_empty, dma_busy, ret = 0; u32 value; - ret = aes_hw_init(dd); - if (ret < 0) { - dev_err(dd->dev, "%s: hw init fail(%d)\n", __func__, ret); - return ret; - } - cmdq[qlen++] = UCQOPCODE_DMASETUP << ICQBITSHIFT_OPCODE; cmdq[qlen++] = in_addr; cmdq[qlen++] = UCQOPCODE_BLKSTARTENGINE << ICQBITSHIFT_OPCODE | @@ -314,6 +317,7 @@ static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr, aes_writel(dd, value, SECURE_INPUT_SELECT); aes_writel(dd, out_addr, SECURE_DEST_ADDR); + INIT_COMPLETION(dd->op_complete); for (i = 0; i < qlen - 1; i++) { do { @@ -325,13 +329,10 @@ static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr, aes_writel(dd, cmdq[i], ICMDQUE_WR); } - INIT_COMPLETION(dd->op_complete); ret = wait_for_completion_timeout(&dd->op_complete, msecs_to_jiffies(150)); if (ret == 0) { dev_err(dd->dev, "timed out (0x%x)\n", aes_readl(dd, INTR_STATUS)); - clk_disable(dd->iclk); - clk_disable(dd->pclk); return -ETIMEDOUT; } @@ -343,8 +344,6 @@ static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr, dma_busy = value & (0x1<<23); } while (eng_busy & (!icq_empty) & dma_busy); - clk_disable(dd->iclk); - clk_disable(dd->pclk); return 0; } @@ -379,7 +378,7 @@ static int aes_set_key(struct tegra_aes_dev *dd) { u32 value, cmdq[2]; struct tegra_aes_ctx *ctx = dd->ctx; - int i, eng_busy, icq_empty, dma_busy, ret = 0; + int i, eng_busy, icq_empty, dma_busy; bool use_ssk = false; if (!ctx) { @@ -394,12 +393,6 @@ static int aes_set_key(struct tegra_aes_dev *dd) use_ssk = true; } - ret = aes_hw_init(dd); - if (ret < 0) { - dev_err(dd->dev, "%s: hw init fail(%d)\n", __func__, ret); - return ret; - } - /* disable key read from hw */ value = aes_readl(dd, SECURE_SEC_SEL0+(ctx->slot->slot_num*4)); value &= ~SECURE_SEL0_KEYREAD_ENB0_FIELD; @@ -451,8 +444,6 @@ static int aes_set_key(struct tegra_aes_dev *dd) } while (eng_busy & (!icq_empty)); out: - clk_disable(dd->iclk); - clk_disable(dd->pclk); return 0; } @@ -536,6 +527,12 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd) return -EBUSY; } + ret = aes_hw_init(dd); + if (ret < 0) { + dev_err(dd->dev, "%s: hw init fail(%d)\n", __func__, ret); + goto fail; + } + aes_set_key(dd); /* set iv to the aes hw slot */ @@ -599,6 +596,9 @@ static int tegra_aes_handle_req(struct tegra_aes_dev *dd) } out: + aes_hw_deinit(dd); + +fail: /* release the hardware semaphore */ tegra_arb_mutex_unlock(dd->res_id); @@ -703,7 +703,7 @@ static int tegra_aes_crypt(struct ablkcipher_request *req, unsigned long mode) spin_unlock_irqrestore(&dd->lock, flags); if (!busy) - schedule_work(&aes_wq); + queue_work(aes_wq, &aes_work); return err; } @@ -746,6 +746,13 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata, return -EBUSY; } + ret = aes_hw_init(dd); + if (ret < 0) { + dev_err(dd->dev, "%s: hw init fail(%d)\n", __func__, ret); + dlen = ret; + goto fail; + } + ctx->dd = dd; dd->ctx = ctx; dd->flags = FLAGS_ENCRYPT | FLAGS_RNG; @@ -770,6 +777,9 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata, } out: + aes_hw_deinit(dd); + +fail: /* release the hardware semaphore */ tegra_arb_mutex_unlock(dd->res_id); mutex_unlock(&aes_lock); @@ -835,6 +845,12 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed, return -EBUSY; } + ret = aes_hw_init(dd); + if (ret < 0) { + dev_err(dd->dev, "%s: hw init fail(%d)\n", __func__, ret); + goto fail; + } + aes_set_key(dd); /* set seed to the aes hw slot */ @@ -862,6 +878,9 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed, memcpy(dd->dt, dt, DEFAULT_RNG_BLK_SZ); out: + aes_hw_deinit(dd); + +fail: /* release the hardware semaphore */ tegra_arb_mutex_unlock(dd->res_id); mutex_unlock(&aes_lock); @@ -879,7 +898,7 @@ static int tegra_aes_cra_init(struct crypto_tfm *tfm) static struct crypto_alg algs[] = { { - .cra_name = "ecb(aes)", + .cra_name = "disabled_ecb(aes)", .cra_driver_name = "ecb-aes-tegra", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, @@ -897,7 +916,7 @@ static struct crypto_alg algs[] = { .decrypt = tegra_aes_ecb_decrypt, }, }, { - .cra_name = "cbc(aes)", + .cra_name = "disabled_cbc(aes)", .cra_driver_name = "cbc-aes-tegra", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, @@ -916,7 +935,7 @@ static struct crypto_alg algs[] = { .decrypt = tegra_aes_cbc_decrypt, } }, { - .cra_name = "ansi_cprng", + .cra_name = "disabled_ansi_cprng", .cra_driver_name = "rng-aes-tegra", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_RNG, @@ -1024,6 +1043,11 @@ static int tegra_aes_probe(struct platform_device *pdev) } init_completion(&dd->op_complete); + aes_wq = alloc_workqueue("aes_wq", WQ_HIGHPRI, 16); + if (!aes_wq) { + dev_err(dev, "alloc_workqueue failed\n"); + goto out; + } /* get the irq */ err = request_irq(INT_VDE_BSE_V, aes_irq, IRQF_TRIGGER_HIGH, @@ -1072,7 +1096,8 @@ out: clk_put(dd->iclk); if (dd->pclk) clk_put(dd->pclk); - + if (aes_wq) + destroy_workqueue(aes_wq); free_irq(INT_VDE_BSE_V, dd); spin_lock(&list_lock); list_del(&dev_list); @@ -1094,7 +1119,8 @@ static int __devexit tegra_aes_remove(struct platform_device *pdev) if (!dd) return -ENODEV; - cancel_work_sync(&aes_wq); + cancel_work_sync(&aes_work); + destroy_workqueue(aes_wq); free_irq(INT_VDE_BSE_V, dd); spin_lock(&list_lock); list_del(&dev_list); diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index 20bdab258433..bffe59160c54 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -6,7 +6,7 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -Wall -Wstrict-prototypes -Werror -DCUSTOMER_HW2 -DMMC_SDIO_ABORT \ -DDHD_DEBUG_TRAP -DSOFTAP -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT \ -DPKT_FILTER_SUPPORT -DSET_RANDOM_MAC_SOFTAP -DCSCAN \ - -DKEEP_ALIVE -DCONFIG_US_NON_DFS_CHANNELS_ONLY \ + -DKEEP_ALIVE \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include ifeq ($(CONFIG_BCM4329_WIFI_CONTROL_FUNC),y) diff --git a/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c index 8915857cf2c8..1742deaf74b0 100644 --- a/drivers/net/wireless/bcm4329/dhd_sdio.c +++ b/drivers/net/wireless/bcm4329/dhd_sdio.c @@ -430,7 +430,6 @@ static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start); #ifdef DHD_DEBUG_TRAP static int dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size); -static int dhdsdio_mem_dump(dhd_bus_t *bus); #endif /* DHD_DEBUG_TRAP */ static int dhdsdio_download_state(dhd_bus_t *bus, bool enter); @@ -1847,11 +1846,6 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf)); } - if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { - /* Mem dump to a file on device */ - dhdsdio_mem_dump(bus); - } - done: if (mbuffer) MFREE(bus->dhd->osh, mbuffer, msize); @@ -1860,60 +1854,6 @@ done: return bcmerror; } - -static int -dhdsdio_mem_dump(dhd_bus_t *bus) -{ - int ret = 0; - int size; /* Full mem size */ - int start = 0; /* Start address */ - int read_size = 0; /* Read size of each iteration */ - uint8 *buf = NULL, *databuf = NULL; - - /* Get full mem size */ - size = bus->ramsize; - buf = MALLOC(bus->dhd->osh, size); - if (!buf) { - printf("%s: Out of memory (%d bytes)\n", __FUNCTION__, size); - return -1; - } - - /* Read mem content */ - printf("Dump dongle memory"); - databuf = buf; - while (size) - { - read_size = MIN(MEMBLOCK, size); - if ((ret = dhdsdio_membytes(bus, FALSE, start, databuf, read_size))) - { - printf("%s: Error membytes %d\n", __FUNCTION__, ret); - if (buf) { - MFREE(bus->dhd->osh, buf, size); - } - return -1; - } - printf("."); - - /* Decrement size and increment start address */ - size -= read_size; - start += read_size; - databuf += read_size; - } - printf("Done\n"); - -#ifdef DHD_DEBUG - /* free buf before return !!! */ - if (write_to_file(bus->dhd, buf, bus->ramsize)) - { - printf("%s: Error writing to files\n", __FUNCTION__); - return -1; - } - /* buf free handled in write_to_file, not here */ -#else - MFREE(bus->dhd->osh, buf, size); -#endif - return 0; -} #endif /* DHD_DEBUG_TRAP */ #ifdef DHD_DEBUG diff --git a/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h index 23ee514a7b6b..00e3cac14dc5 100644 --- a/drivers/net/wireless/bcm4329/include/epivers.h +++ b/drivers/net/wireless/bcm4329/include/epivers.h @@ -33,16 +33,16 @@ #define EPI_RC_NUMBER 248 -#define EPI_INCREMENTAL_NUMBER 18 +#define EPI_INCREMENTAL_NUMBER 20 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 4, 218, 248, 18 +#define EPI_VERSION 4, 218, 248, 20 -#define EPI_VERSION_NUM 0x04daf812 +#define EPI_VERSION_NUM 0x04daf814 -#define EPI_VERSION_STR "4.218.248.18" -#define EPI_ROUTER_VERSION_STR "4.219.248.18" +#define EPI_VERSION_STR "4.218.248.20" +#define EPI_ROUTER_VERSION_STR "4.219.248.20" #endif diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index 45be9101a1d8..abefa27a6745 100755 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -109,12 +109,15 @@ static void alarm_enqueue_locked(struct alarm *alarm) struct rb_node *parent = NULL; struct alarm *entry; int leftmost = 1; + bool was_first = false; pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n", alarm->type, alarm->function, ktime_to_ns(alarm->expires)); - if (base->first == &alarm->node) + if (base->first == &alarm->node) { base->first = rb_next(&alarm->node); + was_first = true; + } if (!RB_EMPTY_NODE(&alarm->node)) { rb_erase(&alarm->node, &base->alarms); RB_CLEAR_NODE(&alarm->node); @@ -134,10 +137,10 @@ static void alarm_enqueue_locked(struct alarm *alarm) leftmost = 0; } } - if (leftmost) { + if (leftmost) base->first = &alarm->node; - update_timer_locked(base, false); - } + if (leftmost || was_first) + update_timer_locked(base, was_first); rb_link_node(&alarm->node, parent, link); rb_insert_color(&alarm->node, &base->alarms); diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 64fe3b3cd9e6..e07224fd9f89 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -756,8 +756,10 @@ static void receive_file_work(struct work_struct *data) /* wait for our last read to complete */ ret = wait_event_interruptible(dev->read_wq, dev->rx_done || dev->state != STATE_BUSY); - if (ret < 0 || dev->state != STATE_BUSY) { - r = ret; + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + if (!dev->rx_done) + usb_ep_dequeue(dev->ep_out, read_req); break; } /* if xfer_file_length is 0xFFFFFFFF, then we read until diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index 2b8160877688..c431cc670a46 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -61,5 +61,14 @@ config NVMAP_HIGHMEM_ONLY Say Y here to restrict nvmap system memory allocations (both physical system memory and IOVMM) to just HIGHMEM pages. +config NVMAP_CARVEOUT_KILLER + bool "Reclaim nvmap carveout by killing processes" + depends on TEGRA_NVMAP + default n + help + Say Y here to allow the system to reclaim carveout space by killing + processes. This will kill the largest consumers of lowest priority + first. + endif diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 1777de0f0215..9c8fac2b7149 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -886,6 +886,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) struct tegra_dc *dc = ptr; unsigned long status; unsigned long val; + unsigned long underflow_mask; int i; status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); @@ -915,6 +916,45 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) wake_up(&dc->wq); } + + /* + * Overlays can get thier internal state corrupted during and underflow + * condition. The only way to fix this state is to reset the DC. + * if we get 4 consecutive frames with underflows, assume we're + * hosed and reset. + */ + underflow_mask = status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT); + if (underflow_mask) { + val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); + val |= V_BLANK_INT; + tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); + dc->underflow_mask |= underflow_mask; + } + + if (status & V_BLANK_INT) { + int i; + + for (i = 0; i< DC_N_WINDOWS; i++) { + if (dc->underflow_mask & (WIN_A_UF_INT <<i)) { + dc->windows[i].underflows++; + + if (dc->windows[i].underflows > 4) + schedule_work(&dc->reset_work); + } else { + dc->windows[i].underflows = 0; + } + } + + if (!dc->underflow_mask) { + val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); + val &= ~V_BLANK_INT; + tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); + } + + dc->underflow_mask = 0; + } + + return IRQ_HANDLED; } @@ -1005,8 +1045,14 @@ static void tegra_dc_init(struct tegra_dc *dc) tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY); tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER); - tegra_dc_writel(dc, 0x00000002, DC_CMD_INT_MASK); - tegra_dc_writel(dc, 0x00000000, DC_CMD_INT_ENABLE); + tegra_dc_writel(dc, (FRAME_END_INT | + V_BLANK_INT | + WIN_A_UF_INT | + WIN_B_UF_INT | + WIN_C_UF_INT), DC_CMD_INT_MASK); + tegra_dc_writel(dc, (WIN_A_UF_INT | + WIN_B_UF_INT | + WIN_C_UF_INT), DC_CMD_INT_ENABLE); tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); @@ -1105,6 +1151,25 @@ void tegra_dc_disable(struct tegra_dc *dc) mutex_unlock(&dc->lock); } +static void tegra_dc_reset_worker(struct work_struct *work) +{ + struct tegra_dc *dc = + container_of(work, struct tegra_dc, reset_work); + + dev_warn(&dc->ndev->dev, "overlay stuck in underflow state. resetting.\n"); + + mutex_lock(&dc->lock); + _tegra_dc_disable(dc); + + tegra_periph_reset_assert(dc->clk); + msleep(10); + tegra_periph_reset_deassert(dc->clk); + + _tegra_dc_enable(dc); + mutex_unlock(&dc->lock); +} + + static int tegra_dc_probe(struct nvhost_device *ndev) { struct tegra_dc *dc; @@ -1194,6 +1259,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev) mutex_init(&dc->lock); init_waitqueue_head(&dc->wq); + INIT_WORK(&dc->reset_work, tegra_dc_reset_worker); dc->n_windows = DC_N_WINDOWS; for (i = 0; i < dc->n_windows; i++) { diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index 253d03f057d7..3f7fdbff023b 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -84,6 +84,9 @@ struct tegra_dc { u32 syncpt_id; u32 syncpt_min; u32 syncpt_max; + + unsigned long underflow_mask; + struct work_struct reset_work; }; static inline void tegra_dc_io_start(struct tegra_dc *dc) diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 01eaad925da6..2f51c47a67ab 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -89,9 +89,27 @@ static int tegra_fb_open(struct fb_info *info, int user) static int tegra_fb_release(struct fb_info *info, int user) { struct tegra_fb_info *tegra_fb = info->par; + struct fb_var_screeninfo *var = &info->var; flush_workqueue(tegra_fb->flip_wq); + if (tegra_fb->win->cur_handle) { + nvmap_unpin(tegra_fb->fb_nvmap, tegra_fb->win->cur_handle); + nvmap_free(tegra_fb->fb_nvmap, tegra_fb->win->cur_handle); + + tegra_fb->win->cur_handle = NULL; + + tegra_fb->win->x = 0; + tegra_fb->win->y = 0; + tegra_fb->win->w = var->xres; + tegra_fb->win->h = var->yres; + tegra_fb->win->out_x = 0; + tegra_fb->win->out_y = 0; + tegra_fb->win->out_w = var->xres; + tegra_fb->win->out_h = var->yres; + tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED; + } + if (tegra_fb->user_nvmap) { nvmap_client_put(tegra_fb->user_nvmap); tegra_fb->user_nvmap = NULL; @@ -262,25 +280,21 @@ static int tegra_fb_pan_display(struct fb_var_screeninfo *var, char __iomem *flush_end; u32 addr; - flush_start = info->screen_base + (var->yoffset * info->fix.line_length); - flush_end = flush_start + (var->yres * info->fix.line_length); - - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; + if (!tegra_fb->win->cur_handle) { + flush_start = info->screen_base + (var->yoffset * info->fix.line_length); + flush_end = flush_start + (var->yres * info->fix.line_length); - addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) + - (var->xoffset * (var->bits_per_pixel/8)); + info->var.xoffset = var->xoffset; + info->var.yoffset = var->yoffset; - tegra_fb->win->phys_addr = addr; - /* TODO: update virt_addr */ + addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) + + (var->xoffset * (var->bits_per_pixel/8)); - tegra_dc_update_windows(&tegra_fb->win, 1); - tegra_dc_sync_windows(&tegra_fb->win, 1); + tegra_fb->win->phys_addr = addr; + /* TODO: update virt_addr */ - if (WARN_ON(tegra_fb->win->cur_handle)) { - nvmap_unpin(tegra_fb->fb_nvmap, tegra_fb->win->cur_handle); - nvmap_free(tegra_fb->fb_nvmap, tegra_fb->win->cur_handle); - tegra_fb->win->cur_handle = NULL; + tegra_dc_update_windows(&tegra_fb->win, 1); + tegra_dc_sync_windows(&tegra_fb->win, 1); } return 0; diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c index 348d0d36a9a0..361a74a6a7cf 100644 --- a/drivers/video/tegra/host/nvhost_acm.c +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -31,6 +31,7 @@ #define ACM_TIMEOUT 1*HZ #define DISABLE_3D_POWERGATING +#define DISABLE_MPE_POWERGATING void nvhost_module_busy(struct nvhost_module *mod) { @@ -98,6 +99,12 @@ static const char *get_module_clk_id(const char *module, int index) { if (index == 1 && strcmp(module, "gr2d") == 0) return "epp"; + else if (index == 2 && strcmp(module, "gr2d") == 0) + return "emc"; + else if (index == 1 && strcmp(module, "gr3d") == 0) + return "emc"; + else if (index == 1 && strcmp(module, "mpe") == 0) + return "emc"; else if (index == 0) return module; return NULL; @@ -156,6 +163,19 @@ int nvhost_module_init(struct nvhost_module *mod, const char *name, } #endif +#ifdef DISABLE_MPE_POWERGATING + /* + * Disable power gating for MPE as it seems to cause issues with + * camera record stress tests when run in loop. + */ + if (mod->powergate_id == TEGRA_POWERGATE_MPE) { + tegra_powergate_sequence_power_up(mod->powergate_id, + mod->clk[0]); + clk_disable(mod->clk[0]); + mod->powergate_id = -1; + } +#endif + mutex_init(&mod->lock); init_waitqueue_head(&mod->idle); INIT_DELAYED_WORK(&mod->powerdown, powerdown_handler); diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h index c765d983afb4..f7e28af8e9cb 100644 --- a/drivers/video/tegra/host/nvhost_acm.h +++ b/drivers/video/tegra/host/nvhost_acm.h @@ -28,7 +28,7 @@ #include <linux/mutex.h> #include <linux/clk.h> -#define NVHOST_MODULE_MAX_CLOCKS 2 +#define NVHOST_MODULE_MAX_CLOCKS 3 struct nvhost_module; diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c index 1961c714efe5..674b34ab6f45 100644 --- a/drivers/video/tegra/nvmap/nvmap_dev.c +++ b/drivers/video/tegra/nvmap/nvmap_dev.c @@ -23,9 +23,11 @@ #include <linux/backing-dev.h> #include <linux/bitmap.h> #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/mm.h> +#include <linux/oom.h> #include <linux/platform_device.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -44,6 +46,7 @@ #include "nvmap_mru.h" #define NVMAP_NUM_PTES 64 +#define NVMAP_CARVEOUT_KILLER_RETRY_TIME 100 /* msecs */ struct nvmap_carveout_node { unsigned int heap_bit; @@ -321,10 +324,77 @@ static struct nvmap_client* get_client_from_carveout_commit( carveout_commit); } -struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client, - size_t len, size_t align, - unsigned long usage, - unsigned int prot) +#ifdef CONFIG_NVMAP_CARVEOUT_KILLER +static DECLARE_WAIT_QUEUE_HEAD(wait_reclaim); +bool nvmap_shrink_carveout(struct nvmap_carveout_node *node) +{ + struct nvmap_carveout_commit *commit; + size_t selected_size = 0; + int selected_oom_adj = OOM_ADJUST_MIN; + struct task_struct *selected_task = NULL; + unsigned long flags; + bool wait = false; + int current_oom_adj = OOM_ADJUST_MIN; + + task_lock(current); + if (current->signal) + current_oom_adj = current->signal->oom_adj; + task_unlock(current); + + spin_lock_irqsave(&node->clients_lock, flags); + /* find the task with the smallest oom_adj (lowest priority) + * and largest carveout allocation -- ignore kernel allocations, + * there's no way to handle them */ + list_for_each_entry(commit, &node->clients, list) { + struct nvmap_client *client = + get_client_from_carveout_commit(node, commit); + size_t size = commit->commit; + struct task_struct *task = client->task; + struct signal_struct *sig; + + if (!task) + continue; + + task_lock(task); + sig = task->signal; + if (!task->mm || !sig) + goto end; + /* don't try to kill higher priority tasks */ + if (sig->oom_adj < current_oom_adj) + goto end; + if (sig->oom_adj < selected_oom_adj) + goto end; + if (sig->oom_adj == selected_oom_adj && + size <= selected_size) + goto end; + selected_oom_adj = sig->oom_adj; + selected_size = size; + selected_task = task; +end: + task_unlock(task); + } + if (selected_task) { + wait = selected_task != current; + if (fatal_signal_pending(selected_task)) { + pr_warning("carveout_killer: process %d dying " + "slowly\n", selected_task->pid); + goto out; + } + pr_info("carveout_killer: killing process %d with oom_adj %d " + "to reclaim %d\n", selected_task->pid, selected_oom_adj, + selected_size); + force_sig(SIGKILL, selected_task); + } +out: + spin_unlock_irqrestore(&node->clients_lock, flags); + return wait; +} +#endif + +struct nvmap_heap_block *do_nvmap_carveout_alloc(struct nvmap_client *client, + size_t len, size_t align, + unsigned long usage, + unsigned int prot) { struct nvmap_carveout_node *co_heap; struct nvmap_device *dev = client->dev; @@ -349,8 +419,66 @@ struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client, return block; } } + return NULL; +} + +struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client, + size_t len, size_t align, + unsigned long usage, + unsigned int prot) +{ + struct nvmap_heap_block *block; +#ifdef CONFIG_NVMAP_CARVEOUT_KILLER + struct nvmap_carveout_node *co_heap; + struct nvmap_device *dev = client->dev; + int i; + unsigned long end = jiffies + + msecs_to_jiffies(NVMAP_CARVEOUT_KILLER_RETRY_TIME); + int count = 0; + DEFINE_WAIT(wait); + + do { + block = do_nvmap_carveout_alloc(client, len, align, usage, + prot); + if (block) + return block; + + if (!count++) + printk("%s: failed to allocate %u bytes, " + "firing carveout killer!\n", __func__, len); + else + printk("%s: still can't allocate %u bytes, " + "attempt %d!\n", __func__, len, count); + + /* shrink carveouts that matter and try again */ + for (i = 0; i < dev->nr_carveouts; i++) { + co_heap = &dev->heaps[i]; + + if (!(co_heap->heap_bit & usage)) + continue; + + /* indicates we just delivered a sigkill to current, + or didn't find anything to kill might as well stop + trying */ + if (!nvmap_shrink_carveout(co_heap)) + return NULL; + + prepare_to_wait(&wait_reclaim, &wait, + TASK_INTERRUPTIBLE); + schedule_timeout(end - jiffies); + finish_wait(&wait_reclaim, &wait); + } + } while (time_is_after_jiffies(end)); + + if (time_is_before_jiffies(end)) + printk("carveout_killer: timeout expired without allocation " + "succeeding.\n"); return NULL; +#else + block = do_nvmap_carveout_alloc(client, len, align, usage, prot); + return block; +#endif } /* remove a handle from the device's tree of all handles; called @@ -433,6 +561,7 @@ struct nvmap_client *nvmap_create_client(struct nvmap_device *dev, const char *name) { struct nvmap_client *client; + struct task_struct *task; int i; if (WARN_ON(!dev)) @@ -460,7 +589,17 @@ struct nvmap_client *nvmap_create_client(struct nvmap_device *dev, } get_task_struct(current); - client->task = current; + task_lock(current); + /* don't bother to store task struct for kernel threads, + they can't be killed anyway */ + if (current->flags & PF_KTHREAD) { + put_task_struct(current); + task = NULL; + } else { + task = current; + } + task_unlock(current); + client->task = task; spin_lock_init(&client->ref_lock); atomic_set(&client->count, 1); @@ -476,6 +615,7 @@ static void destroy_client(struct nvmap_client *client) if (!client) return; + while ((n = rb_first(&client->handle_refs))) { struct nvmap_handle_ref *ref; int pins, dupes; @@ -501,6 +641,10 @@ static void destroy_client(struct nvmap_client *client) kfree(ref); } +#ifdef CONFIG_NVMAP_CARVEOUT_KILLER + wake_up_all(&wait_reclaim); +#endif + for (i = 0; i < client->dev->nr_carveouts; i++) list_del(&client->carveout_commit[i].list); @@ -757,7 +901,11 @@ static struct attribute_group heap_extra_attr_group = { static void client_stringify(struct nvmap_client *client, struct seq_file *s) { - char task_comm[sizeof(client->task->comm)]; + char task_comm[TASK_COMM_LEN]; + if (!client->task) { + seq_printf(s, "%8s %16s %8u", client->name, "kernel", 0); + return; + } get_task_comm(task_comm, client->task); seq_printf(s, "%8s %16s %8u", client->name, task_comm, client->task->pid); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1addd785739a..f775da5a1be5 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -205,6 +205,12 @@ config TEGRA_WATCHDOG To compile this driver as a module, choose M here: the module will be called tegra_wdt. +config TEGRA_WATCHDOG_ENABLE_ON_PROBE + tristate "Tegra watchdog" + depends on ARCH_TEGRA && TEGRA_WATCHDOG + help + Say Y here to enable the tegra watchdog at driver + probe time, rather than when the device is opened. config EP93XX_WATCHDOG tristate "EP93xx Watchdog" diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index d7a9d7609a1f..d7ad6238d4f3 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -50,7 +50,7 @@ #define WDT_SEL_TMR1 (0 << 4) #define WDT_SYS_RST (1 << 2) -static int heartbeat = 60; +static int heartbeat = 30; struct tegra_wdt { struct miscdevice miscdev; @@ -208,6 +208,7 @@ static int tegra_wdt_probe(struct platform_device *pdev) { struct resource *res_src, *res_wdt, *res_irq; struct tegra_wdt *wdt; + u32 src; int ret = 0; if (pdev->id != -1) { @@ -262,6 +263,10 @@ static int tegra_wdt_probe(struct platform_device *pdev) goto fail; } + src = readl(wdt->wdt_source); + if (src & BIT(12)) + dev_info(&pdev->dev, "last reset due to watchdog timeout\n"); + tegra_wdt_disable(wdt); ret = request_irq(res_irq->start, tegra_wdt_interrupt, IRQF_DISABLED, @@ -292,6 +297,9 @@ static int tegra_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdt); tegra_wdt_dev = wdt; +#ifdef CONFIG_TEGRA_WATCHDOG_ENABLE_ON_PROBE + tegra_wdt_enable(wdt); +#endif return 0; fail: if (wdt->irq != -1) |