diff options
author | Gary King <gking@nvidia.com> | 2010-03-22 17:29:20 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-03-23 10:42:58 -0800 |
commit | b488d3228e76fb434010ff5e3f1044ce62196217 (patch) | |
tree | 13588762a236191c06092cd85f71b4342c63bfca | |
parent | 854209499f803b7670599c720a9cf1b154e7334c (diff) |
input: add keyboard controller support for tegra
implement a keyboard input driver for tegra's internal matrix keyboard
controller (16x8 support on Tegra 2 class processors).
supports
* power management
* limit wakeup to a subset of keys
* platform-defined remapping of key to keycode
* platform-defined pin configuration
add support for converting tegra ODM kit APIs to kbc platform data, so
existing clients will continue to function
Change-Id: If1496e7ada4f6f18a3e98a15ebc5f925f254bf65
Reviewed-on: http://git-master/r/933
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Tested-by: Gary King <gking@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/kbc.h | 66 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvddk/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvddk/nvddk_kbc.c | 1112 | ||||
-rw-r--r-- | arch/arm/mach-tegra/odm_to_plat.c | 129 | ||||
-rwxr-xr-x | arch/arm/mach-tegra/tegra_sysmap.c | 2 | ||||
-rw-r--r-- | drivers/input/keyboard/tegra-kbc.c | 677 |
7 files changed, 691 insertions, 1297 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 8a73618b997c..5b4c352e0d4e 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_MACH_TEGRA_GENERIC) += nvos_user.o obj-$(CONFIG_MACH_TEGRA_GENERIC) += nvrm_user.o obj-$(CONFIG_MACH_TEGRA_GENERIC) += init_common.o obj-$(CONFIG_MACH_TEGRA_GENERIC) += board_nvodm.o +obj-$(CONFIG_MACH_TEGRA_GENERIC) += odm_to_plat.o # Misc drivers obj-$(CONFIG_TEGRA_ODM_RFKILL) += tegra_rfkill_odm.o diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h new file mode 100644 index 000000000000..83810aef2612 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/kbc.h @@ -0,0 +1,66 @@ +/* + * arch/arm/mach-tegra/include/mach/kbc.h + * + * Platform definitions for tegra-kbc keyboard input driver + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef ASMARM_ARCH_TEGRA_KBC_H +#define ASMARM_ARCH_TEGRA_KBC_H + +#include <linux/types.h> + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define KBC_MAX_GPIO 24 +#define KBC_MAX_KPENT 8 +#else +#define KBC_MAX_GPIO 20 +#define KBC_MAX_KPENT 7 +#endif + +#define KBC_MAX_ROW 16 +#define KBC_MAX_COL 8 + +#define KBC_MAX_KEY (KBC_MAX_ROW*KBC_MAX_COL) + +struct tegra_kbc_pin_cfg { + bool is_row; + bool is_col; + unsigned char num; +}; + +struct tegra_kbc_wake_key { + u8 row:4; + u8 col:4; +}; + +struct tegra_kbc_plat { + unsigned int debounce_cnt; + unsigned int repeat_cnt; + int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */ + struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; + struct tegra_kbc_wake_key *wake_cfg; + int *keymap; +}; + +static inline unsigned int kbc_indexof(unsigned r, unsigned c) +{ + return c*KBC_MAX_ROW + r; +} + +#endif diff --git a/arch/arm/mach-tegra/nvddk/Makefile b/arch/arm/mach-tegra/nvddk/Makefile index 347675bb74cb..1716bf866abe 100644 --- a/arch/arm/mach-tegra/nvddk/Makefile +++ b/arch/arm/mach-tegra/nvddk/Makefile @@ -16,7 +16,6 @@ obj-y += nvddk_usbphy_ap16.o obj-y += nvddk_usbphy_ap20.o endif -obj-$(CONFIG_KEYBOARD_TEGRA) += nvddk_kbc.o obj-$(CONFIG_MTD_NAND_TEGRA) += nvddk_nand.o obj-$(CONFIG_MMC_TEGRA_SDIO) += nvddk_sdio.o diff --git a/arch/arm/mach-tegra/nvddk/nvddk_kbc.c b/arch/arm/mach-tegra/nvddk/nvddk_kbc.c deleted file mode 100644 index 2418a97729ba..000000000000 --- a/arch/arm/mach-tegra/nvddk/nvddk_kbc.c +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * Copyright (c) 2007-2009 NVIDIA Corporation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NVIDIA Corporation nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "nvddk_kbc.h" -#include "ap20/arapbdev_kbc.h" -#include "nvrm_drf.h" -#include "nvrm_module.h" -#include "nvrm_hardware_access.h" -#include "nvassert.h" -#include "nvrm_interrupt.h" -#include "nvodm_query_kbc.h" -#include "nvodm_kbc.h" -#include "nvrm_power.h" -#include "nvrm_pmu.h" -#include "nvodm_query_discovery.h" - -#define KBC_REG_READ32(pKbcVirtAdd, reg) \ - NV_READ32(pKbcVirtAdd + ((APBDEV_KBC_##reg##_0)/4)) - -#define KBC_REG_WRITE32(pKbcVirtAdd, reg, Data2Write) \ - do \ - { \ - NV_WRITE32(pKbcVirtAdd + ((APBDEV_KBC_##reg##_0)/4), (Data2Write)); \ - }while (0) - -#define GET_KEY_CODE(hKbc, col, row, reg, ent, k) \ - do \ - { \ - if (NV_DRF_VAL(APBDEV_KBC, KP_ENT##reg, KP_NEW_ENT##ent, k)) \ - { \ - col = NV_DRF_VAL(APBDEV_KBC, KP_ENT##reg, KP_COL_NUM_ENT##ent, k); \ - row = NV_DRF_VAL(APBDEV_KBC, KP_ENT##reg, KP_ROW_NUM_ENT##ent, k); \ - hKbc->RowNumbers[ValidKeyCount] = row; \ - hKbc->ColNumbers[ValidKeyCount] = col; \ - ValidKeyCount++; \ - } \ - }while (0) - -enum -{ - /* - * This is the value of the KBC Cycle time in microseconds. The KBC clock - * is 32Khz. therefore the time-period is 31.25 usecs. - */ - KBC_CYCLE_TIME_US = 32, - KBC_CP_TIME_OUT_MS = 2000, - KBC_KEY_QUEUE_DEPTH = 64 -}; - -typedef struct -{ - // Is per-key masking supported by the controller; - NvBool IsKeyMaskingSupported; - - // Is Minimum row required for proper functioning - NvU32 MinRowCount; - - // Maximum number of events in the fifo - NvU32 MaxEvents; -} NvDdkKbcCapability; - -typedef struct KeyInfoRec -{ - // Key number. - NvU32 KeyNumber; - // Key event. - NvDdkKbcKeyEvent KeyEvent; - // Holds that time stamp. - NvU32 TimeMs; -}KeyInfo; - -typedef struct KbcKeyEventProcessInfoRec -{ - // Holds the client sema to be signaled. - NvOsSemaphoreHandle KbcClientSema; - // Holds the handle to KBC. - NvDdkKbcHandle hKbc; - // Repeat delay in Micro seconds. - NvU32 RepeatDelayMs; - // Pressed keys in the current cycle. - NvU32 PressedKeys[APBDEV_KBC_MAX_ENT]; - // Pressed keys in the previous cycle. - NvU32 PreviouslyPressedKeys[APBDEV_KBC_MAX_ENT]; - // Gives the number of keys that are pressed in the current cycle of scan. - NvU32 CurrentPressedKeyCount; - // Gives the number of keys that are pressed during the previous cycle of scan. - NvU32 PreviouslyPressedKeyCount; -}KbcKeyEventProcessInfo; - -// struct holding the kbc related info. -typedef struct NvDdkKbcRec -{ - // Rm device handle. - NvRmDeviceHandle RmDevHandle; - // Holds Kbc Controller registers physical base address. - NvRmPhysAddr BaseAddress; - // Holds the register map size. - NvU32 BankSize; - // Holds the virtual address for accessing registers. - NvU32* pVirtualAddress; - // Key event info - KbcKeyEventProcessInfo KEPInfo; - // Indicates whether Kbc is started. - NvBool KbcStarted; - // Contains the Power Client ID - NvU32 KbcRmPowerClientID; - // Peripheral DataBase - const NvOdmPeripheralConnectivity *pConnectivity; - // Number of Rows present in the key matrix. - NvU32 NumberOfRows; - // Number of Columns present in the key matrix. - NvU32 NumberOfColumns; - // Interrupt handle - NvOsInterruptHandle InterruptHandle; - // Time delay between key scans. - NvU32 DelayBetweenScans; - // Is Key Masking supported - NvBool IsKeyMaskingSupported; - - // Minimum number of rows need to be enable - NvU32 MinRowCount; - - // Maximum number of events in the fifo - NvU32 MaxEvents; - - NvU32 RowNumbers[APBDEV_KBC_MAX_ENT]; - NvU32 ColNumbers[APBDEV_KBC_MAX_ENT]; - - // Stores the the values of Row Configuration Registers - NvU32 KbcRegRowCfg0; - NvU32 KbcRegRowCfg1; - NvU32 KbcRegRowCfg2; - NvU32 KbcRegRowCfg3; - // Stores the values of Column Configuration Registers - NvU32 KbcRegColCfg0; - NvU32 KbcRegColCfg1; - NvU32 KbcRegColCfg2; - NvU32 KbcSuspendedRowReg[4]; - NvU32 KbcSuspendedColReg[3]; - NvU32 KbcRowMaskReg[APBDEV_KBC_NUM_ROWS]; - NvBool IsSelectKeysWkUpEnabled; -}NvDdkKbc; - -static NvDdkKbcHandle s_hKbc = NULL; - -static NvError NvddkPrivKbcEnableClock(NvDdkKbcHandle hKbc) -{ - NvError Error = NvSuccess; - - Error = NvRmPowerVoltageControl(hKbc->RmDevHandle, - NvRmModuleID_Kbc, - hKbc->KbcRmPowerClientID, - NvRmVoltsUnspecified, - NvRmVoltsUnspecified, - NULL, - 0, - NULL); - if (Error) - return Error; - - Error = NvRmPowerModuleClockControl(hKbc->RmDevHandle, - NvRmModuleID_Kbc, - hKbc->KbcRmPowerClientID, - NV_TRUE); - return Error; -} - -static NvError NvddkPrivKbcDisableClock(NvDdkKbcHandle hKbc) -{ - NvError Error; - Error = NvRmPowerModuleClockControl(hKbc->RmDevHandle, - NvRmModuleID_Kbc, - hKbc->KbcRmPowerClientID, - NV_FALSE); - return Error; -} - -static void NvDdkPrivKbcSuspendController(NvDdkKbcHandle hKbc) -{ - (void)NvRmPowerVoltageControl(hKbc->RmDevHandle, - NvRmModuleID_Kbc, - hKbc->KbcRmPowerClientID, - NvRmVoltsOff, - NvRmVoltsOff, - NULL, - 0, - NULL); -} - -static NvError NvDdkPrivKbcResumeController(NvDdkKbcHandle hKbc) -{ - NvError Error; - - Error = NvRmPowerVoltageControl(hKbc->RmDevHandle, - NvRmModuleID_Kbc, - hKbc->KbcRmPowerClientID, - NvRmVoltsUnspecified, - NvRmVoltsUnspecified, - NULL, - 0, - NULL); - return Error; -} -static void SetPowerOnKeyboard(NvDdkKbcHandle hKbc, NvBool IsEnable) -{ - NvU32 i; - NvRmPmuVddRailCapabilities RailCaps; - NvU32 SettlingTime; - - for (i = 0; i < (hKbc->pConnectivity->NumAddress); i++) - { - // Search for the vdd rail entry - if (hKbc->pConnectivity->AddressList[i].Interface == NvOdmIoModule_Vdd) - { - NvRmPmuGetCapabilities(hKbc->RmDevHandle, - hKbc->pConnectivity->AddressList[i].Address, &RailCaps); - if (IsEnable) - { - NvRmPmuSetVoltage(hKbc->RmDevHandle, - hKbc->pConnectivity->AddressList[i].Address, - RailCaps.requestMilliVolts, &SettlingTime); - } - else - { - NvRmPmuSetVoltage(hKbc->RmDevHandle, - hKbc->pConnectivity->AddressList[i].Address, - ODM_VOLTAGE_OFF, &SettlingTime); - } - if (SettlingTime) - NvOsWaitUS(SettlingTime); - } - } -} - - - -static void SuspendedConfiguration(NvDdkKbcHandle hKbc) -{ - NvU32 i; - NvU32 PinNumber; - NvU32 NewVal; - NvU32 ShiftVal; - NvU32 RowCfgFldSize; - NvU32 ColCfgFldSize; - NvU32 RowCfgMask; - NvU32 ColCfgMask; - NvU32 RowsPerReg; - NvU32 ColsPerReg; - NvBool IsColumn; - NvU32 RowConnectionNumber = 0; - NvU32 ColumnConnectionNumber = 0; - NvU32 *RowNums; - NvU32 *ColNums; - NvU32 NumOfKeys; - NvU32 j; - NvBool IsWakeupEnable = NV_FALSE; - NvU32 ActiveRows[APBDEV_KBC_NUM_ROWS]; - NvU32 ActiveCols[APBDEV_KBC_NUM_COLS]; - NvU32 MaskRow; - NvU32 MaskColumn; - hKbc->IsSelectKeysWkUpEnabled = NvOdmKbcIsSelectKeysWkUpEnabled( - &RowNums, - &ColNums, - &NumOfKeys); - if (!hKbc->IsSelectKeysWkUpEnabled) - return; - - if (hKbc->IsKeyMaskingSupported) - { - for (i = 0; i < (hKbc->pConnectivity->NumAddress); i++) - { - if (hKbc->pConnectivity->AddressList[i].Interface != NvOdmIoModule_Kbd) - continue; - - PinNumber = hKbc->pConnectivity->AddressList[i].Address; - IsColumn = (NvBool)hKbc->pConnectivity->AddressList[i].Instance; - - if (IsColumn) - ActiveCols[ColumnConnectionNumber++] = PinNumber; - else - ActiveRows[RowConnectionNumber++] = PinNumber; - } - - - for(i = 0; i < RowConnectionNumber; ++i) - hKbc->KbcRowMaskReg[i] = (0xFF >> (APBDEV_KBC_NUM_COLS - ColumnConnectionNumber)); - - for (i = 0; i < NumOfKeys; ++i) - { - // Get the row and column number index - for (MaskRow = 0; MaskRow < RowConnectionNumber; ++MaskRow) - { - if (RowNums[i] == ActiveRows[MaskRow]) - break; - } - - for (MaskColumn = 0; MaskColumn < ColumnConnectionNumber; ++MaskColumn) - { - if (ColNums[i] == ActiveCols[MaskColumn]) - break; - } - - // Check for valid row and colum mask index - NV_ASSERT(MaskRow < RowConnectionNumber); - NV_ASSERT(MaskColumn < ColumnConnectionNumber); - - // Make clear to the corresponding row and column - hKbc->KbcRowMaskReg[MaskRow] &= (~(1 << MaskColumn)); - } - } - else - { - /* - * The variable RowCfgFldSize contains the number of bits needed to - * configure a Gpio pins as a Row. This includes the number of bits for the - * Row number as well as a Flag bit to indicate whether it has been mapped - * as a row. - */ - RowCfgFldSize = NV_FIELD_SIZE(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_NUM_RANGE) + - NV_FIELD_SIZE(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_EN_RANGE); - // Caluclating the Mask for the RowCfgFldSize number of bits - RowCfgMask = (NV_FIELD_MASK(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_NUM_RANGE) << - NV_FIELD_SIZE(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_EN_RANGE)) | - NV_FIELD_MASK(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_EN_RANGE); - /* - * The variable ColCfgFldSize contains the number of bits needed to - * configure a Gpio pins as a Column. This includes the number of bits - * for the Col number as well as a Flag bit to indicate whether it - * has been mapped as a Columns. - */ - ColCfgFldSize = NV_FIELD_SIZE(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_NUM_RANGE) + - NV_FIELD_SIZE(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_EN_RANGE); - // Caluclating the Mask for the ColCfgFldSize number of bits - ColCfgMask = (NV_FIELD_MASK(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_NUM_RANGE) << - NV_FIELD_SIZE(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_EN_RANGE)) | - NV_FIELD_MASK(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_EN_RANGE); - // Number of Gpio pins that can be configured as a row per each register - RowsPerReg = 32/RowCfgFldSize; - // Number of Gpio pins that can be configured as a col per each register - ColsPerReg = 32/ColCfgFldSize; - - for (i = 0; i < (hKbc->pConnectivity->NumAddress); i++) - { - if (hKbc->pConnectivity->AddressList[i].Interface != NvOdmIoModule_Kbd) - continue; - - PinNumber = hKbc->pConnectivity->AddressList[i].Address; - IsColumn = (NvBool)hKbc->pConnectivity->AddressList[i].Instance; - - if (IsColumn) - { - for (j=0; j < NumOfKeys; j++) - { - if (ColNums[j] == ColumnConnectionNumber) - { - IsWakeupEnable = NV_TRUE; - break; - } - else - { - IsWakeupEnable = NV_FALSE; - } - } - if (IsWakeupEnable) - { - ShiftVal = (PinNumber%ColsPerReg)*ColCfgFldSize; - NewVal = ((ColumnConnectionNumber << 1) | 0x1); - NewVal = NewVal << ShiftVal; - hKbc->KbcSuspendedColReg[PinNumber/ColsPerReg] &= - (~(ColCfgMask << ShiftVal)); - hKbc->KbcSuspendedColReg[PinNumber/ColsPerReg] |= NewVal; - } - ColumnConnectionNumber++; - } - else - { - - for (j=0; j<NumOfKeys; j++) - { - if (RowNums[j] == RowConnectionNumber) - { - IsWakeupEnable = NV_TRUE; - break; - } - else - { - IsWakeupEnable = NV_FALSE; - } - } - if (IsWakeupEnable) - { - ShiftVal = (PinNumber%RowsPerReg)*RowCfgFldSize; - NewVal = (((RowConnectionNumber) << 1) | 0x1); - NewVal = NewVal << ShiftVal; - hKbc->KbcSuspendedRowReg[PinNumber/RowsPerReg] &= - (~(RowCfgMask << ShiftVal)); - hKbc->KbcSuspendedRowReg[PinNumber/RowsPerReg] |= NewVal; - } - RowConnectionNumber++; - } - } - } -} - -static void ConfigRowAndColumnInfo(NvDdkKbcHandle hKbc) -{ - NvU32 i; - NvU32 PinNumber; - NvU32 NewVal; - NvU32 ShiftVal; - NvU32 ColCfg[3] = {0, 0, 0}; - NvU32 RowCfg[4] = {0, 0, 0, 0}; - NvU32 RowCfgFldSize; - NvU32 ColCfgFldSize; - NvU32 RowCfgMask; - NvU32 ColCfgMask; - NvU32 RowsPerReg; - NvU32 ColsPerReg; - NvBool IsColumn; - NvU32 RowConnectionNumber = 0; - NvU32 ColumnConnectionNumber = 0; - - SuspendedConfiguration(hKbc); - - /* - * The variable RowCfgFldSize contains the number of bits needed to - * configure a Gpio pins as a Row. This includes the number of bits for the - * Row number as well as a Flag bit to indicate whether it has been mapped - * as a row. - */ - RowCfgFldSize = NV_FIELD_SIZE(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_NUM_RANGE) + - NV_FIELD_SIZE(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_EN_RANGE); - // Caluclating the Mask for the RowCfgFldSize number of bits - RowCfgMask = (NV_FIELD_MASK(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_NUM_RANGE) << - NV_FIELD_SIZE(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_EN_RANGE)) | - NV_FIELD_MASK(APBDEV_KBC_ROW_CFG0_0_GPIO_0_ROW_EN_RANGE); - /* - * The variable ColCfgFldSize contains the number of bits needed to - * configure a Gpio pins as a Column. This includes the number of bits - * for the Col number as well as a Flag bit to indicate whether it - * has been mapped as a Columns. - */ - ColCfgFldSize = NV_FIELD_SIZE(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_NUM_RANGE) + - NV_FIELD_SIZE(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_EN_RANGE); - // Caluclating the Mask for the ColCfgFldSize number of bits - ColCfgMask = (NV_FIELD_MASK(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_NUM_RANGE) << - NV_FIELD_SIZE(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_EN_RANGE)) | - NV_FIELD_MASK(APBDEV_KBC_COL_CFG0_0_GPIO_0_COL_EN_RANGE); - // Number of Gpio pins that can be configured as a row per each register - RowsPerReg = 32/RowCfgFldSize; - // Number of Gpio pins that can be configured as a col per each register - ColsPerReg = 32/ColCfgFldSize; - - for (i = 0; i < (hKbc->pConnectivity->NumAddress); i++) - { - if (hKbc->pConnectivity->AddressList[i].Interface != NvOdmIoModule_Kbd) - continue; - - PinNumber = hKbc->pConnectivity->AddressList[i].Address; - IsColumn = (NvBool)hKbc->pConnectivity->AddressList[i].Instance; - - if (IsColumn) - { - ShiftVal = (PinNumber%ColsPerReg)*ColCfgFldSize; - NewVal = ((ColumnConnectionNumber << 1) | 0x1); - NewVal = NewVal << ShiftVal; - ColCfg[PinNumber/ColsPerReg] &= (~(ColCfgMask << ShiftVal)); - ColCfg[PinNumber/ColsPerReg] |= NewVal; - ColumnConnectionNumber++; - } - else - { - ShiftVal = (PinNumber%RowsPerReg)*RowCfgFldSize; - NewVal = (((RowConnectionNumber) << 1) | 0x1); - NewVal = NewVal << ShiftVal; - RowCfg[PinNumber/RowsPerReg] &= (~(RowCfgMask << ShiftVal)); - RowCfg[PinNumber/RowsPerReg] |= NewVal; - RowConnectionNumber++; - } - } - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG0, ColCfg[0]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG1, ColCfg[1]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG2, ColCfg[2]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG0, RowCfg[0]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG1, RowCfg[1]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG2, RowCfg[2]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG3, RowCfg[3]); - hKbc->KbcRegColCfg0 = ColCfg[0]; - hKbc->KbcRegColCfg1 = ColCfg[1]; - hKbc->KbcRegColCfg2 = ColCfg[2]; - hKbc->KbcRegRowCfg0 = RowCfg[0]; - hKbc->KbcRegRowCfg1 = RowCfg[1]; - hKbc->KbcRegRowCfg2 = RowCfg[2]; - hKbc->KbcRegRowCfg3 = RowCfg[3]; - - hKbc->NumberOfRows = RowConnectionNumber; - hKbc->NumberOfColumns = ColumnConnectionNumber; - - NV_ASSERT(RowConnectionNumber); - NV_ASSERT(ColumnConnectionNumber); - - // We have minimum row count limitation on AP15/20. Is it satisfy? - if(hKbc->MinRowCount > RowConnectionNumber) - { - NV_ASSERT(!"Minimum row count does not satisfy"); - } - -} - - -static NvU32 GetKeyCode(NvDdkKbcHandle hKbc, NvU32 ColumnNumber, NvU32 RowNumber) { - NvU32 KeyCode; - KeyCode = NvOdmKbcGetKeyCode(RowNumber, ColumnNumber, - hKbc->NumberOfRows, hKbc->NumberOfColumns); - return KeyCode; -} - -static NvU32 ExtractPressedKeys(NvDdkKbcHandle hKbc, NvU32 index) -{ - NvU32 ColumnNumber; - NvU32 RowNumber; - NvU32 ValidKeyCount = 0; - NvU32 KeyCode; - NvU32 i=0; - - KeyCode = KBC_REG_READ32(hKbc->KEPInfo.hKbc->pVirtualAddress, KP_ENT0); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 0, 0, KeyCode); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 0, 1, KeyCode); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 0, 2, KeyCode); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 0, 3, KeyCode); - - KeyCode = KBC_REG_READ32(hKbc->KEPInfo.hKbc->pVirtualAddress, KP_ENT1); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 1, 4, KeyCode); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 1, 5, KeyCode); - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 1, 6, KeyCode); - - // If more than 7 events are available then read that also. - if (hKbc->MaxEvents > 7) - { - GET_KEY_CODE(hKbc, ColumnNumber, RowNumber, 1, 7, KeyCode); - } - - ValidKeyCount = NvOdmKbcFilterKeys( - hKbc->RowNumbers, - hKbc->ColNumbers, - ValidKeyCount); - - for (i=0; i<ValidKeyCount; i++) - { - hKbc->KEPInfo.PressedKeys[i] = GetKeyCode(hKbc, - hKbc->ColNumbers[i], - hKbc->RowNumbers[i]); - } - return ValidKeyCount; -} - -static NvBool IsKeyReleased(NvDdkKbcHandle hKbc, NvU32 Key) -{ - NvBool Error = NV_TRUE; - NvU32 i; - for (i = 0; i < hKbc->KEPInfo.CurrentPressedKeyCount; i++) - { - if (Key == hKbc->KEPInfo.PressedKeys[i]) - { - Error = NV_FALSE; - break; - } - } - return Error; -} - -static NvBool IsKeyEventAlreadyDispatched(NvDdkKbcHandle hKbc, NvU32 Key) -{ - NvBool Error = NV_FALSE; - NvU32 i; - for (i = 0; i < hKbc->KEPInfo.PreviouslyPressedKeyCount; i++) - { - if (Key == hKbc->KEPInfo.PreviouslyPressedKeys[i]) - { - Error = NV_TRUE; - break; - } - } - return Error; -} - -static void -GetKeyEvents( - NvDdkKbcHandle hKbc, - NvU32* pKeyCount, - NvU32* pKeyCodes, - NvDdkKbcKeyEvent* pKeyEvents) -{ - NvU32 i; - *pKeyCount = 0; - - /* This function compares the the currently pressed to keys to the Keys - * that were pressed in the previous call of this function. Based on this - * comparision the Key Release and Key Press events are queued in the - * KeyEventsToDispatch Queue. */ - if (hKbc->KEPInfo.PreviouslyPressedKeyCount) - { - for (i = 0; i < hKbc->KEPInfo.PreviouslyPressedKeyCount; i++) - { - if (IsKeyReleased(hKbc, hKbc->KEPInfo.PreviouslyPressedKeys[i])) - { - pKeyCodes[*pKeyCount] = hKbc->KEPInfo.PreviouslyPressedKeys[i]; - pKeyEvents[*pKeyCount] = NvDdkKbcKeyEvent_KeyRelease; - *pKeyCount = *pKeyCount + 1; - } - } - } - - for (i = 0; i < hKbc->KEPInfo.CurrentPressedKeyCount; i++) - { - if (hKbc->KEPInfo.PreviouslyPressedKeyCount) - { - // Check if the KeyPress has already been dispatched in the queue - if (IsKeyEventAlreadyDispatched(hKbc, hKbc->KEPInfo.PressedKeys[i])) - { - // if the Event has already been dispatched, goto the next - // iteration or else add the key press event to queue. - continue; - } - } - - pKeyCodes[*pKeyCount] = hKbc->KEPInfo.PressedKeys[i]; - pKeyEvents[*pKeyCount] = NvDdkKbcKeyEvent_KeyPress; - *pKeyCount = *pKeyCount + 1; - } - - // Move the Current Pressed Keys to Previously Pressed Keys array - for (i = 0; i < hKbc->KEPInfo.CurrentPressedKeyCount; i++) - { - hKbc->KEPInfo.PreviouslyPressedKeys[i] = hKbc->KEPInfo.PressedKeys[i]; - } - hKbc->KEPInfo.PreviouslyPressedKeyCount = hKbc->KEPInfo.CurrentPressedKeyCount; -} - -static void KbcIsr(void* args) -{ - NvU32 RegValue; - NvDdkKbcHandle hKbc = args; - - // Disable the interrupt first - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - RegValue = NV_FLD_SET_DRF_NUM(APBDEV_KBC, CONTROL, FIFO_CNT_INT_EN, 0, RegValue); - KBC_REG_WRITE32(hKbc->pVirtualAddress, CONTROL, RegValue); - - // Clear the interrupt. - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, INT); - RegValue = NV_FLD_SET_DRF_DEF(APBDEV_KBC, INT, FIFO_CNT_INT_STATUS, - DEFAULT_MASK, RegValue); - KBC_REG_WRITE32(hKbc->pVirtualAddress, INT, RegValue); - // Signal the client sema. - NvOsSemaphoreSignal(hKbc->KEPInfo.KbcClientSema); - - // Call interrupt done. - // Fixme!! Not calling here as the KBC is generating the spurious interrupt. - // NvRmInterruptDone(hKbc->InterruptHandle); -} - -static void FindDelayBetweenScans(NvDdkKbcHandle hKbc) -{ - NvU32 RptTime; - NvU32 DebounceVal; - NvU32 DelayInClocks; - - // Findout the delay need to be given between between scans. - RptTime = KBC_REG_READ32(hKbc->pVirtualAddress, RPT_DLY); - DebounceVal = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - DebounceVal = NV_DRF_VAL(APBDEV_KBC, CONTROL, DBC_CNT, DebounceVal); - - /* - * The time delay between two consecutive reads is the sum of the - * repeat time and the time taken for scanning the rows. - * The time taken for scanning the rows is the product of the - * number of rows and the Debounce value set (in number of KBC cycles). - * So we have to wait for this much time before we read the - * AV_FIFO_CNT value again. - */ - DelayInClocks = 5 + hKbc->NumberOfRows* (DebounceVal + 16) + RptTime; - hKbc->DelayBetweenScans = DelayInClocks * KBC_CYCLE_TIME_US; - hKbc->DelayBetweenScans = (hKbc->DelayBetweenScans + 999)/1000; -} - -NvError -NvDdkKbcOpen( - NvRmDeviceHandle hDevice, - NvDdkKbcHandle* phKbc) -{ - NvError Error = NvSuccess; - const NvOdmPeripheralConnectivity *pConnectivity = NULL; - NvU64 KbdGuid = 0; - NvU32 NumGuid = 0; - static NvDdkKbcCapability KbcCaps[2]; - NvDdkKbcCapability *pKbcCapability = NULL; - static NvRmModuleCapability s_KbcCap[] = - { - { 1, 0, 0, &KbcCaps[0]}, - { 1, 1, 0, &KbcCaps[1]} - }; - NvOdmPeripheralSearch SearchAttrs[] = - { - NvOdmPeripheralSearch_IoModule, - }; - NvU32 SearchVals[] = - { - NvOdmIoModule_Kbd, - }; - - NV_ASSERT(phKbc); - NV_ASSERT(hDevice); - - *phKbc = NULL; - - /* - * The NvddkKbcOpen() is called more than once before closing the handle - * that has been opened before NvError_AlreadyAllocated is returned. - */ - if (s_hKbc) - { - *phKbc = s_hKbc; - return NvError_AlreadyAllocated; - } - NumGuid = NvOdmPeripheralEnumerate(SearchAttrs, SearchVals, - NV_ARRAY_SIZE(SearchAttrs), &KbdGuid, 1); - if (!NumGuid) - return NvError_ModuleNotPresent; - - pConnectivity = NvOdmPeripheralGetGuid(KbdGuid); - - if (!pConnectivity) - return NvError_ModuleNotPresent; - - // Allocate memory for the handle. - s_hKbc = NvOsAlloc(sizeof(NvDdkKbc)); - if (!s_hKbc) - { - return NvError_InsufficientMemory; - } - else - { - NvOsMemset(s_hKbc, 0, sizeof(NvDdkKbc)); - } - - s_hKbc->pConnectivity = pConnectivity; - s_hKbc->RmDevHandle = hDevice; - - NvRmModuleGetBaseAddress(s_hKbc->RmDevHandle, - NVRM_MODULE_ID(NvRmModuleID_Kbc, 0), - &s_hKbc->BaseAddress, &s_hKbc->BankSize); - - Error = NvRmPhysicalMemMap(s_hKbc->BaseAddress, s_hKbc->BankSize, - NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, - (void **)&s_hKbc->pVirtualAddress); - if (Error != NvSuccess) - goto cleanup_exit; - - KbcCaps[0].IsKeyMaskingSupported = NV_FALSE; - KbcCaps[0].MinRowCount = 2; - KbcCaps[0].MaxEvents = 7; - - KbcCaps[1].IsKeyMaskingSupported = NV_TRUE; - KbcCaps[1].MinRowCount = 2; - KbcCaps[1].MaxEvents = 8; - - // Getting the Mojor and minor version numbers of kbc controller - Error = NvRmModuleGetCapabilities(hDevice, - NVRM_MODULE_ID(NvRmModuleID_Kbc,0), - s_KbcCap, - 2, - (void **)&(pKbcCapability)); - if (Error) - goto cleanup_exit; - - s_hKbc->IsKeyMaskingSupported = pKbcCapability->IsKeyMaskingSupported; - s_hKbc->MinRowCount = pKbcCapability->MinRowCount; - s_hKbc->MaxEvents = pKbcCapability->MaxEvents; - - /* - * Registering with RmPower. The Power and Clock to the Module are enabled - * in the NvDdkKbcStart() function. - */ - Error = NvRmPowerRegister(s_hKbc->RmDevHandle, NULL, &s_hKbc->KbcRmPowerClientID); - if (Error) - goto cleanup_exit; - *phKbc = s_hKbc; - return NvSuccess; - -cleanup_exit: - // Freeing allocated stuff in reverse order. - NvRmPhysicalMemUnmap(s_hKbc->pVirtualAddress, s_hKbc->BankSize); - NvOsFree(s_hKbc); - s_hKbc = *phKbc = NULL; - return Error; -} - -void NvDdkKbcClose(NvDdkKbcHandle hKbc) -{ - NV_ASSERT(hKbc); - // Disabling Module power - NV_ASSERT((NvRmPowerVoltageControl(hKbc->RmDevHandle, - NvRmModuleID_Kbc, - hKbc->KbcRmPowerClientID, - NvRmVoltsOff, - NvRmVoltsOff, - NULL, - 0, - NULL)) == NvSuccess); - // Un-Registering as Power-client - NvRmPowerUnRegister(hKbc->RmDevHandle, hKbc->KbcRmPowerClientID); - // unmap the memory that was mapped during the open call. - NvRmPhysicalMemUnmap(hKbc->pVirtualAddress, hKbc->BankSize); - // free the handle memory. - NvOsFree(hKbc); - s_hKbc = NULL; -} - -NvError NvDdkKbcStart(NvDdkKbcHandle hKbc, NvOsSemaphoreHandle SemaphoreId) -{ - NvError Error = NvSuccess; - NvU32 RegValue; - NvU32 IrqList; - NvU32 RepeatCycleTime; - NvU32 RepeatCycleTimeInKbcCycles; - NvU32 DebounceTimeInKbcCycles; - NvOsInterruptHandler IntHandlers = KbcIsr; - - NV_ASSERT(hKbc); - - // store the sema to be signaled on key events. - hKbc->KEPInfo.KbcClientSema = SemaphoreId; - hKbc->KEPInfo.hKbc = hKbc; - - // Start the Kbc, if it has not been already started. - if (hKbc->KbcStarted) - { - /* - * If it is already started, return error. It cannot be started - * multiple times. - */ - Error = NvError_AlreadyAllocated; - goto exit_gracefully; - } - - Error = NvddkPrivKbcEnableClock(hKbc); - if (Error) - goto FailureExit; - - // Reset the KBC controller to clear all previous status. - NvRmModuleReset(hKbc->RmDevHandle, NVRM_MODULE_ID(NvRmModuleID_Kbc, 0)); - - hKbc->KbcStarted = NV_TRUE; - // Register with RM for the interrupt. - if (!hKbc->InterruptHandle) - { - IrqList = NvRmGetIrqForLogicalInterrupt(hKbc->RmDevHandle, - NvRmModuleID_Kbc, 0); - Error = NvRmInterruptRegister(hKbc->RmDevHandle, 1, &IrqList, - & IntHandlers, hKbc, &hKbc->InterruptHandle, NV_TRUE); - if (Error) - goto FailureExit; - } - - /* - *Config the row info, column info, de-bounce time etc., which - * comes from ODM. - */ - ConfigRowAndColumnInfo(hKbc); - // Power on the keyboard interface if required - SetPowerOnKeyboard(hKbc, NV_TRUE); - // Set de-bounce value. - NvOdmKbcGetParameter(NvOdmKbcParameter_DebounceTime, 1, - &DebounceTimeInKbcCycles); - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - RegValue = NV_FLD_SET_DRF_NUM(APBDEV_KBC, CONTROL, DBC_CNT, - DebounceTimeInKbcCycles, RegValue); - - KBC_REG_WRITE32(hKbc->pVirtualAddress, CONTROL, RegValue); - - // Set RepeatCycleTime value. - NvOdmKbcGetParameter(NvOdmKbcParameter_RepeatCycleTime, 1, - &RepeatCycleTime); - RepeatCycleTimeInKbcCycles = ((RepeatCycleTime*1000)/KBC_CYCLE_TIME_US); - RegValue = NV_DRF_NUM(APBDEV_KBC, RPT_DLY, RPT_DLY_VAL, - RepeatCycleTimeInKbcCycles); - KBC_REG_WRITE32(hKbc->pVirtualAddress, RPT_DLY, RegValue); - // enable the kbc interrupt. - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - RegValue = NV_FLD_SET_DRF_NUM(APBDEV_KBC, CONTROL, FIFO_CNT_INT_EN, 1, - RegValue); - RegValue = NV_FLD_SET_DRF_NUM(APBDEV_KBC, CONTROL, FIFO_TH_CNT, 1, - RegValue); - // enable the kbc. - RegValue = NV_FLD_SET_DRF_DEF(APBDEV_KBC, CONTROL, EN, ENABLE, RegValue); - KBC_REG_WRITE32(hKbc->pVirtualAddress, CONTROL, RegValue); - // Findout the delay need to be given between between scans. - FindDelayBetweenScans(hKbc); - NvDdkPrivKbcSuspendController(hKbc); - return NvSuccess; - -FailureExit: - hKbc->KbcStarted = NV_FALSE; -exit_gracefully: - return Error; -} - -NvError NvDdkKbcStop(NvDdkKbcHandle hKbc) -{ - NvU32 RegValue; - NvError Error; - - NV_ASSERT(hKbc); - NV_ASSERT(hKbc->KbcStarted); - - // disable the kbc interrupt. - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - RegValue = NV_FLD_SET_DRF_DEF(APBDEV_KBC, CONTROL, KP_INT_EN, - DISABLE, RegValue); - // disable the kbc. - RegValue = NV_FLD_SET_DRF_DEF(APBDEV_KBC, CONTROL, EN, DISABLE, RegValue); - KBC_REG_WRITE32(hKbc->pVirtualAddress, CONTROL, RegValue); - - if (hKbc->KbcStarted == NV_TRUE) - { - NvRmInterruptUnregister(hKbc->RmDevHandle, hKbc->InterruptHandle); - hKbc->InterruptHandle = NULL; - hKbc->KbcStarted = NV_FALSE; - /* - * Clear it so that on next start, it will not have previous - * left over values. - */ - NvOsMemset(&hKbc->KEPInfo, 0, sizeof(KbcKeyEventProcessInfo)); - } - Error = NvddkPrivKbcDisableClock(hKbc); - return Error; -} - -void -NvDdkKbcSetRepeatTime( - NvDdkKbcHandle hKbc, - NvU32 RepeatTimeMs) -{ - NvU32 RegValue; - NvU32 TimeInKbcCycles = (RepeatTimeMs *1000) / KBC_CYCLE_TIME_US; - hKbc->KEPInfo.RepeatDelayMs = RepeatTimeMs; - - NV_ASSERT(hKbc); - NV_ASSERT(RepeatTimeMs); - RegValue = NV_DRF_NUM(APBDEV_KBC, RPT_DLY, RPT_DLY_VAL, TimeInKbcCycles); - KBC_REG_WRITE32(hKbc->pVirtualAddress, RPT_DLY, RegValue); - // Findout the delay need to be given between between scans. - FindDelayBetweenScans(hKbc); -} - -NvU32 -NvDdkKbcGetKeyEvents( - NvDdkKbcHandle hKbc, - NvU32* pKeyCount, - NvU32* pKeyCodes, - NvDdkKbcKeyEvent* pKeyEvents) -{ - NvU32 RegValue; - NvU32 KeyCount; - NvError Error; - - Error = NvDdkPrivKbcResumeController(hKbc); - if (Error) - { - *pKeyCount = 0; - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - RegValue = NV_FLD_SET_DRF_NUM(APBDEV_KBC, CONTROL, - FIFO_CNT_INT_EN, 1, RegValue); - KBC_REG_WRITE32(hKbc->pVirtualAddress, CONTROL, RegValue); - // Call interrupt done here to reenable the interrupt. - NvRmInterruptDone(hKbc->InterruptHandle); - return 0; - } - - /* - * Read the AV_FIFO_CNT value. A non-zero value indicates that - * new key press events are available in the Key Press - * Entry Reg. - */ - hKbc->KEPInfo.CurrentPressedKeyCount = 0; - KeyCount = KBC_REG_READ32(hKbc->pVirtualAddress, INT); - KeyCount = NV_DRF_VAL(APBDEV_KBC, INT, AV_FIFO_CNT, KeyCount); - - if (KeyCount > 0) - { - // Read the Key-Press Entries Register to find the new key - // presses and store them in the PressedKeys[] array. - hKbc->KEPInfo.CurrentPressedKeyCount = ExtractPressedKeys(hKbc, 0); - /* - * Compare the newly pressed keys to the Keys pressed in - * the previous iteration and signal the client sema if - * there are any new Key Press or Release events - */ - GetKeyEvents(hKbc, pKeyCount, pKeyCodes, pKeyEvents); - - // If the number of keys in fifo is more than 1 then need not to wait - // till full scan is complete as the client can read it immediately. - // Can not return 0 as return of 0 to the client means there is no - // more key event in the fifo so returning 1. - if(KeyCount > 1) - return 1; - else - return hKbc->DelayBetweenScans; - } - else - { - // Since there are no keys pressed, send out the Release - // events - GetKeyEvents(hKbc, pKeyCount, pKeyCodes, pKeyEvents); - RegValue = KBC_REG_READ32(hKbc->pVirtualAddress, CONTROL); - RegValue = NV_FLD_SET_DRF_NUM(APBDEV_KBC, CONTROL, - FIFO_CNT_INT_EN, 1, RegValue); - KBC_REG_WRITE32(hKbc->pVirtualAddress, CONTROL, RegValue); - // Call interrupt done here to reenable the interrupt. - NvRmInterruptDone(hKbc->InterruptHandle); - NvDdkPrivKbcSuspendController(hKbc); - return 0; - } -} - -NvError NvDdkKbcSuspend(NvDdkKbcHandle hKbc) -{ - NvU32 RowCount; - NV_ASSERT(hKbc); - if (hKbc->IsSelectKeysWkUpEnabled) - { - if (hKbc->IsKeyMaskingSupported) - { - for (RowCount =0; RowCount < hKbc->NumberOfRows; ++RowCount) - KBC_REG_WRITE32(hKbc->pVirtualAddress + RowCount, - ROW0_MASK, hKbc->KbcRowMaskReg[RowCount]); - } - else - { - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG0, hKbc->KbcSuspendedColReg[0]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG1, hKbc->KbcSuspendedColReg[1]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG2, hKbc->KbcSuspendedColReg[2]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG0, hKbc->KbcSuspendedRowReg[0]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG1, hKbc->KbcSuspendedRowReg[1]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG2, hKbc->KbcSuspendedRowReg[2]); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG3, hKbc->KbcSuspendedRowReg[3]); - } - } - - // We are not suspending the kbc driver here because the driver is suspended - // after every keypress. We need not suspend the driver again here - return NvSuccess; -} - -NvError NvDdkKbcResume(NvDdkKbcHandle hKbc) -{ - NvU32 RowCount; - NV_ASSERT(hKbc); - if (hKbc->IsSelectKeysWkUpEnabled) - { - if (hKbc->IsKeyMaskingSupported) - { - for (RowCount =0; RowCount < hKbc->NumberOfRows; ++RowCount) - KBC_REG_WRITE32(hKbc->pVirtualAddress + RowCount, ROW0_MASK, 0); - } - else - { - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG0, hKbc->KbcRegColCfg0); - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG1, hKbc->KbcRegColCfg1); - KBC_REG_WRITE32(hKbc->pVirtualAddress, COL_CFG2, hKbc->KbcRegColCfg2); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG0, hKbc->KbcRegRowCfg0); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG1, hKbc->KbcRegRowCfg1); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG2, hKbc->KbcRegRowCfg2); - KBC_REG_WRITE32(hKbc->pVirtualAddress, ROW_CFG3, hKbc->KbcRegRowCfg3); - } - } - - // We resume the kbc controller only when there is key press detected. So - // there is no need to resume the controller here. - return NvSuccess; -} - diff --git a/arch/arm/mach-tegra/odm_to_plat.c b/arch/arm/mach-tegra/odm_to_plat.c new file mode 100644 index 000000000000..7ccc982d1f25 --- /dev/null +++ b/arch/arm/mach-tegra/odm_to_plat.c @@ -0,0 +1,129 @@ +/* + * arch/arm/mach-tegra/board-nvodm.c + * + * Board registration for ODM-kit generic Tegra boards + * + * Copyright (c) 2009, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <mach/kbc.h> +#include <linux/tegra_devices.h> + +#include "nvodm_query_discovery.h" +#include "nvodm_kbc.h" +#include "nvodm_query_kbc.h" +#include "nvodm_kbc_keymapping.h" + +#ifdef CONFIG_KEYBOARD_TEGRA +struct tegra_kbc_plat *tegra_kbc_odm_to_plat(void) +{ + struct tegra_kbc_plat *pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + const NvOdmPeripheralConnectivity *conn; + NvOdmPeripheralSearch srch_attr = NvOdmPeripheralSearch_IoModule; + const struct NvOdmKeyVirtTableDetail **vkeys; + NvU32 srch_val = NvOdmIoModule_Kbd; + NvU32 temp; + NvU64 guid; + NvU32 i, j, k; + NvU32 cols=0; + NvU32 rows=0; + NvU32 *wake_row; + NvU32 *wake_col; + NvU32 wake_num; + NvU32 vnum; + + if (!pdata) return NULL; + pdata->keymap = kzalloc(sizeof(*pdata->keymap)*KBC_MAX_KEY, GFP_KERNEL); + if (!pdata->keymap) { + kfree(pdata->keymap); + return NULL; + } + if (NvOdmKbcIsSelectKeysWkUpEnabled(&wake_row, &wake_col, &wake_num)) { + BUG_ON(!wake_num || wake_num>=KBC_MAX_KEY); + pdata->wake_cfg = kzalloc(sizeof(*pdata->wake_cfg)*wake_num, + GFP_KERNEL); + if (pdata->wake_cfg) { + pdata->wake_cnt = (int)wake_num; + for (i=0; i<wake_num; i++) { + pdata->wake_cfg[i].row=wake_row[i]; + pdata->wake_cfg[i].col=wake_col[i]; + } + } else + pr_err("disabling wakeup key filtering due to " + "out-of-memory error\n"); + } + + NvOdmKbcGetParameter(NvOdmKbcParameter_DebounceTime, 1, &temp); + + pdata->debounce_cnt = temp; + + /* repeat cycle is reported from ODM in milliseconds, + * but needs to be specified in 32KHz ticks */ + NvOdmKbcGetParameter(NvOdmKbcParameter_RepeatCycleTime, 1, &temp); + pdata->repeat_cnt = temp * 4096 / 125; + + temp = NvOdmPeripheralEnumerate(&srch_attr, &srch_val, 1, &guid, 1); + if (!temp) { + kfree(pdata); + return NULL; + } + conn = NvOdmPeripheralGetGuid(guid); + if (!conn) { + kfree(pdata); + return NULL; + } + + for (i=0; i<conn->NumAddress; i++) { + NvU32 addr = conn->AddressList[i].Address; + + if (conn->AddressList[i].Interface!=NvOdmIoModule_Kbd) continue; + + if (conn->AddressList[i].Instance) { + pdata->pin_cfg[addr].num = cols++; + pdata->pin_cfg[addr].is_col = true; + } else { + pdata->pin_cfg[addr].num = rows++; + pdata->pin_cfg[addr].is_row = true; + } + } + + for (i=0; i<KBC_MAX_KEY; i++) + pdata->keymap[i] = -1; + + vnum = NvOdmKbcKeyMappingGetVirtualKeyMappingList(&vkeys); + + for (i=0; i<rows; i++) { + for (j=0; j<cols; j++) { + NvU32 sc = NvOdmKbcGetKeyCode(i, j, rows, cols); + for (k=0; k<vnum; k++) { + if (sc >= vkeys[k]->StartScanCode && + sc <= vkeys[k]->EndScanCode) { + sc -= vkeys[k]->StartScanCode; + sc = vkeys[k]->pVirtualKeyTable[sc]; + if (!sc) continue; + pdata->keymap[kbc_indexof(i,j)]=sc; + } + + } + } + } + + return pdata; +} +#endif diff --git a/arch/arm/mach-tegra/tegra_sysmap.c b/arch/arm/mach-tegra/tegra_sysmap.c index ab504d1d56bb..6b5553fc6b30 100755 --- a/arch/arm/mach-tegra/tegra_sysmap.c +++ b/arch/arm/mach-tegra/tegra_sysmap.c @@ -46,6 +46,8 @@ static NvRmModuleID tegra_map_name_to_mod(const char *name, int inst) return NVRM_MODULE_ID(NvRmPrivModuleID_Gart, inst); else if (!strcmp(name, "iram")) return NVRM_MODULE_ID(NvRmPrivModuleID_Iram, inst); + else if (!strcmp(name, "kbc")) + return NVRM_MODULE_ID(NvRmModuleID_Kbc, 0); return (NvRmModuleID) 0; } diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index ea400bdeadca..1a5768fb602f 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -4,7 +4,7 @@ * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix * keyboard controller * - * Copyright (c) 2009, NVIDIA Corporation. + * Copyright (c) 2009-2010, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,277 +21,586 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#define RM_SUPPORT + #include <linux/module.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/kthread.h> - +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> +#include <linux/clk.h> +#include <mach/kbc.h> + +#ifdef RM_SUPPORT +#include <linux/tegra_devices.h> #include <mach/nvrm_linux.h> -#include <nvddk_kbc.h> -#include <nvos.h> -#include <nvodm_kbc_keymapping.h> +#include "nvrm_power.h" +#endif + +#define KBC_CONTROL_0 0 +#define KBC_INT_0 4 +#define KBC_ROW_CFG0_0 8 +#define KBC_COL_CFG0_0 0x18 +#define KBC_RPT_DLY_0 0x2c +#define KBC_KP_ENT0_0 0x30 +#define KBC_KP_ENT1_0 0x34 +#define KBC_ROW0_MASK_0 0x38 + +#define res_size(res) ((res)->end - (res)->start + 1) + +struct tegra_kbc { + void __iomem *mmio; + struct input_dev *idev; + int irq; + spinlock_t lock; + unsigned int repoll_time; + struct tegra_kbc_plat *pdata; + struct work_struct key_repeat; +#ifdef RM_SUPPORT + NvU32 client_id; +#else + struct clk *clk; + struct regulator *reg; +#endif +}; + +#ifdef RM_SUPPORT +static int alloc_resource(struct tegra_kbc *kbc, struct platform_device *pdev) +{ + NvError e = NvRmPowerRegister(s_hRmGlobal, NULL, &kbc->client_id); + if (e!=NvSuccess) return -ENXIO; + return 0; +} + +static void free_resource(struct tegra_kbc *kbc) +{ + NvRmPowerUnRegister(s_hRmGlobal, kbc->client_id); +} + +static void enable_power(struct tegra_kbc *kbc) +{ + NvError e; + e = NvRmPowerVoltageControl(s_hRmGlobal, + NVRM_MODULE_ID(NvRmModuleID_Kbc, 0), + kbc->client_id, NvRmVoltsUnspecified, + NvRmVoltsUnspecified, NULL, 0, NULL); + BUG_ON(e!=NvSuccess); +} + +static void disable_power(struct tegra_kbc *kbc) +{ + NvError e; + e = NvRmPowerVoltageControl(s_hRmGlobal, + NVRM_MODULE_ID(NvRmModuleID_Kbc, 0), + kbc->client_id, NvRmVoltsOff, + NvRmVoltsOff, NULL, 0, NULL); + BUG_ON(e!=NvSuccess); +} + +static void enable_clock(struct tegra_kbc *kbc) +{ + NvError e; + e = NvRmPowerModuleClockControl(s_hRmGlobal, + NVRM_MODULE_ID(NvRmModuleID_Kbc,0), 0, NV_TRUE); + BUG_ON(e!=NvSuccess); + NvRmModuleReset(s_hRmGlobal, NVRM_MODULE_ID(NvRmModuleID_Kbc, 0)); +} +static void disable_clock(struct tegra_kbc *kbc) +{ + NvRmPowerModuleClockControl(s_hRmGlobal, + NVRM_MODULE_ID(NvRmModuleID_Kbc,0), 0, NV_FALSE); +} -static int tegra_kbc_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) +#else +static int alloc_resource(struct tegra_kbc *kbc, struct platform_device *pdev) { + kbc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(kbc->clk)) { + int err; + dev_err(&pdev->dev, "failed to get keypad clock\n"); + err = PTR_ERR(kbc->clk); + kbc->clk = NULL; + return err; + } + kbc->reg = regulator_get(&pdev->dev, "Vcc"); + if (IS_ERR(kbc->reg)) { + dev_err(&pdev->dev, "no regulator support\n"); + kbc->reg = NULL; + } return 0; } -struct NvOdmKeyVirtTableDetail **key_tab = NULL; -NvU32 key_tab_total = 0; +static void free_resource(struct tegra_kbc *kbc) +{ + if (kbc->clk) clk_put(kbc->clk); + if (kbc->reg) regulator_put(kbc->reg); +} -struct tegra_kbc_driver_data { - struct input_dev *input_dev; - struct task_struct *task; - NvOsSemaphoreHandle semaphore; - NvDdkKbcHandle ddkHandle; - int done; -}; +static void enable_power(struct tegra_kbc *kbc) +{ + if (kbc->reg) regulator_enable(kbc->reg); +} -#define in_table(_code, _tabl) \ - (((_code)>=(_tabl)->StartScanCode) && ((_code)<=(_tabl)->EndScanCode)) +static void disable_power(struct tegra_kbc *kbc) +{ + if (kbc->reg) regulator_disable(kbc->reg); +} -#define table_size(_tabl) ((_tabl)->EndScanCode - (_tabl)->StartScanCode + 1) +static void enable_clock(struct tegra_kbc *kbc) +{ + clk_enable(kbc->clk); +} -static NvU32 tegra_kbc_handle_keyev(struct tegra_kbc_driver_data *kbc) +static void disable_clock(struct tegra_kbc *kbc) { - NvDdkKbcKeyEvent key_ev[16]; - NvU32 codes[16]; - NvU32 k_idx; - NvU32 l_idx; - NvU32 EventCount; - NvU32 WaitTime; - NvU32 i; - NvU32 value; - NvU32 key; - - WaitTime = NvDdkKbcGetKeyEvents(kbc->ddkHandle, - &EventCount, codes, key_ev); - - for (i = 0; i < EventCount; i++) { - if (key_ev[i] == NvDdkKbcKeyEvent_KeyPress) { - value = 1; - } else if (key_ev[i] == NvDdkKbcKeyEvent_KeyRelease) { - value = 0; - } else - continue; + clk_disable(kbc->clk); +} +#endif - key = 0; +static int tegra_kbc_keycode(const struct tegra_kbc *kbc, int r, int c) { + unsigned int i = kbc_indexof(r,c); - for (l_idx = 0; l_idx < key_tab_total; ++l_idx) { - if (!in_table(codes[i], key_tab[l_idx])) - continue; - k_idx = codes[i] - key_tab[l_idx]->StartScanCode; - key = key_tab[l_idx]->pVirtualKeyTable[k_idx]; - } + if (kbc->pdata->keymap) + return kbc->pdata->keymap[i]; - input_report_key(kbc->input_dev, key, value); - } + return i; +} + +#ifdef CONFIG_PM +static int tegra_kbc_open(struct input_dev *dev); +static void tegra_kbc_close(struct input_dev *dev); +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter); - if (WaitTime) { - NvOsSleepMS(WaitTime); +static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) { + tegra_kbc_setup_wakekeys(kbc, true); + enable_irq_wake(kbc->irq); + disable_power(kbc); + } else { + tegra_kbc_close(kbc->idev); } - return WaitTime; + return 0; } -static int tegra_kbc_thread(void *pdata) +static int tegra_kbc_resume(struct platform_device *pdev) { - struct tegra_kbc_driver_data *kbc = pdata; - NvU32 loop; + struct tegra_kbc *kbc = platform_get_drvdata(pdev); - for (;;) { - /* FIXME should we use a NvOsSemaphoreWaitTimeout instead? */ - NvOsSemaphoreWait(kbc->semaphore); - if (kbc->done) - break; - do { - loop = tegra_kbc_handle_keyev(kbc); - } while (loop); - } + if (device_may_wakeup(&pdev->dev)) { + enable_power(kbc); + disable_irq_wake(kbc->irq); + tegra_kbc_setup_wakekeys(kbc, false); + } else if (kbc->idev->users) + return tegra_kbc_open(kbc->idev); return 0; } +#endif -static void tegra_kbc_cleanup(struct tegra_kbc_driver_data *kbc) +static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo) { - if (!kbc) - return; + int curr_fifo[KBC_MAX_KPENT]; + u32 kp_ent_val[(KBC_MAX_KPENT*8 + 3) / 4]; + u32 *kp_ents = kp_ent_val; + u32 kp_ent; + unsigned long flags; + int i, j, valid=0; + + local_irq_save(flags); + for (i=0; i<ARRAY_SIZE(kp_ent_val); i++) + kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4)); + local_irq_restore(flags); + + for (i=0; i<KBC_MAX_KPENT; i++) { + if (!(i&3)) kp_ent=*kp_ents++; + + if (kp_ent & 0x80) { + int c = kp_ent & 0x7; + int r = (kp_ent >> 3) & 0xf; + int k = tegra_kbc_keycode(kbc, r, c); + if (likely(k!=-1)) curr_fifo[valid++] = k; + } + kp_ent >>= 8; + } - if (kbc->task) { - kbc->done = 1; - NvOsSemaphoreSignal(kbc->semaphore); - kthread_stop(kbc->task); + for (i=0; i<KBC_MAX_KPENT; i++) { + if (fifo[i]==-1) continue; + for (j=0; j<valid; j++) { + if (curr_fifo[j] == fifo[i]) { + curr_fifo[j] = -1; + break; + } + } + if (j==valid) { + input_report_key(kbc->idev, fifo[i], 0); + fifo[i] = -1; + } } - if (kbc->ddkHandle) { - NvDdkKbcStop(kbc->ddkHandle); - NvDdkKbcClose(kbc->ddkHandle); + for (j=0; j<valid; j++) { + if (curr_fifo[j]==-1) continue; + for (i=0; i<KBC_MAX_KPENT; i++) { + if (fifo[i]==-1) break; + } + if (i!=KBC_MAX_KPENT) { + fifo[i] = curr_fifo[j]; + input_report_key(kbc->idev, fifo[i], 1); + } else + WARN_ON(1); } +} - if (kbc->semaphore) - NvOsSemaphoreDestroy(kbc->semaphore); - - if (kbc->input_dev) - input_free_device(kbc->input_dev); +static void tegra_kbc_key_repeat(struct work_struct *work) +{ + struct tegra_kbc *kbc; + unsigned long flags; + u32 val; + int fifo[KBC_MAX_KPENT]; + int i; + + kbc = container_of(work, struct tegra_kbc, key_repeat); + for (i=0; i<ARRAY_SIZE(fifo); i++) fifo[i] = -1; + + while (1) { + val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; + if (!val) { + /* release any pressed keys and exit the loop */ + for (i=0; i<ARRAY_SIZE(fifo); i++) { + if (fifo[i]==-1) continue; + input_report_key(kbc->idev, fifo[i], 0); + } + break; + } + tegra_kbc_report_keys(kbc, fifo); + /* FIXME: why is this here? */ + msleep((val==1) ? kbc->repoll_time : 1); + } - kfree(kbc); + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val |= (1<<3); + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); } -static int __init tegra_kbc_probe(struct platform_device *pdev) +static void tegra_kbc_close(struct input_dev *dev) { - struct tegra_kbc_driver_data *kbc = NULL; - struct input_dev *input_dev = NULL; - int err; - NvError nverr; - NvU32 l_idx; - NvU32 k_idx; - NvU32 TotalKey; + struct tegra_kbc *kbc = input_get_drvdata(dev); + unsigned long flags; + u32 val; - kbc = kzalloc(sizeof(struct tegra_kbc_driver_data), GFP_KERNEL); - input_dev = input_allocate_device(); + val = readl(kbc->mmio + KBC_CONTROL_0); + val &= ~1; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); - if (input_dev == NULL || kbc == NULL) { - input_free_device(input_dev); - kfree(kbc); - err = -ENOMEM; - pr_err("tegra_kbc_probe: Failed to allocate input device\n"); - return err; - } - nverr = NvOsSemaphoreCreate(&kbc->semaphore, 0); - if (nverr != NvSuccess) { - err = -1; - pr_err("tegra_kbc_probe: Semaphore creation failed\n"); - goto fail; - } + disable_clock(kbc); + disable_power(kbc); +} - nverr = NvDdkKbcOpen (s_hRmGlobal, &kbc->ddkHandle); - if (nverr != NvSuccess) { - err = -1; - pr_err("tegra_kbc_probe: NvDdkKbcOpen failed\n"); - goto fail; +#ifdef CONFIG_ARCH_TEGRA_1x_SOC +#define tegra_kbc_setup_wakekeys(kbc, filter) do { } while (0) +#else +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) +{ + int i; + unsigned int rst_val; + + BUG_ON(kbc->pdata->wake_cnt > KBC_MAX_KEY); + rst_val = (filter && kbc->pdata->wake_cnt) ? ~0 : 0; + + for (i=0; i<KBC_MAX_ROW; i++) + writel(rst_val, kbc->mmio+KBC_ROW0_MASK_0+i*4); + + if (filter) { + for (i=0; i<kbc->pdata->wake_cnt; i++) { + u32 val, addr; + addr = kbc->pdata->wake_cfg[i].row*4 + KBC_ROW0_MASK_0; + val = readl(kbc->mmio + addr); + val &= ~(1<<kbc->pdata->wake_cfg[i].col); + writel(val, kbc->mmio + addr); + } } +} +#endif - nverr = NvDdkKbcStart(kbc->ddkHandle, kbc->semaphore); - if (nverr != NvSuccess) { - err = -1; - pr_err("tegra_kbc_probe: NvDdkKbcStart failed\n"); - goto fail; +static void tegra_kbc_config_pins(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_plat *pdata = kbc->pdata; + int i; + + for (i=0; i<KBC_MAX_GPIO; i++) { + u32 row_cfg, col_cfg; + u32 r_shift = 5 * (i%6); + u32 c_shift = 4 * (i%8); + u32 r_mask = 0x1f << r_shift; + u32 c_mask = 0xf << c_shift; + u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; + u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; + + row_cfg = readl(kbc->mmio + r_offs); + col_cfg = readl(kbc->mmio + c_offs); + + row_cfg &= ~r_mask; + col_cfg &= ~c_mask; + + if (pdata->pin_cfg[i].is_row) + row_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << r_shift; + else if (pdata->pin_cfg[i].is_col) + col_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << c_shift; + + writel(row_cfg, kbc->mmio + r_offs); + writel(col_cfg, kbc->mmio + c_offs); } +} - kbc->task = kthread_create(tegra_kbc_thread, kbc, "tegra_kbc_thread"); - if(kbc->task == NULL) { - err = -1; - goto fail; +static int tegra_kbc_open(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + unsigned long flags; + u32 val = 0; + + enable_power(kbc); + enable_clock(kbc); + + tegra_kbc_config_pins(kbc); + tegra_kbc_setup_wakekeys(kbc, false); + + /* atomically clear out any remaining entries in the key FIFO + * and enable keyboard interrupts */ + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_INT_0); + val >>= 4; + if (val) { + val = readl(kbc->mmio + KBC_KP_ENT0_0); + val = readl(kbc->mmio + KBC_KP_ENT1_0); } - wake_up_process( kbc->task ); + writel(0x7, kbc->mmio + KBC_INT_0); + spin_unlock_irqrestore(&kbc->lock, flags); - kbc->input_dev = input_dev; - input_dev->event = tegra_kbc_event; - input_dev->name = "tegra-kbc"; - __set_bit(EV_KEY, input_dev->evbit); + writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); - key_tab_total = NvOdmKbcKeyMappingGetVirtualKeyMappingList(&key_tab); + val = kbc->pdata->debounce_cnt << 4; + val |= 1<<14; /* fifo interrupt threshold = 1 entry */ + val |= 1<<3; /* interrupt on FIFO threshold reached */ + val |= 1; /* enable */ + writel(val, kbc->mmio + KBC_CONTROL_0); - for (l_idx = 0; l_idx < key_tab_total; ++l_idx) { - TotalKey = table_size(key_tab[l_idx]); - for (k_idx = 0; k_idx < TotalKey; ++k_idx) { - __set_bit(key_tab[l_idx]->pVirtualKeyTable[k_idx], - input_dev->keybit); - } - } + return 0; +} - platform_set_drvdata(pdev, kbc); - err = input_register_device(input_dev); - if (err) { - pr_err("tegra_kbc_probe: Unable to register %s input device\n", - input_dev->name); - goto fail; - } +static int __devexit tegra_kbc_remove(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + struct resource *res; - return 0; + free_irq(kbc->irq, pdev); + disable_clock(kbc); + disable_power(kbc); + free_resource(kbc); -fail: - tegra_kbc_cleanup(kbc); - return err; + input_unregister_device(kbc->idev); + input_free_device(kbc->idev); + iounmap(kbc->mmio); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res_size(res)); + + kfree(kbc); + return 0; } -static int tegra_kbc_remove(struct platform_device *pdev) +static irqreturn_t tegra_kbc_isr(int irq, void *args) { - struct tegra_kbc_driver_data *kbc = platform_get_drvdata(pdev); - tegra_kbc_cleanup(kbc); - return 0; + struct tegra_kbc *kbc = args; + u32 val, ctl; + + /* until all keys are released, defer further processing to + * the polling loop in tegra_kbc_key_repeat */ + ctl = readl(kbc->mmio + KBC_CONTROL_0); + ctl &= ~(1<<3); + writel(ctl, kbc->mmio + KBC_CONTROL_0); + + /* quickly bail out & reenable interrupts if the interrupt source + * wasn't fifo count threshold */ + val = readl(kbc->mmio + KBC_INT_0); + writel(val, kbc->mmio + KBC_INT_0); + + if (!(val & (1<<2))) { + ctl |= 1<<3; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + return IRQ_HANDLED; + } + + schedule_work(&kbc->key_repeat); + return IRQ_HANDLED; } -static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) +static int __init tegra_kbc_probe(struct platform_device *pdev) { - struct tegra_kbc_driver_data *kbc = platform_get_drvdata(pdev); - NvError e = NvError_Success; + struct tegra_kbc *kbc; + struct tegra_kbc_plat *pdata = pdev->dev.platform_data; + struct resource *res; + int irq; + int err; + int rows[KBC_MAX_ROW]; + int cols[KBC_MAX_COL]; + int i, j; + int nr = 0; + + if (!pdata) return -EINVAL; + + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); + if (!kbc) return -ENOMEM; - if (!kbc) - return -1; + kbc->pdata = pdata; + kbc->irq = -EINVAL; - if (!kbc->ddkHandle) { - printk("%s: device handle is NULL\n", __func__); - return -1; + memset(rows, 0, sizeof(rows)); + memset(cols, 0, sizeof(cols)); + + kbc->idev = input_allocate_device(); + if (!kbc->idev) { + err = -ENOMEM; + goto fail; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + err = -ENXIO; + goto fail; + } + res = request_mem_region(res->start, res_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + err = -EBUSY; + goto fail; } + kbc->mmio = ioremap(res->start, res_size(res)); + if (!kbc->mmio) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + err = -ENXIO; + goto fail; + } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad IRQ\n"); + err = -ENXIO; + goto fail; + } + err = alloc_resource(kbc, pdev); + if (err) goto fail; - /* power down hardware */ - e = NvDdkKbcSuspend(kbc->ddkHandle); - if (e != NvSuccess) { - printk("%s: hardware power down fail\n", __func__); - return -1; + platform_set_drvdata(pdev, kbc); + + kbc->idev->name = pdev->name; + input_set_drvdata(kbc->idev, kbc); + kbc->idev->id.bustype = BUS_HOST; + kbc->idev->open = tegra_kbc_open; + kbc->idev->close = tegra_kbc_close; + kbc->idev->dev.parent = &pdev->dev; + spin_lock_init(&kbc->lock); + + for (i=0; i<KBC_MAX_GPIO; i++) { + if (pdata->pin_cfg[i].is_row && pdata->pin_cfg[i].is_col) { + dev_err(&pdev->dev, "invalid pin configuration data\n"); + err = -EINVAL; + goto fail; + } + + if (pdata->pin_cfg[i].is_row) { + if (pdata->pin_cfg[i].num >= KBC_MAX_ROW) { + dev_err(&pdev->dev, "invalid row number\n"); + err = -EINVAL; + goto fail; + } + rows[pdata->pin_cfg[i].num] = 1; + nr++; + } else if (pdata->pin_cfg[i].is_col) { + if (pdata->pin_cfg[i].num >= KBC_MAX_COL) { + dev_err(&pdev->dev, "invalid column number\n"); + err = -EINVAL; + goto fail; + } + cols[pdata->pin_cfg[i].num] = 1; + } } - return 0; -} + kbc->repoll_time = 5 + (16+pdata->debounce_cnt)*nr + pdata->repeat_cnt; + kbc->repoll_time = (kbc->repoll_time*1000 + 16384) / 32768; -static int tegra_kbc_resume(struct platform_device *pdev) -{ - struct tegra_kbc_driver_data *kbc = platform_get_drvdata(pdev); - NvError e = NvError_Success; + kbc->idev->evbit[0] = BIT_MASK(EV_KEY); - if (!kbc) - return -1; + for (i=0; i<KBC_MAX_COL; i++) { + if (!cols[i]) continue; + for (j=0; j<KBC_MAX_ROW; j++) { + int keycode; + if (!rows[j]) continue; + keycode = tegra_kbc_keycode(kbc, j, i); + if (keycode==-1) continue; + set_bit(keycode, kbc->idev->keybit); + } + } - if (!kbc->ddkHandle) { - printk("%s: device handle is NULL\n", __func__); - return -1; + /* keycode FIFO needs to be read atomically; leave local + * interrupts disabled when handling KBC interrupt */ + INIT_WORK(&kbc->key_repeat, tegra_kbc_key_repeat); + err = request_irq(irq, tegra_kbc_isr, IRQF_DISABLED, pdev->name, kbc); + if (err) { + dev_err(&pdev->dev, "failed to request keypad IRQ\n"); + goto fail; } + kbc->irq = irq; - /* power up hardware */ - e = NvDdkKbcResume(kbc->ddkHandle); - if (e != NvSuccess) { - printk("%s: hardware power up fail\n", __func__); - return -1; + err = input_register_device(kbc->idev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto fail; } + device_init_wakeup(&pdev->dev, 1); return 0; + +fail: + if (kbc->irq >= 0) free_irq(kbc->irq, pdev); + if (kbc->idev) input_free_device(kbc->idev); + free_resource(kbc); + if (kbc->mmio) iounmap(kbc->mmio); + kfree(kbc); + return err; } static struct platform_driver tegra_kbc_driver = { .probe = tegra_kbc_probe, .remove = tegra_kbc_remove, +#ifdef CONFIG_PM .suspend = tegra_kbc_suspend, .resume = tegra_kbc_resume, - .driver = { - .name = "tegra_kbc", - }, +#endif + .driver = { + .name = "tegra_kbc" + } }; -static int __devinit tegra_kbc_init(void) +static void __exit tegra_kbc_exit(void) { - return platform_driver_register(&tegra_kbc_driver); + platform_driver_unregister(&tegra_kbc_driver); } -static void __exit tegra_kbc_exit(void) +static int __devinit tegra_kbc_init(void) { - platform_driver_unregister(&tegra_kbc_driver); + return platform_driver_register(&tegra_kbc_driver); } -module_init(tegra_kbc_init); module_exit(tegra_kbc_exit); +module_init(tegra_kbc_init); -MODULE_DESCRIPTION("Tegra Key board controller driver"); - +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); |