summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-03-22 17:29:20 -0700
committerGary King <gking@nvidia.com>2010-03-23 10:42:58 -0800
commitb488d3228e76fb434010ff5e3f1044ce62196217 (patch)
tree13588762a236191c06092cd85f71b4342c63bfca
parent854209499f803b7670599c720a9cf1b154e7334c (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/Makefile1
-rw-r--r--arch/arm/mach-tegra/include/mach/kbc.h66
-rw-r--r--arch/arm/mach-tegra/nvddk/Makefile1
-rw-r--r--arch/arm/mach-tegra/nvddk/nvddk_kbc.c1112
-rw-r--r--arch/arm/mach-tegra/odm_to_plat.c129
-rwxr-xr-xarch/arm/mach-tegra/tegra_sysmap.c2
-rw-r--r--drivers/input/keyboard/tegra-kbc.c677
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");