diff options
22 files changed, 1046 insertions, 582 deletions
diff --git a/arch/arm/configs/tegra_harmony_android_defconfig b/arch/arm/configs/tegra_harmony_android_defconfig index 489e7e6253a9..c24816c5b36b 100644 --- a/arch/arm/configs/tegra_harmony_android_defconfig +++ b/arch/arm/configs/tegra_harmony_android_defconfig @@ -1484,6 +1484,7 @@ CONFIG_MMC_BLOCK_BOUNCE=y CONFIG_MMC_SDHCI=y # CONFIG_MMC_SDHCI_PCI is not set CONFIG_MMC_SDHCI_TEGRA=y +CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y # CONFIG_MMC_TIFM_SD is not set # CONFIG_MMC_SPI is not set # CONFIG_MEMSTICK is not set diff --git a/arch/arm/configs/tegra_whistler_android_defconfig b/arch/arm/configs/tegra_whistler_android_defconfig index fe2839172cd2..6e341676dba1 100644 --- a/arch/arm/configs/tegra_whistler_android_defconfig +++ b/arch/arm/configs/tegra_whistler_android_defconfig @@ -1199,6 +1199,7 @@ CONFIG_MMC_BLOCK_BOUNCE=y # CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_TEGRA=y +CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y # CONFIG_MMC_SPI is not set # CONFIG_MEMSTICK is not set # CONFIG_ACCESSIBILITY is not set diff --git a/arch/arm/mach-tegra/board_nvodm.c b/arch/arm/mach-tegra/board_nvodm.c index 237844085c84..b1404b6ff00f 100644 --- a/arch/arm/mach-tegra/board_nvodm.c +++ b/arch/arm/mach-tegra/board_nvodm.c @@ -26,6 +26,7 @@ #include <linux/platform_device.h> #include <linux/synaptics_i2c_rmi.h> #include <linux/i2c.h> +#include <linux/pm.h> #include <linux/spi/spi.h> #include <asm/mach-types.h> @@ -56,7 +57,6 @@ #include "mach/nvrm_linux.h" #include "nvassert.h" #include "nvodm_query_discovery.h" -#include "mach/pci.h" #include <../../../drivers/staging/android/timed_output.h> @@ -277,13 +277,13 @@ static void __init NvConfigDebugConsole( return; } -void __init pci_tegra_power(int on) +void tegra_set_voltage(NvU64 guid, int on) { u32 settling_time; const NvOdmPeripheralConnectivity *con = NULL; int i; - con = NvOdmPeripheralGetGuid(NV_VDD_PEX_CLK_ODM_ID); + con = NvOdmPeripheralGetGuid(guid); if (con == NULL) return; @@ -311,6 +311,12 @@ extern void __init tegra_clk_init(void); extern void __init tegra_init_snor_controller(void); #endif + +static void tegra_system_power_off(void) +{ + tegra_set_voltage(NV_VDD_SoC_ODM_ID, 0); +} + #if !(defined(CONFIG_ENC28J60) && defined(CONFIG_SPI_TEGRA)) #define register_enc28j60() do {} while (0) #else @@ -476,11 +482,12 @@ static void __init tegra_machine_init(void) #endif #ifdef CONFIG_TEGRA_PCI - pci_tegra_power(1); + tegra_set_voltage( NV_VDD_PEX_CLK_ODM_ID, 1); #else - pci_tegra_power(0); + tegra_set_voltage( NV_VDD_PEX_CLK_ODM_ID, 0); #endif + pm_power_off = tegra_system_power_off; } MACHINE_START(TEGRA_GENERIC, "Tegra generic") diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index fa4186513ce5..bbd3691240ea 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -78,8 +78,6 @@ struct tegra_dma_channel { unsigned long phys_addr; int mode; - int odd_interrupt; - /* Register shadow */ unsigned long csr; unsigned long ahb_seq; @@ -102,7 +100,6 @@ static void tegra_dma_stop(struct tegra_dma_channel *ch); void tegra_dma_flush(int channel) { - } EXPORT_SYMBOL(tegra_dma_flush); @@ -142,6 +139,8 @@ int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *_req) struct tegra_dma_req *req = NULL; int found = 0; unsigned long irq_flags; + int to_transfer; + int req_transfer_count; spin_lock_irqsave(&ch->lock, irq_flags); list_for_each_entry (req, &ch->list, list) { @@ -151,80 +150,102 @@ int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *_req) break; } } - BUG_ON(found==0); - - if (found) { - int to_transfer; - int req_transfer_count; - - /* STOP the DMA and get the transfer count. - * Getting the transfer count is tricky. - * - Change the source selector to invalid to stop the DMA from - * FIFO to memory. - * - Read the status register to knoe the number of pending - * bytes to be transfered. - * - Finally stop or program the DMA to the next buffer in the - * list. - */ - csr = ch->csr; - csr = NV_FLD_SET_DRF_DEF(APBDMACHAN_CHANNEL_0, CSR, - REQ_SEL, NA31, csr); - /* Set the enable as that is not shadowed */ - csr = NV_FLD_SET_DRF_DEF(APBDMACHAN_CHANNEL_0, CSR, - ENB, ENABLE, csr); - writel(csr, ch->addr + APBDMACHAN_CHANNEL_0_CSR_0); - - /* Get the transfer count */ - status = readl(ch->addr + APBDMACHAN_CHANNEL_0_STA_0); - to_transfer = NV_DRF_VAL(APBDMACHAN_CHANNEL_0, STA, - COUNT, status); - req_transfer_count = NV_DRF_VAL(APBDMACHAN_CHANNEL_0, - CSR, WCOUNT, ch->csr); - - req->bytes_transferred = req_transfer_count - to_transfer; - req->bytes_transferred *= 4; - - tegra_dma_stop(ch); - if (!list_empty(&ch->list)) { - /* if the list is not empty, queue the next request */ - struct tegra_dma_req *next_req; - next_req = list_entry(ch->list.next, - typeof(*next_req), list); - tegra_dma_update_hw(ch, next_req); - } - req->status = -TEGRA_DMA_REQ_ERROR_ABOTRED; - + if (found==0) { spin_unlock_irqrestore(&ch->lock, irq_flags); - req->complete(req, req->status); - spin_lock_irqsave(&ch->lock, irq_flags); + return 0; + } + + /* STOP the DMA and get the transfer count. + * Getting the transfer count is tricky. + * - Change the source selector to invalid to stop the DMA from + * FIFO to memory. + * - Read the status register to know the number of pending + * bytes to be transfered. + * - Finally stop or program the DMA to the next buffer in the + * list. + */ + csr = ch->csr; + csr = NV_FLD_SET_DRF_DEF(APBDMACHAN_CHANNEL_0, CSR, + REQ_SEL, NA31, csr); + /* Set the enable as that is not shadowed */ + csr = NV_FLD_SET_DRF_DEF(APBDMACHAN_CHANNEL_0, CSR, + ENB, ENABLE, csr); + writel(csr, ch->addr + APBDMACHAN_CHANNEL_0_CSR_0); + + /* Get the transfer count */ + status = readl(ch->addr + APBDMACHAN_CHANNEL_0_STA_0); + to_transfer = NV_DRF_VAL(APBDMACHAN_CHANNEL_0, STA, + COUNT, status); + req_transfer_count = NV_DRF_VAL(APBDMACHAN_CHANNEL_0, + CSR, WCOUNT, ch->csr); + + req->bytes_transferred = req_transfer_count - to_transfer; + req->bytes_transferred *= 4; + /* In continous transfer mode, DMA only tracks the count of the + * half DMA buffer. So, if the DMA already finished half the DMA + * then add the half buffer to the completed count. + * + * FIXME: There can be a race here. What if the req to + * dequue happens at the same time as the DMA just moved to + * the new buffer and SW didn't yet received the interrupt? + */ + if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) { + req->bytes_transferred += 4 * req_transfer_count; + } + tegra_dma_stop(ch); + if (!list_empty(&ch->list)) { + /* if the list is not empty, queue the next request */ + struct tegra_dma_req *next_req; + next_req = list_entry(ch->list.next, + typeof(*next_req), list); + tegra_dma_update_hw(ch, next_req); } + req->status = -TEGRA_DMA_REQ_ERROR_ABOTRED; + spin_unlock_irqrestore(&ch->lock, irq_flags); - if (found) - return 0; - else - return -ENOENT; + /* Callback should be called without any lock */ + req->complete(req, req->status); + return 0; } EXPORT_SYMBOL(tegra_dma_dequeue_req); -int tegra_dma_is_empty(int channel) +bool tegra_dma_is_empty(int channel) { unsigned long irq_flags; struct tegra_dma_channel *ch = &dma_channels[channel]; - int is_empty; + bool is_empty; spin_lock_irqsave(&ch->lock, irq_flags); if (list_empty(&ch->list)) - is_empty = 1; + is_empty = true; else - is_empty = 0; + is_empty = false; spin_unlock_irqrestore(&ch->lock, irq_flags); - return is_empty; } EXPORT_SYMBOL(tegra_dma_is_empty); +bool tegra_dma_is_req_inflight(int channel, struct tegra_dma_req *_req) +{ + unsigned long irq_flags; + struct tegra_dma_channel *ch = &dma_channels[channel]; + struct tegra_dma_req *req; + + spin_lock_irqsave(&ch->lock, irq_flags); + list_for_each_entry (req, &ch->list, list) { + if (req == _req) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return true; + } + } + spin_unlock_irqrestore(&ch->lock, irq_flags); + return false; +} +EXPORT_SYMBOL(tegra_dma_is_req_inflight); + int tegra_dma_enqueue_req(int channel, struct tegra_dma_req *req) { unsigned long irq_flags; @@ -241,6 +262,7 @@ int tegra_dma_enqueue_req(int channel, struct tegra_dma_req *req) req->bytes_transferred = 0; req->status = 0; + req->buffer_status = 0; if (list_empty(&ch->list)) start_dma = 1; @@ -466,9 +488,11 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch) { static struct tegra_dma_req *req; - if (list_empty(&ch->list)) - /* Why did we got an interrupt? */ + spin_lock(&ch->lock); + if (list_empty(&ch->list)) { + spin_unlock(&ch->lock); return; + } req = list_entry(ch->list.next, typeof(*req), list); if (req) { @@ -483,33 +507,51 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch) list_del(&req->list); req->bytes_transferred = bytes_transferred; req->status = 0; + + spin_unlock(&ch->lock); + /* Callback should be called without any lock */ req->complete(req, 0); + spin_lock(&ch->lock); } if (!list_empty(&ch->list)) { req = list_entry(ch->list.next, typeof(*req), list); tegra_dma_update_hw(ch, req); } + spin_unlock(&ch->lock); } static void handle_continuous_dma(struct tegra_dma_channel *ch) { static struct tegra_dma_req *req; - if (list_empty(&ch->list)) - /* Why did we got an interrupt? */ + spin_lock(&ch->lock); + if (list_empty(&ch->list)) { + spin_unlock(&ch->lock); return; + } req = list_entry(ch->list.next, typeof(*req), list); if (req) { - ch->odd_interrupt = (~ch->odd_interrupt & 0x1); - if (ch->odd_interrupt) { - struct tegra_dma_req *next_req; - /* Load the next request into the hardware */ - next_req = list_first_entry(ch->list.next, - typeof(*next_req), list); - tegra_dma_update_hw_partial(ch, next_req); - } else { + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) { + /* Load the next request into the hardware, if available + * */ + if (!list_is_last(&req->list, &ch->list)) { + struct tegra_dma_req *next_req; + + printk("Queue the next request\n"); + next_req = list_entry(req->list.next, + typeof(*next_req), list); + tegra_dma_update_hw_partial(ch, next_req); + } + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL; + /* DMA lock is NOT held when callbak is called */ + spin_unlock(&ch->lock); + req->threshold(req, 0); + return; + + } else if (req->buffer_status == + TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) { /* Callback when the buffer is completely full (i.e on * the second interrupt */ int bytes_transferred; @@ -519,12 +561,21 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) bytes_transferred += 1; bytes_transferred <<= 3; + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; req->bytes_transferred = bytes_transferred; req->status = 0; list_del(&req->list); + + /* DMA lock is NOT held when callbak is called */ + spin_unlock(&ch->lock); req->complete(req, 0); + return; + + } else { + BUG(); } } + spin_unlock(&ch->lock); } static irqreturn_t dma_isr(int irq, void *id) @@ -546,6 +597,7 @@ static irqreturn_t dma_isr(int irq, void *id) else handle_continuous_dma(ch); + return IRQ_HANDLED; } @@ -608,4 +660,3 @@ fail: /* FIXME cleanup */ return ret; } - diff --git a/arch/arm/mach-tegra/include/mach/board.h b/arch/arm/mach-tegra/include/mach/board.h index 10f0f0232b90..51e85fdb7bb5 100644 --- a/arch/arm/mach-tegra/include/mach/board.h +++ b/arch/arm/mach-tegra/include/mach/board.h @@ -22,5 +22,11 @@ #define __MACH_TEGRA_BOARD_H #include <linux/types.h> +#include "nvodm_pmu.h" +#include "mach/nvrm_linux.h" +#include "nvodm_query_discovery.h" + +/* ON/off power rail given the GUID */ +void tegra_set_voltage(NvU64 guid, int on); #endif diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h index 964af099a392..508e2fcf8409 100644 --- a/arch/arm/mach-tegra/include/mach/dma.h +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -34,12 +34,46 @@ enum tegra_dma_req_error { TEGRA_DMA_REQ_ERROR_ABOTRED, }; +enum tegra_dma_req_buff_status { + TEGRA_DMA_REQ_BUF_STATUS_EMPTY, + TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL, + TEGRA_DMA_REQ_BUF_STATUS_FULL, +}; + struct tegra_dma_req { struct list_head list; unsigned int modid; int instance; + /* Called when the req is complete and from the DMA ISR context. + * When this is called the req structure is no longer queued by + * the DMA channel. + * + * State of the DMA depends on the number of req it has. If there are + * no DMA requests queued up, then it will STOP the DMA. It there are + * more requests in the DMA, then it will queue the next request. + */ void (*complete)(struct tegra_dma_req *req, int err); + + /* This is a called from the DMA ISR context when the DMA is still in + * progress and is actively filling same buffer. + * + * In case of continous mode receive, this threshold is 1/2 the buffer + * size. In other cases, this will not even be called as there is no + * hardware support for it. + * + * In the case of continous mode receive, if there is next req already + * queued, DMA programs the HW to use that req when this req is + * completed. If there is no "next req" queued, then DMA ISR doesn't do + * anything before calling this callback. + * + * This is mainly used by the cases, where the clients has queued + * only one req and want to get some sort of DMA threshold + * callback to program the next buffer. + * + */ + void (*threshold)(struct tegra_dma_req *req, int err); + /* 1 to copy to memory. * 0 to copy from the memory to device FIFO */ int to_memory; @@ -57,6 +91,9 @@ struct tegra_dma_req { int bytes_transferred; int status; + /* DMA completion tracking information */ + int buffer_status; + /* Client specific data */ void *data; }; @@ -64,12 +101,11 @@ struct tegra_dma_req { int tegra_dma_enqueue_req(int channel, struct tegra_dma_req *req); int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *req); void tegra_dma_dequeue(int channel); - -/* Returns 1 if there are DMA is empty. - */ -int tegra_dma_is_empty(int channel); void tegra_dma_flush(int channel); +bool tegra_dma_is_req_inflight(int channel, struct tegra_dma_req *req); +bool tegra_dma_is_empty(int channel); + int tegra_dma_allocate_channel(int mode); void tegra_dma_free_channel(int channel); diff --git a/arch/arm/mach-tegra/include/mach/pci.h b/arch/arm/mach-tegra/include/mach/pci.h index 81f149717d05..e5eac3ad5640 100644 --- a/arch/arm/mach-tegra/include/mach/pci.h +++ b/arch/arm/mach-tegra/include/mach/pci.h @@ -320,6 +320,4 @@ static inline void __iomem *pci_tegra_config_addr(u8 bus_number, void pci_tegra_enumerate(void); -void __init pci_tegra_power(int on); - #endif diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c index fe3c3252d39f..435ff7dcc54c 100755..100644 --- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c +++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c @@ -47,6 +47,10 @@ #define MAX_USB_INSTANCES 5 +// On platforms that never disable USB controller clock, use 1KHz as an +// indicator that USB controller is idle, and core voltage can be scaled down +#define USBC_IDLE_KHZ (1) + static NvDdkUsbPhy *s_pUsbPhy = NULL; static NvDdkUsbPhyUtmiPadConfig *s_pUtmiPadConfig = NULL; @@ -247,22 +251,54 @@ UsbPhyDfsBusyHint( { NvRmDfsClockId_Emc, 0, 0, NV_TRUE }, { NvRmDfsClockId_Ahb, 0, 0, NV_TRUE } }; + NvError e = NvSuccess; pUsbHintOn[0].BoostDurationMs = BoostDurationMs; pUsbHintOn[1].BoostDurationMs = BoostDurationMs; if (DfsOn) + { + if (hUsbPhy->Caps.PhyRegInController) + { + // Indicate USB controller is active + NvRmFreqKHz PrefFreq = NvRmPowerModuleGetMaxFrequency( + hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance)); + + NV_CHECK_ERROR_CLEANUP( + NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), + hUsbPhy->RmPowerClientId, PrefFreq, PrefFreq, &PrefFreq, + 1, NULL, 0)); + } return NvRmPowerBusyHintMulti(hUsbPhy->hRmDevice, hUsbPhy->RmPowerClientId, pUsbHintOn, NV_ARRAY_SIZE(pUsbHintOn), NvRmDfsBusyHintSyncMode_Async); + } else + { + if (hUsbPhy->Caps.PhyRegInController) + { + // Indicate USB controller is idle + NvRmFreqKHz PrefFreq = USBC_IDLE_KHZ; + + NV_CHECK_ERROR_CLEANUP( + NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), + hUsbPhy->RmPowerClientId, PrefFreq, PrefFreq, &PrefFreq, + 1, NULL, 0)); + } return NvRmPowerBusyHintMulti(hUsbPhy->hRmDevice, hUsbPhy->RmPowerClientId, pUsbHintOff, NV_ARRAY_SIZE(pUsbHintOff), NvRmDfsBusyHintSyncMode_Async); + } + +fail: + return e; } @@ -274,7 +310,8 @@ UsbPhyInitialize( NvRmFreqKHz CurrentFreq = 0; NvRmFreqKHz PrefFreqList[3] = {12000, 60000, NvRmFreqUnspecified}; - //NvOsDebugPrintf("UsbPhyInitialize::VOLTAGE ON\n"); + // NvOsDebugPrintf("UsbPhyInitialize::VOLTAGE ON, instance %d\n", + // hUsbPhy->Instance); // request power NV_CHECK_ERROR_CLEANUP( NvRmPowerVoltageControl(hUsbPhy->hRmDevice, @@ -288,23 +325,31 @@ UsbPhyInitialize( NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NV_TRUE)); - if (hUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiNullPhy) + if (!hUsbPhy->Caps.PhyRegInController) { - /* Request for 60MHz clk */ - NV_CHECK_ERROR_CLEANUP( - NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, - NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), - hUsbPhy->RmPowerClientId, PrefFreqList[1], - PrefFreqList[1], &PrefFreqList[1], 1, &CurrentFreq, 0)); + if (hUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiNullPhy) + { + /* Request for 60MHz clk */ + NV_CHECK_ERROR_CLEANUP( + NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), + hUsbPhy->RmPowerClientId, PrefFreqList[1], + PrefFreqList[1], &PrefFreqList[1], 1, &CurrentFreq, 0)); + } + else + { + /* Request for 12 MHz clk */ + NV_CHECK_ERROR_CLEANUP( + NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), + hUsbPhy->RmPowerClientId, PrefFreqList[0], + PrefFreqList[0], &PrefFreqList[0], 1, &CurrentFreq, 0)); + } } - else + // else { - /* Request for 12 MHz clk */ - NV_CHECK_ERROR_CLEANUP( - NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, - NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), - hUsbPhy->RmPowerClientId, PrefFreqList[0], - PrefFreqList[0], &PrefFreqList[0], 1, &CurrentFreq, 0)); + /* No need for actual clock configuration - all USB PLL frequencies + are available concurrently in this case. */ } // Reset controller diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c index 198a2b20ce3d..70138eae4d7c 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c @@ -1279,7 +1279,8 @@ NvRmPrivAp15IsModuleClockException( Ap15PllUConfigure(hRmDevice, PrefFreqList[0]); pCstate->SourceClock = 0; pCstate->Divider = 1; - pCstate->actual_freq = PrefFreqList[0]; + pCstate->actual_freq = + NvRmPrivGetClockSourceFreq(pCinfo->Sources[0]); return NV_TRUE; default: diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c b/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c index 87775f87a4db..8ae5c9dfba72 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c +++ b/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c @@ -1471,9 +1471,9 @@ NvRmPowerModuleClockConfig ( // Display configuration always at nominal voltage. UART divider is not // in CAR, and clock state contains source, rather than UART frequency. - // Hence, get ready for fastest clock. For other modules use maximum of - // target and current frequency. Make sure voltage is high enough for - // maximum module source frequency. + // Hence, get ready for fastest clock. Same for USB clock. For other + // modules use maximum of target and current frequency. Make sure that + // voltage is high enough for maximum module source frequency. if ((ModuleName == NvRmModuleID_Display) || (ModuleName == NvRmModuleID_Dsi)) { @@ -1482,7 +1482,8 @@ NvRmPowerModuleClockConfig ( } else { - if (ModuleName == NvRmModuleID_Uart) + if ((ModuleName == NvRmModuleID_Uart) || + (ModuleName == NvRmModuleID_Usb2Otg)) f = NvRmFreqMaximum; else f = NV_MAX(MaxFreq, state->actual_freq); @@ -1664,7 +1665,7 @@ leave: (ModuleName == NvRmModuleID_Dsi) || state->Vscale)) { // Tune voltage level to the actually configured frequency; for Display - // and UART, use maximum requested frequency. Make sure voltage is + // UART, and USB use maximum requested frequency. Make sure voltage is // updated after display configuration, which may change DVFS clocks. SourceClockFreq = s_ClockSourceFreq[(cinfo->Sources[state->SourceClock])]; @@ -1685,7 +1686,8 @@ leave: } else { - if (ModuleName == NvRmModuleID_Uart) + if ((ModuleName == NvRmModuleID_Uart) || + (ModuleName == NvRmModuleID_Usb2Otg)) f = MaxFreq; else f = state->actual_freq; diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_i2c.c b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_i2c.c index 6e61ddd95135..0f7a09446876 100644 --- a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_i2c.c +++ b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_i2c.c @@ -37,6 +37,9 @@ #define MAX8907B_I2C_SPEED_KHZ 400 #define MAX8907B_I2C_RETRY_CNT 2 +// Maximum i2c transaction count +#define MAX_TRANSACTION_COUNT 5 + NvBool Max8907bI2cWrite8( NvOdmPmuDeviceHandle hDevice, NvU8 Addr, @@ -45,14 +48,14 @@ NvBool Max8907bI2cWrite8( NvU32 i; NvU8 WriteBuffer[2]; NvOdmI2cTransactionInfo TransactionInfo; - NvOdmI2cStatus status = NvOdmI2cStatus_Success; + NvOdmI2cStatus status = NvOdmI2cStatus_Success; Max8907bPrivData *hPmu = (Max8907bPrivData*)hDevice->pPrivate; for (i = 0; i < MAX8907B_I2C_RETRY_CNT; i++) { WriteBuffer[0] = Addr & 0xFF; // PMU offset WriteBuffer[1] = Data & 0xFF; // written data - + TransactionInfo.Address = hPmu->DeviceAddr; TransactionInfo.Buf = &WriteBuffer[0]; TransactionInfo.Flags = NVODM_I2C_IS_WRITE; @@ -69,12 +72,12 @@ NvBool Max8907bI2cWrite8( switch (status) { case NvOdmI2cStatus_Timeout: - NVODMPMU_PRINTF(("NvOdmPmuI2cWrite8 Failed: Timeout\n")); + NVODMPMU_PRINTF(("NvOdmPmuI2cWrite8 Failed: Timeout\n")); break; case NvOdmI2cStatus_SlaveNotFound: default: NVODMPMU_PRINTF(("NvOdmPmuI2cWrite8 Failed: SlaveNotFound\n")); - break; + break; } return NV_FALSE; } @@ -86,28 +89,30 @@ NvBool Max8907bI2cRead8( { NvU32 i; NvU8 ReadBuffer = 0; - NvOdmI2cStatus status = NvOdmI2cStatus_Success; + NvOdmI2cStatus status = NvOdmI2cStatus_Success; Max8907bPrivData *hPmu = (Max8907bPrivData*)hDevice->pPrivate; NvOdmI2cTransactionInfo TransactionInfo[2]; for (i = 0; i < MAX8907B_I2C_RETRY_CNT; i++) { + NvU32 TransactionCount = 0; // Write the PMU offset ReadBuffer = Addr & 0xFF; - TransactionInfo[0].Address = hPmu->DeviceAddr; - TransactionInfo[0].Buf = &ReadBuffer; - TransactionInfo[0].Flags = NVODM_I2C_IS_WRITE | NVODM_I2C_USE_REPEATED_START; - TransactionInfo[0].NumBytes = 1; + TransactionInfo[TransactionCount].Address = hPmu->DeviceAddr; + TransactionInfo[TransactionCount].Buf = &ReadBuffer; + TransactionInfo[TransactionCount].Flags = + NVODM_I2C_IS_WRITE | NVODM_I2C_USE_REPEATED_START; + TransactionInfo[TransactionCount++].NumBytes = 1; - TransactionInfo[1].Address = (hPmu->DeviceAddr | 0x1); - TransactionInfo[1].Buf = &ReadBuffer; - TransactionInfo[1].Flags = 0; - TransactionInfo[1].NumBytes = 1; + TransactionInfo[TransactionCount].Address = (hPmu->DeviceAddr | 0x1); + TransactionInfo[TransactionCount].Buf = &ReadBuffer; + TransactionInfo[TransactionCount].Flags = 0; + TransactionInfo[TransactionCount++].NumBytes = 1; // Read data from PMU at the specified offset - status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo[0], 2, - MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); + status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo[0], + TransactionCount, MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); if (status == NvOdmI2cStatus_Success) { @@ -120,12 +125,12 @@ NvBool Max8907bI2cRead8( switch (status) { case NvOdmI2cStatus_Timeout: - NVODMPMU_PRINTF(("NvOdmPmuI2cRead8 Failed: Timeout\n")); + NVODMPMU_PRINTF(("NvOdmPmuI2cRead8 Failed: Timeout\n")); break; case NvOdmI2cStatus_SlaveNotFound: default: NVODMPMU_PRINTF(("NvOdmPmuI2cRead8 Failed: SlaveNotFound\n")); - break; + break; } return NV_FALSE; } @@ -137,13 +142,13 @@ NvBool Max8907bI2cWrite32( { NvU32 i; NvU8 WriteBuffer[5]; - NvOdmI2cStatus status = NvOdmI2cStatus_Success; + NvOdmI2cStatus status = NvOdmI2cStatus_Success; Max8907bPrivData *hPmu = (Max8907bPrivData*)hDevice->pPrivate; NvOdmI2cTransactionInfo TransactionInfo; for (i = 0; i < MAX8907B_I2C_RETRY_CNT; i++) { - WriteBuffer[0] = (NvU8)(Addr & 0xFF); + WriteBuffer[0] = (NvU8)(Addr & 0xFF); WriteBuffer[1] = (NvU8)((Data >> 24) & 0xFF); WriteBuffer[2] = (NvU8)((Data >> 16) & 0xFF); WriteBuffer[3] = (NvU8)((Data >> 8) & 0xFF); @@ -154,7 +159,7 @@ NvBool Max8907bI2cWrite32( TransactionInfo.Flags = NVODM_I2C_IS_WRITE; TransactionInfo.NumBytes = 5; - status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo, 1, + status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo, 1, MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); if (status == NvOdmI2cStatus_Success) @@ -165,12 +170,12 @@ NvBool Max8907bI2cWrite32( switch (status) { case NvOdmI2cStatus_Timeout: - NVODMPMU_PRINTF(("NvOdmPmuI2cWrite32 Failed: Timeout\n")); + NVODMPMU_PRINTF(("NvOdmPmuI2cWrite32 Failed: Timeout\n")); break; case NvOdmI2cStatus_SlaveNotFound: default: NVODMPMU_PRINTF(("NvOdmPmuI2cWrite32 Failed: SlaveNotFound\n")); - break; + break; } return NV_FALSE; } @@ -182,30 +187,32 @@ NvBool Max8907bI2cRead32( { NvU32 i; NvU8 ReadBuffer[5]; - NvOdmI2cStatus status = NvOdmI2cStatus_Success; + NvOdmI2cStatus status = NvOdmI2cStatus_Success; Max8907bPrivData *hPmu = (Max8907bPrivData*)hDevice->pPrivate; NvOdmI2cTransactionInfo TransactionInfo[2]; for (i = 0; i < MAX8907B_I2C_RETRY_CNT; i++) { + NvU32 TransactionCount = 0; ReadBuffer[0] = Addr & 0xFF; - TransactionInfo[0].Address = hPmu->DeviceAddr; - TransactionInfo[0].Buf = &ReadBuffer[0]; - TransactionInfo[0].Flags = NVODM_I2C_IS_WRITE | NVODM_I2C_USE_REPEATED_START; - TransactionInfo[0].NumBytes = 1; + TransactionInfo[TransactionCount].Address = hPmu->DeviceAddr; + TransactionInfo[TransactionCount].Buf = &ReadBuffer[0]; + TransactionInfo[TransactionCount].Flags = + NVODM_I2C_IS_WRITE | NVODM_I2C_USE_REPEATED_START; + TransactionInfo[TransactionCount++].NumBytes = 1; - TransactionInfo[1].Address = (hPmu->DeviceAddr | 0x1); - TransactionInfo[1].Buf = &ReadBuffer[0]; - TransactionInfo[1].Flags = 0; - TransactionInfo[1].NumBytes = 4; + TransactionInfo[TransactionCount].Address = (hPmu->DeviceAddr | 0x1); + TransactionInfo[TransactionCount].Buf = &ReadBuffer[0]; + TransactionInfo[TransactionCount].Flags = 0; + TransactionInfo[TransactionCount++].NumBytes = 4; - status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo[0], 2, - MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); + status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo[0], + TransactionCount, MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); if (status == NvOdmI2cStatus_Success) { - *Data = (ReadBuffer[0] << 24) | (ReadBuffer[1] << 16) | + *Data = (ReadBuffer[0] << 24) | (ReadBuffer[1] << 16) | (ReadBuffer[2] << 8) | ReadBuffer[3]; return NV_TRUE; @@ -216,12 +223,12 @@ NvBool Max8907bI2cRead32( switch (status) { case NvOdmI2cStatus_Timeout: - NVODMPMU_PRINTF(("NvOdmPmuI2cRead32 Failed: Timeout\n")); + NVODMPMU_PRINTF(("NvOdmPmuI2cRead32 Failed: Timeout\n")); break; case NvOdmI2cStatus_SlaveNotFound: default: NVODMPMU_PRINTF(("NvOdmPmuI2cRead32 Failed: SlaveNotFound\n")); - break; + break; } return NV_FALSE; } @@ -233,13 +240,14 @@ NvBool Max8907bRtcI2cWriteTime( { NvU32 i; NvU8 WriteBuffer[5]; - NvOdmI2cStatus status = NvOdmI2cStatus_Success; + NvOdmI2cStatus status = NvOdmI2cStatus_Success; Max8907bPrivData *hPmu = (Max8907bPrivData*)hDevice->pPrivate; NvOdmI2cTransactionInfo TransactionInfo; + NVODMPMU_PRINTF(("\n RTC I2C write: Addr=0x%x, Data=0x%x ", Addr, Data)); for (i = 0; i < MAX8907B_I2C_RETRY_CNT; i++) { - WriteBuffer[0] = (NvU8)(Addr & 0xFF); + WriteBuffer[0] = (NvU8)(Addr & 0xFF); WriteBuffer[1] = (NvU8)((Data >> 24) & 0xFF); WriteBuffer[2] = (NvU8)((Data >> 16) & 0xFF); WriteBuffer[3] = (NvU8)((Data >> 8) & 0xFF); @@ -250,7 +258,7 @@ NvBool Max8907bRtcI2cWriteTime( TransactionInfo.Flags = NVODM_I2C_IS_WRITE; TransactionInfo.NumBytes = 5; - status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo, 1, + status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo, 1, MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); if (status == NvOdmI2cStatus_Success) @@ -261,12 +269,12 @@ NvBool Max8907bRtcI2cWriteTime( switch (status) { case NvOdmI2cStatus_Timeout: - NVODMPMU_PRINTF(("Max8907bRtcI2cWrite32 Failed: Timeout\n")); + NVODMPMU_PRINTF(("Max8907bRtcI2cWrite32 Failed: Timeout\n")); break; case NvOdmI2cStatus_SlaveNotFound: default: NVODMPMU_PRINTF(("Max8907bRtcI2cWrite32 Failed: SlaveNotFound\n")); - break; + break; } return NV_FALSE; } @@ -278,46 +286,63 @@ NvBool Max8907bRtcI2cReadTime( { NvU32 i; NvU8 ReadBuffer[4]; - NvOdmI2cStatus status = NvOdmI2cStatus_Success; + NvOdmI2cStatus status = NvOdmI2cStatus_Success; Max8907bPrivData *hPmu = (Max8907bPrivData*)hDevice->pPrivate; - NvOdmI2cTransactionInfo TransactionInfo[4]; + NvOdmI2cTransactionInfo TransactionInfo[MAX_TRANSACTION_COUNT]; + NVODMPMU_PRINTF(("\n RTC I2C read: Addr=0x%x ", Addr)); for (i = 0; i < MAX8907B_I2C_RETRY_CNT; i++) { - ReadBuffer[0] = Addr++ & 0xFF; - ReadBuffer[1] = Addr++ & 0xFF; - ReadBuffer[2] = Addr++ & 0xFF; - ReadBuffer[3] = 0; - - TransactionInfo[0].Address = MAX8907B_RTC_SLAVE_ADDR; - TransactionInfo[0].Buf = &ReadBuffer[0]; - TransactionInfo[0].Flags = NVODM_I2C_IS_WRITE | NVODM_I2C_USE_REPEATED_START; - TransactionInfo[0].NumBytes = 1; - - // Seconds - TransactionInfo[1].Address = (MAX8907B_RTC_SLAVE_ADDR | 0x1); - TransactionInfo[1].Buf = &ReadBuffer[0]; - TransactionInfo[1].Flags = 0; - TransactionInfo[1].NumBytes = 1; - - // Minutes - TransactionInfo[2].Address = (MAX8907B_RTC_SLAVE_ADDR | 0x1); - TransactionInfo[2].Buf = &ReadBuffer[1]; - TransactionInfo[2].Flags = 0; - TransactionInfo[2].NumBytes = 1; - - // Hours - TransactionInfo[3].Address = (MAX8907B_RTC_SLAVE_ADDR | 0x1); - TransactionInfo[3].Buf = &ReadBuffer[2]; - TransactionInfo[3].Flags = 0; - TransactionInfo[3].NumBytes = 1; - - status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo[0], 4, - MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); + NvU32 TransactionCount = 0; + ReadBuffer[0] = Addr & 0xFF; + TransactionInfo[TransactionCount].Address = MAX8907B_RTC_SLAVE_ADDR; + TransactionInfo[TransactionCount].Buf = &ReadBuffer[0]; + TransactionInfo[TransactionCount].Flags = + NVODM_I2C_IS_WRITE | NVODM_I2C_USE_REPEATED_START; + TransactionInfo[TransactionCount++].NumBytes = 1; + + // Seconds / day + if (TransactionCount >= MAX_TRANSACTION_COUNT) + return NV_FALSE; + TransactionInfo[TransactionCount].Address = + (MAX8907B_RTC_SLAVE_ADDR | 0x1); + TransactionInfo[TransactionCount].Buf = &ReadBuffer[0]; + TransactionInfo[TransactionCount].Flags = 0; + TransactionInfo[TransactionCount++].NumBytes = 1; + + // Minutes / month + if (TransactionCount >= MAX_TRANSACTION_COUNT) + return NV_FALSE; + TransactionInfo[TransactionCount].Address = + (MAX8907B_RTC_SLAVE_ADDR | 0x1); + TransactionInfo[TransactionCount].Buf = &ReadBuffer[1]; + TransactionInfo[TransactionCount].Flags = 0; + TransactionInfo[TransactionCount++].NumBytes = 1; + + // Hours / YY1 + if (TransactionCount >= MAX_TRANSACTION_COUNT) + return NV_FALSE; + TransactionInfo[TransactionCount].Address = + (MAX8907B_RTC_SLAVE_ADDR | 0x1); + TransactionInfo[TransactionCount].Buf = &ReadBuffer[2]; + TransactionInfo[TransactionCount].Flags = 0; + TransactionInfo[TransactionCount++].NumBytes = 1; + + // Weekday / YY2 + if (TransactionCount >= MAX_TRANSACTION_COUNT) + return NV_FALSE; + TransactionInfo[TransactionCount].Address = + (MAX8907B_RTC_SLAVE_ADDR | 0x1); + TransactionInfo[TransactionCount].Buf = &ReadBuffer[3]; + TransactionInfo[TransactionCount].Flags = 0; + TransactionInfo[TransactionCount++].NumBytes = 1; + + status = NvOdmI2cTransaction(hPmu->hOdmI2C, &TransactionInfo[0], + TransactionCount, MAX8907B_I2C_SPEED_KHZ, NV_WAIT_INFINITE); if (status == NvOdmI2cStatus_Success) { - *Data = (ReadBuffer[0] << 24) | (ReadBuffer[1] << 16) | + *Data = (ReadBuffer[0] << 24) | (ReadBuffer[1] << 16) | (ReadBuffer[2] << 8) | ReadBuffer[3]; return NV_TRUE; @@ -328,12 +353,12 @@ NvBool Max8907bRtcI2cReadTime( switch (status) { case NvOdmI2cStatus_Timeout: - NVODMPMU_PRINTF(("Max8907bRtcI2cRead32 Failed: Timeout\n")); + NVODMPMU_PRINTF(("Max8907bRtcI2cRead32 Failed: Timeout\n")); break; case NvOdmI2cStatus_SlaveNotFound: default: NVODMPMU_PRINTF(("Max8907bRtcI2cRead32 Failed: SlaveNotFound\n")); - break; + break; } return NV_FALSE; } diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_rtc.c b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_rtc.c index 0c179efdf21c..50afb66beba3 100644 --- a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_rtc.c +++ b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/max8907b/max8907b_rtc.c @@ -30,151 +30,215 @@ * */ +#include <linux/time.h> +#include <linux/rtc.h> #include "max8907b.h" #include "max8907b_rtc.h" #include "max8907b_i2c.h" #include "max8907b_reg.h" -/** +/** * The Maxim 8907B does not have an RTC that simply counts * seconds from some time t0 (as defined by the OS API). * Instead, this RTC contains several BCD (Binary Coded Decimal) * registers, including: seconds, minutes, hours, days, day of * week, date, etc... These registers account for leap year and * the various days of the month as well. -* +* * Since the OS interpretation of seconds to a particular * date/time from some OS-defined t0 is unknown at this level of * the implementation, it is not possible to translate the given * seconds into these registers (at least, not without a * dependency on some OS-specific information). -* -* Therefore, this implementation contains a static variable -* (RtcDays) which is derived from the number of seconds given -* when Max8907bRtcCountWrite() is called. The seconds, minutes -* and hours are then programmed to the RTC and used to keep -* track of the current time within the day. -* -* TO DO: Increment the day whenever it rolls over (requires -* handling an interrupt at midnight each day). +* */ #define MAX8907B_SECONDS_PER_DAY (60*60*24) #define MAX8907B_SECONDS_PER_HOUR (60*60) #define MAX8907B_SECONDS_PER_MINUTE (60) +#define LINUX_RTC_BASE_YEAR 1900 + static NvBool bRtcNotInitialized = NV_TRUE; -static NvU32 RtcDays = 0; -NvBool +NvBool Max8907bRtcCountRead( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32* Count) { NvU32 data = 0; NvU32 BcdHours, BcdMinutes, BcdSeconds; NvU32 Hours, Minutes, Seconds; + NvU32 BcdDD, BcdMM, BcdYY1, BcdYY2; + NvU32 DD, MM, YY1, YY2, YYYY; + // struct rtc_time tm; - if (Max8907bRtcWasStartUpFromNoPower(hDevice) && bRtcNotInitialized) - { - Max8907bRtcCountWrite(hDevice, 0); - *Count = 0; - } - else + *Count = 0; + // Read seconds, minute, hour and weekday data from RTC registers + if (Max8907bRtcI2cReadTime(hDevice, MAX8907B_RTC_SEC, &data)) { - if (Max8907bRtcI2cReadTime(hDevice, MAX8907B_RTC_SEC, &data)) + NVODMPMU_PRINTF(("\n Read time data-sec=0x%x ", data)); + // Extract seconds, minute and hour data from RTC registers read + BcdHours = (data >> 8) & 0xFF; + BcdMinutes = (data >> 16) & 0xFF; + BcdSeconds = (data >> 24) & 0xFF; + + // Convert BCT time into decimal values + Hours = ((BcdHours & 0xF0)>>4)*10 + (BcdHours & 0xF); + Minutes = ((BcdMinutes & 0xF0)>>4)*10 + (BcdMinutes & 0xF); + Seconds = ((BcdSeconds & 0xF0)>>4)*10 + (BcdSeconds & 0xF); + + // Read day, month, yy1 and yy2 data from RTC registers + if (Max8907bRtcI2cReadTime(hDevice, MAX8907B_RTC_DATE, &data)) { - - BcdHours = (data >> 8) & 0xFF; - BcdMinutes = (data >> 16) & 0xFF; - BcdSeconds = (data >> 24) & 0xFF; - - Hours = ((BcdHours & 0xF0)>>4)*10 + (BcdHours & 0xF); - Minutes = ((BcdMinutes & 0xF0)>>4)*10 + (BcdMinutes & 0xF); - Seconds = ((BcdSeconds & 0xF0)>>4)*10 + (BcdSeconds & 0xF); - - *Count = (Hours * MAX8907B_SECONDS_PER_HOUR) + - (Minutes * MAX8907B_SECONDS_PER_MINUTE) + Seconds; + NVODMPMU_PRINTF(("\n Read time data-year=0x%x ", data)); + // Extract day, month, yy1 and yy2 data from RTC registers read + BcdYY2 = (data & 0xFF); + BcdYY1 = (data >> 8) & 0xFF; + BcdMM = (data >> 16) & 0xFF; + BcdDD = (data >> 24) & 0xFF; + // convert bct day/month/year data to decimal values + YY2 = ((BcdYY2 & 0xF0)>>4)*10 + (BcdYY2 & 0xF); + YY1 = ((BcdYY1 & 0xF0)>>4)*10 + (BcdYY1 & 0xF); + YYYY = (YY2 * 100 + YY1) & 0xFFFF; + MM = ((BcdMM & 0xF0)>>4)*10 + (BcdMM & 0xF); + DD = ((BcdDD & 0xF0)>>4)*10 + (BcdDD & 0xF); + // get seconds since reference time value given + // year, month, day, hour, minutes and seconds + // NOTE: Using linux specific API mktime for conversion + *Count = mktime(YYYY, (MM + 1), DD, Hours, Minutes, Seconds); + NVODMPMU_PRINTF(("\n Rtc read count=0x%x ", *Count)); + NVODMPMU_PRINTF(("\n mktime: YYYY=%d MM=%d DD=%d Hr=%d Min=%d " + "Sec=%d, *Count=0x%x ", YYYY, (MM + 1), DD, Hours, Minutes, + Seconds, *Count)); +#if NV_DEBUG + // Call to verify that reverse conversion of seconds matches date + rtc_time_to_tm(*Count, &tm); + // Check if Local_rtc_time_to_tm can return values sent to mktime + NVODMPMU_PRINTF(("\n rtc_time_to_tm: YYYY=%d MM=%d DD=%d Hr=%d " + "Min=%d Sec=%d, *Count=0x%x ", (tm.tm_year + + LINUX_RTC_BASE_YEAR), tm.tm_mon, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec, *Count)); +#endif } else { - NvOdmOsDebugPrintf("Max8907bRtcCountRead() error. "); + NVODMPMU_PRINTF(("\n Max8907bRtcCountRead() error. ")); return NV_FALSE; } } + else + { + NVODMPMU_PRINTF(("\n Max8907bRtcCountRead() error. ")); + return NV_FALSE; + } + NVODMPMU_PRINTF(("\n *Count=0x%x ", *Count)); return NV_TRUE; } -NvBool +NvBool Max8907bRtcAlarmCountRead( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32* Count) { return NV_FALSE; } -NvBool +NvBool Max8907bRtcCountWrite( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32 Count) { - NvU32 Hours, Minutes, Seconds; NvU32 BcdHours, BcdMinutes, BcdSeconds; NvU32 data = 0; - - BcdHours = BcdMinutes = BcdSeconds = 0; - - RtcDays = Count / MAX8907B_SECONDS_PER_DAY; - - Hours = (Count % MAX8907B_SECONDS_PER_DAY) / MAX8907B_SECONDS_PER_HOUR; - Minutes = ((Count % MAX8907B_SECONDS_PER_DAY) % MAX8907B_SECONDS_PER_HOUR) / MAX8907B_SECONDS_PER_MINUTE; - Seconds = Count % MAX8907B_SECONDS_PER_MINUTE; - - BcdHours = (( Hours/10) << 4) | ( Hours%10); - BcdMinutes = ((Minutes/10) << 4) | (Minutes%10); - BcdSeconds = ((Seconds/10) << 4) | (Seconds%10); + NvU8 BcdDD, BcdMM, BcdYY1, BcdYY2; + NvU16 YYYY; + struct rtc_time tm; + NvU32 data1; + + NVODMPMU_PRINTF(("\n Rtc write count=0x%x ", Count)); + // convert seconds since reference time into date + // NOTE: using linux specific convert function rtc_time_to_tm + rtc_time_to_tm(Count, &tm); + NVODMPMU_PRINTF(("\n rtc_time_to_tm: YYYY=%d MM=%d DD=%d Hr=%d Min=%d " + "Sec=%d, *Count=0x%x ", (tm.tm_year + LINUX_RTC_BASE_YEAR), + (tm.tm_mon + 1), tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, Count)); + + // Convert time to bcd format + BcdHours = ((tm.tm_hour / 10) << 4) | (tm.tm_hour % 10); + BcdMinutes = ((tm.tm_min/10) << 4) | (tm.tm_min%10); + BcdSeconds = ((tm.tm_sec/10) << 4) | (tm.tm_sec%10); data = (BcdSeconds << 24) | (BcdMinutes << 16) | (BcdHours << 8); + // write time - seconds, minutes and hours in a day to RTC registers if (Max8907bRtcI2cWriteTime(hDevice, MAX8907B_RTC_SEC, data)) { - bRtcNotInitialized = NV_FALSE; - return NV_TRUE; + // set the day, month, year + // Assuming we get the days since 1 Jan 1970 + + // convert date to bct format + BcdDD = (((NvU8)tm.tm_mday / 10) << 4) | ((NvU8)tm.tm_mday % 10); + BcdMM = (((NvU8)tm.tm_mon / 10) << 4) | ((NvU8)tm.tm_mon % 10); + YYYY = (NvU16)tm.tm_year + LINUX_RTC_BASE_YEAR; + BcdYY1 = (((NvU8)(YYYY % 100) / 10) << 4) | ((NvU8)(YYYY % 100) % 10); + BcdYY2 = (((NvU8)(YYYY / 100) / 10) << 4) | ((NvU8)(YYYY / 100) % 10); + data = (NvU32)((BcdDD << 24) | (BcdMM << 16) | (BcdYY1 << 8) | BcdYY2); + // write date - day, month, and year to RTC registers + if (!(Max8907bRtcI2cWriteTime(hDevice, MAX8907B_RTC_DATE, data))) + { + NVODMPMU_PRINTF(("\n Max8907bRtcCountWrite() error. ")); + return NV_FALSE; + } +#if NV_DEBUG + // verify that read back values from RTC matches written values + if (!(Max8907bRtcI2cReadTime(hDevice, MAX8907B_RTC_DATE, &data1))) + { + NVODMPMU_PRINTF(("\n Max8907bRtcCountRead() error. ")); + return NV_FALSE; + } + if (data1 == data) + { + NVODMPMU_PRINTF(("\n Write read Success. ")); + return NV_TRUE; + } + else + { + // return error when read data does not match written data + NVODMPMU_PRINTF(("\n Error: write data=0x%x, rd data=0x%x. ", data, data1)); + return NV_FALSE; + } +#endif } else { - NvOdmOsDebugPrintf("Max8907bRtcCountWrite() error. "); + NVODMPMU_PRINTF(("\n Max8907bRtcCountWrite() error. ")); return NV_FALSE; } } -NvBool +NvBool Max8907bRtcAlarmCountWrite( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32 Count) { return NV_FALSE; } -NvBool +NvBool Max8907bRtcIsAlarmIntEnabled(NvOdmPmuDeviceHandle hDevice) { return NV_FALSE; } -NvBool +NvBool Max8907bRtcAlarmIntEnable( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvBool Enable) { return NV_FALSE; } -NvBool -Max8907bRtcWasStartUpFromNoPower(NvOdmPmuDeviceHandle hDevice) -{ - return NV_TRUE; -} - NvBool Max8907bIsRtcInitialized(NvOdmPmuDeviceHandle hDevice) { diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/tps6586x/nvodm_pmu_tps6586x_rtc.c b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/tps6586x/nvodm_pmu_tps6586x_rtc.c index d897eb9f4ee3..29f52e2d7ae0 100644 --- a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/tps6586x/nvodm_pmu_tps6586x_rtc.c +++ b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/tps6586x/nvodm_pmu_tps6586x_rtc.c @@ -30,10 +30,20 @@ * */ +#include <linux/time.h> #include "nvodm_pmu_tps6586x_rtc.h" #include "nvodm_pmu_tps6586x_i2c.h" #include "tps6586x_reg.h" +// macro SHIFT_TO_2009 if 1, uses 2009 as reference year instead of 1970 +// This is because RTC in PMU TPS6586x can store duration of 34 years, +// else we cannot retain date beyond 2004 +#define SHIFT_TO_2009 1 +#if SHIFT_TO_2009 +static unsigned long epoch = 2009; +static unsigned long epoch_sec = 0; +#endif + static NvBool bRtcNotInitialized = NV_TRUE; /* Read RTC count register */ @@ -43,12 +53,12 @@ Tps6586xRtcCountRead( NvU32* Count) { NvU32 ReadBuffer[2]; - + // 1) The I2C address pointer must not be left pointing in the range 0xC6 to 0xCA // 2) The maximum time for the address pointer to be in this range is 1ms // 3) Always read RTC_ALARM2 in the following order to prevent the address pointer - // from stopping at 0xC6: RTC_ALARM2_LO, then RTC_ALARM2_HI - + // from stopping at 0xC6: RTC_ALARM2_LO, then RTC_ALARM2_HI + if (Tps6586xRtcWasStartUpFromNoPower(hDevice) && bRtcNotInitialized) { Tps6586xRtcCountWrite(hDevice, 0); @@ -59,55 +69,76 @@ Tps6586xRtcCountRead( // The unit of the RTC count is second!!! 1024 tick = 1s. // Read all 40 bit and right move 10 = Read the hightest 32bit and right move 2 Tps6586xI2cRead32(hDevice, TPS6586x_RC6_RTC_COUNT4, &ReadBuffer[0]); - + Tps6586xI2cRead8(hDevice, TPS6586x_RCA_RTC_COUNT0, &ReadBuffer[1]); - + Tps6586xI2cRead8(hDevice, TPS6586x_RC0_RTC_CTRL, &ReadBuffer[1]); - + // return second *Count = ReadBuffer[0]>>2; } - +#if SHIFT_TO_2009 + // calculate epoch_sec once + if (!epoch_sec) + epoch_sec = mktime(epoch,1,1,0,0,0); + *Count += epoch_sec; +#endif + return NV_TRUE; } /* Write RTC count register */ -NvBool +NvBool Tps6586xRtcCountWrite( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32 Count) { NvU32 ReadBuffer = 0; - +#if SHIFT_TO_2009 + // calculate epoch_sec once + if (!epoch_sec) + epoch_sec = mktime(epoch,1,1,0,0,0); + if (Count < (NvU32)epoch_sec) + { + // prevent setting date earlier than 'epoch' + pr_warning("\n Date being set cannot be earlier than least year=%d. " + "Setting as least year. ", (int)epoch); + // base year seconds count is 0 + Count = 0; + } + else + Count -= (NvU32)epoch_sec; +#endif + // To enable incrementing of the RTC_COUNT[39:0] from an initial value set by the host, - // the RTC_ENABLE bit should be written to 1 only after the RTC_OUT voltage reaches + // the RTC_ENABLE bit should be written to 1 only after the RTC_OUT voltage reaches // the operating range - + // Clear RTC_ENABLE before writing RTC_COUNT Tps6586xI2cRead8(hDevice, TPS6586x_RC0_RTC_CTRL, &ReadBuffer); ReadBuffer = ReadBuffer & 0xDF; Tps6586xI2cWrite8(hDevice, TPS6586x_RC0_RTC_CTRL, ReadBuffer); - + Tps6586xI2cWrite32(hDevice, TPS6586x_RC6_RTC_COUNT4, (Count<<2)); - Tps6586xI2cWrite8(hDevice, TPS6586x_RCA_RTC_COUNT0, 0); - + Tps6586xI2cWrite8(hDevice, TPS6586x_RCA_RTC_COUNT0, 0); + // Set RTC_ENABLE after writing RTC_COUNT Tps6586xI2cRead8(hDevice, TPS6586x_RC0_RTC_CTRL, &ReadBuffer); ReadBuffer = ReadBuffer | 0x20; Tps6586xI2cWrite8(hDevice, TPS6586x_RC0_RTC_CTRL, ReadBuffer); - + if (bRtcNotInitialized) bRtcNotInitialized = NV_FALSE; - + return NV_TRUE; } /* Read RTC alarm count register */ -NvBool +NvBool Tps6586xRtcAlarmCountRead( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32* Count) { return NV_FALSE; @@ -115,9 +146,9 @@ Tps6586xRtcAlarmCountRead( /* Write RTC alarm count register */ -NvBool +NvBool Tps6586xRtcAlarmCountWrite( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvU32 Count) { return NV_FALSE; @@ -125,7 +156,7 @@ Tps6586xRtcAlarmCountWrite( /* Reads RTC alarm interrupt mask status */ -NvBool +NvBool Tps6586xRtcIsAlarmIntEnabled(NvOdmPmuDeviceHandle hDevice) { return NV_FALSE; @@ -133,9 +164,9 @@ Tps6586xRtcIsAlarmIntEnabled(NvOdmPmuDeviceHandle hDevice) /* Enables / Disables the RTC alarm interrupt */ -NvBool +NvBool Tps6586xRtcAlarmIntEnable( - NvOdmPmuDeviceHandle hDevice, + NvOdmPmuDeviceHandle hDevice, NvBool Enable) { return NV_FALSE; @@ -143,7 +174,7 @@ Tps6586xRtcAlarmIntEnable( /* Checks if boot was from nopower / powered state */ -NvBool +NvBool Tps6586xRtcWasStartUpFromNoPower(NvOdmPmuDeviceHandle hDevice) { NvU32 Data = 0; diff --git a/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c b/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c index 7aded70cbb6c..ed5e81da1d38 100644 --- a/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c +++ b/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c @@ -1187,7 +1187,7 @@ NvOdmQueryGetUsbProperty(NvOdmIoModule OdmIoModule, (NvOdmUsbChargerType_SE0 | NvOdmUsbChargerType_SE1 | NvOdmUsbChargerType_SK), 20, NV_TRUE, - NvOdmUsbModeType_Device, + NvOdmUsbModeType_OTG, NvOdmUsbIdPinType_CableId, NvOdmUsbConnectorsMuxType_None, NV_FALSE diff --git a/arch/arm/mach-tegra/pci-enum.c b/arch/arm/mach-tegra/pci-enum.c index 4f8c614ff8fc..1bcf659fe696 100644 --- a/arch/arm/mach-tegra/pci-enum.c +++ b/arch/arm/mach-tegra/pci-enum.c @@ -494,7 +494,7 @@ static void pci_tegra_setup_pci_device(struct pci_tegra_device *dev) } dev->res[bar_index].flags = IORESOURCE_MEM; - dev->res[bar_index].start = 0; + dev->res[bar_index].start = addr; dev->res[bar_index].end = dev->res[bar_index].start + size - 1; @@ -522,7 +522,7 @@ static void pci_tegra_setup_pci_device(struct pci_tegra_device *dev) /* Handle 64 bit addresses by forcing to 32 bit addresses */ if ((flags == 0x0c) || (flags==0x04)) { bar_index++; - BUG_ON(bar_index < 6); + BUG_ON(bar_index > PCI_STD_RESOURCE_END); pci_conf_write32(dev->bus, dev->devfn, bar_index * 4 + PCI_BASE_ADDRESS_0, 0); } diff --git a/arch/arm/mach-tegra/pci.c b/arch/arm/mach-tegra/pci.c index c4ced311e223..5118692b1b79 100644 --- a/arch/arm/mach-tegra/pci.c +++ b/arch/arm/mach-tegra/pci.c @@ -33,6 +33,7 @@ #include <mach/pci.h> #include <mach/nvrm_linux.h> +#include <mach/board.h> #include "nvrm_pmu.h" #include "nvodm_query_discovery.h" @@ -362,7 +363,7 @@ static int __init pci_tegra_setup(int nr, struct pci_sys_data *data) 0, NULL); NvRmPowerModuleClockControl(s_hRmGlobal, NvRmPrivModuleID_Pcie, pci_tegra_powerid, NV_FALSE); - pci_tegra_power(0); + tegra_set_voltage(NV_VDD_PEX_CLK_ODM_ID, 0); return 0; } pci_tegra_device_attached = true; @@ -402,12 +403,21 @@ static struct pci_bus __init *pci_tegra_scan_bus(int nr, return NULL; } +int pci_tegra_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int ret = 0; + pci_tegra_read_conf(dev->bus, dev->devfn, PCI_INTERRUPT_LINE, + sizeof(int), &ret); + return (ret & 0x000000ff); +} + static struct hw_pci pci_tegra_data __initdata = { .nr_controllers = 2, .preinit = pci_tegra_preinit, .setup = pci_tegra_setup, .scan = pci_tegra_scan_bus, .swizzle = pci_std_swizzle, + .map_irq = pci_tegra_map_irq, }; late_initcall(pcie_tegra_init); diff --git a/arch/arm/mach-tegra/power-lp.S b/arch/arm/mach-tegra/power-lp.S index 48c09640fd30..acc7645e34c1 100644 --- a/arch/arm/mach-tegra/power-lp.S +++ b/arch/arm/mach-tegra/power-lp.S @@ -66,7 +66,9 @@ wait_for_other_cores: beq finish_power_state //Save the local timers + stmfd sp!, {r1} bl save_local_timers + ldmfd sp!, {r1} //Ok we can save state for core0 now save_arm_state: diff --git a/arch/arm/mach-tegra/tegra_rfkill_odm.c b/arch/arm/mach-tegra/tegra_rfkill_odm.c index ff4ebfb4361d..0ef0f060b9b0 100644 --- a/arch/arm/mach-tegra/tegra_rfkill_odm.c +++ b/arch/arm/mach-tegra/tegra_rfkill_odm.c @@ -34,7 +34,7 @@ #define DRIVER_DESC "Nvidia Tegra rfkill" static NvRmGpioHandle hGpio = NULL; -static NvRmGpioPinHandle hBlueToothPowerPin = 0; +static NvRmGpioPinHandle hBlueToothResetPin = 0; static NvU32 blueToothPowerRailId = 0xff; static struct rfkill *bt = NULL; @@ -47,7 +47,7 @@ static int bluetooth_set_power(void *data, enum rfkill_state state) NvU32 settletime = 0; NvU32 GpioLevel; - if (blueToothPowerRailId == 0xff || !hBlueToothPowerPin || !hGpio) + if (blueToothPowerRailId == 0xff || !hBlueToothResetPin || !hGpio) return -ENXIO; hPmu = NvOdmServicesPmuOpen(); @@ -58,21 +58,24 @@ static int bluetooth_set_power(void *data, enum rfkill_state state) case RFKILL_STATE_UNBLOCKED: NvOdmServicesPmuGetCapabilities(hPmu, blueToothPowerRailId, &vddrailcap); - NvOdmServicesPmuSetVoltage(hPmu, blueToothPowerRailId, + NvOdmServicesPmuSetVoltage(hPmu, blueToothPowerRailId, vddrailcap.requestMilliVolts, &settletime); if (settletime) NvOdmOsWaitUS(settletime); - /* Enable power */ + /* Pulse a reset */ GpioLevel = 0; - NvRmGpioWritePins(hGpio, &hBlueToothPowerPin, &GpioLevel, 1); + NvRmGpioWritePins(hGpio, &hBlueToothResetPin, &GpioLevel, 1); /* Configure as output */ - NvRmGpioConfigPins(hGpio, &hBlueToothPowerPin, 1, + NvRmGpioConfigPins(hGpio, &hBlueToothResetPin, 1, NvRmGpioPinMode_Output); - + + /* Give 5 milli seconds for the reset pulse */ + NvOdmOsSleepMS(5); + GpioLevel = 1; - NvRmGpioWritePins(hGpio, &hBlueToothPowerPin, &GpioLevel, 1); + NvRmGpioWritePins(hGpio, &hBlueToothResetPin, &GpioLevel, 1); printk(KERN_INFO "Bluetooth power ON\n"); break; @@ -80,13 +83,13 @@ static int bluetooth_set_power(void *data, enum rfkill_state state) case RFKILL_STATE_SOFT_BLOCKED: /* Disable power */ GpioLevel = 0; - NvRmGpioWritePins(hGpio, &hBlueToothPowerPin, &GpioLevel, 1); + NvRmGpioWritePins(hGpio, &hBlueToothResetPin, &GpioLevel, 1); /* Configure as output */ - NvRmGpioConfigPins(hGpio, &hBlueToothPowerPin, 1, + NvRmGpioConfigPins(hGpio, &hBlueToothResetPin, 1, NvRmGpioPinMode_Output); - NvOdmServicesPmuSetVoltage( hPmu, blueToothPowerRailId, + NvOdmServicesPmuSetVoltage( hPmu, blueToothPowerRailId, NVODM_VOLTAGE_OFF, &settletime); if (settletime) NvOdmOsWaitUS(settletime); @@ -112,7 +115,7 @@ static int __init tegra_rfkill_probe(struct platform_device *pdev) NvU64 bluetooth = NV_ODM_GUID('b','l','u','t','o','o','t','h'); /* conn will be null if bluetooth is not present. */ - conn = + conn = NvOdmPeripheralGetGuid(bluetooth); if (!conn) return -ENXIO; @@ -121,7 +124,7 @@ static int __init tegra_rfkill_probe(struct platform_device *pdev) if (conn->AddressList[i].Interface == NvOdmIoModule_Gpio) { port = conn->AddressList[i].Instance; pin = conn->AddressList[i].Address; - } + } if (conn->AddressList[i].Interface == NvOdmIoModule_Vdd) { blueToothPowerRailId = conn->AddressList[i].Address; } @@ -137,7 +140,7 @@ static int __init tegra_rfkill_probe(struct platform_device *pdev) printk(KERN_ERR "NvRmGpioOpen failed\n"); return -ENXIO; } - err = NvRmGpioAcquirePinHandle(hGpio, port, pin, &hBlueToothPowerPin); + err = NvRmGpioAcquirePinHandle(hGpio, port, pin, &hBlueToothResetPin); if (err) { printk(KERN_ERR "NvRmGpioAcquirePinHandle failed\n"); NvRmGpioClose(hGpio); @@ -146,7 +149,7 @@ static int __init tegra_rfkill_probe(struct platform_device *pdev) rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); - + bt = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); if (!bt) { rc = -ENOMEM; @@ -163,12 +166,12 @@ static int __init tegra_rfkill_probe(struct platform_device *pdev) rc = rfkill_register(bt); if (rc) goto fail; - + return rc; fail: - if (hBlueToothPowerPin) - NvRmGpioReleasePinHandles(hGpio, &hBlueToothPowerPin, 1); + if (hBlueToothResetPin) + NvRmGpioReleasePinHandles(hGpio, &hBlueToothResetPin, 1); if (hGpio) NvRmGpioClose(hGpio); @@ -183,8 +186,8 @@ static int __init tegra_rfkill_remove(struct platform_device *pdev) rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); - if (hBlueToothPowerPin) - NvRmGpioReleasePinHandles(hGpio, &hBlueToothPowerPin, 1); + if (hBlueToothResetPin) + NvRmGpioReleasePinHandles(hGpio, &hBlueToothResetPin, 1); if (hGpio) NvRmGpioClose(hGpio); @@ -214,4 +217,3 @@ static int __init tegra_rfkill_init(void) module_init(tegra_rfkill_init); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); - diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 3f8a5bb69e42..aca32075e12d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -37,6 +37,18 @@ config MMC_SDHCI If unsure, say N. +config MMC_SDHCI_DYNAMIC_SDMEM_CLOCK + depends on MMC_SDHCI + bool "Dynamically control the card clock for SD memory devices" + default y + help + On certain embedded devices, leaving the card clock enabled to + SD memory devices with no active transactions can increase power + consumption. Enable this option to automatically disable the card + clock after transfers complete. + + If unsure, say N here. + config MMC_SDHCI_PCI tristate "SDHCI support on PCI bus" depends on MMC_SDHCI && PCI diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8bb9366bbf0c..a0591db8c152 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/dma-mapping.h> #include <linux/scatterlist.h> +#include <linux/mmc/card.h> #include <linux/leds.h> @@ -134,6 +135,9 @@ static void sdhci_init(struct sdhci_host *host) writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); +#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK + host->last_clock = 0; +#endif } static void sdhci_activate_led(struct sdhci_host *host) @@ -909,7 +913,9 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == 0) goto out; - +#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK + host->last_clock = clock; +#endif div = 0; if (host->ops->set_clock) div = host->ops->set_clock(host, clock); @@ -1010,6 +1016,15 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) unsigned long flags; host = mmc_priv(mmc); +#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK + if (host->mmc->card != NULL) { + if (host->mmc->card->type != MMC_TYPE_SDIO) { + if (host->last_clock) + /* Enable clock */ + sdhci_set_clock(host, host->last_clock); + } + } +#endif spin_lock_irqsave(&host->lock, flags); @@ -1240,7 +1255,14 @@ static void sdhci_tasklet_finish(unsigned long param) mmiowb(); spin_unlock_irqrestore(&host->lock, flags); - +#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK + /* Disable clock */ + if (host->mmc->card != NULL) { + if (host->mmc->card->type != MMC_TYPE_SDIO) { + sdhci_set_clock(host, 0); + } + } +#endif mmc_request_done(host->mmc, mrq); } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5805e301e604..fe800af6b7d0 100644..100755 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -58,7 +58,7 @@ #define SDHCI_CARD_PRESENT 0x00010000 #define SDHCI_WRITE_PROTECT 0x00080000 -#define SDHCI_HOST_CONTROL 0x28 +#define SDHCI_HOST_CONTROL 0x28 #define SDHCI_CTRL_LED 0x01 #define SDHCI_CTRL_4BITBUS 0x02 #define SDHCI_CTRL_8BITBUS 0x20 @@ -246,6 +246,9 @@ struct sdhci_host { unsigned int timeout_clk; /* Timeout freq (KHz) */ unsigned int clock; /* Current clock (MHz) */ +#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK + unsigned int last_clock; /* Last used clock (MHz) */ +#endif unsigned short power; /* Current voltage */ struct mmc_request *mrq; /* Current request */ diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index fac5e5eff24f..e58473a88fa5 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -47,9 +47,15 @@ #include "nvrm_interrupt.h" #include "nvrm_power.h" -#define UART_RX_DMA_PING_BUFFER_SIZE 0x800 -static int use_dma = 1; +#define TX_EMPTY_STATUS (NV_DRF_DEF(UART, LSR, TMTY, EMPTY) | \ + NV_DRF_DEF(UART, LSR, THRE, EMPTY)) + +#define UART_RX_DMA_BUFFER_SIZE (4 * 1024 * 16) + +static int tx_force_pio = 0; +static int rx_force_pio = 0; + struct tegra_uart_port { struct uart_port uport; @@ -62,6 +68,7 @@ struct tegra_uart_port { NvOsPhysAddr phys; NvU32 size; struct clk *clk; + unsigned int baud; /* Register shadow */ unsigned char fcr_shadow; @@ -85,7 +92,7 @@ struct tegra_uart_port { int tx_dma; /* DMA requests */ - struct tegra_dma_req rx_dma_req[2]; + struct tegra_dma_req rx_dma_req; int rx_dma; struct tegra_dma_req tx_dma_req; @@ -95,12 +102,45 @@ struct tegra_uart_port { int rx_pio_buffer_size; bool use_rx_dma; - bool dma_for_tx; + bool use_tx_dma; + + bool tx_pio_inflight; + + struct work_struct work; + struct workqueue_struct *work_queue; - struct tasklet_struct tasklet; }; static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud); +static void tegra_set_mctrl(struct uart_port *u, unsigned int mctrl); +static void do_handle_rx_pio(struct uart_port *u); +static inline bool tegra_wait_for_tx_fifo_empty(struct uart_port *u); + +/* + * Attempts to wait for some time before to make sure that the data is drained. + * Assumes that the caller has taken the u->lock. + * + * */ +static inline bool tegra_wait_for_tx_fifo_empty(struct uart_port *u) +{ + /* FIXME compute the correct value based on the baud rate */ + struct tegra_uart_port *t; + int timeout = 10; + unsigned char lsr; + + t = container_of(u, struct tegra_uart_port, uport); + do { + lsr = readb(t->regs + UART_LSR_0); + if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) + return true; + spin_unlock(&u->lock); + msleep(1); + spin_lock(&u->lock); + timeout--; + } while (timeout); + + return false; +} static inline int tegra_uart_isbreak(struct uart_port *u) { @@ -122,22 +162,120 @@ static inline int tegra_uart_isbreak(struct uart_port *u) return 0; } -static inline void tegra_uart_set_rts(struct tegra_uart_port *t, - int logical_value) +void tegra_rx_dma_threshold_callback(struct tegra_dma_req *req, int err) { - unsigned char mcr; + struct uart_port *u = req->data; + struct tegra_uart_port *t; + unsigned long flags; - /* To set to logical value 0, set the bit to 1 and vice versa */ - mcr = t->mcr_shadow; - if (logical_value) - mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_LOW, mcr); - else - mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_HI, mcr); + t = container_of(u, struct tegra_uart_port, uport); - if (mcr != t->mcr_shadow) { - writeb(mcr, t->regs + UART_MCR_0); - t->mcr_shadow = mcr; + spin_lock_irqsave(&u->lock, flags); + + u->mctrl &= ~TIOCM_RTS; + u->mctrl &= ~TIOCM_DTR; + tegra_set_mctrl(u, u->mctrl); + + tegra_dma_dequeue(t->rx_dma); + + u->mctrl |= TIOCM_RTS; + u->mctrl |= TIOCM_DTR; + tegra_set_mctrl(u, u->mctrl); + + spin_unlock_irqrestore(&u->lock, flags); +} + +/* It is expected that the callers take the UART lock when this API is called. + * + * There are 2 contexts when this function is called: + * + * 1. DMA ISR - DMA ISR triggers the threshold complete calback, which calls the + * dequue API which in-turn calls this callback. UART lock is taken during + * the call to the threshold callback. + * + * 2. UART ISR - UART calls the dequue API which in-turn will call this API. + * In this case, UART ISR takes the UART lock. + * */ +void tegra_rx_dma_complete_callback(struct tegra_dma_req *req, int err) +{ + struct uart_port *u = req->data; + struct tegra_uart_port *t; + struct tty_struct *tty = u->info->port.tty; + + /* If we are here, DMA is stopped */ + + t = container_of(u, struct tegra_uart_port, uport); + if (req->bytes_transferred) { + t->uport.icount.rx += req->bytes_transferred; + tty_insert_flip_string(tty, + ((unsigned char *)(req->virt_addr)), + req->bytes_transferred); + dev_dbg(u->dev, "Received %d bytes\n", req->bytes_transferred); + } + + if (req->status == -TEGRA_DMA_REQ_ERROR_ABOTRED) { + do_handle_rx_pio(u); } + + spin_unlock(&u->lock); + tty_flip_buffer_push(u->info->port.tty); + spin_lock(&u->lock); + + /* Enqueue the request again */ + tegra_dma_enqueue_req(t->rx_dma, req); +} + +/* Lock already taken */ +static void do_handle_rx_dma(struct uart_port *u) +{ + struct tegra_uart_port *t; + + t = container_of(u, struct tegra_uart_port, uport); + + u->mctrl &= ~TIOCM_RTS; + u->mctrl &= ~TIOCM_DTR; + tegra_set_mctrl(u, u->mctrl); + + tegra_dma_dequeue(t->rx_dma); + + u->mctrl |= TIOCM_RTS; + u->mctrl |= TIOCM_DTR; + tegra_set_mctrl(u, u->mctrl); + +} + +static char do_decode_rx_error(struct uart_port *u) +{ + struct tegra_uart_port *t; + char flag = TTY_NORMAL; + unsigned char lsr; + + t = container_of(u, struct tegra_uart_port, uport); + + lsr = readb(t->regs + UART_LSR_0); + if (lsr & NV_DRF_DEF(UART, LSR, OVRF, OVERRUN_ERROR)) { + /* Overrrun error */ + flag |= TTY_OVERRUN; + t->uport.icount.overrun++; + dev_err(u->dev, "Got overrun errors\n"); + } else if (lsr & NV_DRF_DEF(UART, LSR, PERR, PARITY_ERR)) { + /* Parity error */ + flag |= TTY_PARITY; + t->uport.icount.parity++; + dev_err(u->dev, "Got Parity errors\n"); + } else if (lsr & NV_DRF_DEF(UART, LSR, FERR, FRAME_ERR)) { + flag |= TTY_FRAME; + dev_err(u->dev, "Got frame errors\n"); + } else if (lsr & NV_DRF_DEF(UART, LSR, BRK, BREAK)) { + dev_err(u->dev, "Got Break \n"); + /* If FIFO read error without any data, reset Rx FIFO */ + if (!(lsr & 0x1) && (lsr & 0x80)) { + unsigned char fcr = t->fcr_shadow; + fcr |= 0x2; + writeb(fcr, t->regs + UART_IIR_FCR_0); + } + } + return flag; } static void do_handle_rx_pio(struct uart_port *u) @@ -152,115 +290,116 @@ static void do_handle_rx_pio(struct uart_port *u) unsigned char lsr; unsigned char ch; - lsr = readb(t->regs + UART_LSR_0); - if (lsr & NV_DRF_DEF(UART, LSR, OVRF, OVERRUN_ERROR)) { - /* Overrrun error */ - flag |= TTY_OVERRUN; - t->uport.icount.overrun++; - dev_err(u->dev, "Got overrun errors\n"); - } else if (lsr & NV_DRF_DEF(UART, LSR, PERR, PARITY_ERR)) { - /* Parity error */ - flag |= TTY_PARITY; - t->uport.icount.parity++; - dev_err(u->dev, "Got Parity errors\n"); - } else if (lsr & NV_DRF_DEF(UART, LSR, FERR, FRAME_ERR)) { - flag |= TTY_FRAME; - dev_err(u->dev, "Got frame errors\n"); - } else if (lsr & NV_DRF_DEF(UART, LSR, BRK, BREAK)) { - dev_err(u->dev, "Got Break \n"); - /* If FIFO read error without any data, reset Rx FIFO */ - if (!(lsr & 0x1) && (lsr & 0x80)) { - unsigned char fcr = t->fcr_shadow; - fcr |= 0x2; - writeb(fcr, t->regs + UART_IIR_FCR_0); - } - } else if (lsr & NV_DRF_DEF(UART, LSR, FIFOE, ERR)) { - unsigned char fcr; - fcr = t->fcr_shadow; - fcr = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, - RX_CLR, CLEAR, fcr); - writeb(fcr, t->regs + UART_IIR_FCR_0); - dev_err(u->dev, "Got fifo errors\n"); - } + flag = do_decode_rx_error(u); + lsr = readb(t->regs + UART_LSR_0); if (!(lsr & NV_DRF_DEF(UART, LSR, RDR, DATA_IN_FIFO))) break; ch = readb(t->regs + UART_THR_DLAB_0_0); dev_vdbg(u->dev, "%c\n", ch); - if (!uart_handle_sysrq_char(u, c)) + if (!uart_handle_sysrq_char(u, c)) { tty_insert_flip_char(tty, ch, flag); + t->uport.icount.rx ++; + } } while (1); return; } -static void tegra_dma_tasklet_func(unsigned long data) + +static void tegra_tx_dma_workqueue(struct work_struct *w) { - struct uart_port *u = (struct uart_port *)data; + struct uart_port *u; struct tegra_uart_port *t; - struct circ_buf *xmit = &u->info->xmit; + struct circ_buf *xmit; unsigned int count; unsigned int to_send; unsigned long flags; - t = container_of(u, struct tegra_uart_port, uport); + t = container_of(w, struct tegra_uart_port, work); + u = &t->uport; + xmit = &u->info->xmit; + spin_lock_irqsave(&u->lock, flags); - if (uart_circ_chars_pending(xmit)) { - dma_sync_single_for_device(u->dev, t->tx_dma_phys, - t->tx_dma_size, DMA_BIDIRECTIONAL); + t = container_of(u, struct tegra_uart_port, uport); + + /* DMA request is already queued, just return */ + if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req)) { + spin_unlock_irqrestore(&u->lock, flags); + return; + } - to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail, - UART_XMIT_SIZE); + /* DMA just finished. Wait for the FIFO to drain. */ + if (t->tx_dma_req.size) { + bool empty; + empty = tegra_wait_for_tx_fifo_empty(u); + BUG_ON(empty != true); + } - /* DMA can only handle 4 byte aligned trasfers. So, align the - * size */ - count = (to_send >> 2) << 2; - if (count && !(xmit->tail & 0x3)) { - dev_vdbg(u->dev, "Tx DMA starting 0x%x size %d\n", - xmit->tail, count); - t->tx_dma_req.source_addr = t->tx_dma_phys + xmit->tail; - t->tx_dma_req.size = count; + /* Update the DMA tail pointer */ + xmit->tail += t->tx_dma_req.size; + xmit->tail &= UART_XMIT_SIZE - 1; + u->icount.tx += t->tx_dma_req.size; + t->tx_dma_req.size = 0; - xmit->tail += count; - xmit->tail &= UART_XMIT_SIZE - 1; - u->icount.tx += count; + /* PIO is in flight. Just return */ + if (t->tx_pio_inflight == true) { + spin_unlock_irqrestore(&u->lock, flags); + return; + } - tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req); + if (uart_circ_empty(xmit)) { + spin_unlock_irqrestore(&u->lock, flags); + return; + } - /* Just return as the rest of the chars, if any, will - * be scheduled when the DMA is completed */ - spin_unlock_irqrestore(&u->lock, flags); - return; + dma_sync_single_for_device(u->dev, t->tx_dma_phys, + t->tx_dma_size, DMA_TO_DEVICE); - } else { - /* Use PIO for cases that cannot be handled by the DMA. - * FIXME: We need to optimize this. - * */ - if (tegra_dma_is_empty(t->tx_dma)) { - count = to_send & 0x3; - while (count) { - unsigned char lsr; - - lsr = readb(t->regs + UART_LSR_0); - if (!(lsr & (NV_DRF_DEF(UART, LSR, - TMTY, EMPTY)))) - goto end; - writeb(xmit->buf[xmit->tail], - t->regs + UART_THR_DLAB_0_0); - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); - u->icount.tx++; - count--; - } - } + to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail, + UART_XMIT_SIZE); + + /* DMA can only handle 4 byte aligned trasfers. So, align the + * size */ + count = (to_send >> 2) << 2; + if (count && !(xmit->tail & 0x3)) { + dev_dbg(u->dev, "Tx DMA starting 0x%x size %d\n", + xmit->tail, count); + + t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG, + FIFO_COUNT_GREATER_4, t->fcr_shadow); + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); + + t->tx_dma_req.source_addr = t->tx_dma_phys + xmit->tail; + t->tx_dma_req.size = count; + t->tx_pio_inflight = false; + + tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(u); + spin_unlock_irqrestore(&u->lock, flags); + return; + + } else { + /* Trasnfer in PIO mode */ + + t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG, + FIFO_COUNT_GREATER_8, t->fcr_shadow); + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); + + if (!t->tx_pio_inflight) { + t->tx_pio_inflight = true; + t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, + IE_THR, ENABLE, t->ier_shadow); + writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0); } + spin_unlock_irqrestore(&u->lock, flags); + return; + } -end: - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(u); - spin_unlock_irqrestore(&u->lock, flags); } static void do_handle_modem_signal(struct uart_port *u) @@ -299,6 +438,7 @@ static void do_handle_tx_pio(struct uart_port *u) t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_THR, DISABLE, t->ier_shadow); writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0); + t->tx_pio_inflight = false; break; } @@ -314,52 +454,13 @@ static void do_handle_tx_pio(struct uart_port *u) void tegra_tx_dma_complete_callback(struct tegra_dma_req *req, int err) { - tasklet_schedule((struct tasklet_struct *)req->data); -} - -void do_handle_rx_dma(struct uart_port *u, int eord_int) -{ + struct uart_port *u = (struct uart_port *)req->data; struct tegra_uart_port *t; - unsigned char ier; - - t = container_of(u, struct tegra_uart_port, uport); - if (eord_int) { - /* As per hw spec, to clear EORD interrupt, we need to disable - * and then re-enable the interrupt. - */ - ier = t->ier_shadow; - ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD, - DISABLE, ier); - writeb(ier, t->regs + UART_IER_DLAB_0_0); - ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD, - ENABLE, ier); - writeb(ier, t->regs + UART_IER_DLAB_0_0); - } - tegra_dma_dequeue(t->rx_dma); -} - -void tegra_rx_dma_complete_callback(struct tegra_dma_req *req, int err) -{ - struct uart_port *u = req->data; - struct tegra_uart_port *t; - struct tty_struct *tty = u->info->port.tty; - - t = container_of(u, struct tegra_uart_port, uport); - if (req->bytes_transferred) { - tty_insert_flip_string(tty, - ((unsigned char *)(req->virt_addr)), - req->bytes_transferred); - } - - if (req->status == -TEGRA_DMA_REQ_ERROR_ABOTRED) { - /* Drain the FIFO entries, only for the aborted requests */ - do_handle_rx_pio(u); - } - - tty_flip_buffer_push(u->info->port.tty); + if (err == -TEGRA_DMA_REQ_ERROR_ABOTRED) + return; - /* Enqueue the request again */ - tegra_dma_enqueue_req(t->rx_dma, req); + t = container_of(u, struct tegra_uart_port, uport); + queue_work(t->work_queue, &t->work); } static irqreturn_t tegra_uart_isr(int irq, void *data) @@ -367,12 +468,15 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) struct uart_port *u = (struct uart_port *)data; struct tegra_uart_port *t; unsigned char iir_fcr; + unsigned char ier; + spin_lock(&u->lock); t = container_of(u, struct tegra_uart_port, uport); /* FIXME why do we need to loop here? */ while (1) { iir_fcr = readb(t->regs + UART_IIR_FCR_0); if (iir_fcr & NV_DRF_DEF(UART, IIR_FCR, IS_STA, NO_INTR_PEND)) { + spin_unlock(&u->lock); return IRQ_HANDLED; } @@ -384,20 +488,34 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) case 1: /* Transmit interrupt only triggered when using PIO */ do_handle_tx_pio(u); break; + case 4: /* End of data */ + /* As per hw spec, to clear EORD interrupt, we need + * to disable and then re-enable the interrupt. + */ + ier = t->ier_shadow; + ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD, + DISABLE, ier); + writeb(ier, t->regs + UART_IER_DLAB_0_0); + ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD, + ENABLE, ier); + writeb(ier, t->regs + UART_IER_DLAB_0_0); + /* fallthrough */ case 2: /* Receive */ - case 3: /* Receive error */ case 6: /* Rx timeout */ - if (likely(t->use_rx_dma)) - do_handle_rx_dma(u, 0); - else + if (likely(t->use_rx_dma)) { + do_handle_rx_dma(u); + } else { do_handle_rx_pio(u); - tty_flip_buffer_push(u->info->port.tty); + + spin_unlock(&u->lock); + tty_flip_buffer_push(u->info->port.tty); + spin_lock(&u->lock); + } break; - case 4: /* End of data */ - do_handle_rx_dma(u, 1); - tty_flip_buffer_push(u->info->port.tty); + case 3: /* Receive error */ + /* FIXME how to handle this? Why do we get here */ + do_decode_rx_error(u); break; - case 5: /* break nothing to handle */ case 7: /* break nothing to handle */ break; @@ -407,19 +525,40 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) static void tegra_stop_rx(struct uart_port *u) { - dev_vdbg(u->dev, "+tegra_stop_rx\n"); - dev_vdbg(u->dev, "-tegra_stop_rx\n"); return; } -static int tegra_uart_hwinit(struct tegra_uart_port *t) +static void tegra_uart_hw_deinit(struct tegra_uart_port *t) +{ + unsigned char fcr; + + /* Disable interrupts */ + writeb(0, t->regs + UART_IER_DLAB_0_0); + + /* Reset the Rx and Tx FIFOs */ + fcr = t->fcr_shadow; + fcr = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_CLR, CLEAR, fcr); + fcr = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, RX_CLR, CLEAR, fcr); + writeb(fcr, t->regs + UART_IIR_FCR_0); + + NvRmModuleReset(s_hRmGlobal, t->modid); + clk_disable(t->clk); + NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE); +} + +static int tegra_uart_hw_init(struct tegra_uart_port *t) { unsigned char fcr; unsigned char mcr; unsigned char ier; NvError err; - dev_vdbg(t->uport.dev, "+tegra_uart_hwinit\n"); + dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n"); + + t->fcr_shadow = 0; + t->mcr_shadow = 0; + t->lcr_shadow = 0; + t->ier_shadow = 0; err = NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_FALSE); if (err != NvSuccess) { @@ -428,8 +567,6 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t) } clk_enable(t->clk); - - tegra_set_baudrate(t, 9600); NvRmModuleReset(s_hRmGlobal, t->modid); /* Reset the FIFO twice with some delay to make sure that the FIFOs are @@ -471,7 +608,7 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t) t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, RX_TRIG, FIFO_COUNT_GREATER_4, t->fcr_shadow); - if (t->dma_for_tx) { + if (t->use_tx_dma) { t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG, FIFO_COUNT_GREATER_4, t->fcr_shadow); } else { @@ -495,7 +632,9 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t) */ mcr = t->mcr_shadow; mcr = NV_FLD_SET_DRF_DEF(UART, MCR, CTS_EN, ENABLE, mcr); - mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS_EN, ENABLE, mcr); + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS_EN, DISABLE, mcr); + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_HI, mcr); + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, DTR, FORCE_DTR_HI, mcr); t->mcr_shadow = mcr; writeb(mcr, t->regs + UART_MCR_0); @@ -529,68 +668,56 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t) t->ier_shadow = ier; writeb(ier, t->regs + UART_IER_DLAB_0_0); - dev_vdbg(t->uport.dev, "-tegra_uart_hwinit\n"); + dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n"); return 0; fail: - dev_vdbg(t->uport.dev, "-tegra_uart_hwinit\n"); + dev_err(t->uport.dev, "HW init failed\n"); return -ENODEV; } static int tegra_uart_init_rx_dma(struct tegra_uart_port *t) { - int i; + dma_addr_t rx_dma_phys; + void *rx_dma_virt; - /* Rx uses 1 DMA channel and 2 chained buffers */ t->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINOUS); if (t->rx_dma < 0) return -ENODEV; - memset(t->rx_dma_req, 0, sizeof(t->rx_dma_req)); - for (i=0; i<2; i++) { - dma_addr_t rx_dma_phys; - void *rx_dma_virt; + memset(&t->rx_dma_req, 0, sizeof(t->rx_dma_req)); - t->rx_dma_req[i].size = UART_RX_DMA_PING_BUFFER_SIZE * 2; - /* Allocate receive DMA buffer - * This buffer can hold data for 50 ms with 4.5 Mbps data rate. - */ - rx_dma_virt = dma_alloc_coherent(t->uport.dev, - t->rx_dma_req[i].size, &rx_dma_phys, GFP_KERNEL); - if (!rx_dma_virt) { - dev_err(t->uport.dev, "DMA buffers allocate failed \n"); - goto fail; - } - t->rx_dma_req[i].dest_addr = rx_dma_phys; - t->rx_dma_req[i].virt_addr = rx_dma_virt; + t->rx_dma_req.size = UART_RX_DMA_BUFFER_SIZE; + rx_dma_virt = dma_alloc_coherent(t->uport.dev, + t->rx_dma_req.size, &rx_dma_phys, GFP_KERNEL); + if (!rx_dma_virt) { + dev_err(t->uport.dev, "DMA buffers allocate failed \n"); + goto fail; } - - for (i=0; i<2; i++) { - t->rx_dma_req[i].source_addr = t->phys; - t->rx_dma_req[i].source_wrap = 4; - t->rx_dma_req[i].dest_wrap = 0; - t->rx_dma_req[i].to_memory = 1; - t->rx_dma_req[i].modid = NvRmModuleID_Uart; - t->rx_dma_req[i].instance = t->uport.line; - t->rx_dma_req[i].complete = tegra_rx_dma_complete_callback; - t->rx_dma_req[i].size = t->rx_dma_req[i].size; - t->rx_dma_req[i].data = &t->uport; - INIT_LIST_HEAD(&(t->rx_dma_req[i].list)); - if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req[i])) { - dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n"); - goto fail; - } + t->rx_dma_req.dest_addr = rx_dma_phys; + t->rx_dma_req.virt_addr = rx_dma_virt; + + t->rx_dma_req.source_addr = t->phys; + t->rx_dma_req.source_wrap = 4; + t->rx_dma_req.dest_wrap = 0; + t->rx_dma_req.to_memory = 1; + t->rx_dma_req.modid = NvRmModuleID_Uart; + t->rx_dma_req.instance = t->uport.line; + t->rx_dma_req.complete = tegra_rx_dma_complete_callback; + t->rx_dma_req.threshold = tegra_rx_dma_threshold_callback; + t->rx_dma_req.data = &t->uport; + INIT_LIST_HEAD(&(t->rx_dma_req.list)); + if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req)) { + dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n"); + goto fail; } return 0; fail: tegra_dma_free_channel(t->rx_dma); - if (t->rx_dma_req[0].dest_addr) - dma_free_coherent(t->uport.dev, t->rx_dma_req[0].size, - t->rx_dma_req[0].virt_addr, t->rx_dma_req[0].dest_addr); - if (t->rx_dma_req[1].dest_addr) - dma_free_coherent(t->uport.dev, t->rx_dma_req[1].size, - t->rx_dma_req[1].virt_addr, t->rx_dma_req[1].dest_addr); + if (t->rx_dma_req.dest_addr) + dma_free_coherent(t->uport.dev, t->rx_dma_req.size, + t->rx_dma_req.virt_addr, t->rx_dma_req.dest_addr); return -ENODEV; } @@ -613,16 +740,16 @@ static int tegra_startup(struct uart_port *u) t->irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, t->modid, 0); BUG_ON(t->irq == (NvU32)(-1)); - t->dma_for_tx = false; - if (use_dma) { + t->use_tx_dma = false; + if (!tx_force_pio) { t->tx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); if (t->tx_dma >= 0) - t->dma_for_tx = true; + t->use_tx_dma = true; } - if (t->dma_for_tx) { + if (t->use_tx_dma) { t->tx_dma_virt = xmit->buf; t->tx_dma_phys = dma_map_single(u->dev, xmit->buf, - UART_XMIT_SIZE, DMA_BIDIRECTIONAL); + UART_XMIT_SIZE, DMA_TO_DEVICE); t->tx_dma_size = UART_XMIT_SIZE; t->tx_dma_offset = 0; @@ -636,15 +763,16 @@ static int tegra_startup(struct uart_port *u) t->tx_dma_req.dest_addr = t->phys; t->tx_dma_req.dest_wrap = 4; t->tx_dma_req.source_wrap = 0; - t->tx_dma_req.data = &t->tasklet; + t->tx_dma_req.data = u; + t->tx_dma_req.size = 0; } t->use_rx_dma = false; - if (use_dma) { + if (!rx_force_pio) { if (!tegra_uart_init_rx_dma(t)) t->use_rx_dma = true; } - ret = tegra_uart_hwinit(t); + ret = tegra_uart_hw_init(t); if (ret) goto fail; @@ -664,57 +792,35 @@ fail: return ret; } -#define TX_EMPTY_STATUS (NV_DRF_DEF(UART, LSR, TMTY, EMPTY) | \ - NV_DRF_DEF(UART, LSR, THRE, EMPTY)) - static void tegra_shutdown(struct uart_port *u) { - int timeout = 10; - struct tegra_uart_port *t; + unsigned long flags; + spin_lock_irqsave(&u->lock, flags); t = container_of(u, struct tegra_uart_port, uport); dev_vdbg(u->dev, "+tegra_shutdown\n"); - if (!t->dma_for_tx) { - /* wait for 10 msec to drain the Tx buffer, if not empty */ - unsigned char lsr; - do { - lsr = readb(t->regs + UART_LSR_0); - if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) - break; - timeout--; - msleep(1); - } while (timeout); - } else { - do { - timeout = 10; - if (tegra_dma_is_empty(t->tx_dma)) - break; - timeout--; - msleep(1); - } while (timeout); - if (!timeout) - dev_info(u->dev, "DMA wait timedout\n"); + if (t->use_tx_dma) { + /* FIXME: dequeue means abort. Should we need to abort + * or wait for the DMA to complete? + */ + tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req); + t->tx_dma_req.size = 0; } + tegra_uart_hw_deinit(t); + spin_unlock_irqrestore(&u->lock, flags); if (t->use_rx_dma) { - tegra_dma_flush(t->rx_dma); + dma_free_coherent(u->dev, t->rx_dma_req.size, + t->rx_dma_req.virt_addr, t->rx_dma_req.dest_addr); tegra_dma_free_channel(t->rx_dma); - dma_free_coherent(u->dev, t->rx_dma_req[0].size, - t->rx_dma_req[0].virt_addr, t->rx_dma_req[0].dest_addr); - dma_free_coherent(u->dev, t->rx_dma_req[1].size, - t->rx_dma_req[1].virt_addr, t->rx_dma_req[1].dest_addr); } - if (t->dma_for_tx) { + if (t->use_tx_dma) { tegra_dma_free_channel(t->tx_dma); } - clk_disable(t->clk); - - NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE); free_irq(t->irq, u); - dev_vdbg(u->dev, "-tegra_shutdown\n"); } @@ -733,7 +839,27 @@ static unsigned int tegra_get_mctrl(struct uart_port *u) static void tegra_set_mctrl(struct uart_port *u, unsigned int mctrl) { - dev_vdbg(u->dev, "tegra_set_mctrl\n"); + unsigned char mcr; + struct tegra_uart_port *t; + + dev_vdbg(u->dev, "tegra_set_mctrl called with %d\n", mctrl); + t = container_of(u, struct tegra_uart_port, uport); + + mcr = t->mcr_shadow; + if (mctrl & TIOCM_RTS) + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_LOW, mcr); + else + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_HI, mcr); + + if (mctrl & TIOCM_DTR) + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, DTR, FORCE_DTR_LOW, mcr); + else + mcr = NV_FLD_SET_DRF_DEF(UART, MCR, DTR, FORCE_DTR_HI, mcr); + + if (mcr != t->mcr_shadow) { + writeb(mcr, t->regs + UART_MCR_0); + t->mcr_shadow = mcr; + } return; } @@ -759,6 +885,8 @@ static int tegra_request_port(struct uart_port *u) static void tegra_release_port(struct uart_port *u) { + + } static unsigned int tegra_tx_empty(struct uart_port *u) @@ -770,20 +898,21 @@ static unsigned int tegra_tx_empty(struct uart_port *u) t = container_of(u, struct tegra_uart_port, uport); dev_vdbg(u->dev, "+tegra_tx_empty\n"); + if (t->use_tx_dma) { + if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req)) + return 0; + } lsr = readb(t->regs + UART_LSR_0); if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) ret = TIOCSER_TEMT; else ret = 0; - dev_vdbg(u->dev, "-tegra_tx_empty\n"); return 0; } static void tegra_stop_tx(struct uart_port *u) { - dev_vdbg(u->dev, "+tegra_stop_tx\n"); - dev_vdbg(u->dev, "-tegra_stop_tx\n"); return; } @@ -793,25 +922,20 @@ static void tegra_start_tx_locked(struct uart_port *u) t = container_of(u, struct tegra_uart_port, uport); - // dev_vdbg(t->uport.dev, "+tegra_start_tx_locked\n"); + dev_vdbg(t->uport.dev, "+tegra_start_tx_locked\n"); - if (!t->dma_for_tx) { + if (!t->use_tx_dma) { /* Enable interrupt on transmit FIFO empty, if it is disabled */ - if (!(t->ier_shadow & NV_DRF_DEF(UART, IER_DLAB_0, IE_THR, - ENABLE))) { + if (!t->tx_pio_inflight) { + t->tx_pio_inflight = true; t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_THR, ENABLE, t->ier_shadow); writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0); } } else { - /* If DMA is empty try to scheule new requests */ - if (tegra_dma_is_empty(t->tx_dma)) { - dev_vdbg(t->uport.dev, "Scheduling Tx\n"); - tasklet_schedule(&t->tasklet); - } + queue_work(t->work_queue, &t->work); } - - // dev_vdbg(t->uport.dev, "-tegra_start_tx_locked\n"); + dev_vdbg(t->uport.dev, "-tegra_start_tx_locked\n"); } static void tegra_enable_ms(struct uart_port *u) @@ -830,6 +954,9 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud) NvError err; unsigned char lcr; + if (t->baud == baud) + return; + clock = (baud * 16) / 1000; clock = clock ? clock : 1; @@ -848,10 +975,10 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud) return; } - /* FIXME is this division correct? */ - divisor = (actual_clock * 1000) / 16; + divisor = (actual_clock * 1000); + do_div(divisor, 16); divisor += baud/2; - divisor /= baud; + do_div(divisor, baud); lcr = t->lcr_shadow; lcr = NV_FLD_SET_DRF_DEF(UART, LCR, DLAB, ENABLE, lcr); @@ -863,7 +990,8 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud) lcr = NV_FLD_SET_DRF_DEF(UART, LCR, DLAB, DISABLE, lcr); writeb(lcr, t->regs + UART_LCR_0); - dev_dbg(t->uport.dev, "Baud %d clock freq %d and divisor of %d\n", + t->baud = baud; + dev_info(t->uport.dev, "Baud %d clock freq %d and divisor of %d\n", baud, actual_clock, divisor); } @@ -892,18 +1020,18 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios, lcr = t->lcr_shadow; lcr = NV_FLD_SET_DRF_DEF(UART, LCR, PAR, NO_PARITY, lcr); if (PARENB == (c_cflag & PARENB)) { - if (PARODD == (c_cflag & PARODD)) { - strlcat(debug_string, "even parity ", 50); + if (CMSPAR == (c_cflag & CMSPAR)) { + strlcat(debug_string, "space parity ", 50); + /* FIXME What is space parity? */ + /* data |= SPACE_PARITY; */ + } else if (c_cflag & PARODD) { + strlcat(debug_string, "ODD parity ", 50); lcr = NV_FLD_SET_DRF_DEF(UART, LCR, PAR, PARITY, lcr); lcr = NV_FLD_SET_DRF_DEF(UART, LCR, EVEN, DISABLE, lcr); lcr = NV_FLD_SET_DRF_DEF(UART, LCR, SET_P, NO_PARITY, lcr); - } else if (CMSPAR == (c_cflag & CMSPAR)) { - strlcat(debug_string, "space parity ", 50); - /* FIXME What is space parity? */ - /* data |= SPACE_PARITY; */ } else { - strlcat(debug_string, "odd parity ", 50); + strlcat(debug_string, "Even parity ", 50); lcr = NV_FLD_SET_DRF_DEF(UART, LCR, PAR, PARITY, lcr); lcr = NV_FLD_SET_DRF_DEF(UART, LCR, EVEN, ENABLE, lcr); lcr = NV_FLD_SET_DRF_DEF(UART, LCR, SET_P, NO_PARITY, @@ -951,36 +1079,49 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios, return; } -static void tegra_pm(struct uart_port *u, unsigned int state, - unsigned int oldstate) +/* + * Flush any TX data submitted for DMA. Called when the TX circular + * buffer is reset. + */ +static void tegra_flush_buffer(struct uart_port *u) { + struct tegra_uart_port *t; + dev_vdbg(u->dev, "tegra_flush_buffer called"); + + t = container_of(u, struct tegra_uart_port, uport); + + if (t->use_tx_dma) { + tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req); + t->tx_dma_req.size = 0; + } + return; } -static const char *tegra_type(struct uart_port *u) -{ + +static void tegra_pm(struct uart_port *u, unsigned int state, + unsigned int oldstate) { + +} + +static const char *tegra_type(struct uart_port *u) { return 0; } static struct uart_ops tegra_uart_ops = { .tx_empty = tegra_tx_empty, - .set_mctrl = tegra_set_mctrl, .get_mctrl = tegra_get_mctrl, - .stop_tx = tegra_stop_tx, .start_tx = tegra_start_tx_locked, .stop_rx = tegra_stop_rx, - + .flush_buffer = tegra_flush_buffer, .enable_ms = tegra_enable_ms, .break_ctl = tegra_break_ctl, - .startup = tegra_startup, .shutdown = tegra_shutdown, .set_termios = tegra_set_termios, - .pm = tegra_pm, .type = tegra_type, - .request_port = tegra_request_port, .release_port = tegra_release_port, }; @@ -1015,6 +1156,8 @@ static int __devexit tegra_uart_remove(struct platform_device *pdev) u = &t->uport; uart_remove_one_port(&tegra_uart_driver, u); + destroy_workqueue(t->work_queue); + platform_set_drvdata(pdev, NULL); NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE); @@ -1032,7 +1175,7 @@ static int __init tegra_uart_probe(struct platform_device *pdev) struct uart_port *u; int ret; char clk_name[MAX_CLK_NAME_CHARS]; - + char name[64]; if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr) { printk(KERN_ERR "Invalid Uart instance (%d) \n", pdev->id); return -ENODEV; @@ -1058,9 +1201,6 @@ static int __init tegra_uart_probe(struct platform_device *pdev) goto fail; } - tasklet_init(&t->tasklet, tegra_dma_tasklet_func, - (unsigned long)u); - if (NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_FALSE) != NvSuccess) { dev_err(u->dev, "No Pin Mux - not registering the port\n"); @@ -1078,11 +1218,16 @@ static int __init tegra_uart_probe(struct platform_device *pdev) return ret; } + snprintf(name, sizeof(name), "tegra_hsuart_%d", u->line); + t->work_queue = create_singlethread_workqueue(name); + if (t->work_queue == NULL) { + dev_err(u->dev, "Failed to create work queue\n"); + goto fail; + } + INIT_WORK(&t->work, tegra_tx_dma_workqueue); printk(KERN_INFO "Registered UART port %s%d\n", tegra_uart_driver.dev_name, u->line); - return ret; - fail: kfree(t); return -ENODEV; |