1002 lines
33 KiB
C
1002 lines
33 KiB
C
/* Copyright (c) 2014, 2018 The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <media/rc-core.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#define MSM_GENI_IR_DRIVER_NAME "msm_geni_ir"
|
|
#define MSM_GENI_IR_RX_DEVICE_NAME "msm_geni_ir_rx"
|
|
#define MSM_GENI_IR_TX_DEVICE_NAME "msm_geni_ir_tx"
|
|
|
|
/* GENI Configuration registers */
|
|
#define IR_GENI_CLK_CTRL 0x0000
|
|
#define IR_GENI_FW_REVISION 0x0008
|
|
#define IR_GENI_S_FW_REVISION 0x000C
|
|
#define IR_GENI_FORCE_DEFAULT_REG 0x0010
|
|
#define IR_GENI_OUTPUT_CTRL 0x0014
|
|
#define IR_GENI_TEST_BUS_CTRL 0x0030
|
|
#define IR_GENI_SER_CLK_CFG 0x0034
|
|
#define IR_GENI_STOP_REG 0x0040
|
|
#define IR_GENI_CFG_REG(N) (0x0100 + (0x4 * (N)))
|
|
#define IR_GENI_CFG_RAM(N) (0x0200 + (0x4 * (N)))
|
|
|
|
/* GENI Data registers */
|
|
#define IR_GENI_M_CMD0 0x0C00
|
|
#define IR_GENI_S_CMD0 0x0C08
|
|
#define IR_GENI_IRQ_STATUS 0x0C20
|
|
#define IR_GENI_IRQ_ENABLE 0x0C24
|
|
#define IR_GENI_IRQ_CLEAR 0x0C28
|
|
#define IR_GENI_RX_FIFO(N) (0x0C80 + (0x4 * (N)))
|
|
#define IR_GENI_RX_FIFO_STATUS 0x0CC4
|
|
#define IR_GENI_GP_OUTPUT_REG 0x0CCC
|
|
#define IR_GENI_RX_FIFO_STATUS_WC(X) ((X) & 0xF)
|
|
|
|
/* IR Configuration registers */
|
|
#define GENI_IR_CLK_MUX 0x0800
|
|
#define IR_RX_FILTER_VAL_MASK 0x0810
|
|
#define GENI_IR_AHB_MUX_SEL 0x0814
|
|
#define GENI_IR_RX_FILTER_EN 0x0818
|
|
#define GENI_IR_IRQ_STATUS 0x0820
|
|
#define GENI_IR_IRQ_ENABLE 0x0824
|
|
#define GENI_IR_IRQ_CLEAR 0x0828
|
|
#define GENI_IR_RX_FILTER_TABLE(N) (0x0834 + (0x4 * (N)))
|
|
|
|
#define S_GENI_STOP 0x2
|
|
#define M_GENI_STOP 0x1
|
|
|
|
#define S_CMD_DONE 0x2000
|
|
#define M_CMD_DONE 0x1
|
|
|
|
#define RX_POL_LOW 0x2
|
|
|
|
#define RX_SER_CLK_EN 0x1
|
|
#define TX_SER_CLK_EN 0x2
|
|
#define RX_CLK_DIV_VALUE(X) (((X) & 0xFFF) << 2)
|
|
#define TX_CLK_DIV_VALUE(X) (((X) & 0xFFF) << 14)
|
|
|
|
#define GENI_IR_RX_FIFO_WTRMRK_EN 0x400
|
|
#define GENI_IR_RX_NEC_REPEAT_DETECT 0x40000
|
|
|
|
#define GENI_IR_DEF_IRQ_EN (GENI_IR_RX_FIFO_WTRMRK_EN | \
|
|
GENI_IR_RX_NEC_REPEAT_DETECT)
|
|
|
|
#define GENI_IR_DEF_WAKEUP_MASK 0x00FFFFFF
|
|
#define GENI_IR_MAX_WAKEUP_CODES 10
|
|
#define RX_CLK_DIV 0x258
|
|
#define TX_CLK_DIV 0x58
|
|
#define RX_CLK_DIV_LP 0x9
|
|
#define TX_BUF_SIZE 256
|
|
|
|
struct geni_image {
|
|
u32 reg;
|
|
u32 val;
|
|
};
|
|
|
|
/* FW images */
|
|
static struct geni_image rc5_geni_rx_cfg_reg_image[] = {
|
|
{ 7, 0x042F8101 }, { 8, 0x0000007F }, { 9, 0x0005C190 },
|
|
{ 10, 0x0002E958 }, { 13, 0x00000000 }, { 14, 0x00000000 },
|
|
{ 21, 0x00000300 }, { 22, 0x00000000 }, { 23, 0x00000000 },
|
|
{ 24, 0x00000000 }, { 28, 0x00000200 }, { 30, 0x00000002 },
|
|
};
|
|
|
|
static struct geni_image rc5_geni_rx_cfg_ram_image[] = {
|
|
{ 1, 0x00000001 }, { 34, 0x0004D000 }, { 35, 0x000E48C0 },
|
|
{ 36, 0x00000000 }, { 37, 0x00000000 }, { 38, 0x000C0035 },
|
|
{ 39, 0x00081C4C }, { 40, 0x0008044E }, { 41, 0x00080E52 },
|
|
{ 42, 0x000C000A }, { 43, 0x00081C5A }, { 44, 0x000800EE },
|
|
{ 45, 0x00080456 }, { 46, 0x000C000B }, { 47, 0x00081E96 },
|
|
{ 48, 0x0008045E }, { 49, 0x000C8014 }, { 50, 0x00081E6A },
|
|
{ 51, 0x00080464 }, { 52, 0x000800EE }, { 53, 0x000A026E },
|
|
{ 54, 0x000800E2 }, { 55, 0x000C0006 }, { 56, 0x00081CEE },
|
|
{ 57, 0x00080470 }, { 58, 0x000C000B }, { 59, 0x00081C88 },
|
|
{ 60, 0x00080476 }, { 61, 0x000C9004 }, { 62, 0x00081CEE },
|
|
{ 63, 0x0008047C }, { 64, 0x000C000C }, { 65, 0x00081CA4 },
|
|
{ 66, 0x00080482 }, { 67, 0x000800EE }, { 68, 0x000C8006 },
|
|
{ 69, 0x00081EEE }, { 70, 0x0008048A }, { 71, 0x000C000B },
|
|
{ 72, 0x00081E6A }, { 73, 0x00080490 }, { 74, 0x000800EE },
|
|
{ 75, 0x000C9006 }, { 76, 0x00081CEE }, { 77, 0x00080498 },
|
|
{ 78, 0x000C000C }, { 79, 0x00081CA4 }, { 80, 0x0008049E },
|
|
{ 81, 0x000800EE }, { 82, 0x000A02A8 }, { 83, 0x000800D0 },
|
|
{ 84, 0x000C0006 }, { 85, 0x00081EEE }, { 86, 0x000804AA },
|
|
{ 87, 0x000C000B }, { 88, 0x00081EC2 }, { 89, 0x000804B0 },
|
|
{ 90, 0x000C8004 }, { 91, 0x00081EEE }, { 92, 0x000804B6 },
|
|
{ 93, 0x000C000C }, { 94, 0x00081E6A }, { 95, 0x000804BC },
|
|
{ 96, 0x000800EE }, { 97, 0x000C9006 }, { 98, 0x00081CEE },
|
|
{ 99, 0x000804C4 }, { 100, 0x000C000B }, { 101, 0x00081CA4 },
|
|
{ 102, 0x000804CA }, { 103, 0x000800EE }, { 104, 0x000C0006 },
|
|
{ 105, 0x00081EEE }, { 106, 0x000804D2 }, { 107, 0x000C000A },
|
|
{ 108, 0x00081EDE }, { 109, 0x000804D8 }, { 110, 0x000800EE },
|
|
{ 111, 0x000C0023 }, { 112, 0x000800E4 }, { 113, 0x000C0033 },
|
|
{ 114, 0x00081CEE }, { 115, 0x000804E4 }, { 116, 0x000E0C05 },
|
|
{ 117, 0x000C040C }, { 118, 0x00080052 }, { 119, 0x000E0E01 },
|
|
{ 120, 0x000C040C }, { 121, 0x0008004C }, { 122, 0x00000000 },
|
|
{ 123, 0x00000000 }, { 124, 0x00000000 }, { 125, 0x00000000 },
|
|
{ 126, 0x00000000 }, { 127, 0x00000000 }, { 128, 0x00000000 },
|
|
{ 129, 0x00000000 }, { 130, 0x00000000 }, { 131, 0x00000000 },
|
|
{ 132, 0x00000000 }, { 133, 0x00000000 }, { 134, 0x00000000 },
|
|
{ 135, 0x00000000 }, { 136, 0x00000000 }, { 137, 0x00000000 },
|
|
{ 138, 0x00000000 }, { 139, 0x00000000 }, { 140, 0x00000000 },
|
|
{ 141, 0x00000000 }, { 142, 0x00000000 }, { 143, 0x00000000 },
|
|
{ 144, 0x00000000 }, { 145, 0x00000000 }, { 146, 0x00000000 },
|
|
{ 147, 0x00000000 }, { 148, 0x00000000 }, { 149, 0x00000000 },
|
|
{ 150, 0x00000000 }, { 151, 0x00000000 }, { 152, 0x00000000 },
|
|
{ 153, 0x00000000 }, { 154, 0x00000000 }, { 155, 0x00000000 },
|
|
{ 156, 0x00000000 }, { 157, 0x00000000 }, { 158, 0x00000000 },
|
|
{ 159, 0x00000000 }, { 160, 0x00000000 },
|
|
};
|
|
|
|
static struct geni_image rc5_geni_tx_cfg_reg_image[] = {
|
|
{ 0, 0x200009F9 }, { 1, 0x000170C8 }, { 2, 0x0000BAAC },
|
|
{ 3, 0x00066210 }, { 4, 0x000040BB }, { 5, 0x00080000 },
|
|
{ 6, 0x00088100 }, { 11, 0x0000AAA0 }, { 12, 0x00000000 },
|
|
{ 15, 0x00402000 }, { 16, 0x00000000 }, { 17, 0x00000000 },
|
|
{ 18, 0x00000000 }, { 19, 0x00000000 }, { 20, 0x00000000 },
|
|
{ 25, 0x00000000 }, { 26, 0x00000000 }, { 27, 0x000000E0 },
|
|
{ 29, 0x00000024 },
|
|
};
|
|
|
|
static struct geni_image rc5_geni_tx_cfg_ram_image[] = {
|
|
{ 0, 0x00000001 }, { 2, 0x00142000 }, { 3, 0x001E48C0 },
|
|
{ 4, 0x00000000 }, { 5, 0x00000000 }, { 6, 0x00000000 },
|
|
{ 7, 0x00000000 }, { 8, 0x00000000 }, { 9, 0x00000000 },
|
|
{ 10, 0x00000000 }, { 11, 0x00000000 }, { 12, 0x00000000 },
|
|
{ 13, 0x00000000 }, { 14, 0x00000000 }, { 15, 0x00000000 },
|
|
{ 16, 0x00000000 }, { 17, 0x00000000 }, { 18, 0x00000000 },
|
|
{ 19, 0x00000000 }, { 20, 0x00000000 }, { 21, 0x00000000 },
|
|
{ 22, 0x00000000 }, { 23, 0x00000000 }, { 24, 0x00000000 },
|
|
{ 25, 0x00000000 }, { 26, 0x00000000 }, { 27, 0x00000000 },
|
|
{ 28, 0x00000000 }, { 29, 0x00000000 }, { 30, 0x00000000 },
|
|
{ 31, 0x00000000 }, { 32, 0x00000000 }, { 33, 0x00000000 },
|
|
{ 161, 0x000C301E }, { 162, 0x00082544 }, { 163, 0x000C601D },
|
|
{ 164, 0x00085548 }, { 165, 0x0008675E }, { 166, 0x000C601E },
|
|
{ 167, 0x0008554E }, { 168, 0x000C301B }, { 169, 0x00082552 },
|
|
{ 170, 0x000A3358 }, { 171, 0x00083172 }, { 172, 0x0008475C },
|
|
{ 173, 0x0008314C }, { 174, 0x00003000 }, { 175, 0x000C301E },
|
|
{ 176, 0x00082560 }, { 177, 0x000C601B }, { 178, 0x00085564 },
|
|
{ 179, 0x000A636A }, { 180, 0x00086170 }, { 181, 0x0008776E },
|
|
{ 182, 0x0008614C }, { 183, 0x0008615E }, { 184, 0x00086174 },
|
|
{ 185, 0x00003000 }, { 186, 0x000C3052 }, { 187, 0x00082576 },
|
|
{ 188, 0x00103000 }, { 189, 0x00000000 },
|
|
};
|
|
|
|
static struct geni_image rc6_geni_rx_cfg_reg_image[] = {
|
|
{ 7, 0x042F8101 }, { 8, 0x0000027F }, { 9, 0x0007FA10 },
|
|
{ 10, 0x000C44FE }, { 13, 0x00000000 }, { 14, 0x00000000 },
|
|
{ 21, 0x00000300 }, { 22, 0x00000000 }, { 23, 0x00000000 },
|
|
{ 24, 0x00000000 }, { 28, 0x00000200 }, { 30, 0x00000002 },
|
|
};
|
|
|
|
static struct geni_image rc6_geni_rx_cfg_ram_image[] = {
|
|
{ 1, 0x00000001 }, { 34, 0x0004D000 }, { 35, 0x000E48F0 },
|
|
{ 36, 0x00000000 }, { 37, 0x00000000 }, { 38, 0x000C001E },
|
|
{ 39, 0x00081C4C }, { 40, 0x0008044E }, { 41, 0x00080E52 },
|
|
{ 42, 0x000C0022 }, { 43, 0x00081C5A }, { 44, 0x0008012A },
|
|
{ 45, 0x00080456 }, { 46, 0x000C0014 }, { 47, 0x00081E64 },
|
|
{ 48, 0x0008045E }, { 49, 0x0008012A }, { 50, 0x000C0012 },
|
|
{ 51, 0x00081C6C }, { 52, 0x00080466 }, { 53, 0x0008012A },
|
|
{ 54, 0x000C000A }, { 55, 0x00081E74 }, { 56, 0x0008046E },
|
|
{ 57, 0x0008012A }, { 58, 0x000C0009 }, { 59, 0x00081E7A },
|
|
{ 60, 0x0008012A }, { 61, 0x00080476 }, { 62, 0x000C0008 },
|
|
{ 63, 0x00081C84 }, { 64, 0x0008047E }, { 65, 0x0008012A },
|
|
{ 66, 0x000C000A }, { 67, 0x00081E8C }, { 68, 0x00080486 },
|
|
{ 69, 0x0008012A }, { 70, 0x000C000A }, { 71, 0x00081C94 },
|
|
{ 72, 0x0008048E }, { 73, 0x0008012A }, { 74, 0x000C000A },
|
|
{ 75, 0x00081E9C }, { 76, 0x00080496 }, { 77, 0x0008012A },
|
|
{ 78, 0x000C000A }, { 79, 0x00081CA4 }, { 80, 0x0008049E },
|
|
{ 81, 0x0008012A }, { 82, 0x000C000A }, { 83, 0x00081EDE },
|
|
{ 84, 0x000804A6 }, { 85, 0x000C9007 }, { 86, 0x00081CB0 },
|
|
{ 87, 0x0008012A }, { 88, 0x000804AC }, { 89, 0x000C000A },
|
|
{ 90, 0x00081EBA }, { 91, 0x000804B4 }, { 92, 0x0008012A },
|
|
{ 93, 0x000C0011 }, { 94, 0x00081CD6 }, { 95, 0x000804BC },
|
|
{ 96, 0x000C8008 }, { 97, 0x0008010C }, { 98, 0x000A02C8 },
|
|
{ 99, 0x0008011E }, { 100, 0x000C0009 }, { 101, 0x00081CD6 },
|
|
{ 102, 0x000804CA }, { 103, 0x000C8008 }, { 104, 0x00081CF8 },
|
|
{ 105, 0x000804D0 }, { 106, 0x0008012A }, { 107, 0x000C900A },
|
|
{ 108, 0x00081EC4 }, { 109, 0x000804D8 }, { 110, 0x0008012A },
|
|
{ 111, 0x000C800A }, { 112, 0x00081EE4 }, { 113, 0x0008012A },
|
|
{ 114, 0x000804E0 }, { 115, 0x000C0007 }, { 116, 0x00081CEE },
|
|
{ 117, 0x000804E8 }, { 118, 0x0008012A }, { 119, 0x000C0011 },
|
|
{ 120, 0x00081F0A }, { 121, 0x000804F0 }, { 122, 0x000C9008 },
|
|
{ 123, 0x000800D8 }, { 124, 0x000A02FC }, { 125, 0x00080112 },
|
|
{ 126, 0x000C0009 }, { 127, 0x00081F0A }, { 128, 0x000804FE },
|
|
{ 129, 0x000C9008 }, { 130, 0x00081EC4 }, { 131, 0x00080504 },
|
|
{ 132, 0x0008012A }, { 133, 0x000C800A }, { 134, 0x00081CF8 },
|
|
{ 135, 0x0008050C }, { 136, 0x0008012A }, { 137, 0x000C0009 },
|
|
{ 138, 0x00081F1A }, { 139, 0x00080514 }, { 140, 0x0008012A },
|
|
{ 141, 0x000C001E }, { 142, 0x00080120 }, { 143, 0x000C0028 },
|
|
{ 144, 0x00081D2A }, { 145, 0x00080520 }, { 146, 0x000E0C05 },
|
|
{ 147, 0x000C040F }, { 148, 0x00080052 }, { 149, 0x000E0E01 },
|
|
{ 150, 0x000C040F }, { 151, 0x0008004C }, { 152, 0x00000000 },
|
|
{ 153, 0x00000000 }, { 154, 0x00000000 }, { 155, 0x00000000 },
|
|
{ 156, 0x00000000 }, { 157, 0x00000000 }, { 158, 0x00000000 },
|
|
{ 159, 0x00000000 }, { 160, 0x00000000 },
|
|
};
|
|
|
|
static struct geni_image rc6_geni_tx_cfg_reg_image[] = {
|
|
{ 0, 0x200019F9 }, { 1, 0x0001FF08 }, { 2, 0x0000107F },
|
|
{ 3, 0x0003F810 }, { 4, 0x000FFDFE }, { 5, 0x00880000 },
|
|
{ 6, 0x00088100 }, { 11, 0x0000AAA0 }, { 12, 0x00000000 },
|
|
{ 15, 0x00402000 }, { 16, 0x00000000 }, { 17, 0x00000000 },
|
|
{ 18, 0x00000000 }, { 19, 0x00000000 }, { 20, 0x00000000 },
|
|
{ 25, 0x00000000 }, { 26, 0x00000000 }, { 27, 0x000000E0 },
|
|
{ 29, 0x00000024 },
|
|
};
|
|
|
|
static struct geni_image rc6_geni_tx_cfg_ram_image[] = {
|
|
{ 0, 0x00000001 }, { 2, 0x00142000 }, { 3, 0x001E48F0 },
|
|
{ 4, 0x00000000 }, { 5, 0x00000000 }, { 6, 0x00000000 },
|
|
{ 7, 0x00000000 }, { 8, 0x00000000 }, { 9, 0x00000000 },
|
|
{ 10, 0x00000000 }, { 11, 0x00000000 }, { 12, 0x00000000 },
|
|
{ 13, 0x00000000 }, { 14, 0x00000000 }, { 15, 0x00000000 },
|
|
{ 16, 0x00000000 }, { 17, 0x00000000 }, { 18, 0x00000000 },
|
|
{ 19, 0x00000000 }, { 20, 0x00000000 }, { 21, 0x00000000 },
|
|
{ 22, 0x00000000 }, { 23, 0x00000000 }, { 24, 0x00000000 },
|
|
{ 25, 0x00000000 }, { 26, 0x00000000 }, { 27, 0x00000000 },
|
|
{ 28, 0x00000000 }, { 29, 0x00000000 }, { 30, 0x00000000 },
|
|
{ 31, 0x00000000 }, { 32, 0x00000000 }, { 33, 0x00000000 },
|
|
{ 161, 0x000C605E }, { 162, 0x00085544 }, { 163, 0x000C301E },
|
|
{ 164, 0x00082548 }, { 165, 0x00046E37 }, { 166, 0x00043E1F },
|
|
{ 167, 0x00043E1F }, { 168, 0x00046E37 }, { 169, 0x00043E1F },
|
|
{ 170, 0x00046E37 }, { 171, 0x00043E1F }, { 172, 0x00046E36 },
|
|
{ 173, 0x00086774 }, { 174, 0x000C301E }, { 175, 0x0008255E },
|
|
{ 176, 0x000C601D }, { 177, 0x00085562 }, { 178, 0x00087782 },
|
|
{ 179, 0x00043E1F }, { 180, 0x00046E34 }, { 181, 0x000A636E },
|
|
{ 182, 0x00086190 }, { 183, 0x00087772 }, { 184, 0x00086166 },
|
|
{ 185, 0x00086182 }, { 186, 0x000C601E }, { 187, 0x00085576 },
|
|
{ 188, 0x000C301C }, { 189, 0x0008257A }, { 190, 0x00084780 },
|
|
{ 191, 0x00083166 }, { 192, 0x00003000 }, { 193, 0x00046E37 },
|
|
{ 194, 0x00043E1C }, { 195, 0x000A338A }, { 196, 0x00083192 },
|
|
{ 197, 0x0008478E }, { 198, 0x00083166 }, { 199, 0x00083182 },
|
|
{ 200, 0x00086194 }, { 201, 0x00003000 }, { 202, 0x000C3067 },
|
|
{ 203, 0x00082596 }, { 204, 0x00103000 }, { 205, 0x00000000 },
|
|
};
|
|
|
|
//{ HWIO_PHYS(GENI_IR(S_FW_REVISION)), 0x0280 },
|
|
static struct geni_image nec_geni_rx_cfg_reg_image[] = {
|
|
{ 7, 0x042F8101 }, { 8, 0x0000007F }, { 9, 0x00083B0E },
|
|
{ 10, 0x0000390E }, { 13, 0x00000000 }, { 14, 0x00000000 },
|
|
{ 21, 0x00000180 }, { 22, 0x00000000 }, { 23, 0x00000000 },
|
|
{ 24, 0x00000000 }, { 28, 0x00000100 }, { 30, 0x00000002 },
|
|
};
|
|
|
|
// Program ram
|
|
static struct geni_image nec_geni_rx_cfg_ram_image[] = {
|
|
{ 1, 0x00000001 }, { 34, 0x0004D000 }, { 35, 0x000009F0 },
|
|
{ 36, 0x00000000 }, { 37, 0x00000000 }, { 38, 0x000C005F },
|
|
{ 39, 0x00081C4C }, { 40, 0x0008044E }, { 41, 0x00080E52 },
|
|
{ 42, 0x000C005F }, { 43, 0x00081C5A }, { 44, 0x000800BA },
|
|
{ 45, 0x00080456 }, { 46, 0x000C005F }, { 47, 0x00081E64 },
|
|
{ 48, 0x0008045E }, { 49, 0x000800BA }, { 50, 0x000C0017 },
|
|
{ 51, 0x00081CBA }, { 52, 0x00080466 }, { 53, 0x000C001D },
|
|
{ 54, 0x00081C78 }, { 55, 0x0008046C }, { 56, 0x000C0020 },
|
|
{ 57, 0x00081C84 }, { 58, 0x00080472 }, { 59, 0x000800BA },
|
|
{ 60, 0x000C0010 }, { 61, 0x00081E80 }, { 62, 0x0008047A },
|
|
{ 63, 0x000800BA }, { 64, 0x000E0D00 }, { 65, 0x0008004C },
|
|
{ 66, 0x000C0010 }, { 67, 0x00081E8C }, { 68, 0x00080486 },
|
|
{ 69, 0x000800BA }, { 70, 0x000C0010 }, { 71, 0x00081C9A },
|
|
{ 72, 0x0008048E }, { 73, 0x000C0014 }, { 74, 0x00081CA0 },
|
|
{ 75, 0x00080494 }, { 76, 0x000800CC }, { 77, 0x000A029E },
|
|
{ 78, 0x000800A6 }, { 79, 0x00087084 }, { 80, 0x000A02A4 },
|
|
{ 81, 0x000800B0 }, { 82, 0x00088084 }, { 83, 0x000C0010 },
|
|
{ 84, 0x00081EAE }, { 85, 0x000804A8 }, { 86, 0x000800BA },
|
|
{ 87, 0x000870C0 }, { 88, 0x000C0010 }, { 89, 0x00081EB8 },
|
|
{ 90, 0x000804B2 }, { 91, 0x000800BA }, { 92, 0x000880C0 },
|
|
{ 93, 0x000E0E01 }, { 94, 0x000C041F }, { 95, 0x0008004C },
|
|
{ 96, 0x000C005E }, { 97, 0x00081CBA }, { 98, 0x000804C2 },
|
|
{ 99, 0x000E0C05 }, { 100, 0x000C041F }, { 101, 0x00080052 },
|
|
{ 102, 0x000E0E01 }, { 103, 0x000C041F }, { 104, 0x000C003B },
|
|
{ 105, 0x00081C4C }, { 106, 0x000804D2 }, { 107, 0x00080052 },
|
|
{ 108, 0x00000000 }, { 109, 0x00000000 }, { 110, 0x00000000 },
|
|
{ 111, 0x00000000 }, { 112, 0x00000000 }, { 113, 0x00000000 },
|
|
{ 114, 0x00000000 }, { 115, 0x00000000 }, { 116, 0x00000000 },
|
|
{ 117, 0x00000000 }, { 118, 0x00000000 }, { 119, 0x00000000 },
|
|
{ 120, 0x00000000 }, { 121, 0x00000000 }, { 122, 0x00000000 },
|
|
{ 123, 0x00000000 }, { 124, 0x00000000 }, { 125, 0x00000000 },
|
|
{ 126, 0x00000000 }, { 127, 0x00000000 }, { 128, 0x00000000 },
|
|
{ 129, 0x00000000 }, { 130, 0x00000000 }, { 131, 0x00000000 },
|
|
{ 132, 0x00000000 }, { 133, 0x00000000 }, { 134, 0x00000000 },
|
|
{ 135, 0x00000000 }, { 136, 0x00000000 }, { 137, 0x00000000 },
|
|
{ 138, 0x00000000 }, { 139, 0x00000000 }, { 140, 0x00000000 },
|
|
{ 141, 0x00000000 }, { 142, 0x00000000 }, { 143, 0x00000000 },
|
|
{ 144, 0x00000000 }, { 145, 0x00000000 }, { 146, 0x00000000 },
|
|
{ 147, 0x00000000 }, { 148, 0x00000000 }, { 149, 0x00000000 },
|
|
{ 150, 0x00000000 }, { 151, 0x00000000 }, { 152, 0x00000000 },
|
|
{ 153, 0x00000000 }, { 154, 0x00000000 }, { 155, 0x00000000 },
|
|
{ 156, 0x00000000 }, { 157, 0x00000000 }, { 158, 0x00000000 },
|
|
{ 159, 0x00000000 }, { 160, 0x00000000 },
|
|
};
|
|
/* IR controller modes */
|
|
enum geni_ir_mode {
|
|
GENI_IR_NORMAL_MODE = 0,
|
|
GENI_IR_LOW_POWER_MODE = 1, /* RX filter is the source */
|
|
};
|
|
|
|
/* Firmware image */
|
|
struct msm_geni_fw_data {
|
|
u32 rx_revision;
|
|
struct geni_image *rx_cfg_data; /* Configuration data */
|
|
u32 rx_cfg_len;
|
|
struct geni_image *rx_ram_data; /* RAM data */
|
|
u32 rx_ram_len;
|
|
|
|
u32 tx_revision;
|
|
struct geni_image *tx_cfg_data; /* Configuration data */
|
|
u32 tx_cfg_len;
|
|
struct geni_image *tx_ram_data; /* RAM data */
|
|
u32 tx_ram_len;
|
|
};
|
|
|
|
struct msm_geni_ir {
|
|
struct device *dev;
|
|
struct rc_dev *rcdev;
|
|
|
|
struct mutex lock;
|
|
void __iomem *base;
|
|
|
|
unsigned int gpio_rx;
|
|
|
|
struct clk *ahb_clk;
|
|
struct clk *serial_clk;
|
|
struct reset_control *reset_core;
|
|
unsigned int irq;
|
|
unsigned int wakeup_irq;
|
|
|
|
const struct msm_geni_fw_data *image_requested;
|
|
const struct msm_geni_fw_data *image_loaded;
|
|
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
struct miscdevice misc;
|
|
u32 tx_data[TX_BUF_SIZE];
|
|
|
|
unsigned int gpio_tx;
|
|
#endif
|
|
u32 wakeup_mask;
|
|
u32 rx_data;
|
|
u32 wakeup_codes[GENI_IR_MAX_WAKEUP_CODES];
|
|
u8 num_wakeup_codes;
|
|
};
|
|
|
|
static struct msm_geni_fw_data rc5_geni_image = {
|
|
.rx_revision = 0x180,
|
|
.rx_cfg_data = rc5_geni_rx_cfg_reg_image,
|
|
.rx_cfg_len = ARRAY_SIZE(rc5_geni_rx_cfg_reg_image),
|
|
.rx_ram_data = rc5_geni_rx_cfg_ram_image,
|
|
.rx_ram_len = ARRAY_SIZE(rc5_geni_rx_cfg_ram_image),
|
|
|
|
.tx_revision = 0x180,
|
|
.tx_cfg_data = rc5_geni_tx_cfg_reg_image,
|
|
.tx_cfg_len = ARRAY_SIZE(rc5_geni_tx_cfg_reg_image),
|
|
.tx_ram_data = rc5_geni_tx_cfg_ram_image,
|
|
.tx_ram_len = ARRAY_SIZE(rc5_geni_tx_cfg_ram_image),
|
|
};
|
|
|
|
static struct msm_geni_fw_data rc6_geni_image = {
|
|
.rx_revision = 0x1c0,
|
|
.rx_cfg_data = rc6_geni_rx_cfg_reg_image,
|
|
.rx_cfg_len = ARRAY_SIZE(rc6_geni_rx_cfg_reg_image),
|
|
.rx_ram_data = rc6_geni_rx_cfg_ram_image,
|
|
.rx_ram_len = ARRAY_SIZE(rc6_geni_rx_cfg_ram_image),
|
|
|
|
.tx_revision = 0x1c0,
|
|
.tx_cfg_data = rc6_geni_tx_cfg_reg_image,
|
|
.tx_cfg_len = ARRAY_SIZE(rc6_geni_tx_cfg_reg_image),
|
|
.tx_ram_data = rc6_geni_tx_cfg_ram_image,
|
|
.tx_ram_len = ARRAY_SIZE(rc6_geni_tx_cfg_ram_image),
|
|
};
|
|
|
|
static struct msm_geni_fw_data nec_geni_image = {
|
|
.rx_revision = 0x0280,
|
|
.rx_cfg_data = nec_geni_rx_cfg_reg_image,
|
|
.rx_cfg_len = ARRAY_SIZE(nec_geni_rx_cfg_reg_image),
|
|
.rx_ram_data = nec_geni_rx_cfg_ram_image,
|
|
.rx_ram_len = ARRAY_SIZE(nec_geni_rx_cfg_ram_image),
|
|
};
|
|
|
|
/* loads the GENI ir firmware */
|
|
static void msm_geni_ir_load_firmware(struct msm_geni_ir *ir)
|
|
{
|
|
const struct msm_geni_fw_data *fw = ir->image_requested;
|
|
struct geni_image *img;
|
|
u32 i, clk_cfg;
|
|
|
|
pr_debug("protocol Load Firmware\n");
|
|
|
|
/* enable High-Z on GENI output pad */
|
|
writel_relaxed(0x0, ir->base + IR_GENI_OUTPUT_CTRL);
|
|
|
|
/* switch program-ram clock muxing to ahb clock */
|
|
writel_relaxed(0x0, ir->base + IR_GENI_CLK_CTRL);
|
|
|
|
/* write firmware revisions */
|
|
writel_relaxed(fw->tx_revision, ir->base + IR_GENI_FW_REVISION);
|
|
|
|
writel_relaxed(fw->rx_revision, ir->base + IR_GENI_S_FW_REVISION);
|
|
|
|
/* write firmware image */
|
|
img = fw->rx_cfg_data;
|
|
for (i = 0; i < fw->rx_cfg_len; i++, img++)
|
|
writel_relaxed(img->val, ir->base + IR_GENI_CFG_REG(img->reg));
|
|
|
|
img = fw->rx_ram_data;
|
|
for (i = 0; i < fw->rx_ram_len; i++, img++)
|
|
writel_relaxed(img->val, ir->base + IR_GENI_CFG_RAM(img->reg));
|
|
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
|
|
img = fw->tx_cfg_data;
|
|
for (i = 0; i < fw->tx_cfg_len; i++, img++)
|
|
writel_relaxed(img->val, ir->base + IR_GENI_CFG_REG(img->reg));
|
|
|
|
img = fw->tx_ram_data;
|
|
for (i = 0; i < fw->tx_ram_len; i++, img++)
|
|
writel_relaxed(img->val, ir->base + IR_GENI_CFG_RAM(img->reg));
|
|
#endif
|
|
|
|
/* force default values on GENI output pad */
|
|
writel_relaxed(0x1, ir->base + IR_GENI_FORCE_DEFAULT_REG);
|
|
|
|
/* switch program-ram clock muxing to serial clock */
|
|
writel_relaxed(0x1, ir->base + IR_GENI_CLK_CTRL);
|
|
|
|
/* disable High-Z on GENI output pad */
|
|
writel_relaxed(0x3, ir->base + IR_GENI_OUTPUT_CTRL);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
/* configure serial clock */
|
|
clk_cfg = RX_CLK_DIV_VALUE(RX_CLK_DIV) | RX_SER_CLK_EN;
|
|
clk_cfg |= (TX_CLK_DIV_VALUE(TX_CLK_DIV) | TX_SER_CLK_EN);
|
|
//writel_relaxed(clk_cfg, ir->base + IR_GENI_SER_CLK_CFG);
|
|
writel_relaxed(0x961, ir->base + IR_GENI_SER_CLK_CFG);
|
|
|
|
/* set rx polarization to active low */
|
|
writel_relaxed(RX_POL_LOW, ir->base + IR_GENI_GP_OUTPUT_REG);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
/* enable RX */
|
|
writel_relaxed(0, ir->base + IR_GENI_S_CMD0);
|
|
|
|
writel_relaxed(0x0A, ir->base + IR_GENI_TEST_BUS_CTRL);
|
|
|
|
/* enable interrupts */
|
|
writel_relaxed(GENI_IR_DEF_IRQ_EN, ir->base + IR_GENI_IRQ_ENABLE);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
pr_debug("Load Firmware done\n");
|
|
}
|
|
EXPORT_SYMBOL(msm_geni_ir_load_firmware);
|
|
|
|
/* stop GENI IR */
|
|
static void msm_geni_ir_stop(struct msm_geni_ir *ir)
|
|
{
|
|
u32 i, status, cnt;
|
|
int rc;
|
|
|
|
/* stop main and secondary sequencer */
|
|
writel_relaxed((S_GENI_STOP | M_GENI_STOP),
|
|
ir->base + IR_GENI_STOP_REG);
|
|
|
|
/* poll every 1000us till 10ms for S_CMD_DONE bit to set */
|
|
rc = readl_poll_timeout((ir->base + IR_GENI_IRQ_STATUS), status,
|
|
(status & S_CMD_DONE), 100, 1000);
|
|
if (rc)
|
|
dev_err(ir->dev, "failed to stop\n");
|
|
|
|
writel_relaxed((status & ~M_CMD_DONE), ir->base + IR_GENI_IRQ_CLEAR);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
/* flush the RX FIFO */
|
|
status = readl_relaxed(ir->base + IR_GENI_RX_FIFO_STATUS);
|
|
cnt = IR_GENI_RX_FIFO_STATUS_WC(status);
|
|
for (i = 0; i < cnt; ++i)
|
|
readl_relaxed(ir->base + IR_GENI_RX_FIFO(i));
|
|
}
|
|
|
|
/* sets the core for the specified protocol */
|
|
static int msm_geni_ir_change_protocol(struct rc_dev *dev, u64 *rc_type)
|
|
{
|
|
struct msm_geni_ir *ir = dev->priv;
|
|
const struct msm_geni_fw_data *image = NULL;
|
|
int rc;
|
|
|
|
pr_debug("protocol 0x%llx\n", *rc_type);
|
|
/* select either rc6, rc5 or unknown (disable) in that order */
|
|
if (*rc_type & RC_PROTO_BIT_RC6_0) {
|
|
pr_debug("Loading RC6\n");
|
|
image = &rc6_geni_image;
|
|
*rc_type = RC_PROTO_RC6_0;
|
|
} else if (*rc_type & RC_PROTO_BIT_RC5) {
|
|
pr_debug("Loading RC5\n");
|
|
image = &rc5_geni_image;
|
|
*rc_type = RC_PROTO_RC5;
|
|
} else if (*rc_type & RC_PROTO_BIT_NEC) {
|
|
pr_debug("Loading NEC\n");
|
|
image = &nec_geni_image;
|
|
*rc_type = RC_PROTO_NEC;
|
|
} else if (*rc_type & RC_PROTO_BIT_UNKNOWN) {
|
|
pr_debug("Unknown proto\n");
|
|
image = NULL;
|
|
*rc_type = RC_PROTO_UNKNOWN;
|
|
} else {
|
|
pr_debug("Invalid proto\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (image == ir->image_requested)
|
|
return 0;
|
|
|
|
ir->image_requested = image;
|
|
|
|
mutex_lock(&ir->lock);
|
|
|
|
if (image) {
|
|
if (ir->image_loaded == NULL) {
|
|
pr_debug("enable ahb clk\n");
|
|
rc = clk_prepare_enable(ir->ahb_clk);
|
|
if (rc) {
|
|
pr_err("ahb clk enable failed %d\n", rc);
|
|
mutex_unlock(&ir->lock);
|
|
return rc;
|
|
}
|
|
pr_debug("enable serial clk\n");
|
|
rc = clk_prepare_enable(ir->serial_clk);
|
|
if (rc) {
|
|
pr_err("serial clk enable failed %d\n", rc);
|
|
mutex_unlock(&ir->lock);
|
|
return rc;
|
|
}
|
|
} else {
|
|
pr_debug("Disable interrupts\n");
|
|
/* disable interrupts */
|
|
writel_relaxed(0, ir->base + IR_GENI_IRQ_ENABLE);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
synchronize_irq(ir->irq);
|
|
}
|
|
|
|
/* load requested fw */
|
|
msm_geni_ir_load_firmware(ir);
|
|
} else {
|
|
/* stop GENI IR */
|
|
pr_debug("Stop GENI IR\n");
|
|
msm_geni_ir_stop(ir);
|
|
|
|
clk_disable_unprepare(ir->ahb_clk);
|
|
clk_disable_unprepare(ir->serial_clk);
|
|
}
|
|
|
|
ir->image_loaded = image;
|
|
mutex_unlock(&ir->lock);
|
|
|
|
pr_debug("protocol 0x%llx\n", *rc_type);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(msm_geni_ir_change_protocol);
|
|
|
|
static irqreturn_t geni_ir_wakeup_handler(int irq, void *data)
|
|
{
|
|
pr_debug("%s:Received wake up Interrupt\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* interrupt routine */
|
|
static irqreturn_t geni_ir_interrupt(int irq, void *data)
|
|
{
|
|
struct msm_geni_ir *ir = data;
|
|
u32 i, irq_sts, fifo_sts, cnt;
|
|
|
|
pr_debug("Received Interrupt\n");
|
|
/* read irq status */
|
|
irq_sts = readl_relaxed(ir->base + IR_GENI_IRQ_STATUS);
|
|
pr_debug("irq_status 0x%x\n", irq_sts);
|
|
|
|
/* read RX fifo */
|
|
fifo_sts = readl_relaxed(ir->base + IR_GENI_RX_FIFO_STATUS);
|
|
pr_debug("status - irq 0x%x fifo 0x%x\n", irq_sts, fifo_sts);
|
|
|
|
cnt = IR_GENI_RX_FIFO_STATUS_WC(fifo_sts);
|
|
for (i = 0; i < cnt; i++) {
|
|
u8 command, system, toggle = 0, address1, address2;
|
|
u32 scancode;
|
|
|
|
ir->rx_data = readl_relaxed(ir->base + IR_GENI_RX_FIFO(i));
|
|
if (ir->image_loaded == &rc5_geni_image) {
|
|
/* RC5 */
|
|
command = (ir->rx_data & 0x00003F);
|
|
system = (ir->rx_data & 0x0007C0) >> 6;
|
|
toggle = (ir->rx_data & 0x000800) ? 1 : 0;
|
|
command += (ir->rx_data & 0x001000) ? 0 : 0x40;
|
|
scancode = system << 8 | command;
|
|
rc_keydown(ir->rcdev, RC_PROTO_RC5, scancode, toggle);
|
|
} else if (ir->image_loaded == &rc6_geni_image) {
|
|
/* RC6 */
|
|
scancode = (ir->rx_data & 0x00FFFF);
|
|
toggle = (ir->rx_data & 0x010000) ? 1 : 0;
|
|
rc_keydown(ir->rcdev, RC_PROTO_RC6_0, scancode, toggle);
|
|
} else {
|
|
/*NEC*/
|
|
scancode = (ir->rx_data & 0x0000FF);
|
|
scancode = ~scancode;
|
|
scancode = (scancode & 0x0000FF);
|
|
address2 = (ir->rx_data & 0x00FF0000);
|
|
address1 = (ir->rx_data & 0xFF000000);
|
|
rc_keydown(ir->rcdev, RC_PROTO_NEC, scancode, 0);
|
|
}
|
|
|
|
pr_debug("rcvd code 0x%x scancode 0x%x toggle %d\n",
|
|
ir->rx_data, scancode, toggle);
|
|
|
|
}
|
|
if ((irq_sts & GENI_IR_RX_NEC_REPEAT_DETECT) &&
|
|
(ir->image_loaded == &nec_geni_image)) {
|
|
u32 nec_scancode;
|
|
nec_scancode = (ir->rx_data & 0x0000FF);
|
|
nec_scancode = ~nec_scancode;
|
|
nec_scancode = (nec_scancode & 0x0000FF);
|
|
rc_keydown(ir->rcdev, RC_PROTO_NEC, nec_scancode, 0);
|
|
}
|
|
/* ack irq */
|
|
writel_relaxed((irq_sts & ~M_CMD_DONE), ir->base + IR_GENI_IRQ_CLEAR);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
/* transmit IR code */
|
|
static int msm_geni_ir_tx_code(struct msm_geni_ir *ir, u32 code)
|
|
{
|
|
u32 val;
|
|
int rc;
|
|
|
|
/* write code to be transmitted */
|
|
writel_relaxed(code, ir->base + IR_GENI_M_CMD0);
|
|
|
|
/* poll every 10ms till 100ms for M_CMD_DONE bit to set */
|
|
rc = readl_poll_timeout((ir->base + IR_GENI_IRQ_STATUS), val,
|
|
(val & M_CMD_DONE), 10000, 100000);
|
|
if (rc) {
|
|
pr_err("failed to transmit code 0x%x\n", code);
|
|
return rc;
|
|
}
|
|
|
|
/* clear M_CMD_DONE interrupt */
|
|
writel_relaxed(M_CMD_DONE, ir->base + IR_GENI_IRQ_CLEAR);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
dev_debug(ir->dev, "transmitted code 0x%x\n", code);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* receive IR codes to transmit and transmit them */
|
|
static ssize_t msm_geni_ir_tx_write(struct file *fp, const char __user *buf,
|
|
size_t count, loff_t *pos)
|
|
{
|
|
struct msm_geni_ir *ir = container_of(fp->private_data,
|
|
struct msm_geni_ir, misc);
|
|
u8 i, cnt = 0;
|
|
int rc;
|
|
|
|
if (count % sizeof(u32)) {
|
|
pr_err("invalid no of bytes %d\n", count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* process a maximum of TX_BUF_SIZE bytes at a time */
|
|
count = count > TX_BUF_SIZE ? TX_BUF_SIZE : count;
|
|
rc = copy_from_user(ir->tx_data, buf, count);
|
|
if (rc) {
|
|
pr_err("could not copy %i bytes\n", rc);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&ir->lock);
|
|
if (ir->image_loaded == NULL) {
|
|
mutex_unlock(&ir->lock);
|
|
return -ENODEV;
|
|
}
|
|
|
|
cnt = count / sizeof(u32);
|
|
for (i = 0; i < cnt; i++)
|
|
msm_geni_ir_tx_code(ir, ir->tx_data[i]);
|
|
|
|
mutex_unlock(&ir->lock);
|
|
return count;
|
|
}
|
|
|
|
static int msm_geni_ir_tx_open(struct inode *ip, struct file *fp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations msm_geni_ir_tx_fops = {
|
|
.owner = THIS_MODULE,
|
|
.write = msm_geni_ir_tx_write,
|
|
.open = msm_geni_ir_tx_open,
|
|
};
|
|
#endif
|
|
|
|
/* get resources */
|
|
static int msm_geni_ir_get_res(struct platform_device *pdev,
|
|
struct msm_geni_ir *ir)
|
|
{
|
|
struct device_node *node = pdev->dev.of_node;
|
|
struct resource *res;
|
|
int rc;
|
|
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
rc = of_get_named_gpio(node, "qcom,geni-ir-gpio-tx", 0);
|
|
if (rc < 0) {
|
|
pr_err("could not get tx gpio %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
ir->gpio_tx = rc;
|
|
#endif
|
|
|
|
rc = of_get_named_gpio(node, "qcom,geni-ir-wakeup-gpio", 0);
|
|
if (rc < 0) {
|
|
pr_err("could not get wakeup gpio %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
ir->wakeup_irq = gpio_to_irq(rc);
|
|
rc = platform_get_irq_byname(pdev, "geni-ir-core-irq");
|
|
if (rc < 0) {
|
|
pr_err("could not get core irq %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
ir->irq = rc;
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
|
|
if (!res) {
|
|
pr_err("missing memory resource\n");
|
|
return -EINVAL;
|
|
}
|
|
ir->base = ioremap(res->start, resource_size(res));
|
|
if (!ir->base) {
|
|
pr_err("ioremap failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
pr_debug("ir->base: 0x%lx\n", (unsigned long int)ir->base);
|
|
ir->ahb_clk = clk_get(&pdev->dev, "iface_clk");
|
|
ir->serial_clk = clk_get(&pdev->dev, "serial_clk");
|
|
if (IS_ERR(ir->ahb_clk)) {
|
|
rc = PTR_ERR(ir->ahb_clk);
|
|
pr_err("could not get ahb clk %d\n", rc);
|
|
iounmap(ir->base);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* release resources */
|
|
static void msm_geni_ir_rel_res(struct platform_device *pdev,
|
|
struct msm_geni_ir *ir)
|
|
{
|
|
if (ir->ahb_clk)
|
|
clk_put(ir->ahb_clk);
|
|
|
|
if (ir->base)
|
|
iounmap(ir->base);
|
|
}
|
|
|
|
int msm_geni_ir_probe(struct platform_device *pdev)
|
|
{
|
|
struct rc_dev *rcdev;
|
|
struct msm_geni_ir *ir;
|
|
int rc;
|
|
|
|
ir = kzalloc(sizeof(struct msm_geni_ir), GFP_KERNEL);
|
|
if (!ir)
|
|
return -ENOMEM;
|
|
|
|
platform_set_drvdata(pdev, ir);
|
|
rc = msm_geni_ir_get_res(pdev, ir);
|
|
if (rc)
|
|
goto resource_err;
|
|
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
rc = gpio_request(ir->gpio_tx, "qcom,geni-ir-gpio-tx");
|
|
if (rc) {
|
|
pr_err("tx gpio request failed %d\n", rc);
|
|
goto gpio_err;
|
|
}
|
|
#endif
|
|
rc = gpio_request(ir->gpio_rx, "qcom,geni-ir-gpio-rx");
|
|
if (rc) {
|
|
pr_err("rx gpio request failed %d\n", rc);
|
|
goto gpio_err;
|
|
}
|
|
rc = request_irq(ir->irq, geni_ir_interrupt,
|
|
IRQ_TYPE_LEVEL_HIGH, "geni-ir-core-irq", ir);
|
|
if (rc) {
|
|
pr_err("core irq request failed %d\n", rc);
|
|
goto core_irq_err;
|
|
}
|
|
rc = request_irq(ir->wakeup_irq, geni_ir_wakeup_handler,
|
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT
|
|
| IRQF_NO_SUSPEND, "geni-ir-wakeup-irq", ir);
|
|
if (rc) {
|
|
pr_err("wakeup irq request failed %d\n", rc);
|
|
goto rc_alloc_err;
|
|
}
|
|
|
|
mutex_init(&ir->lock);
|
|
rcdev = rc_allocate_device(RC_DRIVER_SCANCODE);
|
|
if (!rcdev) {
|
|
pr_err("failed to allocate rc device\n");
|
|
rc = -ENOMEM;
|
|
goto rc_alloc_err;
|
|
}
|
|
|
|
rcdev->priv = ir;
|
|
rcdev->driver_type = RC_DRIVER_SCANCODE;
|
|
rcdev->allowed_protocols = RC_PROTO_RC5 | RC_PROTO_RC6_0 | RC_PROTO_NEC;
|
|
rcdev->enabled_protocols = RC_PROTO_RC5 | RC_PROTO_RC6_0 | RC_PROTO_NEC;
|
|
rcdev->driver_name = MSM_GENI_IR_DRIVER_NAME;
|
|
rcdev->device_name = MSM_GENI_IR_RX_DEVICE_NAME;
|
|
rcdev->change_protocol = msm_geni_ir_change_protocol;
|
|
rcdev->input_id.bustype = BUS_HOST;
|
|
|
|
rcdev->map_name = RC_MAP_EMPTY;
|
|
rcdev->dev.parent = &pdev->dev;
|
|
ir->dev = &pdev->dev;
|
|
ir->rcdev = rcdev;
|
|
|
|
rc = rc_register_device(rcdev);
|
|
if (rc < 0) {
|
|
pr_err("failed to register rc device %d\n", rc);
|
|
goto rc_register_err;
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
ir->misc.minor = MISC_DYNAMIC_MINOR;
|
|
ir->misc.name = MSM_GENI_IR_TX_DEVICE_NAME;
|
|
ir->misc.fops = &msm_geni_ir_tx_fops;
|
|
ir->misc.parent = &pdev->dev;
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
rc_register_err:
|
|
rc_free_device(rcdev);
|
|
mutex_destroy(&ir->lock);
|
|
rc_alloc_err:
|
|
irq_set_irq_wake(ir->wakeup_irq, 0);
|
|
core_irq_err:
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
gpio_free(ir->gpio_tx);
|
|
#endif
|
|
gpio_free(ir->gpio_rx);
|
|
gpio_err:
|
|
msm_geni_ir_rel_res(pdev, ir);
|
|
resource_err:
|
|
platform_set_drvdata(pdev, NULL);
|
|
kfree(ir);
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(msm_geni_ir_probe);
|
|
|
|
static int msm_geni_ir_remove(struct platform_device *pdev)
|
|
{
|
|
struct msm_geni_ir *ir = platform_get_drvdata(pdev);
|
|
|
|
rc_unregister_device(ir->rcdev);
|
|
rc_free_device(ir->rcdev);
|
|
mutex_destroy(&ir->lock);
|
|
irq_set_irq_wake(ir->wakeup_irq, 0);
|
|
if (ir->image_loaded != NULL)
|
|
clk_disable_unprepare(ir->ahb_clk);
|
|
|
|
free_irq(ir->wakeup_irq, ir);
|
|
free_irq(ir->irq, ir);
|
|
#ifdef CONFIG_IR_MSM_GENI_TX
|
|
gpio_free(ir->gpio_tx);
|
|
#endif
|
|
gpio_free(ir->gpio_rx);
|
|
msm_geni_ir_rel_res(pdev, ir);
|
|
platform_set_drvdata(pdev, NULL);
|
|
kfree(ir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int msm_geni_ir_suspend(struct device *dev)
|
|
{
|
|
struct msm_geni_ir *ir = platform_get_drvdata(to_platform_device(dev));
|
|
|
|
enable_irq_wake(ir->wakeup_irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int msm_geni_ir_resume(struct device *dev)
|
|
{
|
|
struct msm_geni_ir *ir = platform_get_drvdata(to_platform_device(dev));
|
|
u32 status;
|
|
|
|
disable_irq_wake(ir->wakeup_irq);
|
|
|
|
/* clear wakeup irq */
|
|
status = readl_relaxed(ir->base + GENI_IR_IRQ_STATUS);
|
|
writel_relaxed(status, ir->base + GENI_IR_IRQ_CLEAR);
|
|
/*write memory barrier*/
|
|
wmb();
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
|
static const struct dev_pm_ops msm_geni_ir_dev_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(
|
|
msm_geni_ir_suspend,
|
|
msm_geni_ir_resume
|
|
)
|
|
};
|
|
|
|
static const struct of_device_id msm_geni_ir_match_table[] = {
|
|
{ .compatible = "qcom,msm-geni-ir",
|
|
},
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver msm_geni_ir_driver = {
|
|
.probe = msm_geni_ir_probe,
|
|
.remove = msm_geni_ir_remove,
|
|
.driver = {
|
|
.name = "GENI Driver",
|
|
.pm = &msm_geni_ir_dev_pm_ops,
|
|
.of_match_table = msm_geni_ir_match_table,
|
|
},
|
|
};
|
|
|
|
static int __init msm_geni_ir_init(void)
|
|
{
|
|
return platform_driver_register(&msm_geni_ir_driver);
|
|
}
|
|
module_init(msm_geni_ir_init);
|
|
|
|
static void __exit msm_geni_ir_exit(void)
|
|
{
|
|
platform_driver_unregister(&msm_geni_ir_driver);
|
|
}
|
|
module_exit(msm_geni_ir_exit)
|
|
|
|
MODULE_DESCRIPTION("GENI IR Driver");
|
|
MODULE_LICENSE("GPL v2");
|