treewide: Import Samsung changes from T725XXU2DUD1
Change-Id: I5c31dc4a8006a967910963fb9e7d1a0ab4ab9731
This commit is contained in:
8
Makefile
8
Makefile
@@ -982,6 +982,14 @@ ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
|
||||
LDFLAGS_vmlinux += $(call ld-option, -X,)
|
||||
endif
|
||||
|
||||
ifneq ($(SEC_BUILD_CONF_USE_FINGERPRINT_TZ),false)
|
||||
ifeq ($(CONFIG_SENSORS_FINGERPRINT),y)
|
||||
ifneq ($(CONFIG_SEC_FACTORY),y)
|
||||
export KBUILD_FP_SENSOR_CFLAGS := -DENABLE_SENSORS_FPRINT_SECURE
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Default kernel image to build when no specific target is given.
|
||||
# KBUILD_IMAGE may be overruled on the command line or
|
||||
# set in the environment
|
||||
|
||||
@@ -1270,6 +1270,8 @@ config KEYS_COMPAT
|
||||
|
||||
endmenu
|
||||
|
||||
source "arch/arm64/Kconfig.sec"
|
||||
|
||||
menu "Power management options"
|
||||
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
@@ -65,6 +65,22 @@ config DEBUG_ALIGN_RODATA
|
||||
config ARM64_STRICT_BREAK_BEFORE_MAKE
|
||||
bool "Enforce strict break-before-make on page table updates "
|
||||
|
||||
comment "PowerManagement Feature"
|
||||
menuconfig SEC_PM
|
||||
bool "Samsung TN PowerManagement Feature"
|
||||
default y
|
||||
help
|
||||
Samsung TN PowerManagement Feature.
|
||||
|
||||
if SEC_PM
|
||||
config SEC_PM_DEBUG
|
||||
bool "Samsung TN PowerManagement Debug Feature"
|
||||
default n
|
||||
help
|
||||
Samsung TN PowerManagement Debug Feature.
|
||||
|
||||
endif
|
||||
|
||||
source "drivers/hwtracing/coresight/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
49
arch/arm64/Kconfig.sec
Normal file
49
arch/arm64/Kconfig.sec
Normal file
@@ -0,0 +1,49 @@
|
||||
config SEC_GTS4LV_PROJECT
|
||||
depends on ARCH_SDM670
|
||||
default n
|
||||
bool "Samsung GTS4LV Project"
|
||||
help
|
||||
Support for Samsung GTS4LV Project
|
||||
|
||||
config SEC_GTS4LV_MEA_PROJECT
|
||||
depends on SEC_GTS4LV_PROJECT
|
||||
default n
|
||||
bool "Samsung GTS4LV MEA Project"
|
||||
help
|
||||
Support for Samsung GTS4LV MEA Project
|
||||
|
||||
config SEC_GTS4LV_EUR_PROJECT
|
||||
depends on SEC_GTS4LV_PROJECT
|
||||
default n
|
||||
bool "Samsung GTS4LV EUR OPEN Project"
|
||||
help
|
||||
Support for Samsung GTS4LV EUR OPEN Project
|
||||
|
||||
config SEC_GTS4LVWIFI_EUR_PROJECT
|
||||
depends on SEC_GTS4LV_PROJECT
|
||||
default n
|
||||
bool "Samsung GTS4LVWIFI EUR OPEN Project"
|
||||
help
|
||||
Support for Samsung GTS4LVWIFI EUR OPEN Project
|
||||
|
||||
config SEC_GTS4LVWIFI_EUR_LDU_PROJECT
|
||||
depends on SEC_GTS4LV_PROJECT
|
||||
default n
|
||||
bool "Samsung GTS4LVWIFI EUR LDU Project"
|
||||
help
|
||||
Support for Samsung GTS4LVWIFI EUR LDU Project
|
||||
|
||||
config SEC_GTS4LV_KOR_PROJECT
|
||||
depends on SEC_GTS4LV_PROJECT
|
||||
default n
|
||||
bool "Samsung GTS4LV KOR OPEN Project"
|
||||
help
|
||||
Support for Samsung GTS4LV KOR OPEN Project
|
||||
|
||||
config SEC_GTS4LV_USA_PROJECT
|
||||
depends on SEC_GTS4LV_PROJECT
|
||||
default n
|
||||
bool "Samsung GTS4LV USA Project"
|
||||
help
|
||||
Support for Samsung GTS4LV USA Project
|
||||
|
||||
194
arch/arm64/include/asm/sec_debug.h
Normal file
194
arch/arm64/include/asm/sec_debug.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* arch/arm64/include/asm/sec_debug.h
|
||||
*
|
||||
* COPYRIGHT(C) 2006-2016 Samsung Electronics Co., Ltd. All Right Reserved.
|
||||
*
|
||||
* 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 SEC_DEBUG_ARM64_H
|
||||
#define SEC_DEBUG_ARM64_H
|
||||
|
||||
#if defined(CONFIG_ARM64) && defined(CONFIG_SEC_DEBUG)
|
||||
|
||||
struct sec_debug_mmu_reg_t {
|
||||
uint64_t TTBR0_EL1;
|
||||
uint64_t TTBR1_EL1;
|
||||
uint64_t TCR_EL1;
|
||||
uint64_t MAIR_EL1;
|
||||
uint64_t ATCR_EL1;
|
||||
uint64_t AMAIR_EL1;
|
||||
|
||||
uint64_t HSTR_EL2;
|
||||
uint64_t HACR_EL2;
|
||||
uint64_t TTBR0_EL2;
|
||||
uint64_t VTTBR_EL2;
|
||||
uint64_t TCR_EL2;
|
||||
uint64_t VTCR_EL2;
|
||||
uint64_t MAIR_EL2;
|
||||
uint64_t ATCR_EL2;
|
||||
|
||||
uint64_t TTBR0_EL3;
|
||||
uint64_t MAIR_EL3;
|
||||
uint64_t ATCR_EL3;
|
||||
};
|
||||
|
||||
/* ARM CORE regs mapping structure */
|
||||
struct sec_debug_core_t {
|
||||
/* COMMON */
|
||||
uint64_t x0;
|
||||
uint64_t x1;
|
||||
uint64_t x2;
|
||||
uint64_t x3;
|
||||
uint64_t x4;
|
||||
uint64_t x5;
|
||||
uint64_t x6;
|
||||
uint64_t x7;
|
||||
uint64_t x8;
|
||||
uint64_t x9;
|
||||
uint64_t x10;
|
||||
uint64_t x11;
|
||||
uint64_t x12;
|
||||
uint64_t x13;
|
||||
uint64_t x14;
|
||||
uint64_t x15;
|
||||
uint64_t x16;
|
||||
uint64_t x17;
|
||||
uint64_t x18;
|
||||
uint64_t x19;
|
||||
uint64_t x20;
|
||||
uint64_t x21;
|
||||
uint64_t x22;
|
||||
uint64_t x23;
|
||||
uint64_t x24;
|
||||
uint64_t x25;
|
||||
uint64_t x26;
|
||||
uint64_t x27;
|
||||
uint64_t x28;
|
||||
uint64_t x29; /* sp */
|
||||
uint64_t x30; /* lr */
|
||||
|
||||
uint64_t pc; /* pc */
|
||||
uint64_t cpsr; /* cpsr */
|
||||
|
||||
/* EL0 */
|
||||
uint64_t sp_el0;
|
||||
|
||||
/* EL1 */
|
||||
uint64_t sp_el1;
|
||||
uint64_t elr_el1;
|
||||
uint64_t spsr_el1;
|
||||
|
||||
/* EL2 */
|
||||
uint64_t sp_el2;
|
||||
uint64_t elr_el2;
|
||||
uint64_t spsr_el2;
|
||||
|
||||
/* EL3 */
|
||||
/* uint64_t sp_el3; */
|
||||
/* uint64_t elr_el3; */
|
||||
/* uint64_t spsr_el3; */
|
||||
};
|
||||
|
||||
#define READ_SPECIAL_REG(x) ({ \
|
||||
uint64_t val; \
|
||||
asm volatile ("mrs %0, " # x : "=r"(val)); \
|
||||
val; \
|
||||
})
|
||||
|
||||
static inline void sec_debug_save_mmu_reg(struct sec_debug_mmu_reg_t *mmu_reg)
|
||||
{
|
||||
uint64_t pstate, which_el;
|
||||
|
||||
pstate = READ_SPECIAL_REG(CurrentEl);
|
||||
which_el = pstate & PSR_MODE_MASK;
|
||||
|
||||
/* pr_emerg("%s: sec_debug EL mode=%d\n", __func__,which_el); */
|
||||
|
||||
mmu_reg->TTBR0_EL1 = READ_SPECIAL_REG(TTBR0_EL1);
|
||||
mmu_reg->TTBR1_EL1 = READ_SPECIAL_REG(TTBR1_EL1);
|
||||
mmu_reg->TCR_EL1 = READ_SPECIAL_REG(TCR_EL1);
|
||||
mmu_reg->MAIR_EL1 = READ_SPECIAL_REG(MAIR_EL1);
|
||||
mmu_reg->AMAIR_EL1 = READ_SPECIAL_REG(AMAIR_EL1);
|
||||
}
|
||||
|
||||
static inline void sec_debug_save_core_reg(struct sec_debug_core_t *core_reg)
|
||||
{
|
||||
uint64_t pstate,which_el;
|
||||
|
||||
pstate = READ_SPECIAL_REG(CurrentEl);
|
||||
which_el = pstate & PSR_MODE_MASK;
|
||||
|
||||
/* pr_emerg("%s: sec_debug EL mode=%d\n", __func__,which_el); */
|
||||
|
||||
asm volatile (
|
||||
"str x0, [%0,#0]\n\t" /* x0 is pushed first to core_reg */
|
||||
"mov x0, %0\n\t"
|
||||
"add x0, x0, 0x8\n\t"
|
||||
"stp x1, x2, [x0], #0x10\n\t"
|
||||
"stp x3, x4, [x0], #0x10\n\t"
|
||||
"stp x5, x6, [x0], #0x10\n\t"
|
||||
"stp x7, x8, [x0], #0x10\n\t"
|
||||
"stp x9, x10, [x0], #0x10\n\t"
|
||||
"stp x11, x12, [x0], #0x10\n\t"
|
||||
"stp x13, x14, [x0], #0x10\n\t"
|
||||
"stp x15, x16, [x0], #0x10\n\t"
|
||||
"stp x17, x18, [x0], #0x10\n\t"
|
||||
"stp x19, x20, [x0], #0x10\n\t"
|
||||
"stp x21, x22, [x0], #0x10\n\t"
|
||||
"stp x23, x24, [x0], #0x10\n\t"
|
||||
"stp x25, x26, [x0], #0x10\n\t"
|
||||
"stp x27, x28, [x0], #0x10\n\t"
|
||||
"stp x29, x30, [x0], #0x10\n\t"
|
||||
|
||||
/* pc */
|
||||
"adr x1, .\n\t"
|
||||
|
||||
/* pstate */
|
||||
"mrs x15, NZCV\n\t"
|
||||
"bic x15, x15, #0xFFFFFFFF0FFFFFFF\n\t"
|
||||
"mrs x9, DAIF\n\t"
|
||||
"bic x9, x9, #0xFFFFFFFFFFFFFC3F\n\t"
|
||||
"orr x15, x15, x9\n\t"
|
||||
"mrs x10, CurrentEL\n\t"
|
||||
"bic x10, x10, #0xFFFFFFFFFFFFFFF3\n\t"
|
||||
"orr x15, x15, x10\n\t"
|
||||
"mrs x11, SPSel\n\t"
|
||||
"bic x11, x11, #0xFFFFFFFFFFFFFFFE\n\t"
|
||||
"orr x15, x15, x11\n\t"
|
||||
|
||||
/* store pc & pstate */
|
||||
"stp x1, x15, [x0], #0x10\n\t"
|
||||
|
||||
: /* output */
|
||||
: "r"(core_reg) /* input */
|
||||
: "%x0", "%x1" /* clobbered registers */
|
||||
);
|
||||
|
||||
core_reg->sp_el0 = READ_SPECIAL_REG(sp_el0);
|
||||
|
||||
if(which_el >= PSR_MODE_EL2t){
|
||||
core_reg->sp_el0 = READ_SPECIAL_REG(sp_el1);
|
||||
core_reg->elr_el1 = READ_SPECIAL_REG(elr_el1);
|
||||
core_reg->spsr_el1 = READ_SPECIAL_REG(spsr_el1);
|
||||
core_reg->sp_el2 = READ_SPECIAL_REG(sp_el2);
|
||||
core_reg->elr_el2 = READ_SPECIAL_REG(elr_el2);
|
||||
core_reg->spsr_el2 = READ_SPECIAL_REG(spsr_el2);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_ARM64) && defined(CONFIG_SEC_DEBUG) */
|
||||
|
||||
#endif /* SEC_DEBUG_ARM64_H */
|
||||
@@ -21,6 +21,6 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define COMMAND_LINE_SIZE 2048
|
||||
#define COMMAND_LINE_SIZE 4096
|
||||
|
||||
#endif
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/ipi.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
|
||||
DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number);
|
||||
EXPORT_PER_CPU_SYMBOL(cpu_number);
|
||||
|
||||
@@ -853,6 +855,7 @@ static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs)
|
||||
pr_crit("CPU%u: stopping\n", cpu);
|
||||
show_regs(regs);
|
||||
dump_stack();
|
||||
sec_debug_save_context();
|
||||
dump_stack_minidump(regs->sp);
|
||||
raw_spin_unlock(&stop_lock);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
#include <asm/sysreg.h>
|
||||
#include <trace/events/exception.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
#include <linux/sec_debug_summary.h>
|
||||
#include <linux/sec_debug_user_reset.h>
|
||||
|
||||
static const char *handler[]= {
|
||||
"Synchronous Abort",
|
||||
"IRQ",
|
||||
@@ -256,6 +260,17 @@ static int __die(const char *str, int err, struct pt_regs *regs)
|
||||
end_of_stack(tsk));
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
#ifdef CONFIG_SEC_DEBUG
|
||||
if (THREAD_SIZE + (unsigned long)task_stack_page(tsk) - regs->sp
|
||||
> THREAD_SIZE) {
|
||||
dump_mem(KERN_EMERG, "Stack: ", regs->sp,
|
||||
THREAD_SIZE / 4 + regs->sp);
|
||||
} else {
|
||||
dump_mem(KERN_EMERG, "Stack: ", regs->sp, THREAD_SIZE
|
||||
+ (unsigned long)task_stack_page(tsk));
|
||||
}
|
||||
#endif
|
||||
|
||||
dump_backtrace(regs, tsk);
|
||||
dump_instr(KERN_EMERG, regs);
|
||||
}
|
||||
@@ -273,6 +288,7 @@ static unsigned long oops_begin(void)
|
||||
unsigned long flags;
|
||||
|
||||
oops_enter();
|
||||
secdbg_sched_msg("!!die!!");
|
||||
|
||||
/* racy, but better than risking deadlock. */
|
||||
raw_local_irq_save(flags);
|
||||
@@ -327,6 +343,8 @@ void die(const char *str, struct pt_regs *regs, int err)
|
||||
if (bug_type != BUG_TRAP_TYPE_NONE && !strlen(str))
|
||||
str = "Oops - BUG";
|
||||
|
||||
sec_debug_save_die_info(str, regs);
|
||||
|
||||
ret = __die(str, err, regs);
|
||||
|
||||
oops_end(flags, regs, ret);
|
||||
@@ -801,6 +819,9 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
|
||||
{
|
||||
console_verbose();
|
||||
|
||||
sec_debug_save_badmode_info(reason, handler[reason],
|
||||
esr, esr_get_class_string(esr));
|
||||
|
||||
pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n",
|
||||
handler[reason], smp_processor_id(), esr,
|
||||
esr_get_class_string(esr));
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include <soc/qcom/scm.h>
|
||||
#include <trace/events/exception.h>
|
||||
|
||||
#include <linux/sec_debug_user_reset.h>
|
||||
|
||||
struct fault_info {
|
||||
int (*fn)(unsigned long addr, unsigned int esr,
|
||||
struct pt_regs *regs);
|
||||
@@ -102,13 +104,19 @@ void show_pte(unsigned long addr)
|
||||
} else {
|
||||
pr_alert("[%016lx] address between user and kernel address ranges\n",
|
||||
addr);
|
||||
sec_debug_store_pte((unsigned long)addr, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_alert("pgd = %p\n", mm->pgd);
|
||||
sec_debug_store_pte((unsigned long)mm->pgd, 0);
|
||||
|
||||
pgd = pgd_offset(mm, addr);
|
||||
pr_alert("[%016lx] *pgd=%016llx", addr, pgd_val(*pgd));
|
||||
|
||||
sec_debug_store_pte((unsigned long)addr, 1);
|
||||
sec_debug_store_pte((unsigned long)pgd_val(*pgd), 2);
|
||||
|
||||
do {
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
@@ -119,16 +127,20 @@ void show_pte(unsigned long addr)
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
pr_cont(", *pud=%016llx", pud_val(*pud));
|
||||
sec_debug_store_pte((unsigned long)pud_val(*pud), 3);
|
||||
|
||||
if (pud_none(*pud) || pud_bad(*pud))
|
||||
break;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
pr_cont(", *pmd=%016llx", pmd_val(*pmd));
|
||||
sec_debug_store_pte((unsigned long)pmd_val(*pmd), 4);
|
||||
if (pmd_none(*pmd) || pmd_bad(*pmd))
|
||||
break;
|
||||
|
||||
pte = pte_offset_map(pmd, addr);
|
||||
pr_cont(", *pte=%016llx", pte_val(*pte));
|
||||
sec_debug_store_pte((unsigned long)pte_val(*pte), 5);
|
||||
pte_unmap(pte);
|
||||
} while(0);
|
||||
|
||||
@@ -203,6 +215,7 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
|
||||
if (!is_el1_instruction_abort(esr) && fixup_exception(regs))
|
||||
return;
|
||||
|
||||
sec_debug_store_extc_idx(false);
|
||||
/*
|
||||
* No handler, we'll have to terminate things with extreme prejudice.
|
||||
*/
|
||||
@@ -639,6 +652,8 @@ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
|
||||
const struct fault_info *inf = esr_to_fault_info(esr);
|
||||
struct siginfo info;
|
||||
|
||||
sec_debug_save_fault_info(esr, inf->name, addr, 0UL);
|
||||
|
||||
if (!inf->fn(addr, esr, regs))
|
||||
return;
|
||||
|
||||
@@ -696,6 +711,9 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr,
|
||||
esr_get_class_string(esr), (void *)regs->pc,
|
||||
(void *)regs->sp);
|
||||
|
||||
sec_debug_save_fault_info(esr, esr_get_class_string(esr),
|
||||
(unsigned long)regs->pc, (unsigned long)regs->sp);
|
||||
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_ADRALN;
|
||||
@@ -743,6 +761,8 @@ asmlinkage int __exception do_debug_exception(unsigned long addr_if_watchpoint,
|
||||
struct siginfo info;
|
||||
int rv;
|
||||
|
||||
sec_debug_save_fault_info(esr, inf->name, addr_if_watchpoint, 0UL);
|
||||
|
||||
/*
|
||||
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
|
||||
* already disabled to preserve the last enabled/disabled addresses.
|
||||
|
||||
@@ -379,7 +379,7 @@ EXPORT_SYMBOL(blk_put_queue);
|
||||
* If not, only ELVPRIV requests are drained. The caller is responsible
|
||||
* for ensuring that no new requests which need to be drained are queued.
|
||||
*/
|
||||
static void __blk_drain_queue(struct request_queue *q, bool drain_all)
|
||||
void __blk_drain_queue(struct request_queue *q, bool drain_all)
|
||||
__releases(q->queue_lock)
|
||||
__acquires(q->queue_lock)
|
||||
{
|
||||
|
||||
@@ -24,6 +24,14 @@ source "drivers/nvme/Kconfig"
|
||||
|
||||
source "drivers/misc/Kconfig"
|
||||
|
||||
source "drivers/staging/samsung/sec_notifier/Kconfig"
|
||||
|
||||
source "drivers/muic/Kconfig"
|
||||
|
||||
source "drivers/muic/universal/Kconfig"
|
||||
|
||||
source "drivers/ccic/Kconfig"
|
||||
|
||||
source "drivers/ide/Kconfig"
|
||||
|
||||
source "drivers/scsi/Kconfig"
|
||||
@@ -106,6 +114,8 @@ source "drivers/memstick/Kconfig"
|
||||
|
||||
source "drivers/leds/Kconfig"
|
||||
|
||||
source "drivers/switch/Kconfig"
|
||||
|
||||
source "drivers/accessibility/Kconfig"
|
||||
|
||||
source "drivers/infiniband/Kconfig"
|
||||
@@ -212,4 +222,20 @@ source "drivers/sensors/Kconfig"
|
||||
|
||||
source "drivers/tee/Kconfig"
|
||||
|
||||
source "drivers/adsp_factory/Kconfig"
|
||||
|
||||
source "drivers/fingerprint/Kconfig"
|
||||
|
||||
source "drivers/redriver/Kconfig"
|
||||
|
||||
source "drivers/debug/Kconfig"
|
||||
|
||||
source "drivers/gud/Kconfig"
|
||||
|
||||
source "drivers/battery_v2/Kconfig"
|
||||
|
||||
source "drivers/motor/Kconfig"
|
||||
|
||||
source "drivers/security/samsung/tzic/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -130,6 +130,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle/
|
||||
obj-y += mmc/
|
||||
obj-$(CONFIG_MEMSTICK) += memstick/
|
||||
obj-$(CONFIG_SWITCH) += switch/
|
||||
obj-$(CONFIG_NEW_LEDS) += leds/
|
||||
obj-$(CONFIG_INFINIBAND) += infiniband/
|
||||
obj-$(CONFIG_SGI_SN) += sn/
|
||||
@@ -161,6 +162,9 @@ obj-$(CONFIG_HYPERV) += hv/
|
||||
|
||||
obj-$(CONFIG_PM_DEVFREQ) += devfreq/
|
||||
obj-$(CONFIG_EXTCON) += extcon/
|
||||
obj-$(CONFIG_VBUS_NOTIFIER) += staging/samsung/sec_notifier/
|
||||
obj-$(CONFIG_USE_MUIC) += muic/
|
||||
obj-y += ccic/
|
||||
obj-$(CONFIG_MEMORY) += memory/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_VME_BUS) += vme/
|
||||
@@ -181,3 +185,20 @@ obj-$(CONFIG_ESOC) += esoc/
|
||||
obj-$(CONFIG_FPGA) += fpga/
|
||||
obj-$(CONFIG_SENSORS_SSC) += sensors/
|
||||
obj-$(CONFIG_TEE) += tee/
|
||||
obj-$(CONFIG_ADSP_FACTORY) += adsp_factory/
|
||||
obj-y += debug/
|
||||
obj-$(CONFIG_BATTERY_SAMSUNG) += battery_v2/
|
||||
# COMBO REDRIVER
|
||||
obj-$(CONFIG_COMBO_REDRIVER) += redriver/
|
||||
|
||||
# FINGERPRINT
|
||||
obj-$(CONFIG_SENSORS_FINGERPRINT) += fingerprint/
|
||||
|
||||
# mobicore
|
||||
obj-$(CONFIG_TRUSTONIC_TEE) += gud/
|
||||
|
||||
# Motor
|
||||
obj-y += motor/
|
||||
|
||||
#TZIC
|
||||
obj-y += security/samsung/tzic/
|
||||
|
||||
81
drivers/adsp_factory/Kconfig
Normal file
81
drivers/adsp_factory/Kconfig
Normal file
@@ -0,0 +1,81 @@
|
||||
#
|
||||
# factory sensor drivers configuration
|
||||
#
|
||||
config ADSP_FACTORY
|
||||
tristate "MSM ADSP factory driver"
|
||||
default n
|
||||
help
|
||||
This driver communicate with SSC DAEMON.
|
||||
register each sensor device.
|
||||
send selftest request using netlink.
|
||||
receive test result using netlink.
|
||||
|
||||
config LSM6DSM_FACTORY
|
||||
tristate "factory test for SSC - LSM6DSM"
|
||||
default n
|
||||
help
|
||||
lsm6dsM factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config ICM42605_FACTORY
|
||||
tristate "factory test for SSC - ICM42605"
|
||||
default n
|
||||
help
|
||||
icm42605 factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config AK09918_FACTORY
|
||||
tristate "factory test for SSC - ak09918"
|
||||
default n
|
||||
help
|
||||
ak09918 factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config LPS22HH_FACTORY
|
||||
tristate "factory test for SSC - lps22hh"
|
||||
default n
|
||||
help
|
||||
lps22hh factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config VCNL36863_FACTORY
|
||||
tristate "factory test for SSC - vcnl36863"
|
||||
default n
|
||||
help
|
||||
vcnl36863 factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config VEML3328_FACTORY
|
||||
tristate "factory test for SSC - veml3328"
|
||||
default n
|
||||
help
|
||||
veml3328 factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config VCNL36658_FACTORY
|
||||
tristate "factory test for SSC - vcnl36658"
|
||||
default n
|
||||
help
|
||||
vcnl36658 factory driver.
|
||||
provide sysfs for factory test.
|
||||
request selftest to adsp_factory.
|
||||
receive test result from adsp_factory.
|
||||
|
||||
config SUPPORT_PROX_AUTO_CAL
|
||||
tristate "Support auto cal function for proximity sensor"
|
||||
default n
|
||||
depends on ADSP_FACTORY
|
||||
help
|
||||
Support the auto cal function.
|
||||
9
drivers/adsp_factory/Makefile
Normal file
9
drivers/adsp_factory/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
obj-$(CONFIG_ADSP_FACTORY) += adsp_factory.o ssc_core.o
|
||||
obj-$(CONFIG_LSM6DSM_FACTORY) += lsm6dsl_accel.o lsm6dsl_gyro.o
|
||||
obj-$(CONFIG_ICM42605_FACTORY) += icm42605_accel.o icm42605_gyro.o
|
||||
obj-$(CONFIG_AK09918_FACTORY) += ak09918_mag.o
|
||||
obj-$(CONFIG_VCNL36863_FACTORY) += vcnl36863_light.o vcnl36863_prox.o
|
||||
obj-$(CONFIG_VCNL36658_FACTORY) += vcnl36658_light.o vcnl36658_prox.o
|
||||
obj-$(CONFIG_VEML3328_FACTORY) += veml3328_light.o
|
||||
obj-$(CONFIG_LPS22HH_FACTORY) += lps22hh_pressure.o
|
||||
70
drivers/adsp_factory/adsp.h
Normal file
70
drivers/adsp_factory/adsp.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#ifndef __ADSP_SENSOR_H__
|
||||
#define __ADSP_SENSOR_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
//#include <linux/sensors.h>
|
||||
#include <linux/adsp/adsp_ft_common.h>
|
||||
|
||||
#define TIMEOUT_CNT 200
|
||||
|
||||
/* Main struct containing all the data */
|
||||
struct adsp_data {
|
||||
struct device *adsp;
|
||||
struct device *sensor_device[MSG_SENSOR_MAX];
|
||||
struct device_attribute **sensor_attr[MSG_SENSOR_MAX];
|
||||
struct device *mobeam_device;
|
||||
struct sock *adsp_skt;
|
||||
int32_t *msg_buf[MSG_SENSOR_MAX];
|
||||
unsigned int ready_flag[MSG_TYPE_MAX];
|
||||
bool sysfs_created[MSG_SENSOR_MAX];
|
||||
struct mutex prox_factory_mutex;
|
||||
struct mutex light_factory_mutex;
|
||||
struct mutex accel_factory_mutex;
|
||||
struct mutex remove_sysfs_mutex;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SUPPORT_MOBEAM
|
||||
void adsp_mobeam_register(struct device_attribute *attributes[]);
|
||||
void adsp_mobeam_unregister(struct device_attribute *attributes[]);
|
||||
#endif
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
int get_mag_raw_data(int32_t *raw_data);
|
||||
#endif
|
||||
int get_accel_raw_data(int32_t *raw_data);
|
||||
int get_prox_raw_data(int *raw_data, int *offset);
|
||||
int adsp_get_sensor_data(int sensor_type);
|
||||
int adsp_factory_register(unsigned int type,
|
||||
struct device_attribute *attributes[]);
|
||||
int adsp_factory_unregister(unsigned int type);
|
||||
int adsp_unicast(void *param, int param_size, u16 sensor_type,
|
||||
u32 portid, u16 msg_type);
|
||||
int sensors_register(struct device **dev, void *drvdata,
|
||||
struct device_attribute *attributes[], char *name);
|
||||
void sensors_unregister(struct device *dev,
|
||||
struct device_attribute *attributes[]);
|
||||
void hidden_hole_init_work(void);
|
||||
void accel_factory_init_work(void);
|
||||
#endif
|
||||
344
drivers/adsp_factory/adsp_factory.c
Normal file
344
drivers/adsp_factory/adsp_factory.c
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/netlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sec_class.h>
|
||||
#include "adsp.h"
|
||||
|
||||
static u8 msg_size[MSG_SENSOR_MAX] = {
|
||||
MSG_ACCEL_MAX,
|
||||
MSG_GYRO_MAX,
|
||||
MSG_MAG_MAX,
|
||||
MSG_PRESSURE_MAX,
|
||||
MSG_LIGHT_MAX,
|
||||
MSG_PROX_MAX,
|
||||
MSG_TYPE_SIZE_ZERO, //hole
|
||||
MSG_MOBEAM_MAX,
|
||||
MSG_TYPE_SIZE_ZERO, //physical
|
||||
MSG_GYRO_TEMP_MAX,
|
||||
MSG_PRESSURE_TEMP_MAX,
|
||||
MSG_TYPE_SIZE_ZERO,
|
||||
MSG_TYPE_SIZE_ZERO,
|
||||
MSG_TYPE_SIZE_ZERO,
|
||||
};
|
||||
|
||||
/* The netlink socket */
|
||||
struct adsp_data *data;
|
||||
|
||||
DEFINE_MUTEX(factory_mutex);
|
||||
|
||||
/* Function used to send message to the user space */
|
||||
int adsp_unicast(void *param, int param_size, u16 sensor_type,
|
||||
u32 portid, u16 msg_type)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct nlmsghdr *nlh;
|
||||
void *msg;
|
||||
int ret = -1;
|
||||
u16 nlmsg_type = (sensor_type << 8) | msg_type;
|
||||
|
||||
data->ready_flag[msg_type] &= ~(1 << sensor_type);
|
||||
skb = nlmsg_new(param_size, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
pr_err("[FACTORY] %s - nlmsg_new fail\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nlh = nlmsg_put(skb, portid, 0, nlmsg_type, param_size, 0);
|
||||
if (nlh == NULL) {
|
||||
pr_err("[FACTORY] %s - nlmsg_put fail\n", __func__);
|
||||
nlmsg_free(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
msg = nlmsg_data(nlh);
|
||||
memcpy(msg, param, param_size);
|
||||
NETLINK_CB(skb).dst_group = 0;
|
||||
ret = nlmsg_unicast(data->adsp_skt, skb, PID);
|
||||
if (ret != 0)
|
||||
pr_err("[FACTORY] %s - ret = %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adsp_factory_register(unsigned int type,
|
||||
struct device_attribute *attributes[])
|
||||
{
|
||||
int ret = 0;
|
||||
char *dev_name;
|
||||
|
||||
switch (type) {
|
||||
case MSG_ACCEL:
|
||||
dev_name = "accelerometer_sensor";
|
||||
break;
|
||||
case MSG_GYRO:
|
||||
dev_name = "gyro_sensor";
|
||||
break;
|
||||
case MSG_MAG:
|
||||
dev_name = "magnetic_sensor";
|
||||
break;
|
||||
case MSG_PRESSURE:
|
||||
dev_name = "barometer_sensor";
|
||||
break;
|
||||
case MSG_LIGHT:
|
||||
dev_name = "light_sensor";
|
||||
break;
|
||||
case MSG_PROX:
|
||||
dev_name = "proximity_sensor";
|
||||
break;
|
||||
case MSG_SSC_CORE:
|
||||
dev_name = "ssc_core";
|
||||
break;
|
||||
case MSG_HH_HOLE:
|
||||
dev_name = "hidden_hole";
|
||||
break;
|
||||
default:
|
||||
dev_name = "unknown_sensor";
|
||||
break;
|
||||
}
|
||||
|
||||
data->sensor_attr[type] = attributes;
|
||||
ret = sensors_register(&data->sensor_device[type], data,
|
||||
data->sensor_attr[type], dev_name);
|
||||
|
||||
data->sysfs_created[type] = true;
|
||||
pr_info("[FACTORY] %s - type:%u ptr:%pK\n",
|
||||
__func__, type, data->sensor_device[type]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adsp_factory_unregister(unsigned int type)
|
||||
{
|
||||
pr_info("[FACTORY] %s - type:%u ptr:%pK\n",
|
||||
__func__, type, data->sensor_device[type]);
|
||||
|
||||
if (data->sysfs_created[type]) {
|
||||
sensors_unregister(data->sensor_device[type],
|
||||
data->sensor_attr[type]);
|
||||
data->sysfs_created[type] = false;
|
||||
} else {
|
||||
pr_info("[FACTORY] %s: skip type %u\n", __func__, type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_prox_raw_data(int *raw_data, int *offset)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_PROX, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*raw_data = data->msg_buf[MSG_PROX][0];
|
||||
*offset = data->msg_buf[MSG_PROX][1];
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_accel_raw_data(int32_t *raw_data)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_ACCEL);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(raw_data, &data->msg_buf[MSG_ACCEL][0], sizeof(int32_t) * 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
int get_mag_raw_data(int32_t *raw_data)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_MAG, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_MAG) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_MAG);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(raw_data, &data->msg_buf[MSG_MAG][0], sizeof(int32_t) * 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SUPPORT_MOBEAM
|
||||
void adsp_mobeam_register(struct device_attribute *attributes[])
|
||||
{
|
||||
int i;
|
||||
|
||||
data->mobeam_device = sec_device_create(0, data, "sec_barcode_emul");
|
||||
|
||||
for (i = 0; attributes[i] != NULL; i++) {
|
||||
if (device_create_file(data->mobeam_device, attributes[i]) < 0)
|
||||
pr_err("%s fail to create %d", __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
void adsp_mobeam_unregister(struct device_attribute *attributes[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; attributes[i] != NULL; i++)
|
||||
device_remove_file(data->mobeam_device, attributes[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int process_received_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
u16 sensor_type = nlh->nlmsg_type >> 8;
|
||||
u16 msg_type = nlh->nlmsg_type & 0xff;
|
||||
|
||||
/* check the boundary to prevent memory attack */
|
||||
if (msg_type >= MSG_TYPE_MAX || sensor_type >= MSG_SENSOR_MAX ||
|
||||
nlh->nlmsg_len - (int32_t)sizeof(struct nlmsghdr) >
|
||||
sizeof(int32_t) * msg_size[sensor_type]) {
|
||||
pr_err("[FACTORY] %d, %d, %d\n", msg_type, sensor_type, nlh->nlmsg_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sensor_type == MSG_FACTORY_INIT_CMD) {
|
||||
accel_factory_init_work();
|
||||
#if defined(CONFIG_SUPPORT_HIDDEN_HOLE)
|
||||
hidden_hole_init_work();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(data->msg_buf[sensor_type],
|
||||
(int32_t *)NLMSG_DATA(nlh),
|
||||
nlh->nlmsg_len - (int32_t)sizeof(struct nlmsghdr));
|
||||
data->ready_flag[msg_type] |= 1 << sensor_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void factory_receive_skb(struct sk_buff *skb)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
int len;
|
||||
int err;
|
||||
|
||||
nlh = (struct nlmsghdr *)skb->data;
|
||||
len = skb->len;
|
||||
while (NLMSG_OK(nlh, len)) {
|
||||
err = process_received_msg(skb, nlh);
|
||||
/* if err or if this message says it wants a response */
|
||||
if (err || (nlh->nlmsg_flags & NLM_F_ACK))
|
||||
netlink_ack(skb, nlh, err);
|
||||
nlh = NLMSG_NEXT(nlh, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive messages from netlink socket. */
|
||||
static void factory_test_result_receive(struct sk_buff *skb)
|
||||
{
|
||||
mutex_lock(&factory_mutex);
|
||||
factory_receive_skb(skb);
|
||||
mutex_unlock(&factory_mutex);
|
||||
}
|
||||
|
||||
struct netlink_kernel_cfg netlink_cfg = {
|
||||
.input = factory_test_result_receive,
|
||||
};
|
||||
|
||||
static int __init factory_adsp_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
|
||||
for (i = 0; i < MSG_SENSOR_MAX; i++) {
|
||||
if (msg_size[i] > 0)
|
||||
data->msg_buf[i] = kzalloc(sizeof(int32_t) * msg_size[i],
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
data->adsp_skt = netlink_kernel_create(&init_net,
|
||||
NETLINK_ADSP_FAC, &netlink_cfg);
|
||||
|
||||
for (i = 0; i < MSG_SENSOR_MAX; i++)
|
||||
data->sysfs_created[i] = false;
|
||||
for (i = 0; i < MSG_TYPE_MAX; i++)
|
||||
data->ready_flag[i] = 0;
|
||||
|
||||
mutex_init(&data->accel_factory_mutex);
|
||||
mutex_init(&data->prox_factory_mutex);
|
||||
mutex_init(&data->light_factory_mutex);
|
||||
mutex_init(&data->remove_sysfs_mutex);
|
||||
|
||||
pr_info("[FACTORY] %s: Timer Init\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit factory_adsp_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_destroy(&data->accel_factory_mutex);
|
||||
mutex_destroy(&data->prox_factory_mutex);
|
||||
mutex_destroy(&data->light_factory_mutex);
|
||||
mutex_destroy(&data->remove_sysfs_mutex);
|
||||
|
||||
for (i = 0; i < MSG_SENSOR_MAX; i++)
|
||||
kfree(data->msg_buf[i]);
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(factory_adsp_init);
|
||||
module_exit(factory_adsp_exit);
|
||||
MODULE_DESCRIPTION("Support for factory test sensors (adsp)");
|
||||
MODULE_LICENSE("GPL");
|
||||
253
drivers/adsp_factory/ak09918_mag.c
Normal file
253
drivers/adsp_factory/ak09918_mag.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "AKM"
|
||||
#define CHIP_ID "AK09918"
|
||||
|
||||
#define MAG_ST_TRY_CNT 3
|
||||
|
||||
static ssize_t mag_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t mag_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t mag_check_cntl(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "OK\n");
|
||||
}
|
||||
|
||||
static ssize_t mag_check_registers(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
static ssize_t mag_get_asa(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
/* Do not have Fuserom */
|
||||
return snprintf(buf, PAGE_SIZE, "%u,%u,%u\n", 128, 128, 128);
|
||||
}
|
||||
|
||||
static ssize_t mag_get_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
/* Do not have Fuserom */
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", "OK");
|
||||
}
|
||||
|
||||
static ssize_t mag_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_MAG, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_MAG) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_MAG);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "0,0,0\n");
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
data->msg_buf[MSG_MAG][0],
|
||||
data->msg_buf[MSG_MAG][1],
|
||||
data->msg_buf[MSG_MAG][2]);
|
||||
}
|
||||
|
||||
static ssize_t mag_raw_data_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_ENABLE);
|
||||
msleep(20);
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
msleep(20);
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_DISABLE);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t mag_selttest_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int retry = 0, i;
|
||||
|
||||
RETRY_MAG_SELFTEST:
|
||||
pr_info("[FACTORY] %s - start", __func__);
|
||||
adsp_unicast(NULL, 0, MSG_MAG, 0, MSG_TYPE_ST_SHOW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_MAG) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_MAG);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
data->msg_buf[MSG_MAG][0] = -1;
|
||||
}
|
||||
|
||||
if (!(data->msg_buf[MSG_MAG][0] == 0)) {
|
||||
if (retry < MAG_ST_TRY_CNT) {
|
||||
retry++;
|
||||
for (i = 0; i < 10; i++)
|
||||
data->msg_buf[MSG_MAG][i] = 0;
|
||||
|
||||
msleep(100);
|
||||
pr_info("[FACTORY] %s - retry %d", __func__, retry);
|
||||
goto RETRY_MAG_SELFTEST;
|
||||
}
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_ENABLE);
|
||||
msleep(20);
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
msleep(20);
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_DISABLE);
|
||||
return snprintf(buf, PAGE_SIZE, "-1,0,0,0,0,0,0,0,0,0\n");
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] status=%d, sf_status=%d, sf_x=%d, sf_y=%d, sf_z=%d\n dac=%d, adc=%d, adc_x=%d, adc_y=%d, adc_z=%d\n",
|
||||
data->msg_buf[MSG_MAG][0], data->msg_buf[MSG_MAG][1],
|
||||
data->msg_buf[MSG_MAG][2], data->msg_buf[MSG_MAG][3],
|
||||
data->msg_buf[MSG_MAG][4], data->msg_buf[MSG_MAG][5],
|
||||
data->msg_buf[MSG_MAG][6], data->msg_buf[MSG_MAG][7],
|
||||
data->msg_buf[MSG_MAG][8], data->msg_buf[MSG_MAG][9]);
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_ENABLE);
|
||||
msleep(20);
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
msleep(20);
|
||||
adsp_unicast(NULL, 0, MSG_MAG_CAL, 0, MSG_TYPE_FACTORY_DISABLE);
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_MAG][0], data->msg_buf[MSG_MAG][1],
|
||||
data->msg_buf[MSG_MAG][2], data->msg_buf[MSG_MAG][3],
|
||||
data->msg_buf[MSG_MAG][4], data->msg_buf[MSG_MAG][5],
|
||||
data->msg_buf[MSG_MAG][6], data->msg_buf[MSG_MAG][7],
|
||||
data->msg_buf[MSG_MAG][8], data->msg_buf[MSG_MAG][9]);
|
||||
}
|
||||
|
||||
static ssize_t mag_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
#if 0
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
struct msg_data message;
|
||||
uint8_t cnt = 0;
|
||||
|
||||
message.msg_type = MSG_MAG;
|
||||
data->calib_ready_flag &= ~(1 << MSG_MAG);
|
||||
adsp_unicast(&message, sizeof(message),
|
||||
MSG_TYPE_GET_CALIB_DATA, 0, 0);
|
||||
|
||||
while (!(data->calib_ready_flag & 1 << MSG_MAG) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->calib_ready_flag &= ~(1 << MSG_MAG);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
pr_info("[FACTORY] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[0],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[1],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[2],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[3],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[4],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[5],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[6],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[7],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[8]);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"\"SI_PARAMETER\":\"0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\"\n",
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[0],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[1],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[2],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[3],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[4],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[5],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[6],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[7],
|
||||
data->sensor_calib_data[MSG_MAG].si_mat[8]);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, 0444, mag_name_show, NULL);
|
||||
static DEVICE_ATTR(vendor, 0444, mag_vendor_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0664, mag_raw_data_show, mag_raw_data_store);
|
||||
static DEVICE_ATTR(adc, 0444, mag_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(dac, 0444, mag_check_cntl, NULL);
|
||||
static DEVICE_ATTR(chk_registers, 0444, mag_check_registers, NULL);
|
||||
static DEVICE_ATTR(selftest, 0440, mag_selttest_show, NULL);
|
||||
static DEVICE_ATTR(asa, 0444, mag_get_asa, NULL);
|
||||
static DEVICE_ATTR(status, 0444, mag_get_status, NULL);
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0440, mag_dhr_sensor_info_show, NULL);
|
||||
|
||||
static struct device_attribute *mag_attrs[] = {
|
||||
&dev_attr_name,
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_adc,
|
||||
&dev_attr_dac,
|
||||
&dev_attr_chk_registers,
|
||||
&dev_attr_selftest,
|
||||
&dev_attr_asa,
|
||||
&dev_attr_status,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init ak09918_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_MAG, mag_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ak09918_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_MAG);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(ak09918_factory_init);
|
||||
module_exit(ak09918_factory_exit);
|
||||
557
drivers/adsp_factory/icm42605_accel.c
Normal file
557
drivers/adsp_factory/icm42605_accel.c
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
#include <linux/adsp/slpi_motor.h>
|
||||
#endif
|
||||
#define VENDOR "TDK"
|
||||
#define CHIP_ID "ICM42605"
|
||||
#define ACCEL_ST_TRY_CNT 3
|
||||
#define ACCEL_FACTORY_CAL_CNT 20
|
||||
#define ACCEL_RAW_DATA_CNT 3
|
||||
#define MAX_ACCEL_1G 4096
|
||||
|
||||
/* Haptic Pattern A vibrate during 7ms.
|
||||
* touch, touchkey, operation feedback use this.
|
||||
* Do not call motor_workfunc when duration is 7ms.
|
||||
*/
|
||||
#define DURATION_SKIP 10
|
||||
#define MOTOR_OFF 0
|
||||
|
||||
#define ACCEL_FACTORY_CAL_PATH "/efs/FactoryApp/accel_factory_cal"
|
||||
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
struct accel_motor_data {
|
||||
struct workqueue_struct *slpi_motor_wq;
|
||||
struct work_struct work_slpi_motor;
|
||||
int motor_state;
|
||||
};
|
||||
|
||||
struct accel_motor_data *pdata_motor;
|
||||
#endif
|
||||
|
||||
struct accel_data {
|
||||
struct work_struct work_accel;
|
||||
struct workqueue_struct *accel_wq;
|
||||
struct adsp_data *dev_data;
|
||||
bool is_complete_cal;
|
||||
int32_t raw_data[ACCEL_RAW_DATA_CNT];
|
||||
int32_t avg_data[ACCEL_RAW_DATA_CNT];
|
||||
};
|
||||
|
||||
static struct accel_data *pdata;
|
||||
|
||||
static ssize_t accel_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t accel_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t sensor_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", "ADSP");
|
||||
}
|
||||
|
||||
int get_accel_cal_data(int32_t *cal_data)
|
||||
{
|
||||
struct file *factory_cal_filp = NULL;
|
||||
mm_segment_t old_fs;
|
||||
int ret = 0;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH,
|
||||
O_RDONLY, 0440);
|
||||
|
||||
if (IS_ERR(factory_cal_filp)) {
|
||||
set_fs(old_fs);
|
||||
ret = PTR_ERR(factory_cal_filp);
|
||||
pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vfs_read(factory_cal_filp, (char *)cal_data,
|
||||
ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos);
|
||||
if (ret < 0) {
|
||||
pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret);
|
||||
filp_close(factory_cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
filp_close(factory_cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_accel_cal_data(int32_t *cal_data, bool first_booting)
|
||||
{
|
||||
struct file *factory_cal_filp = NULL;
|
||||
mm_segment_t old_fs;
|
||||
int flag, ret = 0;
|
||||
umode_t mode = 0;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
if (first_booting) {
|
||||
flag = O_TRUNC | O_RDWR | O_CREAT;
|
||||
mode = 0600;
|
||||
} else {
|
||||
flag = O_RDWR;
|
||||
mode = 0660;
|
||||
}
|
||||
|
||||
factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, flag, mode);
|
||||
|
||||
if (IS_ERR(factory_cal_filp)) {
|
||||
set_fs(old_fs);
|
||||
ret = PTR_ERR(factory_cal_filp);
|
||||
pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vfs_write(factory_cal_filp, (char *)cal_data,
|
||||
ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos);
|
||||
if (ret < 0)
|
||||
pr_err("[FACTORY] %s: fd write %d\n", __func__, ret);
|
||||
|
||||
filp_close(factory_cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
|
||||
adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t accel_calibration_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int32_t cal_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
|
||||
if (get_accel_cal_data(cal_data) > 0) {
|
||||
pr_info("[FACTORY] %s: %d, %d, %d\n", __func__,
|
||||
cal_data[0], cal_data[1], cal_data[2]);
|
||||
if (cal_data[0] == 0 && cal_data[1] == 0 && cal_data[2] == 0)
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n",
|
||||
0, 0, 0, 0);
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n",
|
||||
true, cal_data[0], cal_data[1], cal_data[2]);
|
||||
} else {
|
||||
pr_err("[FACTORY] %s: get_accel_cal_data fail\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t accel_calibration_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
|
||||
pdata->dev_data = data;
|
||||
if (sysfs_streq(buf, "0")) {
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
memset(pdata->avg_data, 0, sizeof(pdata->avg_data));
|
||||
set_accel_cal_data(pdata->avg_data, false);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
} else {
|
||||
pdata->is_complete_cal = false;
|
||||
queue_work(pdata->accel_wq, &pdata->work_accel);
|
||||
while (pdata->is_complete_cal == false) {
|
||||
pr_info("[FACTORY] %s: In factory cal\n", __func__);
|
||||
msleep(20);
|
||||
}
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
set_accel_cal_data(pdata->avg_data, false);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void accel_work_func(struct work_struct *work)
|
||||
{
|
||||
struct accel_data *data = container_of((struct work_struct *)work,
|
||||
struct accel_data, work_accel);
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->dev_data->accel_factory_mutex);
|
||||
memset(pdata->avg_data, 0, sizeof(pdata->avg_data));
|
||||
adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
msleep(30); /* for init of bias */
|
||||
for (i = 0; i < ACCEL_FACTORY_CAL_CNT; i++) {
|
||||
msleep(20);
|
||||
get_accel_raw_data(pdata->raw_data);
|
||||
pdata->avg_data[0] += pdata->raw_data[0];
|
||||
pdata->avg_data[1] += pdata->raw_data[1];
|
||||
pdata->avg_data[2] += pdata->raw_data[2];
|
||||
pr_info("[FACTORY] %s: %d, %d, %d\n", __func__,
|
||||
pdata->raw_data[0], pdata->raw_data[1], pdata->raw_data[2]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ACCEL_RAW_DATA_CNT; i++) {
|
||||
pdata->avg_data[i] /= ACCEL_FACTORY_CAL_CNT;
|
||||
pr_err("[FACTORY] %s: avg : %d\n", __func__, pdata->avg_data[i]);
|
||||
}
|
||||
|
||||
if (pdata->avg_data[2] > 0)
|
||||
pdata->avg_data[2] -= MAX_ACCEL_1G;
|
||||
else if (pdata->avg_data[2] < 0)
|
||||
pdata->avg_data[2] += MAX_ACCEL_1G;
|
||||
|
||||
mutex_unlock(&data->dev_data->accel_factory_mutex);
|
||||
pdata->is_complete_cal = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t accel_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_ST_SHOW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_ACCEL);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
data->msg_buf[MSG_ACCEL][1] = -1;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s : init = %d, result = %d, XYZ = %d, %d, %d, nXYZ = %d, %d, %d\n",
|
||||
__func__, data->msg_buf[MSG_ACCEL][0],
|
||||
data->msg_buf[MSG_ACCEL][7], data->msg_buf[MSG_ACCEL][1],
|
||||
data->msg_buf[MSG_ACCEL][2], data->msg_buf[MSG_ACCEL][3],
|
||||
data->msg_buf[MSG_ACCEL][4], data->msg_buf[MSG_ACCEL][5],
|
||||
data->msg_buf[MSG_ACCEL][6]);
|
||||
|
||||
if (data->msg_buf[MSG_ACCEL][7] == 0) {
|
||||
pr_info("[FACTORY] %s : Pass - result = %d\n",
|
||||
__func__, data->msg_buf[MSG_ACCEL][7]);
|
||||
} else {
|
||||
pr_err("[FACTORY] %s : Fail - result = %d\n",
|
||||
__func__, data->msg_buf[MSG_ACCEL][7]);
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_ACCEL][7],
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][1]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][2]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][3]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][4]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][5]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][6]));
|
||||
}
|
||||
|
||||
static ssize_t accel_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
get_accel_raw_data(raw_data);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
raw_data[0], raw_data[1], raw_data[2]);
|
||||
}
|
||||
|
||||
static ssize_t accel_reactive_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
bool success = false;
|
||||
int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
get_accel_raw_data(raw_data);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
if (raw_data[0] != 0 || raw_data[1] != 0 || raw_data[2] != 0)
|
||||
success = true;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", success);
|
||||
}
|
||||
|
||||
static ssize_t accel_reactive_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
if (sysfs_streq(buf, "1"))
|
||||
pr_info("[FACTORY]: %s - on\n", __func__);
|
||||
else if (sysfs_streq(buf, "0"))
|
||||
pr_info("[FACTORY]: %s - off\n", __func__);
|
||||
else if (sysfs_streq(buf, "2"))
|
||||
pr_info("[FACTORY]: %s - factory\n", __func__);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t accel_lowpassfilter_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int32_t msg_buf;
|
||||
|
||||
if (sysfs_streq(buf, "1")) {
|
||||
msg_buf = 1;
|
||||
} else if (sysfs_streq(buf, "0")) {
|
||||
msg_buf = 0;
|
||||
} else {
|
||||
pr_info("[FACTORY] %s: wrong value\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
adsp_unicast(&msg_buf, sizeof(int32_t), MSG_ACCEL, 0, MSG_TYPE_SET_ACCEL_LPF);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] &= ~(1 << MSG_ACCEL);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: lpf_on_off done (%d)(0x%x)\n", __func__,
|
||||
data->msg_buf[MSG_ACCEL][0], data->msg_buf[MSG_ACCEL][1]);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
int setSensorCallback(int state, int duration)
|
||||
{
|
||||
if (duration > MOTOR_OFF && duration <= DURATION_SKIP)
|
||||
return 0;
|
||||
|
||||
if (pdata_motor->motor_state != state) {
|
||||
pr_info("[FACTORY] %s: state = %d, duration = %d\n",
|
||||
__func__, pdata_motor->motor_state, duration);
|
||||
pdata_motor->motor_state = state;
|
||||
queue_work(pdata_motor->slpi_motor_wq, &pdata_motor->work_slpi_motor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void slpi_motor_work_func(struct work_struct *work)
|
||||
{
|
||||
#if 0
|
||||
struct msg_data message;
|
||||
int motor = 0;
|
||||
|
||||
if (pdata_motor->motor_state == 1) {
|
||||
motor = MSG_TYPE_ACCEL_MOTOR_ON;
|
||||
message.msg_type = MSG_ACCEL_MOT_ON;
|
||||
} else if (pdata_motor->motor_state == 0) {
|
||||
motor = MSG_TYPE_ACCEL_MOTOR_OFF;
|
||||
message.msg_type = MSG_ACCEL_MOT_OFF;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: state = %d\n", __func__, pdata_motor->motor_state);
|
||||
|
||||
adsp_unicast(&message, sizeof(message), motor, 0, 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t accel_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
char ctrl1_xl = 0;
|
||||
uint8_t fullscale = 0;
|
||||
int32_t *info = data->msg_buf[MSG_ACCEL];
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_ACCEL);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
ctrl1_xl = *info;
|
||||
|
||||
ctrl1_xl &= 0xC;
|
||||
|
||||
switch (ctrl1_xl) {
|
||||
case 0xC:
|
||||
fullscale = 8;
|
||||
break;
|
||||
case 0x8:
|
||||
fullscale = 4;
|
||||
break;
|
||||
case 0x4:
|
||||
fullscale = 16;
|
||||
break;
|
||||
case 0:
|
||||
fullscale = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: f/s %u\n", __func__, fullscale);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "\"FULL_SCALE\":\"%uG\"\n", fullscale);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, 0444, accel_name_show, NULL);
|
||||
static DEVICE_ATTR(vendor, 0444, accel_vendor_show, NULL);
|
||||
static DEVICE_ATTR(type, 0444, sensor_type_show, NULL);
|
||||
static DEVICE_ATTR(calibration, 0664,
|
||||
accel_calibration_show, accel_calibration_store);
|
||||
static DEVICE_ATTR(selftest, 0440,
|
||||
accel_selftest_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, accel_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(reactive_alert, 0664,
|
||||
accel_reactive_show, accel_reactive_store);
|
||||
static DEVICE_ATTR(lowpassfilter, 0220,
|
||||
NULL, accel_lowpassfilter_store);
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0444,
|
||||
accel_dhr_sensor_info_show, NULL);
|
||||
#else
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0440,
|
||||
accel_dhr_sensor_info_show, NULL);
|
||||
#endif
|
||||
|
||||
static struct device_attribute *acc_attrs[] = {
|
||||
&dev_attr_name,
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_type,
|
||||
&dev_attr_calibration,
|
||||
&dev_attr_selftest,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_reactive_alert,
|
||||
&dev_attr_lowpassfilter,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void accel_factory_init_work(void)
|
||||
{
|
||||
struct file *cal_filp = NULL;
|
||||
mm_segment_t old_fs;
|
||||
int32_t zero_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
int ret = 0;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, O_RDONLY, 0440);
|
||||
if (PTR_ERR(cal_filp) == -ENOENT || PTR_ERR(cal_filp) == -ENXIO) {
|
||||
pr_info("[FACTORY] %s : no accel_factory_cal file\n", __func__);
|
||||
set_fs(old_fs);
|
||||
set_accel_cal_data(zero_data, true);
|
||||
} else if (IS_ERR(cal_filp)) {
|
||||
pr_err("[FACTORY]: %s - filp_open error\n", __func__);
|
||||
set_fs(old_fs);
|
||||
} else {
|
||||
pr_info("[FACTORY] %s : already exist\n", __func__);
|
||||
ret = vfs_read(cal_filp, (char *)zero_data,
|
||||
ACCEL_RAW_DATA_CNT * sizeof(int32_t), &cal_filp->f_pos);
|
||||
if (ret < 0) {
|
||||
pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret);
|
||||
zero_data[0] = zero_data[1] = zero_data[2] = 0;
|
||||
adsp_unicast(zero_data, sizeof(zero_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
filp_close(cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
return;
|
||||
}
|
||||
adsp_unicast(zero_data, sizeof(zero_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
filp_close(cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init lsm6dsl_accel_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_ACCEL, acc_attrs);
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
pdata_motor = kzalloc(sizeof(*pdata_motor), GFP_KERNEL);
|
||||
|
||||
if (pdata_motor == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata_motor->slpi_motor_wq =
|
||||
create_singlethread_workqueue("slpi_motor_wq");
|
||||
|
||||
if (pdata_motor->slpi_motor_wq == NULL) {
|
||||
pr_err("[FACTORY]: %s - could not create motor wq\n", __func__);
|
||||
kfree(pdata_motor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&pdata_motor->work_slpi_motor, slpi_motor_work_func);
|
||||
|
||||
pdata_motor->motor_state = 0;
|
||||
#endif
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
pdata->accel_wq = create_singlethread_workqueue("accel_wq");
|
||||
INIT_WORK(&pdata->work_accel, accel_work_func);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit lsm6dsl_accel_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_ACCEL);
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
if (pdata_motor != NULL && pdata_motor->slpi_motor_wq != NULL) {
|
||||
cancel_work_sync(&pdata_motor->work_slpi_motor);
|
||||
destroy_workqueue(pdata_motor->slpi_motor_wq);
|
||||
pdata_motor->slpi_motor_wq = NULL;
|
||||
}
|
||||
#endif
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
module_init(lsm6dsl_accel_factory_init);
|
||||
module_exit(lsm6dsl_accel_factory_exit);
|
||||
205
drivers/adsp_factory/icm42605_gyro.c
Normal file
205
drivers/adsp_factory/icm42605_gyro.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "TDK"
|
||||
#define CHIP_ID "ICM42605"
|
||||
#define ST_PASS 1
|
||||
#define ST_FAIL 0
|
||||
#define ST_MAX_DPS 10
|
||||
#define ST_MIN_DPS -10
|
||||
#define ST_MIN_RATIO 50
|
||||
/* Scale Factor = 32768lsb(MAX) / 250dps(ST_FS) */
|
||||
#define ST_SENS_DEF 131
|
||||
#define SELFTEST_REVISED 1
|
||||
|
||||
static ssize_t gyro_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t gyro_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t selftest_revised_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", SELFTEST_REVISED);
|
||||
}
|
||||
|
||||
static ssize_t gyro_power_off(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
pr_info("[FACTORY]: %s\n", __func__);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", 1);
|
||||
}
|
||||
|
||||
static ssize_t gyro_power_on(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
pr_info("[FACTORY]: %s\n", __func__);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", 1);
|
||||
}
|
||||
|
||||
static ssize_t gyro_temp_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_GYRO_TEMP, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_GYRO_TEMP)
|
||||
&& cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_GYRO_TEMP);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "-99\n");
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: gyro_temp = %d\n", __func__,
|
||||
data->msg_buf[MSG_GYRO_TEMP][0]);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
data->msg_buf[MSG_GYRO_TEMP][0]);
|
||||
}
|
||||
|
||||
static ssize_t gyro_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int st_nost_data[3] = {0,};
|
||||
int st_data[3] = {0,};
|
||||
int st_fifo_data[3] = {0,};
|
||||
int st_zro_res = ST_FAIL;
|
||||
int st_ratio_res = ST_FAIL;
|
||||
|
||||
pr_info("[FACTORY] %s - start", __func__);
|
||||
adsp_unicast(NULL, 0, MSG_GYRO, 0, MSG_TYPE_ST_SHOW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_GYRO) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_GYRO);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,%d,%d\n",
|
||||
ST_FAIL, ST_FAIL);
|
||||
}
|
||||
|
||||
/* TDK : selftest fifo data check */
|
||||
st_fifo_data[0] = data->msg_buf[MSG_GYRO][2] / ST_SENS_DEF;
|
||||
st_fifo_data[1] = data->msg_buf[MSG_GYRO][3] / ST_SENS_DEF;
|
||||
st_fifo_data[2] = data->msg_buf[MSG_GYRO][4] / ST_SENS_DEF;
|
||||
|
||||
/* TDK : selftest zro(NOST) check */
|
||||
st_nost_data[0] = data->msg_buf[MSG_GYRO][6] / ST_SENS_DEF;
|
||||
st_nost_data[1] = data->msg_buf[MSG_GYRO][7] / ST_SENS_DEF;
|
||||
st_nost_data[2] = data->msg_buf[MSG_GYRO][8] / ST_SENS_DEF;
|
||||
|
||||
if((ST_MIN_DPS <= st_nost_data[0]) && (st_nost_data[0] <= ST_MAX_DPS)
|
||||
&& (ST_MIN_DPS <= st_nost_data[1]) && (st_nost_data[1] <= ST_MAX_DPS)
|
||||
&& (ST_MIN_DPS <= st_nost_data[2]) && (st_nost_data[2] <= ST_MAX_DPS))
|
||||
st_zro_res = ST_PASS;
|
||||
|
||||
/* TDK : selftest ST data */
|
||||
st_data[0] = data->msg_buf[MSG_GYRO][9] / ST_SENS_DEF;
|
||||
st_data[1] = data->msg_buf[MSG_GYRO][10] / ST_SENS_DEF;
|
||||
st_data[2] = data->msg_buf[MSG_GYRO][11] / ST_SENS_DEF;
|
||||
|
||||
/* TDK : selftest ratio check */
|
||||
if(ST_MIN_RATIO < data->msg_buf[MSG_GYRO][12]
|
||||
&& ST_MIN_RATIO < data->msg_buf[MSG_GYRO][13]
|
||||
&& ST_MIN_RATIO < data->msg_buf[MSG_GYRO][14])
|
||||
st_ratio_res = ST_PASS;
|
||||
|
||||
if (data->msg_buf[MSG_GYRO][1] == ST_FAIL) {
|
||||
pr_info("[FACTORY]: %s - %d,%d,%d\n", __func__,
|
||||
st_fifo_data[0], st_fifo_data[1], st_fifo_data[2]);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
st_fifo_data[0], st_fifo_data[1], st_fifo_data[2]);
|
||||
}
|
||||
|
||||
pr_info("[FACTORY]: %s - %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
||||
__func__,
|
||||
st_fifo_data[0], st_fifo_data[1], st_fifo_data[2],
|
||||
st_nost_data[0], st_nost_data[1], st_nost_data[2],
|
||||
st_data[0], st_data[1], st_data[2],
|
||||
data->msg_buf[MSG_GYRO][12],
|
||||
data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14],
|
||||
st_ratio_res, st_zro_res);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
||||
st_fifo_data[0], st_fifo_data[1], st_fifo_data[2],
|
||||
st_nost_data[0], st_nost_data[1], st_nost_data[2],
|
||||
st_data[0], st_data[1], st_data[2],
|
||||
data->msg_buf[MSG_GYRO][12],
|
||||
data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14],
|
||||
st_ratio_res, st_zro_res);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, 0444, gyro_name_show, NULL);
|
||||
static DEVICE_ATTR(vendor, 0444, gyro_vendor_show, NULL);
|
||||
static DEVICE_ATTR(selftest, 0440, gyro_selftest_show, NULL);
|
||||
static DEVICE_ATTR(power_on, 0444, gyro_power_on, NULL);
|
||||
static DEVICE_ATTR(power_off, 0444, gyro_power_off, NULL);
|
||||
static DEVICE_ATTR(temperature, 0440, gyro_temp_show, NULL);
|
||||
static DEVICE_ATTR(selftest_revised, 0440, selftest_revised_show, NULL);
|
||||
|
||||
static struct device_attribute *gyro_attrs[] = {
|
||||
&dev_attr_name,
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_selftest,
|
||||
&dev_attr_power_on,
|
||||
&dev_attr_power_off,
|
||||
&dev_attr_temperature,
|
||||
&dev_attr_selftest_revised,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init lsm6dsl_gyro_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_GYRO, gyro_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit lsm6dsl_gyro_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_GYRO);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(lsm6dsl_gyro_factory_init);
|
||||
module_exit(lsm6dsl_gyro_factory_exit);
|
||||
235
drivers/adsp_factory/lps22hh_pressure.c
Executable file
235
drivers/adsp_factory/lps22hh_pressure.c
Executable file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "STM"
|
||||
#define CHIP_ID "LPS22HH"
|
||||
|
||||
#define CALIBRATION_FILE_PATH "/efs/FactoryApp/baro_delta"
|
||||
|
||||
#define PR_MAX 8388607 /* 24 bit 2'compl */
|
||||
#define PR_MIN -8388608
|
||||
#define SNS_SUCCESS 0
|
||||
#define ST_PASS 1
|
||||
#define ST_FAIL 0
|
||||
|
||||
static int sea_level_pressure;
|
||||
static int pressure_cal;
|
||||
|
||||
static ssize_t pressure_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t pressure_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t sea_level_pressure_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", sea_level_pressure);
|
||||
}
|
||||
|
||||
static ssize_t sea_level_pressure_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
if (sscanf(buf, "%10d", &sea_level_pressure) != 1) {
|
||||
pr_err("[FACTORY] %s: sscanf error\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
sea_level_pressure = sea_level_pressure / 100;
|
||||
|
||||
pr_info("[FACTORY] %s: sea_level_pressure = %d\n", __func__,
|
||||
sea_level_pressure);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
int pressure_open_calibration(struct adsp_data *data)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
*/
|
||||
|
||||
static ssize_t pressure_cabratioin_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int temp = 0, error = 0;
|
||||
|
||||
error = kstrtoint(buf, 10, &temp);
|
||||
if (error < 0) {
|
||||
pr_err("[FACTORY] %s : kstrtoint failed.(%d)", __func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (temp < PR_MIN || temp > PR_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
pressure_cal = temp;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t pressure_cabratioin_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
//struct adsp_data *data = dev_get_drvdata(dev);
|
||||
|
||||
//pressure_open_calibration(data);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pressure_cal);
|
||||
}
|
||||
|
||||
static ssize_t temperature_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_PRESSURE_TEMP, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] &
|
||||
1 << MSG_PRESSURE_TEMP) && cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_PRESSURE_TEMP);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "-99\n");
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
data->msg_buf[MSG_PRESSURE_TEMP][0]);
|
||||
}
|
||||
|
||||
static ssize_t selftest_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_PRESSURE, 0, MSG_TYPE_ST_SHOW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &
|
||||
1 << MSG_PRESSURE) && cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_PRESSURE);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s : P:%d, T:%d, RES:%d\n",
|
||||
__func__, data->msg_buf[MSG_PRESSURE][0],
|
||||
data->msg_buf[MSG_PRESSURE][1], data->msg_buf[MSG_PRESSURE][2]);
|
||||
|
||||
if (SNS_SUCCESS == data->msg_buf[MSG_PRESSURE][2])
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ST_PASS);
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ST_FAIL);
|
||||
}
|
||||
|
||||
static ssize_t pressure_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_PRESSURE, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_PRESSURE) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_PRESSURE);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
} else {
|
||||
pr_info("[FACTORY] %s - THS_P_L/H: %02x/%02x, WHOAMI: %02x, CTRL1/2/3: %02x/%02x/%02x, STATUS: %02x, DATA: %02x/%02x/%02x/%02x/%02x\n",
|
||||
__func__,
|
||||
data->msg_buf[MSG_PRESSURE][0], data->msg_buf[MSG_PRESSURE][1],
|
||||
data->msg_buf[MSG_PRESSURE][2], data->msg_buf[MSG_PRESSURE][9],
|
||||
data->msg_buf[MSG_PRESSURE][3], data->msg_buf[MSG_PRESSURE][4],
|
||||
data->msg_buf[MSG_PRESSURE][5], data->msg_buf[MSG_PRESSURE][6],
|
||||
data->msg_buf[MSG_PRESSURE][7], data->msg_buf[MSG_PRESSURE][8],
|
||||
data->msg_buf[MSG_PRESSURE][10], data->msg_buf[MSG_PRESSURE][11]);
|
||||
|
||||
pr_info("[FACTORY] %s - 4Fh:%02x, 50h:%02x, 51h:%02x, 52h:%02x, 53h:%02x, 54h:%02x, 4Dh:%02x, 4Ah:%02x",
|
||||
__func__,
|
||||
data->msg_buf[MSG_PRESSURE][12], data->msg_buf[MSG_PRESSURE][13],
|
||||
data->msg_buf[MSG_PRESSURE][14], data->msg_buf[MSG_PRESSURE][15],
|
||||
data->msg_buf[MSG_PRESSURE][16], data->msg_buf[MSG_PRESSURE][17],
|
||||
data->msg_buf[MSG_PRESSURE][18], data->msg_buf[MSG_PRESSURE][19]);
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", "Done");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vendor, 0444, pressure_vendor_show, NULL);
|
||||
static DEVICE_ATTR(name, 0444, pressure_name_show, NULL);
|
||||
static DEVICE_ATTR(calibration, 0664,
|
||||
pressure_cabratioin_show, pressure_cabratioin_store);
|
||||
static DEVICE_ATTR(sea_level_pressure, 0664,
|
||||
sea_level_pressure_show, sea_level_pressure_store);
|
||||
static DEVICE_ATTR(temperature, 0444, temperature_show, NULL);
|
||||
static DEVICE_ATTR(selftest, 0444, selftest_show, NULL);
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0444,
|
||||
pressure_dhr_sensor_info_show, NULL);
|
||||
#else
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0440,
|
||||
pressure_dhr_sensor_info_show, NULL);
|
||||
#endif
|
||||
|
||||
static struct device_attribute *pressure_attrs[] = {
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_name,
|
||||
&dev_attr_calibration,
|
||||
&dev_attr_sea_level_pressure,
|
||||
&dev_attr_temperature,
|
||||
&dev_attr_selftest,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init lps22hh_pressure_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_PRESSURE, pressure_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit lps22hh_pressure_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_PRESSURE);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(lps22hh_pressure_factory_init);
|
||||
module_exit(lps22hh_pressure_factory_exit);
|
||||
568
drivers/adsp_factory/lsm6dsl_accel.c
Normal file
568
drivers/adsp_factory/lsm6dsl_accel.c
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
#include <linux/adsp/slpi_motor.h>
|
||||
#endif
|
||||
#define VENDOR "STM"
|
||||
#define CHIP_ID "LSM6DSL"
|
||||
#define ACCEL_ST_TRY_CNT 3
|
||||
#define ACCEL_FACTORY_CAL_CNT 20
|
||||
#define ACCEL_RAW_DATA_CNT 3
|
||||
#define MAX_ACCEL_1G 4096
|
||||
|
||||
/* Haptic Pattern A vibrate during 7ms.
|
||||
* touch, touchkey, operation feedback use this.
|
||||
* Do not call motor_workfunc when duration is 7ms.
|
||||
*/
|
||||
#define DURATION_SKIP 10
|
||||
#define MOTOR_OFF 0
|
||||
|
||||
#define ACCEL_FACTORY_CAL_PATH "/efs/FactoryApp/accel_factory_cal"
|
||||
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
struct accel_motor_data {
|
||||
struct workqueue_struct *slpi_motor_wq;
|
||||
struct work_struct work_slpi_motor;
|
||||
int motor_state;
|
||||
};
|
||||
|
||||
struct accel_motor_data *pdata_motor;
|
||||
#endif
|
||||
|
||||
struct accel_data {
|
||||
struct work_struct work_accel;
|
||||
struct workqueue_struct *accel_wq;
|
||||
struct adsp_data *dev_data;
|
||||
bool is_complete_cal;
|
||||
int32_t raw_data[ACCEL_RAW_DATA_CNT];
|
||||
int32_t avg_data[ACCEL_RAW_DATA_CNT];
|
||||
};
|
||||
|
||||
static struct accel_data *pdata;
|
||||
|
||||
static ssize_t accel_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t accel_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t sensor_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", "ADSP");
|
||||
}
|
||||
|
||||
int get_accel_cal_data(int32_t *cal_data)
|
||||
{
|
||||
struct file *factory_cal_filp = NULL;
|
||||
mm_segment_t old_fs;
|
||||
int ret = 0;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH,
|
||||
O_RDONLY, 0440);
|
||||
|
||||
if (IS_ERR(factory_cal_filp)) {
|
||||
set_fs(old_fs);
|
||||
ret = PTR_ERR(factory_cal_filp);
|
||||
pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vfs_read(factory_cal_filp, (char *)cal_data,
|
||||
ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos);
|
||||
if (ret < 0) {
|
||||
pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret);
|
||||
filp_close(factory_cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
filp_close(factory_cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_accel_cal_data(int32_t *cal_data, bool first_booting)
|
||||
{
|
||||
struct file *factory_cal_filp = NULL;
|
||||
mm_segment_t old_fs;
|
||||
int flag, ret = 0;
|
||||
umode_t mode = 0;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
if (first_booting) {
|
||||
flag = O_TRUNC | O_RDWR | O_CREAT;
|
||||
mode = 0600;
|
||||
} else {
|
||||
flag = O_RDWR;
|
||||
mode = 0660;
|
||||
}
|
||||
|
||||
factory_cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, flag, mode);
|
||||
|
||||
if (IS_ERR(factory_cal_filp)) {
|
||||
set_fs(old_fs);
|
||||
ret = PTR_ERR(factory_cal_filp);
|
||||
pr_err("[FACTORY] %s: open fail accel_factory_cal:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vfs_write(factory_cal_filp, (char *)cal_data,
|
||||
ACCEL_RAW_DATA_CNT * sizeof(int32_t), &factory_cal_filp->f_pos);
|
||||
if (ret < 0)
|
||||
pr_err("[FACTORY] %s: fd write %d\n", __func__, ret);
|
||||
|
||||
filp_close(factory_cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
|
||||
adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t accel_calibration_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int32_t cal_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
|
||||
if (get_accel_cal_data(cal_data) > 0) {
|
||||
pr_info("[FACTORY] %s: %d, %d, %d\n", __func__,
|
||||
cal_data[0], cal_data[1], cal_data[2]);
|
||||
if (cal_data[0] == 0 && cal_data[1] == 0 && cal_data[2] == 0)
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n",
|
||||
0, 0, 0, 0);
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n",
|
||||
true, cal_data[0], cal_data[1], cal_data[2]);
|
||||
} else {
|
||||
pr_err("[FACTORY] %s: get_accel_cal_data fail\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t accel_calibration_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
|
||||
pdata->dev_data = data;
|
||||
if (sysfs_streq(buf, "0")) {
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
memset(pdata->avg_data, 0, sizeof(pdata->avg_data));
|
||||
set_accel_cal_data(pdata->avg_data, false);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
} else {
|
||||
pdata->is_complete_cal = false;
|
||||
queue_work(pdata->accel_wq, &pdata->work_accel);
|
||||
while (pdata->is_complete_cal == false) {
|
||||
pr_info("[FACTORY] %s: In factory cal\n", __func__);
|
||||
msleep(20);
|
||||
}
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
set_accel_cal_data(pdata->avg_data, false);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void accel_work_func(struct work_struct *work)
|
||||
{
|
||||
struct accel_data *data = container_of((struct work_struct *)work,
|
||||
struct accel_data, work_accel);
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->dev_data->accel_factory_mutex);
|
||||
memset(pdata->avg_data, 0, sizeof(pdata->avg_data));
|
||||
adsp_unicast(pdata->avg_data, sizeof(pdata->avg_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
msleep(30); /* for init of bias */
|
||||
for (i = 0; i < ACCEL_FACTORY_CAL_CNT; i++) {
|
||||
msleep(20);
|
||||
get_accel_raw_data(pdata->raw_data);
|
||||
pdata->avg_data[0] += pdata->raw_data[0];
|
||||
pdata->avg_data[1] += pdata->raw_data[1];
|
||||
pdata->avg_data[2] += pdata->raw_data[2];
|
||||
pr_info("[FACTORY] %s: %d, %d, %d\n", __func__,
|
||||
pdata->raw_data[0], pdata->raw_data[1], pdata->raw_data[2]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ACCEL_RAW_DATA_CNT; i++) {
|
||||
pdata->avg_data[i] /= ACCEL_FACTORY_CAL_CNT;
|
||||
pr_err("[FACTORY] %s: avg : %d\n", __func__, pdata->avg_data[i]);
|
||||
}
|
||||
|
||||
if (pdata->avg_data[2] > 0)
|
||||
pdata->avg_data[2] -= MAX_ACCEL_1G;
|
||||
else if (pdata->avg_data[2] < 0)
|
||||
pdata->avg_data[2] += MAX_ACCEL_1G;
|
||||
|
||||
mutex_unlock(&data->dev_data->accel_factory_mutex);
|
||||
pdata->is_complete_cal = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t accel_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int retry = 0;
|
||||
|
||||
RETRY_ACCEL_SELFTEST:
|
||||
adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_ST_SHOW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_ACCEL);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
data->msg_buf[MSG_ACCEL][1] = -1;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s : init = %d, result = %d, XYZ = %d, %d, %d, nXYZ = %d, %d, %d\n",
|
||||
__func__, data->msg_buf[MSG_ACCEL][0],
|
||||
data->msg_buf[MSG_ACCEL][1], data->msg_buf[MSG_ACCEL][2],
|
||||
data->msg_buf[MSG_ACCEL][3], data->msg_buf[MSG_ACCEL][4],
|
||||
data->msg_buf[MSG_ACCEL][5], data->msg_buf[MSG_ACCEL][6],
|
||||
data->msg_buf[MSG_ACCEL][7]);
|
||||
|
||||
if (data->msg_buf[MSG_ACCEL][1] == 1) {
|
||||
pr_info("[FACTORY] %s : Pass - result = %d, retry = %d\n",
|
||||
__func__, data->msg_buf[MSG_ACCEL][1], retry);
|
||||
} else {
|
||||
data->msg_buf[MSG_ACCEL][1] = -5;
|
||||
pr_err("[FACTORY] %s : Fail - result = %d, retry = %d\n",
|
||||
__func__, data->msg_buf[MSG_ACCEL][1], retry);
|
||||
|
||||
if (retry < ACCEL_ST_TRY_CNT &&
|
||||
data->msg_buf[MSG_ACCEL][2] == 0) {
|
||||
retry++;
|
||||
msleep(200);
|
||||
pr_info("[FACTORY] %s: retry\n", __func__);
|
||||
goto RETRY_ACCEL_SELFTEST;
|
||||
}
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_ACCEL][1],
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][2]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][3]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][4]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][5]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][6]),
|
||||
(int)abs(data->msg_buf[MSG_ACCEL][7]));
|
||||
}
|
||||
|
||||
static ssize_t accel_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
get_accel_raw_data(raw_data);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
raw_data[0], raw_data[1], raw_data[2]);
|
||||
}
|
||||
|
||||
static ssize_t accel_reactive_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
bool success = false;
|
||||
int32_t raw_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
get_accel_raw_data(raw_data);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
if (raw_data[0] != 0 || raw_data[1] != 0 || raw_data[2] != 0)
|
||||
success = true;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", success);
|
||||
}
|
||||
|
||||
static ssize_t accel_reactive_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
if (sysfs_streq(buf, "1"))
|
||||
pr_info("[FACTORY]: %s - on\n", __func__);
|
||||
else if (sysfs_streq(buf, "0"))
|
||||
pr_info("[FACTORY]: %s - off\n", __func__);
|
||||
else if (sysfs_streq(buf, "2"))
|
||||
pr_info("[FACTORY]: %s - factory\n", __func__);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t accel_lowpassfilter_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int32_t msg_buf;
|
||||
|
||||
if (sysfs_streq(buf, "1")) {
|
||||
msg_buf = 1;
|
||||
} else if (sysfs_streq(buf, "0")) {
|
||||
msg_buf = 0;
|
||||
} else {
|
||||
pr_info("[FACTORY] %s: wrong value\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
adsp_unicast(&msg_buf, sizeof(int32_t), MSG_ACCEL, 0, MSG_TYPE_SET_ACCEL_LPF);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_SET_ACCEL_LPF] &= ~(1 << MSG_ACCEL);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: lpf_on_off done (%d)(0x%x)\n", __func__,
|
||||
data->msg_buf[MSG_ACCEL][0], data->msg_buf[MSG_ACCEL][1]);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
int setSensorCallback(int state, int duration)
|
||||
{
|
||||
if (duration > MOTOR_OFF && duration <= DURATION_SKIP)
|
||||
return 0;
|
||||
|
||||
if (pdata_motor->motor_state != state) {
|
||||
pr_info("[FACTORY] %s: state = %d, duration = %d\n",
|
||||
__func__, pdata_motor->motor_state, duration);
|
||||
pdata_motor->motor_state = state;
|
||||
queue_work(pdata_motor->slpi_motor_wq, &pdata_motor->work_slpi_motor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void slpi_motor_work_func(struct work_struct *work)
|
||||
{
|
||||
#if 0
|
||||
struct msg_data message;
|
||||
int motor = 0;
|
||||
|
||||
if (pdata_motor->motor_state == 1) {
|
||||
motor = MSG_TYPE_ACCEL_MOTOR_ON;
|
||||
message.msg_type = MSG_ACCEL_MOT_ON;
|
||||
} else if (pdata_motor->motor_state == 0) {
|
||||
motor = MSG_TYPE_ACCEL_MOTOR_OFF;
|
||||
message.msg_type = MSG_ACCEL_MOT_OFF;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: state = %d\n", __func__, pdata_motor->motor_state);
|
||||
|
||||
adsp_unicast(&message, sizeof(message), motor, 0, 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t accel_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
char ctrl1_xl = 0;
|
||||
uint8_t fullscale = 0;
|
||||
int32_t *info = data->msg_buf[MSG_ACCEL];
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_ACCEL);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
ctrl1_xl = *info;
|
||||
|
||||
ctrl1_xl &= 0xC;
|
||||
|
||||
switch (ctrl1_xl) {
|
||||
case 0xC:
|
||||
fullscale = 8;
|
||||
break;
|
||||
case 0x8:
|
||||
fullscale = 4;
|
||||
break;
|
||||
case 0x4:
|
||||
fullscale = 16;
|
||||
break;
|
||||
case 0:
|
||||
fullscale = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: f/s %u\n", __func__, fullscale);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "\"FULL_SCALE\":\"%uG\"\n", fullscale);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, 0444, accel_name_show, NULL);
|
||||
static DEVICE_ATTR(vendor, 0444, accel_vendor_show, NULL);
|
||||
static DEVICE_ATTR(type, 0444, sensor_type_show, NULL);
|
||||
static DEVICE_ATTR(calibration, 0664,
|
||||
accel_calibration_show, accel_calibration_store);
|
||||
static DEVICE_ATTR(selftest, 0440,
|
||||
accel_selftest_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, accel_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(reactive_alert, 0664,
|
||||
accel_reactive_show, accel_reactive_store);
|
||||
static DEVICE_ATTR(lowpassfilter, 0220,
|
||||
NULL, accel_lowpassfilter_store);
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0444,
|
||||
accel_dhr_sensor_info_show, NULL);
|
||||
#else
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0440,
|
||||
accel_dhr_sensor_info_show, NULL);
|
||||
#endif
|
||||
|
||||
static struct device_attribute *acc_attrs[] = {
|
||||
&dev_attr_name,
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_type,
|
||||
&dev_attr_calibration,
|
||||
&dev_attr_selftest,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_reactive_alert,
|
||||
&dev_attr_lowpassfilter,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void accel_factory_init_work(void)
|
||||
{
|
||||
struct file *cal_filp = NULL;
|
||||
mm_segment_t old_fs;
|
||||
int32_t zero_data[ACCEL_RAW_DATA_CNT] = {0, };
|
||||
int ret = 0;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
cal_filp = filp_open(ACCEL_FACTORY_CAL_PATH, O_RDONLY, 0440);
|
||||
if (PTR_ERR(cal_filp) == -ENOENT || PTR_ERR(cal_filp) == -ENXIO) {
|
||||
pr_info("[FACTORY] %s : no accel_factory_cal file\n", __func__);
|
||||
set_fs(old_fs);
|
||||
set_accel_cal_data(zero_data, true);
|
||||
} else if (IS_ERR(cal_filp)) {
|
||||
pr_err("[FACTORY]: %s - filp_open error\n", __func__);
|
||||
set_fs(old_fs);
|
||||
} else {
|
||||
pr_info("[FACTORY] %s : already exist\n", __func__);
|
||||
ret = vfs_read(cal_filp, (char *)zero_data,
|
||||
ACCEL_RAW_DATA_CNT * sizeof(int32_t), &cal_filp->f_pos);
|
||||
if (ret < 0) {
|
||||
pr_err("[FACTORY] %s: fd read fail:%d\n", __func__, ret);
|
||||
zero_data[0] = zero_data[1] = zero_data[2] = 0;
|
||||
adsp_unicast(zero_data, sizeof(zero_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
filp_close(cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
return;
|
||||
}
|
||||
adsp_unicast(zero_data, sizeof(zero_data),
|
||||
MSG_ACCEL, 0, MSG_TYPE_SET_CAL_DATA);
|
||||
filp_close(cal_filp, current->files);
|
||||
set_fs(old_fs);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init lsm6dsl_accel_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_ACCEL, acc_attrs);
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
pdata_motor = kzalloc(sizeof(*pdata_motor), GFP_KERNEL);
|
||||
|
||||
if (pdata_motor == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata_motor->slpi_motor_wq =
|
||||
create_singlethread_workqueue("slpi_motor_wq");
|
||||
|
||||
if (pdata_motor->slpi_motor_wq == NULL) {
|
||||
pr_err("[FACTORY]: %s - could not create motor wq\n", __func__);
|
||||
kfree(pdata_motor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&pdata_motor->work_slpi_motor, slpi_motor_work_func);
|
||||
|
||||
pdata_motor->motor_state = 0;
|
||||
#endif
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
pdata->accel_wq = create_singlethread_workqueue("accel_wq");
|
||||
INIT_WORK(&pdata->work_accel, accel_work_func);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit lsm6dsl_accel_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_ACCEL);
|
||||
#ifdef CONFIG_SLPI_MOTOR
|
||||
if (pdata_motor != NULL && pdata_motor->slpi_motor_wq != NULL) {
|
||||
cancel_work_sync(&pdata_motor->work_slpi_motor);
|
||||
destroy_workqueue(pdata_motor->slpi_motor_wq);
|
||||
pdata_motor->slpi_motor_wq = NULL;
|
||||
}
|
||||
#endif
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
module_init(lsm6dsl_accel_factory_init);
|
||||
module_exit(lsm6dsl_accel_factory_exit);
|
||||
194
drivers/adsp_factory/lsm6dsl_gyro.c
Normal file
194
drivers/adsp_factory/lsm6dsl_gyro.c
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "STM"
|
||||
#define CHIP_ID "LSM6DSL"
|
||||
#define ST_PASS 1
|
||||
#define ST_FAIL 0
|
||||
#define ST_ZRO_MIN (-40)
|
||||
#define ST_ZRO_MAX 40
|
||||
#define SELFTEST_REVISED 1
|
||||
|
||||
static ssize_t gyro_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t gyro_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t selftest_revised_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", SELFTEST_REVISED);
|
||||
}
|
||||
|
||||
static ssize_t gyro_power_off(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
pr_info("[FACTORY]: %s\n", __func__);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", 1);
|
||||
}
|
||||
|
||||
static ssize_t gyro_power_on(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
pr_info("[FACTORY]: %s\n", __func__);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", 1);
|
||||
}
|
||||
|
||||
static ssize_t gyro_temp_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_GYRO_TEMP, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_GYRO_TEMP)
|
||||
&& cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_GYRO_TEMP);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "-99\n");
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: gyro_temp = %d\n", __func__,
|
||||
data->msg_buf[MSG_GYRO_TEMP][0]);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
data->msg_buf[MSG_GYRO_TEMP][0]);
|
||||
}
|
||||
|
||||
static ssize_t gyro_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int st_diff_res = ST_FAIL;
|
||||
int st_zro_res = ST_FAIL;
|
||||
|
||||
pr_info("[FACTORY] %s - start", __func__);
|
||||
adsp_unicast(NULL, 0, MSG_GYRO, 0, MSG_TYPE_ST_SHOW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_ST_SHOW_DATA] & 1 << MSG_GYRO) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
msleep(20);
|
||||
|
||||
data->ready_flag[MSG_TYPE_ST_SHOW_DATA] &= ~(1 << MSG_GYRO);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,%d,%d\n",
|
||||
ST_FAIL, ST_FAIL);
|
||||
}
|
||||
|
||||
if (data->msg_buf[MSG_GYRO][1] != 0) {
|
||||
pr_info("[FACTORY] %s - failed(%d, %d)\n", __func__,
|
||||
data->msg_buf[MSG_GYRO][1],
|
||||
data->msg_buf[MSG_GYRO][5]);
|
||||
|
||||
pr_info("[FACTORY]: %s - %d,%d,%d\n", __func__,
|
||||
data->msg_buf[MSG_GYRO][2],
|
||||
data->msg_buf[MSG_GYRO][3],
|
||||
data->msg_buf[MSG_GYRO][4]);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
data->msg_buf[MSG_GYRO][2],
|
||||
data->msg_buf[MSG_GYRO][3],
|
||||
data->msg_buf[MSG_GYRO][4]);
|
||||
}
|
||||
|
||||
if (!data->msg_buf[MSG_GYRO][5])
|
||||
st_diff_res = ST_PASS;
|
||||
|
||||
if((ST_ZRO_MIN <= data->msg_buf[MSG_GYRO][6])
|
||||
&& (data->msg_buf[MSG_GYRO][6] <= ST_ZRO_MAX)
|
||||
&& (ST_ZRO_MIN <= data->msg_buf[MSG_GYRO][7])
|
||||
&& (data->msg_buf[MSG_GYRO][7] <= ST_ZRO_MAX)
|
||||
&& (ST_ZRO_MIN <= data->msg_buf[MSG_GYRO][8])
|
||||
&& (data->msg_buf[MSG_GYRO][8]<= ST_ZRO_MAX))
|
||||
st_zro_res = ST_PASS;
|
||||
|
||||
pr_info("[FACTORY]: %s - %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
||||
__func__,
|
||||
data->msg_buf[MSG_GYRO][2], data->msg_buf[MSG_GYRO][3],
|
||||
data->msg_buf[MSG_GYRO][4], data->msg_buf[MSG_GYRO][6],
|
||||
data->msg_buf[MSG_GYRO][7], data->msg_buf[MSG_GYRO][8],
|
||||
data->msg_buf[MSG_GYRO][9], data->msg_buf[MSG_GYRO][10],
|
||||
data->msg_buf[MSG_GYRO][11], data->msg_buf[MSG_GYRO][12],
|
||||
data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14],
|
||||
st_diff_res, st_zro_res);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_GYRO][2], data->msg_buf[MSG_GYRO][3],
|
||||
data->msg_buf[MSG_GYRO][4], data->msg_buf[MSG_GYRO][6],
|
||||
data->msg_buf[MSG_GYRO][7], data->msg_buf[MSG_GYRO][8],
|
||||
data->msg_buf[MSG_GYRO][9], data->msg_buf[MSG_GYRO][10],
|
||||
data->msg_buf[MSG_GYRO][11], data->msg_buf[MSG_GYRO][12],
|
||||
data->msg_buf[MSG_GYRO][13], data->msg_buf[MSG_GYRO][14],
|
||||
st_diff_res, st_zro_res);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, 0444, gyro_name_show, NULL);
|
||||
static DEVICE_ATTR(vendor, 0444, gyro_vendor_show, NULL);
|
||||
static DEVICE_ATTR(selftest, 0440, gyro_selftest_show, NULL);
|
||||
static DEVICE_ATTR(power_on, 0444, gyro_power_on, NULL);
|
||||
static DEVICE_ATTR(power_off, 0444, gyro_power_off, NULL);
|
||||
static DEVICE_ATTR(temperature, 0440, gyro_temp_show, NULL);
|
||||
static DEVICE_ATTR(selftest_revised, 0440, selftest_revised_show, NULL);
|
||||
|
||||
static struct device_attribute *gyro_attrs[] = {
|
||||
&dev_attr_name,
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_selftest,
|
||||
&dev_attr_power_on,
|
||||
&dev_attr_power_off,
|
||||
&dev_attr_temperature,
|
||||
&dev_attr_selftest_revised,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init lsm6dsl_gyro_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_GYRO, gyro_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit lsm6dsl_gyro_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_GYRO);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(lsm6dsl_gyro_factory_init);
|
||||
module_exit(lsm6dsl_gyro_factory_exit);
|
||||
342
drivers/adsp_factory/ssc_core.c
Normal file
342
drivers/adsp_factory/ssc_core.c
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/adsp/slpi_motor.h>
|
||||
#include <linux/adsp/ssc_ssr_reason.h>
|
||||
|
||||
#ifdef CONFIG_SUPPORT_DEVICE_MODE
|
||||
#include <linux/hall.h>
|
||||
#endif
|
||||
#include "adsp.h"
|
||||
|
||||
#define SSR_REASON_LEN 128
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
#define SLPI_STUCK "SLPI_STUCK"
|
||||
#define SLPI_PASS "SLPI_PASS"
|
||||
#endif
|
||||
|
||||
static int pid;
|
||||
static char panic_msg[SSR_REASON_LEN];
|
||||
/*************************************************************************/
|
||||
/* factory Sysfs */
|
||||
/*************************************************************************/
|
||||
|
||||
static char operation_mode_flag[11];
|
||||
|
||||
static ssize_t dumpstate_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int32_t type[1];
|
||||
if (pid != 0) {
|
||||
adsp_unicast(NULL, 0, MSG_ACCEL, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_ACCEL) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_ACCEL);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: accel dhr_sensor_info Timeout!!!\n",
|
||||
__func__);
|
||||
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: light dhr_sensor_info Timeout!!!\n",
|
||||
__func__);
|
||||
|
||||
pr_info("[FACTORY] to take the logs\n");
|
||||
type[0] = 0;
|
||||
} else {
|
||||
type[0] = 2;
|
||||
pr_info("[FACTORY] logging service was stopped %d\n", pid);
|
||||
}
|
||||
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
|
||||
return snprintf(buf, PAGE_SIZE, "SSC_CORE\n");
|
||||
}
|
||||
|
||||
static ssize_t operation_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s", operation_mode_flag);
|
||||
}
|
||||
|
||||
static ssize_t operation_mode_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 10 && buf[i] != '\0'; i++)
|
||||
operation_mode_flag[i] = buf[i];
|
||||
operation_mode_flag[i] = '\0';
|
||||
|
||||
pr_info("[FACTORY] %s: operation_mode_flag = %s\n", __func__,
|
||||
operation_mode_flag);
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned long timeout;
|
||||
unsigned long timeout_2;
|
||||
int32_t type[1];
|
||||
|
||||
if (pid != 0) {
|
||||
timeout = jiffies + (2 * HZ);
|
||||
timeout_2 = jiffies + (10 * HZ);
|
||||
type[0] = 1;
|
||||
pr_info("[FACTORY] To stop logging %d\n", pid);
|
||||
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
|
||||
|
||||
while (pid != 0) {
|
||||
msleep(25);
|
||||
if (time_after(jiffies, timeout))
|
||||
pr_info("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
if (time_after(jiffies, timeout_2)) {
|
||||
// panic("force crash : ssc core\n");
|
||||
pr_info("[FACTORY] pid %d\n", pid);
|
||||
return snprintf(buf, PAGE_SIZE, "1\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t mode_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int data = 0;
|
||||
int32_t type[1];
|
||||
if (kstrtoint(buf, 10, &data)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (data != 1) {
|
||||
pr_err("[FACTORY] %s: data was wrong\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pid != 0) {
|
||||
type[0] = 1;
|
||||
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t pid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pid);
|
||||
}
|
||||
|
||||
static ssize_t pid_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int data = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &data)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pid = data;
|
||||
pr_info("[FACTORY] %s: pid %d\n", __func__, pid);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t remove_sensor_sysfs_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
unsigned int type = MSG_SENSOR_MAX;
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (kstrtouint(buf, 10, &type)) {
|
||||
pr_err("[FACTORY] %s: kstrtouint fail\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (type > PHYSICAL_SENSOR_SYSFS) {
|
||||
pr_err("[FACTORY] %s: Invalid type %u\n", __func__, type);
|
||||
return size;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: type = %u\n", __func__, type);
|
||||
|
||||
mutex_lock(&data->remove_sysfs_mutex);
|
||||
adsp_factory_unregister(type);
|
||||
mutex_unlock(&data->remove_sysfs_mutex);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
extern unsigned int sec_hw_rev(void);
|
||||
int ssc_system_rev_test;
|
||||
static ssize_t ssc_hw_rev_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
pr_info("[FACTORY] %s: ssc_rev:%d\n", __func__, sec_hw_rev());
|
||||
|
||||
if (!ssc_system_rev_test)
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", sec_hw_rev());
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ssc_system_rev_test);
|
||||
}
|
||||
|
||||
static ssize_t ssc_hw_rev_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
if (kstrtoint(buf, 10, &ssc_system_rev_test)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_info("[FACTORY] %s: system_rev_ssc %d\n", __func__, ssc_system_rev_test);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void ssr_reason_call_back(char reason[], int len)
|
||||
{
|
||||
if (len <= 0) {
|
||||
pr_info("[FACTORY] ssr %d\n", len);
|
||||
return;
|
||||
}
|
||||
memset(panic_msg, 0, SSR_REASON_LEN);
|
||||
strlcpy(panic_msg, reason, min(len, (int)(SSR_REASON_LEN - 1)));
|
||||
|
||||
pr_info("[FACTORY] ssr %s\n", panic_msg);
|
||||
}
|
||||
|
||||
static ssize_t ssr_msg_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int32_t raw_data_acc[3] = {0, };
|
||||
int32_t raw_data_mag[3] = {0, };
|
||||
int ret_acc = 0;
|
||||
int ret_mag = 0;
|
||||
|
||||
mutex_lock(&data->accel_factory_mutex);
|
||||
ret_acc = get_accel_raw_data(raw_data_acc);
|
||||
mutex_unlock(&data->accel_factory_mutex);
|
||||
|
||||
ret_mag = get_mag_raw_data(raw_data_mag);
|
||||
|
||||
pr_info("[FACTORY] %s: accel(%d, %d, %d), mag(%d, %d, %d)\n", __func__,
|
||||
raw_data_acc[0], raw_data_acc[1], raw_data_acc[2],
|
||||
raw_data_mag[0], raw_data_mag[1], raw_data_mag[2]);
|
||||
|
||||
if (ret_acc == -1 && ret_mag == -1) {
|
||||
pr_err("[FACTORY] %s: SLPI stuck, check hal log\n", __func__);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", SLPI_STUCK);
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", SLPI_PASS);
|
||||
}
|
||||
#else
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", panic_msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ssize_t ssr_reset_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int32_t type[1];
|
||||
|
||||
type[0] = 0xff;
|
||||
|
||||
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", "Success");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUPPORT_DEVICE_MODE
|
||||
int sns_device_mode_notify(struct notifier_block *nb,
|
||||
unsigned long flip_state, void *v)
|
||||
{
|
||||
int state = (int)flip_state;
|
||||
|
||||
pr_info("[FACTORY] %s - device mode %d", __func__, state);
|
||||
if(state == 0)
|
||||
adsp_unicast(NULL, 0, MSG_SSC_CORE, 0, MSG_TYPE_FACTORY_ENABLE);
|
||||
else
|
||||
adsp_unicast(NULL, 0, MSG_SSC_CORE, 0, MSG_TYPE_FACTORY_DISABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block sns_device_mode_notifier = {
|
||||
.notifier_call = sns_device_mode_notify,
|
||||
.priority = 1,
|
||||
};
|
||||
#endif
|
||||
|
||||
static DEVICE_ATTR(dumpstate, 0440, dumpstate_show, NULL);
|
||||
static DEVICE_ATTR(operation_mode, 0664,
|
||||
operation_mode_show, operation_mode_store);
|
||||
static DEVICE_ATTR(mode, 0660, mode_show, mode_store);
|
||||
static DEVICE_ATTR(ssc_pid, 0660, pid_show, pid_store);
|
||||
static DEVICE_ATTR(remove_sysfs, 0220, NULL, remove_sensor_sysfs_store);
|
||||
static DEVICE_ATTR(ssc_hw_rev, 0664, ssc_hw_rev_show, ssc_hw_rev_store);
|
||||
static DEVICE_ATTR(ssr_msg, 0440, ssr_msg_show, NULL);
|
||||
static DEVICE_ATTR(ssr_reset, 0440, ssr_reset_show, NULL);
|
||||
|
||||
static struct device_attribute *core_attrs[] = {
|
||||
&dev_attr_dumpstate,
|
||||
&dev_attr_operation_mode,
|
||||
&dev_attr_mode,
|
||||
&dev_attr_ssc_pid,
|
||||
&dev_attr_remove_sysfs,
|
||||
&dev_attr_ssc_hw_rev,
|
||||
&dev_attr_ssr_msg,
|
||||
&dev_attr_ssr_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init core_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_SSC_CORE, core_attrs);
|
||||
#ifdef CONFIG_SUPPORT_DEVICE_MODE
|
||||
hall_ic_register_notify(&sns_device_mode_notifier);
|
||||
#endif
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit core_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_SSC_CORE);
|
||||
#ifdef CONFIG_SUPPORT_DEVICE_MODE
|
||||
hall_ic_unregister_notify(&sns_device_mode_notifier);
|
||||
#endif
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
module_init(core_factory_init);
|
||||
module_exit(core_factory_exit);
|
||||
118
drivers/adsp_factory/vcnl36658_light.c
Executable file
118
drivers/adsp_factory/vcnl36658_light.c
Executable file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dirent.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "CAPELLA"
|
||||
#define CHIP_ID "VCNL36658"
|
||||
|
||||
/*************************************************************************/
|
||||
/* factory Sysfs */
|
||||
/*************************************************************************/
|
||||
static ssize_t light_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t light_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t light_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
mutex_lock(&data->light_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return snprintf(buf, PAGE_SIZE, "0,0,0,0,0,0\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_LIGHT][0], data->msg_buf[MSG_LIGHT][1],
|
||||
data->msg_buf[MSG_LIGHT][2], data->msg_buf[MSG_LIGHT][3],
|
||||
data->msg_buf[MSG_LIGHT][4], data->msg_buf[MSG_LIGHT][5]);
|
||||
}
|
||||
|
||||
static ssize_t light_get_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
pr_info("[FACTORY] %s: start\n", __func__);
|
||||
mutex_lock(&data->light_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return data->msg_buf[MSG_LIGHT][0];
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vendor, 0444, light_vendor_show, NULL);
|
||||
static DEVICE_ATTR(name, 0444, light_name_show, NULL);
|
||||
static DEVICE_ATTR(lux, 0444, light_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, light_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0444, light_get_dhr_sensor_info_show, NULL);
|
||||
|
||||
static struct device_attribute *light_attrs[] = {
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_name,
|
||||
&dev_attr_lux,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init veml3328_light_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_LIGHT, light_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit veml3328_light_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_LIGHT);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
module_init(veml3328_light_factory_init);
|
||||
module_exit(veml3328_light_factory_exit);
|
||||
607
drivers/adsp_factory/vcnl36658_prox.c
Executable file
607
drivers/adsp_factory/vcnl36658_prox.c
Executable file
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "CAPELLA"
|
||||
#define CHIP_ID "VCNL36658"
|
||||
|
||||
#define PROX_AVG_COUNT 40
|
||||
#define PROX_ALERT_THRESHOLD 200
|
||||
#define PROX_TH_READ 0
|
||||
#define PROX_TH_WRITE 1
|
||||
#define BUFFER_MAX 128
|
||||
#define PROX_REG_START 0x80
|
||||
#define PROX_DETECT_HIGH_TH 16368
|
||||
#define PROX_DETECT_LOW_TH 1000
|
||||
|
||||
extern unsigned int system_rev;
|
||||
|
||||
struct vcnl36863_prox_data {
|
||||
struct hrtimer prox_timer;
|
||||
struct work_struct work_prox;
|
||||
struct workqueue_struct *prox_wq;
|
||||
int min;
|
||||
int max;
|
||||
int avg;
|
||||
int val;
|
||||
int offset;
|
||||
int reg_backup[2];
|
||||
short avgwork_check;
|
||||
short avgtimer_enabled;
|
||||
short bBarcodeEnabled;
|
||||
};
|
||||
|
||||
enum {
|
||||
PRX_THRESHOLD_DETECT_H,
|
||||
PRX_THRESHOLD_HIGH_DETECT_L,
|
||||
PRX_THRESHOLD_HIGH_DETECT_H,
|
||||
PRX_THRESHOLD_RELEASE_L,
|
||||
};
|
||||
|
||||
static struct vcnl36863_prox_data *pdata;
|
||||
|
||||
static ssize_t prox_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t prox_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t prox_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (pdata->avgwork_check == 0)
|
||||
get_prox_raw_data(&pdata->val, &pdata->offset);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pdata->val);
|
||||
}
|
||||
|
||||
static ssize_t prox_avg_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", pdata->min,
|
||||
pdata->avg, pdata->max);
|
||||
}
|
||||
|
||||
static ssize_t prox_avg_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int new_value;
|
||||
|
||||
if (sysfs_streq(buf, "0"))
|
||||
new_value = 0;
|
||||
else
|
||||
new_value = 1;
|
||||
|
||||
if (new_value == pdata->avgtimer_enabled)
|
||||
return size;
|
||||
|
||||
if (new_value == 0) {
|
||||
pdata->avgtimer_enabled = 0;
|
||||
hrtimer_cancel(&pdata->prox_timer);
|
||||
cancel_work_sync(&pdata->work_prox);
|
||||
} else {
|
||||
pdata->avgtimer_enabled = 1;
|
||||
hrtimer_start(&pdata->prox_timer,
|
||||
ns_to_ktime(2000 * NSEC_PER_MSEC),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void prox_work_func(struct work_struct *work)
|
||||
{
|
||||
int min = 0, max = 0, avg = 0;
|
||||
int i;
|
||||
|
||||
pdata->avgwork_check = 1;
|
||||
for (i = 0; i < PROX_AVG_COUNT; i++) {
|
||||
msleep(20);
|
||||
|
||||
get_prox_raw_data(&pdata->val, &pdata->offset);
|
||||
avg += pdata->val;
|
||||
|
||||
if (!i)
|
||||
min = pdata->val;
|
||||
else if (pdata->val < min)
|
||||
min = pdata->val;
|
||||
|
||||
if (pdata->val > max)
|
||||
max = pdata->val;
|
||||
}
|
||||
avg /= PROX_AVG_COUNT;
|
||||
|
||||
pdata->min = min;
|
||||
pdata->avg = avg;
|
||||
pdata->max = max;
|
||||
pdata->avgwork_check = 0;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart prox_timer_func(struct hrtimer *timer)
|
||||
{
|
||||
queue_work(pdata->prox_wq, &pdata->work_prox);
|
||||
hrtimer_forward_now(&pdata->prox_timer,
|
||||
ns_to_ktime(2000 * NSEC_PER_MSEC));
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
int get_prox_threshold(struct adsp_data *data, int type)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
int32_t msg_buf[2];
|
||||
int ret = 0;
|
||||
|
||||
msg_buf[0] = type;
|
||||
msg_buf[1] = 0;
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_GET_THRESHOLD);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_THRESHOLD] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(2000, 2050);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_THRESHOLD] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->msg_buf[MSG_PROX][0];
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_prox_threshold(struct adsp_data *data, int type, int val)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
int32_t msg_buf[2];
|
||||
|
||||
msg_buf[0] = type;
|
||||
msg_buf[1] = val;
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_SET_THRESHOLD);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_SET_THRESHOLD] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_SET_THRESHOLD] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
}
|
||||
|
||||
static ssize_t prox_cancel_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int hi_thd, low_thd;
|
||||
|
||||
hi_thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H);
|
||||
low_thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L);
|
||||
|
||||
if (pdata->avgwork_check == 0)
|
||||
get_prox_raw_data(&pdata->val, &pdata->offset);
|
||||
|
||||
pr_info("[FACTORY] %s: offset: %d, hi thd: %d, lo thd: %d\n", __func__,
|
||||
pdata->offset, hi_thd, low_thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
pdata->offset, hi_thd, low_thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_cancel_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
pr_info("[FACTORY] %s: skip\n", __func__);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_high_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_high_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_DETECT_H, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_low_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_low_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_RELEASE_L, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL
|
||||
static ssize_t prox_high_thresh_high_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_high_thresh_high_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_high_thresh_low_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_high_thresh_low_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t barcode_emul_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pdata->bBarcodeEnabled);
|
||||
}
|
||||
|
||||
static ssize_t barcode_emul_enable_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int iRet;
|
||||
int64_t dEnable;
|
||||
|
||||
iRet = kstrtoll(buf, 10, &dEnable);
|
||||
if (iRet < 0)
|
||||
return iRet;
|
||||
|
||||
if (dEnable)
|
||||
pdata->bBarcodeEnabled = 1;
|
||||
else
|
||||
pdata->bBarcodeEnabled = 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_cancel_pass_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "1\n");
|
||||
}
|
||||
|
||||
static ssize_t prox_default_trim_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pdata->offset);
|
||||
}
|
||||
|
||||
static ssize_t prox_alert_thresh_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", PROX_ALERT_THRESHOLD);
|
||||
}
|
||||
|
||||
static ssize_t prox_register_read_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int cnt = 0;
|
||||
int32_t msg_buf[1];
|
||||
|
||||
msg_buf[0] = pdata->reg_backup[0];
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_GET_REGISTER);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_REGISTER] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_REGISTER] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pdata->reg_backup[1] = data->msg_buf[MSG_PROX][0];
|
||||
pr_info("[FACTORY] %s: [0x%x]: 0x%x\n",
|
||||
__func__, pdata->reg_backup[0], pdata->reg_backup[1]);
|
||||
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "[0x%x]: 0x%x\n",
|
||||
pdata->reg_backup[0], pdata->reg_backup[1]);
|
||||
}
|
||||
|
||||
static ssize_t prox_register_read_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int reg = 0;
|
||||
|
||||
if (sscanf(buf, "%4x", ®) != 1) {
|
||||
pr_err("[FACTORY]: %s - The number of data are wrong\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata->reg_backup[0] = reg;
|
||||
pr_info("[FACTORY] %s: [0x%x]\n", __func__, pdata->reg_backup[0]);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_register_write_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int cnt = 0;
|
||||
int32_t msg_buf[2];
|
||||
|
||||
if (sscanf(buf, "%2x,%4x", &msg_buf[0], &msg_buf[1]) != 2) {
|
||||
pr_err("[FACTORY]: %s - The number of data are wrong\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_SET_REGISTER);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_SET_REGISTER] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_SET_REGISTER] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pdata->reg_backup[0] = msg_buf[0];
|
||||
pr_info("[FACTORY] %s: 0x%x - 0x%x\n",
|
||||
__func__, msg_buf[0], data->msg_buf[MSG_PROX][0]);
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_light_get_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int offset = 0;
|
||||
int32_t *info = data->msg_buf[MSG_PROX];
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_PROX, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pr_info("[FACTORY] %d,%d,%d,%d,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%d\n",
|
||||
info[0], info[1], info[2], info[3], info[4], info[5],
|
||||
info[6], info[7], info[8], info[9], info[10], info[11]);
|
||||
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"THD\":\"%d %d %d %d\",", info[0], info[1], info[2], info[3]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PDRIVE_CURRENT\":\"%02x\",", info[4]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PERSIST_TIME\":\"%02x\",", info[5]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PPULSE\":\"%02x\",", info[6]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PGAIN\":\"%02x\",", info[7]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PTIME\":\"%02x\",", info[8]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PPLUSE_LEN\":\"%02x\",", info[9]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"ATIME\":\"%02x\",", info[10]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"POFFSET\":\"%d\"\n", info[11]);
|
||||
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vendor, 0444, prox_vendor_show, NULL);
|
||||
static DEVICE_ATTR(name, 0444, prox_name_show, NULL);
|
||||
static DEVICE_ATTR(state, 0444, prox_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, prox_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(prox_avg, 0664,
|
||||
prox_avg_show, prox_avg_store);
|
||||
static DEVICE_ATTR(prox_cal, 0664,
|
||||
prox_cancel_show, prox_cancel_store);
|
||||
static DEVICE_ATTR(thresh_high, 0664,
|
||||
prox_thresh_high_show, prox_thresh_high_store);
|
||||
static DEVICE_ATTR(thresh_low, 0664,
|
||||
prox_thresh_low_show, prox_thresh_low_store);
|
||||
#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL
|
||||
static DEVICE_ATTR(high_thresh_high, 0664,
|
||||
prox_high_thresh_high_show, prox_high_thresh_high_store);
|
||||
static DEVICE_ATTR(high_thresh_low, 0664,
|
||||
prox_high_thresh_low_show, prox_high_thresh_low_store);
|
||||
#endif
|
||||
static DEVICE_ATTR(register_write, 0220,
|
||||
NULL, prox_register_write_store);
|
||||
static DEVICE_ATTR(register_read, 0664,
|
||||
prox_register_read_show, prox_register_read_store);
|
||||
static DEVICE_ATTR(barcode_emul_en, 0664,
|
||||
barcode_emul_enable_show, barcode_emul_enable_store);
|
||||
static DEVICE_ATTR(prox_offset_pass, 0444, prox_cancel_pass_show, NULL);
|
||||
static DEVICE_ATTR(prox_trim, 0444, prox_default_trim_show, NULL);
|
||||
|
||||
static DEVICE_ATTR(prox_alert_thresh, 0444, prox_alert_thresh_show, NULL);
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0440,
|
||||
prox_light_get_dhr_sensor_info_show, NULL);
|
||||
|
||||
static struct device_attribute *prox_attrs[] = {
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_name,
|
||||
&dev_attr_state,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_prox_avg,
|
||||
&dev_attr_prox_cal,
|
||||
&dev_attr_thresh_high,
|
||||
&dev_attr_thresh_low,
|
||||
#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL
|
||||
&dev_attr_high_thresh_high,
|
||||
&dev_attr_high_thresh_low,
|
||||
#endif
|
||||
&dev_attr_barcode_emul_en,
|
||||
&dev_attr_prox_offset_pass,
|
||||
&dev_attr_prox_trim,
|
||||
&dev_attr_prox_alert_thresh,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
&dev_attr_register_write,
|
||||
&dev_attr_register_read,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init vcnl36863_prox_factory_init(void)
|
||||
{
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
adsp_factory_register(MSG_PROX, prox_attrs);
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
hrtimer_init(&pdata->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
pdata->prox_timer.function = prox_timer_func;
|
||||
pdata->prox_wq = create_singlethread_workqueue("prox_wq");
|
||||
|
||||
/* this is the thread function we run on the work queue */
|
||||
INIT_WORK(&pdata->work_prox, prox_work_func);
|
||||
|
||||
pdata->avgwork_check = 0;
|
||||
pdata->avgtimer_enabled = 0;
|
||||
pdata->avg = 0;
|
||||
pdata->min = 0;
|
||||
pdata->max = 0;
|
||||
pdata->offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit vcnl36863_prox_factory_exit(void)
|
||||
{
|
||||
if (pdata->avgtimer_enabled == 1) {
|
||||
hrtimer_cancel(&pdata->prox_timer);
|
||||
cancel_work_sync(&pdata->work_prox);
|
||||
}
|
||||
destroy_workqueue(pdata->prox_wq);
|
||||
adsp_factory_unregister(MSG_PROX);
|
||||
kfree(pdata);
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(vcnl36863_prox_factory_init);
|
||||
module_exit(vcnl36863_prox_factory_exit);
|
||||
118
drivers/adsp_factory/vcnl36863_light.c
Normal file
118
drivers/adsp_factory/vcnl36863_light.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dirent.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "CAPELLA"
|
||||
#define CHIP_ID "VCNL36863"
|
||||
|
||||
/*************************************************************************/
|
||||
/* factory Sysfs */
|
||||
/*************************************************************************/
|
||||
static ssize_t light_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t light_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t light_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
mutex_lock(&data->light_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return snprintf(buf, PAGE_SIZE, "0,0,0,0,0,0\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_LIGHT][0], data->msg_buf[MSG_LIGHT][1],
|
||||
data->msg_buf[MSG_LIGHT][2], data->msg_buf[MSG_LIGHT][3],
|
||||
data->msg_buf[MSG_LIGHT][4], data->msg_buf[MSG_LIGHT][5]);
|
||||
}
|
||||
|
||||
static ssize_t light_get_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
pr_info("[FACTORY] %s: start\n", __func__);
|
||||
mutex_lock(&data->light_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return data->msg_buf[MSG_LIGHT][0];
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vendor, 0444, light_vendor_show, NULL);
|
||||
static DEVICE_ATTR(name, 0444, light_name_show, NULL);
|
||||
static DEVICE_ATTR(lux, 0444, light_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, light_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0444, light_get_dhr_sensor_info_show, NULL);
|
||||
|
||||
static struct device_attribute *light_attrs[] = {
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_name,
|
||||
&dev_attr_lux,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init vcnl36863_light_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_LIGHT, light_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit vcnl36863_light_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_LIGHT);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
module_init(vcnl36863_light_factory_init);
|
||||
module_exit(vcnl36863_light_factory_exit);
|
||||
607
drivers/adsp_factory/vcnl36863_prox.c
Normal file
607
drivers/adsp_factory/vcnl36863_prox.c
Normal file
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "CAPELLA"
|
||||
#define CHIP_ID "VCNL36863"
|
||||
|
||||
#define PROX_AVG_COUNT 40
|
||||
#define PROX_ALERT_THRESHOLD 200
|
||||
#define PROX_TH_READ 0
|
||||
#define PROX_TH_WRITE 1
|
||||
#define BUFFER_MAX 128
|
||||
#define PROX_REG_START 0x80
|
||||
#define PROX_DETECT_HIGH_TH 16368
|
||||
#define PROX_DETECT_LOW_TH 1000
|
||||
|
||||
extern unsigned int system_rev;
|
||||
|
||||
struct vcnl36863_prox_data {
|
||||
struct hrtimer prox_timer;
|
||||
struct work_struct work_prox;
|
||||
struct workqueue_struct *prox_wq;
|
||||
int min;
|
||||
int max;
|
||||
int avg;
|
||||
int val;
|
||||
int offset;
|
||||
int reg_backup[2];
|
||||
short avgwork_check;
|
||||
short avgtimer_enabled;
|
||||
short bBarcodeEnabled;
|
||||
};
|
||||
|
||||
enum {
|
||||
PRX_THRESHOLD_DETECT_H,
|
||||
PRX_THRESHOLD_HIGH_DETECT_L,
|
||||
PRX_THRESHOLD_HIGH_DETECT_H,
|
||||
PRX_THRESHOLD_RELEASE_L,
|
||||
};
|
||||
|
||||
static struct vcnl36863_prox_data *pdata;
|
||||
|
||||
static ssize_t prox_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t prox_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t prox_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (pdata->avgwork_check == 0)
|
||||
get_prox_raw_data(&pdata->val, &pdata->offset);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pdata->val);
|
||||
}
|
||||
|
||||
static ssize_t prox_avg_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", pdata->min,
|
||||
pdata->avg, pdata->max);
|
||||
}
|
||||
|
||||
static ssize_t prox_avg_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int new_value;
|
||||
|
||||
if (sysfs_streq(buf, "0"))
|
||||
new_value = 0;
|
||||
else
|
||||
new_value = 1;
|
||||
|
||||
if (new_value == pdata->avgtimer_enabled)
|
||||
return size;
|
||||
|
||||
if (new_value == 0) {
|
||||
pdata->avgtimer_enabled = 0;
|
||||
hrtimer_cancel(&pdata->prox_timer);
|
||||
cancel_work_sync(&pdata->work_prox);
|
||||
} else {
|
||||
pdata->avgtimer_enabled = 1;
|
||||
hrtimer_start(&pdata->prox_timer,
|
||||
ns_to_ktime(2000 * NSEC_PER_MSEC),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void prox_work_func(struct work_struct *work)
|
||||
{
|
||||
int min = 0, max = 0, avg = 0;
|
||||
int i;
|
||||
|
||||
pdata->avgwork_check = 1;
|
||||
for (i = 0; i < PROX_AVG_COUNT; i++) {
|
||||
msleep(20);
|
||||
|
||||
get_prox_raw_data(&pdata->val, &pdata->offset);
|
||||
avg += pdata->val;
|
||||
|
||||
if (!i)
|
||||
min = pdata->val;
|
||||
else if (pdata->val < min)
|
||||
min = pdata->val;
|
||||
|
||||
if (pdata->val > max)
|
||||
max = pdata->val;
|
||||
}
|
||||
avg /= PROX_AVG_COUNT;
|
||||
|
||||
pdata->min = min;
|
||||
pdata->avg = avg;
|
||||
pdata->max = max;
|
||||
pdata->avgwork_check = 0;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart prox_timer_func(struct hrtimer *timer)
|
||||
{
|
||||
queue_work(pdata->prox_wq, &pdata->work_prox);
|
||||
hrtimer_forward_now(&pdata->prox_timer,
|
||||
ns_to_ktime(2000 * NSEC_PER_MSEC));
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
int get_prox_threshold(struct adsp_data *data, int type)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
int32_t msg_buf[2];
|
||||
int ret = 0;
|
||||
|
||||
msg_buf[0] = type;
|
||||
msg_buf[1] = 0;
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_GET_THRESHOLD);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_THRESHOLD] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(2000, 2050);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_THRESHOLD] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->msg_buf[MSG_PROX][0];
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_prox_threshold(struct adsp_data *data, int type, int val)
|
||||
{
|
||||
uint8_t cnt = 0;
|
||||
int32_t msg_buf[2];
|
||||
|
||||
msg_buf[0] = type;
|
||||
msg_buf[1] = val;
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_SET_THRESHOLD);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_SET_THRESHOLD] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_SET_THRESHOLD] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
}
|
||||
|
||||
static ssize_t prox_cancel_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int hi_thd, low_thd;
|
||||
|
||||
hi_thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H);
|
||||
low_thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L);
|
||||
|
||||
if (pdata->avgwork_check == 0)
|
||||
get_prox_raw_data(&pdata->val, &pdata->offset);
|
||||
|
||||
pr_info("[FACTORY] %s: offset: %d, hi thd: %d, lo thd: %d\n", __func__,
|
||||
pdata->offset, hi_thd, low_thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
||||
pdata->offset, hi_thd, low_thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_cancel_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
pr_info("[FACTORY] %s: skip\n", __func__);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_high_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_DETECT_H);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_high_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_DETECT_H, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_low_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_RELEASE_L);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_thresh_low_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_RELEASE_L, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL
|
||||
static ssize_t prox_high_thresh_high_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_high_thresh_high_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_H, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_high_thresh_low_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd;
|
||||
|
||||
thd = get_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", thd);
|
||||
}
|
||||
|
||||
static ssize_t prox_high_thresh_low_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int thd = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &thd)) {
|
||||
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
|
||||
return size;
|
||||
}
|
||||
|
||||
set_prox_threshold(data, PRX_THRESHOLD_HIGH_DETECT_L, thd);
|
||||
pr_info("[FACTORY] %s: %d\n", __func__, thd);
|
||||
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t barcode_emul_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pdata->bBarcodeEnabled);
|
||||
}
|
||||
|
||||
static ssize_t barcode_emul_enable_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int iRet;
|
||||
int64_t dEnable;
|
||||
|
||||
iRet = kstrtoll(buf, 10, &dEnable);
|
||||
if (iRet < 0)
|
||||
return iRet;
|
||||
|
||||
if (dEnable)
|
||||
pdata->bBarcodeEnabled = 1;
|
||||
else
|
||||
pdata->bBarcodeEnabled = 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_cancel_pass_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "1\n");
|
||||
}
|
||||
|
||||
static ssize_t prox_default_trim_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pdata->offset);
|
||||
}
|
||||
|
||||
static ssize_t prox_alert_thresh_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", PROX_ALERT_THRESHOLD);
|
||||
}
|
||||
|
||||
static ssize_t prox_register_read_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int cnt = 0;
|
||||
int32_t msg_buf[1];
|
||||
|
||||
msg_buf[0] = pdata->reg_backup[0];
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_GET_REGISTER);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_REGISTER] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_REGISTER] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pdata->reg_backup[1] = data->msg_buf[MSG_PROX][0];
|
||||
pr_info("[FACTORY] %s: [0x%x]: 0x%x\n",
|
||||
__func__, pdata->reg_backup[0], pdata->reg_backup[1]);
|
||||
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "[0x%x]: 0x%x\n",
|
||||
pdata->reg_backup[0], pdata->reg_backup[1]);
|
||||
}
|
||||
|
||||
static ssize_t prox_register_read_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int reg = 0;
|
||||
|
||||
if (sscanf(buf, "%4x", ®) != 1) {
|
||||
pr_err("[FACTORY]: %s - The number of data are wrong\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata->reg_backup[0] = reg;
|
||||
pr_info("[FACTORY] %s: [0x%x]\n", __func__, pdata->reg_backup[0]);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_register_write_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
int cnt = 0;
|
||||
int32_t msg_buf[2];
|
||||
|
||||
if (sscanf(buf, "%2x,%4x", &msg_buf[0], &msg_buf[1]) != 2) {
|
||||
pr_err("[FACTORY]: %s - The number of data are wrong\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(msg_buf, sizeof(msg_buf),
|
||||
MSG_PROX, 0, MSG_TYPE_SET_REGISTER);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_SET_REGISTER] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_SET_REGISTER] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pdata->reg_backup[0] = msg_buf[0];
|
||||
pr_info("[FACTORY] %s: 0x%x - 0x%x\n",
|
||||
__func__, msg_buf[0], data->msg_buf[MSG_PROX][0]);
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t prox_light_get_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
int offset = 0;
|
||||
int32_t *info = data->msg_buf[MSG_PROX];
|
||||
|
||||
mutex_lock(&data->prox_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_PROX, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_PROX) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_PROX);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
pr_info("[FACTORY] %d,%d,%d,%d,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%d\n",
|
||||
info[0], info[1], info[2], info[3], info[4], info[5],
|
||||
info[6], info[7], info[8], info[9], info[10], info[11]);
|
||||
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"THD\":\"%d %d %d %d\",", info[0], info[1], info[2], info[3]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PDRIVE_CURRENT\":\"%02x\",", info[4]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PERSIST_TIME\":\"%02x\",", info[5]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PPULSE\":\"%02x\",", info[6]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PGAIN\":\"%02x\",", info[7]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PTIME\":\"%02x\",", info[8]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"PPLUSE_LEN\":\"%02x\",", info[9]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"ATIME\":\"%02x\",", info[10]);
|
||||
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
||||
"\"POFFSET\":\"%d\"\n", info[11]);
|
||||
|
||||
mutex_unlock(&data->prox_factory_mutex);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vendor, 0444, prox_vendor_show, NULL);
|
||||
static DEVICE_ATTR(name, 0444, prox_name_show, NULL);
|
||||
static DEVICE_ATTR(state, 0444, prox_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, prox_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(prox_avg, 0664,
|
||||
prox_avg_show, prox_avg_store);
|
||||
static DEVICE_ATTR(prox_cal, 0664,
|
||||
prox_cancel_show, prox_cancel_store);
|
||||
static DEVICE_ATTR(thresh_high, 0664,
|
||||
prox_thresh_high_show, prox_thresh_high_store);
|
||||
static DEVICE_ATTR(thresh_low, 0664,
|
||||
prox_thresh_low_show, prox_thresh_low_store);
|
||||
#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL
|
||||
static DEVICE_ATTR(high_thresh_high, 0664,
|
||||
prox_high_thresh_high_show, prox_high_thresh_high_store);
|
||||
static DEVICE_ATTR(high_thresh_low, 0664,
|
||||
prox_high_thresh_low_show, prox_high_thresh_low_store);
|
||||
#endif
|
||||
static DEVICE_ATTR(register_write, 0220,
|
||||
NULL, prox_register_write_store);
|
||||
static DEVICE_ATTR(register_read, 0664,
|
||||
prox_register_read_show, prox_register_read_store);
|
||||
static DEVICE_ATTR(barcode_emul_en, 0664,
|
||||
barcode_emul_enable_show, barcode_emul_enable_store);
|
||||
static DEVICE_ATTR(prox_offset_pass, 0444, prox_cancel_pass_show, NULL);
|
||||
static DEVICE_ATTR(prox_trim, 0444, prox_default_trim_show, NULL);
|
||||
|
||||
static DEVICE_ATTR(prox_alert_thresh, 0444, prox_alert_thresh_show, NULL);
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0440,
|
||||
prox_light_get_dhr_sensor_info_show, NULL);
|
||||
|
||||
static struct device_attribute *prox_attrs[] = {
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_name,
|
||||
&dev_attr_state,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_prox_avg,
|
||||
&dev_attr_prox_cal,
|
||||
&dev_attr_thresh_high,
|
||||
&dev_attr_thresh_low,
|
||||
#ifdef CONFIG_SUPPORT_PROX_AUTO_CAL
|
||||
&dev_attr_high_thresh_high,
|
||||
&dev_attr_high_thresh_low,
|
||||
#endif
|
||||
&dev_attr_barcode_emul_en,
|
||||
&dev_attr_prox_offset_pass,
|
||||
&dev_attr_prox_trim,
|
||||
&dev_attr_prox_alert_thresh,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
&dev_attr_register_write,
|
||||
&dev_attr_register_read,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init vcnl36863_prox_factory_init(void)
|
||||
{
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
adsp_factory_register(MSG_PROX, prox_attrs);
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
hrtimer_init(&pdata->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
pdata->prox_timer.function = prox_timer_func;
|
||||
pdata->prox_wq = create_singlethread_workqueue("prox_wq");
|
||||
|
||||
/* this is the thread function we run on the work queue */
|
||||
INIT_WORK(&pdata->work_prox, prox_work_func);
|
||||
|
||||
pdata->avgwork_check = 0;
|
||||
pdata->avgtimer_enabled = 0;
|
||||
pdata->avg = 0;
|
||||
pdata->min = 0;
|
||||
pdata->max = 0;
|
||||
pdata->offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit vcnl36863_prox_factory_exit(void)
|
||||
{
|
||||
if (pdata->avgtimer_enabled == 1) {
|
||||
hrtimer_cancel(&pdata->prox_timer);
|
||||
cancel_work_sync(&pdata->work_prox);
|
||||
}
|
||||
destroy_workqueue(pdata->prox_wq);
|
||||
adsp_factory_unregister(MSG_PROX);
|
||||
kfree(pdata);
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(vcnl36863_prox_factory_init);
|
||||
module_exit(vcnl36863_prox_factory_exit);
|
||||
118
drivers/adsp_factory/veml3328_light.c
Normal file
118
drivers/adsp_factory/veml3328_light.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2012, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dirent.h>
|
||||
#include "adsp.h"
|
||||
#define VENDOR "CAPELLA"
|
||||
#define CHIP_ID "VEML3328"
|
||||
|
||||
/*************************************************************************/
|
||||
/* factory Sysfs */
|
||||
/*************************************************************************/
|
||||
static ssize_t light_vendor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
||||
}
|
||||
|
||||
static ssize_t light_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
||||
}
|
||||
|
||||
static ssize_t light_raw_data_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
mutex_lock(&data->light_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_RAW_DATA);
|
||||
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_RAW_DATA] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_RAW_DATA] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT) {
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return snprintf(buf, PAGE_SIZE, "0,0,0,0,0,0\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n",
|
||||
data->msg_buf[MSG_LIGHT][0], data->msg_buf[MSG_LIGHT][1],
|
||||
data->msg_buf[MSG_LIGHT][2], data->msg_buf[MSG_LIGHT][3],
|
||||
data->msg_buf[MSG_LIGHT][4], data->msg_buf[MSG_LIGHT][5]);
|
||||
}
|
||||
|
||||
static ssize_t light_get_dhr_sensor_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adsp_data *data = dev_get_drvdata(dev);
|
||||
uint8_t cnt = 0;
|
||||
|
||||
pr_info("[FACTORY] %s: start\n", __func__);
|
||||
mutex_lock(&data->light_factory_mutex);
|
||||
adsp_unicast(NULL, 0, MSG_LIGHT, 0, MSG_TYPE_GET_DHR_INFO);
|
||||
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << MSG_LIGHT) &&
|
||||
cnt++ < TIMEOUT_CNT)
|
||||
usleep_range(500, 550);
|
||||
|
||||
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << MSG_LIGHT);
|
||||
|
||||
if (cnt >= TIMEOUT_CNT)
|
||||
pr_err("[FACTORY] %s: Timeout!!!\n", __func__);
|
||||
|
||||
mutex_unlock(&data->light_factory_mutex);
|
||||
return data->msg_buf[MSG_LIGHT][0];
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vendor, 0444, light_vendor_show, NULL);
|
||||
static DEVICE_ATTR(name, 0444, light_name_show, NULL);
|
||||
static DEVICE_ATTR(lux, 0444, light_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(raw_data, 0444, light_raw_data_show, NULL);
|
||||
static DEVICE_ATTR(dhr_sensor_info, 0444, light_get_dhr_sensor_info_show, NULL);
|
||||
|
||||
static struct device_attribute *light_attrs[] = {
|
||||
&dev_attr_vendor,
|
||||
&dev_attr_name,
|
||||
&dev_attr_lux,
|
||||
&dev_attr_raw_data,
|
||||
&dev_attr_dhr_sensor_info,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int __init veml3328_light_factory_init(void)
|
||||
{
|
||||
adsp_factory_register(MSG_LIGHT, light_attrs);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit veml3328_light_factory_exit(void)
|
||||
{
|
||||
adsp_factory_unregister(MSG_LIGHT);
|
||||
|
||||
pr_info("[FACTORY] %s\n", __func__);
|
||||
}
|
||||
module_init(veml3328_light_factory_init);
|
||||
module_exit(veml3328_light_factory_exit);
|
||||
@@ -297,7 +297,9 @@ static const char * const fw_path[] = {
|
||||
"/lib/firmware/updates/" UTS_RELEASE,
|
||||
"/lib/firmware/updates",
|
||||
"/lib/firmware/" UTS_RELEASE,
|
||||
"/lib/firmware"
|
||||
"/lib/firmware",
|
||||
"/firmware/image",
|
||||
"/firmware-modem/image"
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#include <linux/irqdesc.h>
|
||||
|
||||
#include "power.h"
|
||||
#ifdef CONFIG_SEC_PM
|
||||
#include <linux/wakeup_reason.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If set, the suspend/hibernate code will abort transitions to a sleep state
|
||||
@@ -663,6 +666,12 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
|
||||
ws->total_time = ktime_add(ws->total_time, duration);
|
||||
if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))
|
||||
ws->max_time = duration;
|
||||
#ifdef CONFIG_SEC_PM_DEBUG
|
||||
if (ktime_to_ms(duration) >= 10000LL) {
|
||||
pr_info("PM: unlock %s(%lld ms)\n",
|
||||
ws->name, ktime_to_ms(duration));
|
||||
}
|
||||
#endif
|
||||
|
||||
ws->last_time = now;
|
||||
del_timer(&ws->timer);
|
||||
@@ -928,8 +937,12 @@ void pm_system_irq_wakeup(unsigned int irq_number)
|
||||
else if (desc->action && desc->action->name)
|
||||
name = desc->action->name;
|
||||
|
||||
#ifdef CONFIG_SEC_PM
|
||||
log_wakeup_reason(irq_number);
|
||||
#else
|
||||
pr_warn("%s: %d triggered %s\n", __func__,
|
||||
irq_number, name);
|
||||
#endif
|
||||
|
||||
}
|
||||
pm_wakeup_irq = irq_number;
|
||||
@@ -1078,6 +1091,62 @@ static int print_wakeup_source_stats(struct seq_file *m,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_PM_DEBUG
|
||||
static int print_wakeup_source_active(
|
||||
struct wakeup_source *ws)
|
||||
{
|
||||
unsigned long flags;
|
||||
ktime_t total_time;
|
||||
unsigned long active_count;
|
||||
ktime_t active_time;
|
||||
ktime_t prevent_sleep_time;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
total_time = ws->total_time;
|
||||
prevent_sleep_time = ws->prevent_sleep_time;
|
||||
active_count = ws->active_count;
|
||||
if (ws->active) {
|
||||
ktime_t now = ktime_get();
|
||||
|
||||
active_time = ktime_sub(now, ws->last_time);
|
||||
total_time = ktime_add(total_time, active_time);
|
||||
|
||||
if (ws->autosleep_enabled)
|
||||
prevent_sleep_time = ktime_add(prevent_sleep_time,
|
||||
ktime_sub(now, ws->start_prevent_time));
|
||||
} else {
|
||||
active_time = ktime_set(0, 0);
|
||||
}
|
||||
|
||||
ret = pr_info("<%s>\tCount(%lu) Time(%lld/%lld) Prevent(%lld)\n",
|
||||
ws->name, active_count,
|
||||
ktime_to_ms(active_time), ktime_to_ms(total_time),
|
||||
ktime_to_ms(prevent_sleep_time));
|
||||
|
||||
spin_unlock_irqrestore(&ws->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wakeup_sources_stats_active(void)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
|
||||
pr_info("Active wake lock:\n");
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ws, &wakeup_sources, entry)
|
||||
if (ws->active)
|
||||
print_wakeup_source_active(ws);
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_sources_stats_active);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* wakeup_sources_stats_show - Print wakeup sources statistics information.
|
||||
* @m: seq_file to print the statistics into.
|
||||
|
||||
282
drivers/battery_v2/Kconfig
Normal file
282
drivers/battery_v2/Kconfig
Normal file
@@ -0,0 +1,282 @@
|
||||
|
||||
config BATTERY_SAMSUNG
|
||||
tristate "samsung battery driver"
|
||||
help
|
||||
Say Y to include support for samsung battery driver
|
||||
This battery driver integrated all battery-related functions
|
||||
To see battery-related functions,
|
||||
refer to sec_charging_common.h
|
||||
|
||||
config BATTERY_SAMSUNG_V2
|
||||
tristate "samsung battery driver version 2"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to include support for samsung battery driver
|
||||
This battery driver integrated all battery-related functions
|
||||
To see battery-related functions,
|
||||
refer to sec_charging_common.h
|
||||
|
||||
config BATTERY_SAMSUNG_DATA_FILE
|
||||
depends on BATTERY_SAMSUNG
|
||||
string "samsung battery data file"
|
||||
default "default_battery_data.h"
|
||||
help
|
||||
Path to the battery data file.
|
||||
|
||||
config SLOW_CHARGING_CURRENT_STANDARD
|
||||
int "slow charging"
|
||||
depends on BATTERY_SAMSUNG
|
||||
default "1000"
|
||||
help
|
||||
Value for standard of slow-charging.
|
||||
|
||||
config CHARGING_VZWCONCEPT
|
||||
tristate "VZW concept about the charging"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to include support for the VZW concepts.
|
||||
|
||||
config BATTERY_SWELLING
|
||||
bool "prevent battery swelling"
|
||||
help
|
||||
Say Y to include support for prevent battery swelling
|
||||
|
||||
config BATTERY_SWELLING_SELF_DISCHARGING
|
||||
bool "prevent battery swelling with self discharging"
|
||||
help
|
||||
Say Y to include support for prevent battery swelling with self discharging
|
||||
|
||||
config CALC_TIME_TO_FULL
|
||||
tristate "calculate time to full"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to use calc time to full function.
|
||||
|
||||
config MULTI_CHARGING
|
||||
bool "support for multi charger ICs"
|
||||
help
|
||||
Say Y to include support for multi charger ICs
|
||||
|
||||
config STEP_CHARGING
|
||||
bool "support for step charging"
|
||||
help
|
||||
Say Y to include support for step charging
|
||||
|
||||
config UPDATE_BATTERY_DATA
|
||||
bool "support for updating battery data"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG && OF
|
||||
help
|
||||
Say Y to include support for step charging
|
||||
|
||||
config AFC_CURR_CONTROL_BY_TEMP
|
||||
tristate "fast charging current control by temp"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to set afc current control by temp
|
||||
|
||||
config BATTERY_CISD
|
||||
bool "support for cisd"
|
||||
help
|
||||
Say Y to include support for cisd
|
||||
cisd means cell internal short detection
|
||||
|
||||
config DUMMY_BATTERY
|
||||
bool "support for dummy battery"
|
||||
help
|
||||
Say Y to include support for dummy battery
|
||||
for dummy battery recognition
|
||||
|
||||
# Fuel Gauge
|
||||
config FUELGAUGE_DUMMY
|
||||
tristate "dummy fuel gauge driver"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to include support
|
||||
for dummy fuel gauge driver.
|
||||
This driver source code implemented
|
||||
skeleton source code for fuel gauge functions.
|
||||
|
||||
config PREVENT_SOC_JUMP
|
||||
tristate "prevent soc jump at full-charged"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to prevent soc jump
|
||||
|
||||
config FUELGAUGE_SM5705
|
||||
tristate "Siliconmitus SM5705 Fuel Gauge"
|
||||
default n
|
||||
depends on I2C
|
||||
help
|
||||
SM5705 is fuel-gauge systems for lithium-ion (Li+) batteries
|
||||
in handheld and portable equipment. The SM5705 is configured
|
||||
to operate with a single lithium cell
|
||||
|
||||
# Charger
|
||||
config CHARGER_DUMMY
|
||||
tristate "dummy charger driver"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to include support
|
||||
for dummy charger driver.
|
||||
This driver source code implemented
|
||||
skeleton source code for charger functions.
|
||||
|
||||
config CHARGER_SM5705
|
||||
tristate "SM5705 battery charger support"
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y here to enable support for the SM5705 charger
|
||||
|
||||
config CS100_JPNCONCEPT
|
||||
tristate "cs100 command support"
|
||||
depends on BATTERY_SAMSUNG && I2C
|
||||
help
|
||||
Say Y here to support for CS100 command to stop when full-charged
|
||||
on wireless charging for JPN models
|
||||
|
||||
config AFC_CHARGER_MODE
|
||||
bool "afc charging support in sec battery driver"
|
||||
default n
|
||||
help
|
||||
Say Y to include support for sec afc charging support
|
||||
|
||||
config SAMSUNG_LPM_MODE
|
||||
bool "Off charging mode support in sec battery driver"
|
||||
default n
|
||||
help
|
||||
Say Y to include support for sec off charging support
|
||||
This value defined at bootloader.
|
||||
Before enable this feature,
|
||||
implemet power off charging in the bootloader.
|
||||
|
||||
config SAMSUNG_BATTERY_ENG_TEST
|
||||
bool "enable ENG mode for battery test"
|
||||
default n
|
||||
help
|
||||
Say Y to include support for battery test
|
||||
enable this feature only ENG mode
|
||||
this featuren must disabled user binary
|
||||
stability test etc..
|
||||
|
||||
config SAMSUNG_BATTERY_FACTORY
|
||||
bool "enable for factory test"
|
||||
default n
|
||||
help
|
||||
Say Y to include support for factory test
|
||||
enable this feature only factory mode
|
||||
this featuren must disabled user binary
|
||||
stability test etc..
|
||||
|
||||
config SAMSUNG_BATTERY_DISALLOW_DEEP_SLEEP
|
||||
bool "Disallow deep sleep during charging"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG && (ARCH_MSM8974 || ARCH_APQ8084)
|
||||
help
|
||||
Say Y to include support
|
||||
Disallow deep sleep during charging for stablity.
|
||||
|
||||
config DISABLE_SAVE_CAPACITY_MAX
|
||||
bool "Disable to save capacity max in efs"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to include support to disable it
|
||||
capacity_max is saved in /efs/Battery/batt_capacity_max
|
||||
capacity_max is restored after boot up
|
||||
You can find the code in healthd
|
||||
|
||||
config SIOP_CHARGING_LIMIT_CURRENT
|
||||
int "Siop charging limit current"
|
||||
default 0
|
||||
help
|
||||
set SIOP charging limit current.
|
||||
|
||||
config EN_OOPS
|
||||
bool "enable oops filter"
|
||||
default n
|
||||
help
|
||||
Say Y to enable oops filter
|
||||
|
||||
config USE_POGO
|
||||
bool "enable pogo charging"
|
||||
default n
|
||||
help
|
||||
Say Y to enable CONFIG_USE_POGO
|
||||
|
||||
config MACH_KOR_EARJACK_WR
|
||||
bool "enable earjack-noise workaround"
|
||||
default n
|
||||
depends on BATTERY_SAMSUNG
|
||||
help
|
||||
Say Y to enable earjack-noise workaround at charging
|
||||
|
||||
config STORE_MODE
|
||||
bool "enable store mode"
|
||||
default n
|
||||
help
|
||||
Say Y to enable CONFIG_STORE_MODE
|
||||
|
||||
config SW_SELF_DISCHARGING
|
||||
bool "enable sw_self_discharging"
|
||||
default n
|
||||
help
|
||||
Say Y to enable CONFIG_SW_SELF_DISCHARGING
|
||||
|
||||
config BATTERY_AGE_FORECAST
|
||||
tristate "battery age forecast"
|
||||
default n
|
||||
depends on BATTERY_SWELLING
|
||||
help
|
||||
Say Y to use calc time to full function.
|
||||
|
||||
config BATTERY_AGE_FORECAST_DETACHABLE
|
||||
tristate "battery age forecast for detachable"
|
||||
default n
|
||||
select BATTERY_AGE_FORECAST
|
||||
help
|
||||
Say Y to use battery age forecast for detachable
|
||||
|
||||
config ENG_BATTERY_CONCEPT
|
||||
bool "enable temp block"
|
||||
default n
|
||||
help
|
||||
Say Y to enable CONFIG_ENG_BATTERY_CONCEPT
|
||||
|
||||
config QH_ALGORITHM
|
||||
bool "enable QH algorithm"
|
||||
default n
|
||||
help
|
||||
Say Y to enable CONFIG_QH_ALGORITHM to measure leakges of the battery
|
||||
using raw coulomb count generated by the device
|
||||
|
||||
config BATTERY_NOTIFIER
|
||||
bool "battery notifier"
|
||||
default n
|
||||
help
|
||||
Say Y to enable battery notifier
|
||||
|
||||
config LIMIT_CHARGING_DURING_CALL
|
||||
bool "limit charging during call"
|
||||
default n
|
||||
help
|
||||
Say Y to limit charging during call
|
||||
|
||||
config ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED
|
||||
bool "enable 100mA before usb configured"
|
||||
default n
|
||||
help
|
||||
Say Y to enable 100mA before usb configured
|
||||
|
||||
config TABLET_MODEL_CONCEPT
|
||||
bool "tablet model concept"
|
||||
default n
|
||||
help
|
||||
Say Y to enable tablet model concept
|
||||
13
drivers/battery_v2/Makefile
Normal file
13
drivers/battery_v2/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
obj-$(CONFIG_OF) += sec_adc.o
|
||||
obj-$(CONFIG_MULTI_CHARGING) += sec_multi_charger.o
|
||||
obj-$(CONFIG_STEP_CHARGING) += sec_step_charging.o
|
||||
obj-$(CONFIG_BATTERY_CISD) += sec_cisd.o
|
||||
obj-$(CONFIG_UPDATE_BATTERY_DATA) += sec_battery_data.o
|
||||
obj-$(CONFIG_BATTERY_NOTIFIER) += battery_notifier.o
|
||||
|
||||
obj-$(CONFIG_FUELGAUGE_SM5705) += sm5705_fuelgauge.o
|
||||
|
||||
obj-$(CONFIG_CHARGER_SM5705) += sm5705_charger.o sm5705_charger_oper.o
|
||||
|
||||
obj-$(CONFIG_BATTERY_SAMSUNG) += sec_battery.o
|
||||
obj-$(CONFIG_BATTERY_SAMSUNG) += sec_battery_ttf.o
|
||||
205
drivers/battery_v2/battery_notifier.c
Normal file
205
drivers/battery_v2/battery_notifier.c
Normal file
@@ -0,0 +1,205 @@
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/battery/battery_notifier.h>
|
||||
#include <linux/sec_class.h>
|
||||
//#include <linux/sec_sysfs.h>
|
||||
|
||||
#define DEBUG
|
||||
#define SET_BATTERY_NOTIFIER_BLOCK(nb, fn, dev) do { \
|
||||
(nb)->notifier_call = (fn); \
|
||||
(nb)->priority = (dev); \
|
||||
} while (0)
|
||||
|
||||
#define DESTROY_BATTERY_NOTIFIER_BLOCK(nb) \
|
||||
SET_BATTERY_NOTIFIER_BLOCK(nb, NULL, -1)
|
||||
|
||||
static struct charger_notifier_struct charger_notifier;
|
||||
static struct pdic_notifier_struct pdic_notifier;
|
||||
|
||||
struct device *charger_device;
|
||||
struct device *pdic_device;
|
||||
|
||||
int charger_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
|
||||
charger_notifier_device_t listener)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d register\n", __func__, listener);
|
||||
|
||||
/* Check if CHARGER Notifier is ready. */
|
||||
if (!charger_device) {
|
||||
pr_err("%s: Not Initialized...\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SET_BATTERY_NOTIFIER_BLOCK(nb, notifier, listener);
|
||||
ret = blocking_notifier_chain_register(&(charger_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
|
||||
__func__, ret);
|
||||
|
||||
/* current charger's attached_device status notify */
|
||||
nb->notifier_call(nb, charger_notifier.event,
|
||||
&(charger_notifier));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int charger_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&(charger_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
|
||||
__func__, ret);
|
||||
DESTROY_BATTERY_NOTIFIER_BLOCK(nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pdic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
|
||||
pdic_notifier_device_t listener)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d register\n", __func__, listener);
|
||||
|
||||
/* Check if CHARGER Notifier is ready. */
|
||||
if (!pdic_device) {
|
||||
pr_err("%s: Not Initialized...\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SET_BATTERY_NOTIFIER_BLOCK(nb, notifier, listener);
|
||||
ret = blocking_notifier_chain_register(&(pdic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
|
||||
__func__, ret);
|
||||
|
||||
/* current pdic's attached_device status notify */
|
||||
nb->notifier_call(nb, pdic_notifier.event,
|
||||
&(pdic_notifier));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pdic_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&(pdic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
|
||||
__func__, ret);
|
||||
DESTROY_BATTERY_NOTIFIER_BLOCK(nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int battery_notifier_notify(int type)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (type) {
|
||||
case CHARGER_NOTIFY:
|
||||
ret = blocking_notifier_call_chain(&(charger_notifier.notifier_call_chain),
|
||||
charger_notifier.event, &(charger_notifier));
|
||||
break;
|
||||
case PDIC_NOTIFY:
|
||||
ret = blocking_notifier_call_chain(&(pdic_notifier.notifier_call_chain),
|
||||
pdic_notifier.event, &(pdic_notifier));
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case NOTIFY_STOP_MASK:
|
||||
case NOTIFY_BAD:
|
||||
pr_err("%s: notify error occur(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
case NOTIFY_DONE:
|
||||
case NOTIFY_OK:
|
||||
pr_info("%s: notify done(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void charger_notifier_set_property(struct charger_notifier_struct * value)
|
||||
{
|
||||
charger_notifier.event = value->event;
|
||||
switch(value->event) {
|
||||
case CHARGER_NOTIFY_EVENT_AICL:
|
||||
charger_notifier.aicl_status.input_current = value->aicl_status.input_current;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void charger_notifier_call(struct charger_notifier_struct *value)
|
||||
{
|
||||
/* charger's event broadcast */
|
||||
pr_info("%s: CHARGER_NOTIFY_EVENT :%d\n", __func__, value->event);
|
||||
charger_notifier_set_property(value);
|
||||
battery_notifier_notify(CHARGER_NOTIFY);
|
||||
}
|
||||
|
||||
static void pdic_notifier_set_property(struct pdic_notifier_struct *value)
|
||||
{
|
||||
pdic_notifier.event = value->event;
|
||||
switch(value->event) {
|
||||
case PDIC_NOTIFY_EVENT_PD_SINK:
|
||||
pdic_notifier.sink_status = value->sink_status;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pdic_notifier_call(struct pdic_notifier_struct *value)
|
||||
{
|
||||
/* pdic's event broadcast */
|
||||
pdic_notifier_set_property(value);
|
||||
battery_notifier_notify(PDIC_NOTIFY);
|
||||
}
|
||||
|
||||
int battery_notifier_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
charger_device = sec_device_create(0, NULL, "charger_notifier");
|
||||
pdic_device = sec_device_create(0, NULL, "pdic_notifier");
|
||||
if (IS_ERR(charger_device)) {
|
||||
pr_err("%s Failed to create device(charer_notifier)!\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(pdic_device)) {
|
||||
pr_err("%s Failed to create device(pdic_notifier)!\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&(charger_notifier.notifier_call_chain));
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&(pdic_notifier.notifier_call_chain));
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
device_initcall(battery_notifier_init);
|
||||
|
||||
135
drivers/battery_v2/include/charger/sm5705_charger.h
Normal file
135
drivers/battery_v2/include/charger/sm5705_charger.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* sm5705_charger.h
|
||||
* Samsung SM5705 Charger Header
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics, Inc.
|
||||
*
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SM5705_CHARGER_H
|
||||
#define __SM5705_CHARGER_H __FILE__
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/sm5705/sm5705.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include "include/sec_charging_common.h"
|
||||
/* CONFIG: Kernel Feature & Target System configuration */
|
||||
//#define SM5705_SUPPORT_AICL_CONTROL - New A series dosen't support, It's MUST be disabled
|
||||
#define SM5705_SUPPORT_OTG_CONTROL //- New A series dosen't support, It's MUST be disabled
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
#define SM5705_STATUS1_WPCINPOK (1 << 4)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
CHIP_ID = 0,
|
||||
CHARGER_OP_MODE=1,
|
||||
DATA,
|
||||
};
|
||||
ssize_t sm5705_chg_show_attrs(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
ssize_t sm5705_chg_store_attrs(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
#define SM5705_CHARGER_ATTR(_name) \
|
||||
{ \
|
||||
.attr = {.name = #_name, .mode = 0664}, \
|
||||
.show = sm5705_chg_show_attrs, \
|
||||
.store = sm5705_chg_store_attrs, \
|
||||
}
|
||||
enum {
|
||||
SM5705_MANUAL_RESET_TIME_7s = 0x1,
|
||||
SM5705_MANUAL_RESET_TIME_8s = 0x2,
|
||||
SM5705_MANUAL_RESET_TIME_9s = 0x3,
|
||||
};
|
||||
enum {
|
||||
SM5705_WATCHDOG_RESET_TIME_30s = 0x0,
|
||||
SM5705_WATCHDOG_RESET_TIME_60s = 0x1,
|
||||
SM5705_WATCHDOG_RESET_TIME_90s = 0x2,
|
||||
SM5705_WATCHDOG_RESET_TIME_120s = 0x3,
|
||||
};
|
||||
enum {
|
||||
SM5705_TOPOFF_TIMER_10m = 0x0,
|
||||
SM5705_TOPOFF_TIMER_20m = 0x1,
|
||||
SM5705_TOPOFF_TIMER_30m = 0x2,
|
||||
SM5705_TOPOFF_TIMER_45m = 0x3,
|
||||
};
|
||||
enum {
|
||||
SM5705_BUCK_BOOST_FREQ_3MHz = 0x0,
|
||||
SM5705_BUCK_BOOST_FREQ_2_4MHz = 0x1,
|
||||
SM5705_BUCK_BOOST_FREQ_1_5MHz = 0x2,
|
||||
SM5705_BUCK_BOOST_FREQ_1_8MHz = 0x3,
|
||||
};
|
||||
/* for VZW support */
|
||||
#if defined(CONFIG_TABLET_MODEL_CONCEPT) && !defined(CONFIG_SEC_FACTORY)
|
||||
#define SLOW_CHARGING_CURRENT_STANDARD 1000
|
||||
#else
|
||||
#define SLOW_CHARGING_CURRENT_STANDARD 400
|
||||
#endif
|
||||
/* SM5705 Charger - AICL reduce current configuration */
|
||||
#define REDUCE_CURRENT_STEP 100
|
||||
#define MINIMUM_INPUT_CURRENT 300
|
||||
#define AICL_VALID_CHECK_DELAY_TIME 10
|
||||
#define SM5705_EN_DISCHG_FORCE_MASK 0x02
|
||||
#define SM5705_SBPS_MASK 0x07
|
||||
struct sm5705_charger_data {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c;
|
||||
struct sec_charger_platform_data *pdata;
|
||||
struct power_supply *psy_chg;
|
||||
struct power_supply *psy_otg;
|
||||
/* for IRQ-service handling */
|
||||
int irq_aicl;
|
||||
int irq_vbus_pok;
|
||||
int irq_wpcin_pok;
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
int irq_wpcin_pok_pogo;
|
||||
int irq_wpcin_uvlo_pogo;
|
||||
#endif
|
||||
int irq_topoff;
|
||||
int irq_done;
|
||||
int irq_otgfail;
|
||||
/* for Workqueue & wake-lock, mutex process */
|
||||
struct mutex charger_mutex;
|
||||
struct workqueue_struct *wqueue;
|
||||
struct delayed_work wpc_work;
|
||||
struct delayed_work slow_chg_work;
|
||||
struct delayed_work aicl_work;
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
struct delayed_work pogo_work;
|
||||
#endif
|
||||
struct delayed_work topoff_work;
|
||||
// temp for rev2 SW WA
|
||||
struct delayed_work op_mode_switch_work; /* for WA obnormal switch case in JIG cable */
|
||||
struct wake_lock wpc_wake_lock;
|
||||
struct wake_lock afc_wake_lock;
|
||||
#if defined(SM5705_SW_SOFT_START)
|
||||
struct wake_lock softstart_wake_lock;
|
||||
#endif
|
||||
struct wake_lock check_slow_wake_lock;
|
||||
struct wake_lock aicl_wake_lock;
|
||||
/* for charging operation handling */
|
||||
int status;
|
||||
int charge_mode;
|
||||
unsigned int is_charging;
|
||||
unsigned int cable_type;
|
||||
unsigned int input_current;
|
||||
unsigned int charging_current;
|
||||
int irq_wpcin_state;
|
||||
int aicl_on;
|
||||
bool topoff_pending;
|
||||
// temp for rev2 SW WA
|
||||
bool is_rev2_wa_done;
|
||||
bool slow_late_chg_mode;
|
||||
};
|
||||
extern int sm5705_call_fg_device_id(void);
|
||||
#if defined(SM5705_WATCHDOG_RESET_ACTIVATE)
|
||||
extern void sm5705_charger_watchdog_timer_keepalive(void);
|
||||
#endif
|
||||
#endif /* __SM5705_CHARGER_H */
|
||||
45
drivers/battery_v2/include/charger/sm5705_charger_oper.h
Normal file
45
drivers/battery_v2/include/charger/sm5705_charger_oper.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* drivers/battery/sm5705_charger_oper.h
|
||||
*
|
||||
* SM5705 Charger Operation Mode controller
|
||||
*
|
||||
* Copyright (C) 2015 Siliconmitus Technology Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/mfd/sm5705/sm5705.h>
|
||||
|
||||
enum {
|
||||
SM5705_CHARGER_OP_MODE_SUSPEND = 0x0,
|
||||
SM5705_CHARGER_OP_MODE_FACTORY = 0x1,
|
||||
SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_OFF = 0x2,
|
||||
SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON = 0x3,
|
||||
SM5705_CHARGER_OP_MODE_CHG_OFF = 0x4,
|
||||
SM5705_CHARGER_OP_MODE_CHG_ON = 0x5,
|
||||
SM5705_CHARGER_OP_MODE_FLASH_BOOST = 0x6,
|
||||
SM5705_CHARGER_OP_MODE_USB_OTG = 0x7,
|
||||
};
|
||||
enum SM5705_CHARGER_OP_EVENT_TYPE {
|
||||
SM5705_CHARGER_OP_EVENT_SUSPEND_MODE = 0x7,
|
||||
SM5705_CHARGER_OP_EVENT_VBUS = 0x5,
|
||||
SM5705_CHARGER_OP_EVENT_WPC = 0x4,
|
||||
SM5705_CHARGER_OP_EVENT_FLASH = 0x3,
|
||||
SM5705_CHARGER_OP_EVENT_TORCH = 0x2,
|
||||
SM5705_CHARGER_OP_EVENT_OTG = 0x1,
|
||||
SM5705_CHARGER_OP_EVENT_PWR_SHAR = 0x0,
|
||||
};
|
||||
#define make_OP_STATUS(vbus,wpc,flash,torch,otg,pwr_shar) (((vbus & 0x1) << SM5705_CHARGER_OP_EVENT_VBUS) | \
|
||||
((wpc & 0x1) << SM5705_CHARGER_OP_EVENT_WPC) | \
|
||||
((flash & 0x1) << SM5705_CHARGER_OP_EVENT_FLASH) | \
|
||||
((torch & 0x1) << SM5705_CHARGER_OP_EVENT_TORCH) | \
|
||||
((otg & 0x1) << SM5705_CHARGER_OP_EVENT_OTG) | \
|
||||
((pwr_shar & 0x1) << SM5705_CHARGER_OP_EVENT_PWR_SHAR))
|
||||
int sm5705_charger_oper_push_event(int event_type, bool enable);
|
||||
int sm5705_charger_oper_table_init(struct i2c_client *i2c);
|
||||
int sm5705_charger_oper_get_current_status(void);
|
||||
int sm5705_charger_oper_get_current_op_mode(void);
|
||||
220
drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge.h
Normal file
220
drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* drivers/battery/sm5705_fuelgauge.h
|
||||
*
|
||||
* Header of SiliconMitus SM5705 Fuelgauge Driver
|
||||
*
|
||||
* Copyright (C) 2015 SiliconMitus
|
||||
* Author: SW Jung
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef SM5705_FUELGAUGE_H
|
||||
#define SM5705_FUELGAUGE_H
|
||||
#include <linux/i2c.h>
|
||||
//include <linux/mfd/sm5705.h>
|
||||
#include "include/sec_charging_common.h"
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <linux/debugfs.h>
|
||||
#endif /* #ifdef CONFIG_DEBUG_FS */
|
||||
#define FG_DRIVER_VER "0.0.0.1"
|
||||
// #define ENABLE_FULL_OFFSET 1
|
||||
/*To differentiate two battery Packs: SDI & ATL*/
|
||||
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
||||
#define ENABLE_BATT_LONG_LIFE 1
|
||||
#endif
|
||||
enum {
|
||||
SDI_BATTERY_TYPE = 0,
|
||||
ATL_BATTERY_TYPE,
|
||||
UNKNOWN_TYPE
|
||||
};
|
||||
struct battery_data_t {
|
||||
const int battery_type; /* 4200 or 4350 or 4400*/
|
||||
const int battery_table[3][16];
|
||||
const int rce_value[3];
|
||||
const int dtcd_value;
|
||||
const int rs_value[4];
|
||||
const int vit_period;
|
||||
const int mix_value[2];
|
||||
const int topoff_soc[2];
|
||||
const int volt_cal;
|
||||
const int curr_cal;
|
||||
const int temp_std;
|
||||
const int temp_offset;
|
||||
const int temp_offset_cal;
|
||||
};
|
||||
struct sec_fg_info {
|
||||
/* Device_id */
|
||||
int device_id;
|
||||
/* State Of Connect */
|
||||
int online;
|
||||
/* battery SOC (capacity) */
|
||||
int batt_soc;
|
||||
/* battery voltage */
|
||||
int batt_voltage;
|
||||
/* battery AvgVoltage */
|
||||
int batt_avgvoltage;
|
||||
/* battery OCV */
|
||||
int batt_ocv;
|
||||
/* Current */
|
||||
int batt_current;
|
||||
/* battery Avg Current */
|
||||
int batt_avgcurrent;
|
||||
/* battery SOC cycle */
|
||||
int batt_soc_cycle;
|
||||
struct battery_data_t *comp_pdata;
|
||||
struct mutex param_lock;
|
||||
/* copy from platform data /
|
||||
* DTS or update by shell script */
|
||||
struct mutex io_lock;
|
||||
struct device *dev;
|
||||
int32_t temperature;; /* 0.1 deg C*/
|
||||
int32_t temp_fg;; /* 0.1 deg C*/
|
||||
/* register programming */
|
||||
int reg_addr;
|
||||
u8 reg_data[2];
|
||||
int battery_typ; /*SDI_BATTERY_TYPE or ATL_BATTERY_TYPE*/
|
||||
int batt_id_adc_check;
|
||||
int battery_table[3][16];
|
||||
#ifdef ENABLE_BATT_LONG_LIFE
|
||||
#ifdef CONFIG_BATTERY_AGE_FORECAST_DETACHABLE
|
||||
int v_max_table[3];
|
||||
int q_max_table[3];
|
||||
#else
|
||||
int v_max_table[5];
|
||||
int q_max_table[5];
|
||||
#endif
|
||||
int v_max_now;
|
||||
int q_max_now;
|
||||
#endif
|
||||
int rce_value[3];
|
||||
int dtcd_value;
|
||||
int rs_value[5]; /*rs p_mix_factor n_mix_factor max min*/
|
||||
int vit_period;
|
||||
int mix_value[2]; /*mix_rate init_blank*/
|
||||
int misc;
|
||||
int enable_topoff_soc;
|
||||
int topoff_soc;
|
||||
int top_off;
|
||||
int cycle_high_limit;
|
||||
int cycle_low_limit;
|
||||
int cycle_limit_cntl;
|
||||
int enable_v_offset_cancel_p;
|
||||
int enable_v_offset_cancel_n;
|
||||
int v_offset_cancel_level;
|
||||
int v_offset_cancel_mohm;
|
||||
int volt_cal;
|
||||
int curr_offset;
|
||||
int p_curr_cal;
|
||||
int n_curr_cal;
|
||||
int curr_lcal_en;
|
||||
int curr_lcal_0;
|
||||
int curr_lcal_1;
|
||||
int curr_lcal_2;
|
||||
int en_auto_curr_offset;
|
||||
int cntl_value;
|
||||
#ifdef ENABLE_FULL_OFFSET
|
||||
int full_offset_margin;
|
||||
int full_extra_offset;
|
||||
#endif
|
||||
int temp_std;
|
||||
int en_fg_temp_volcal;
|
||||
int fg_temp_volcal_denom;
|
||||
int fg_temp_volcal_fact;
|
||||
int en_high_fg_temp_offset;
|
||||
int high_fg_temp_offset_denom;
|
||||
int high_fg_temp_offset_fact;
|
||||
int en_low_fg_temp_offset;
|
||||
int low_fg_temp_offset_denom;
|
||||
int low_fg_temp_offset_fact;
|
||||
int en_high_fg_temp_cal;
|
||||
int high_fg_temp_p_cal_denom;
|
||||
int high_fg_temp_p_cal_fact;
|
||||
int high_fg_temp_n_cal_denom;
|
||||
int high_fg_temp_n_cal_fact;
|
||||
int en_low_fg_temp_cal;
|
||||
int low_fg_temp_p_cal_denom;
|
||||
int low_fg_temp_p_cal_fact;
|
||||
int low_fg_temp_n_cal_denom;
|
||||
int low_fg_temp_n_cal_fact;
|
||||
int en_high_temp_cal;
|
||||
int high_temp_p_cal_denom;
|
||||
int high_temp_p_cal_fact;
|
||||
int high_temp_n_cal_denom;
|
||||
int high_temp_n_cal_fact;
|
||||
int en_low_temp_cal;
|
||||
int low_temp_p_cal_denom;
|
||||
int low_temp_p_cal_fact;
|
||||
int low_temp_n_cal_denom;
|
||||
int low_temp_n_cal_fact;
|
||||
int battery_type; /* 4200 or 4350 or 4400*/
|
||||
int data_ver;
|
||||
uint32_t soc_alert_flag : 1; /* 0 : nu-occur, 1: occur */
|
||||
uint32_t volt_alert_flag : 1; /* 0 : nu-occur, 1: occur */
|
||||
uint32_t flag_full_charge : 1; /* 0 : no , 1 : yes*/
|
||||
uint32_t flag_chg_status : 1; /* 0 : discharging, 1: charging*/
|
||||
uint32_t flag_charge_health : 1; /* 0 : no , 1 : good*/
|
||||
int32_t irq_ctrl;
|
||||
int value_v_alarm;
|
||||
uint32_t is_FG_initialised;
|
||||
int iocv_error_count;
|
||||
int n_tem_poff;
|
||||
int n_tem_poff_offset;
|
||||
int l_tem_poff;
|
||||
int l_tem_poff_offset;
|
||||
/* previous battery voltage current*/
|
||||
int p_batt_voltage;
|
||||
int p_batt_current;
|
||||
};
|
||||
struct sec_fuelgauge_info {
|
||||
struct i2c_client *client;
|
||||
sec_battery_platform_data_t *pdata;
|
||||
struct power_supply *psy_fg;
|
||||
struct delayed_work isr_work;
|
||||
int cable_type;
|
||||
bool is_charging;
|
||||
bool ta_exist;
|
||||
/* HW-dedicated fuel guage info structure
|
||||
* used in individual fuel gauge file only
|
||||
* (ex. dummy_fuelgauge.c)
|
||||
*/
|
||||
struct sec_fg_info info;
|
||||
bool is_fuel_alerted;
|
||||
bool volt_alert_flag;
|
||||
struct wake_lock fuel_alert_wake_lock;
|
||||
unsigned int capacity_old; /* only for atomic calculation */
|
||||
unsigned int capacity_max; /* only for dynamic calculation */
|
||||
int raw_capacity;
|
||||
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
||||
unsigned int chg_full_soc; /* BATTERY_AGE_FORECAST */
|
||||
#endif
|
||||
bool initial_update_of_soc;
|
||||
struct mutex fg_lock;
|
||||
/* register programming */
|
||||
int reg_addr;
|
||||
u8 reg_data[2];
|
||||
int fg_irq;
|
||||
};
|
||||
|
||||
ssize_t sm5705_fg_show_attrs(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
ssize_t sm5705_fg_store_attrs(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
#ifdef CONFIG_OF
|
||||
extern void board_fuelgauge_init(void *fuelgauge);
|
||||
extern bool sec_bat_check_jig_status(void);
|
||||
#endif
|
||||
#define SM5705_FG_ATTR(_name) \
|
||||
{ \
|
||||
.attr = {.name = #_name, .mode = 0664}, \
|
||||
.show = sm5705_fg_show_attrs, \
|
||||
.store = sm5705_fg_store_attrs, \
|
||||
}
|
||||
enum {
|
||||
FG_REG = 0,
|
||||
FG_DATA,
|
||||
FG_REGS,
|
||||
};
|
||||
#endif // SM5705_FUELGAUGE_H
|
||||
118
drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge_impl.h
Normal file
118
drivers/battery_v2/include/fuelgauge/sm5705_fuelgauge_impl.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* drivers/battery/sm5705_fuelgauge-impl.h
|
||||
*
|
||||
* Header of SiliconMitus SM5705 Fuelgauge Driver Implementation
|
||||
*
|
||||
* Copyright (C) 2015 SiliconMitus
|
||||
* Author: SW Jung
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef SM5705_FUELGAUGE_IMPL_H
|
||||
#define SM5705_FUELGAUGE_IMPL_H
|
||||
/* Definitions of SM5705 Fuelgauge Registers */
|
||||
// I2C Register
|
||||
#define SM5705_REG_DEVICE_ID 0x00
|
||||
#define SM5705_REG_CNTL 0x01
|
||||
#define SM5705_REG_INTFG 0x02
|
||||
#define SM5705_REG_INTFG_MASK 0x03
|
||||
#define SM5705_REG_STATUS 0x04
|
||||
#define SM5705_REG_SOC 0x05
|
||||
#define SM5705_REG_OCV 0x06
|
||||
#define SM5705_REG_VOLTAGE 0x07
|
||||
#define SM5705_REG_CURRENT 0x08
|
||||
#define SM5705_REG_TEMPERATURE 0x09
|
||||
#define SM5705_REG_SOC_CYCLE 0x0A
|
||||
#define SM5705_REG_V_ALARM 0x0C
|
||||
#define SM5705_REG_T_ALARM 0x0D
|
||||
#define SM5705_REG_SOC_ALARM 0x0E
|
||||
#define SM5705_REG_FG_OP_STATUS 0x10
|
||||
#define SM5705_REG_TOPOFFSOC 0x12
|
||||
#define SM5705_REG_PARAM_CTRL 0x13
|
||||
#define SM5705_REG_PARAM_RUN_UPDATE 0x14
|
||||
#define SM5705_REG_SOC_CYCLE_CFG 0x15
|
||||
#define SM5705_REG_VIT_PERIOD 0x1A
|
||||
#define SM5705_REG_MIX_RATE 0x1B
|
||||
#define SM5705_REG_MIX_INIT_BLANK 0x1C
|
||||
#define SM5705_REG_RESERVED 0x1F
|
||||
#define SM5705_REG_RCE0 0x20
|
||||
#define SM5705_REG_RCE1 0x21
|
||||
#define SM5705_REG_RCE2 0x22
|
||||
#define SM5705_REG_DTCD 0x23
|
||||
#define SM5705_REG_AUTO_RS_MAN 0x24
|
||||
#define SM5705_REG_RS_MIX_FACTOR 0x25
|
||||
#define SM5705_REG_RS_MAX 0x26
|
||||
#define SM5705_REG_RS_MIN 0x27
|
||||
#define SM5705_REG_RS_TUNE 0x28
|
||||
#define SM5705_REG_RS_MAN 0x29
|
||||
//for cal
|
||||
#define SM5705_REG_CURR_CAL 0x2C
|
||||
#define SM5705_REG_IOCV_MAN 0x2E
|
||||
#define SM5705_REG_END_V_IDX 0x2F
|
||||
#define SM5705_REG_VOLT_CAL 0x50
|
||||
#define SM5705_REG_CURR_OFF 0x51
|
||||
#define SM5705_REG_CURR_P_SLOPE 0x52
|
||||
#define SM5705_REG_CURR_N_SLOPE 0x53
|
||||
#define SM5705_REG_CURRLCAL_0 0x54
|
||||
#define SM5705_REG_CURRLCAL_1 0x55
|
||||
#define SM5705_REG_CURRLCAL_2 0x56
|
||||
//for debug
|
||||
#define SM5705_REG_OCV_STATE 0x80
|
||||
#define SM5705_REG_CURRENT_EST 0x85
|
||||
#define SM5705_REG_CURRENT_ERR 0x86
|
||||
#define SM5705_REG_Q_EST 0x87
|
||||
#define SM5705_AUX_STAT 0x94
|
||||
//etc
|
||||
#define SM5705_REG_MISC 0x90
|
||||
#define SM5705_REG_RESET 0x91
|
||||
#define SM5705_FG_INIT_MARK 0xA000
|
||||
#define SM5705_FG_PARAM_UNLOCK_CODE 0x3700
|
||||
#define SM5705_FG_PARAM_LOCK_CODE 0x0000
|
||||
#define SM5705_FG_TABLE_LEN 0xF//real table length -1
|
||||
//start reg addr for table
|
||||
#define SM5705_REG_TABLE_START 0xA0
|
||||
#define SM5705_REG_IOCV_B_L_MIN 0x30
|
||||
#define SM5705_REG_IOCV_B_L_MAX 0x35
|
||||
#define SM5705_REG_IOCV_B_C_MIN 0x36
|
||||
#define SM5705_REG_IOCV_B_C_MAX 0x3B
|
||||
#define SM5705_REG_IOCI_B_L_MIN 0x40
|
||||
#define SM5705_REG_IOCI_B_L_MAX 0x45
|
||||
#define SM5705_REG_IOCI_B_C_MIN 0x46
|
||||
#define SM5705_REG_IOCI_B_C_MAX 0x4B
|
||||
#define SW_RESET_CODE 0x00A6
|
||||
#define SW_RESET_OTP_CODE 0x01A6
|
||||
#define RS_MAN_CNTL 0x0800
|
||||
// control register value
|
||||
#define ENABLE_MIX_MODE 0x8000
|
||||
#define ENABLE_TEMP_MEASURE 0x4000
|
||||
#define ENABLE_TOPOFF_SOC 0x2000
|
||||
#define ENABLE_RS_MAN_MODE 0x0800
|
||||
#define ENABLE_MANUAL_OCV 0x0400
|
||||
#define ENABLE_MODE_nENQ4 0x0200
|
||||
#define ENABLE_SOC_ALARM 0x0008
|
||||
#define ENABLE_T_H_ALARM 0x0004
|
||||
#define ENABLE_T_L_ALARM 0x0002
|
||||
#define ENABLE_V_ALARM 0x0001
|
||||
#define CNTL_REG_DEFAULT_VALUE 0x2008
|
||||
#define INIT_CHECK_MASK 0x0010
|
||||
#define DISABLE_RE_INIT 0x0010
|
||||
#define SM5705_JIG_CONNECTED 0x0001
|
||||
#define SM5705_BATTERY_VERSION 0x00F0
|
||||
#define TOPOFF_SOC_97 0x111
|
||||
#define TOPOFF_SOC_96 0x110
|
||||
#define TOPOFF_SOC_95 0x101
|
||||
#define TOPOFF_SOC_94 0x100
|
||||
#define TOPOFF_SOC_93 0x011
|
||||
#define TOPOFF_SOC_92 0x010
|
||||
#define TOPOFF_SOC_91 0x001
|
||||
#define TOPOFF_SOC_90 0x000
|
||||
#define MASK_L_SOC_INT 0x0008
|
||||
#define MASK_H_TEM_INT 0x0004
|
||||
#define MASK_L_TEM_INT 0x0002
|
||||
#define MASK_L_VOL_INT 0x0001
|
||||
#define FULL_SOC 100
|
||||
#endif // SM5705_FUELGAUGE_IMPL_H
|
||||
32
drivers/battery_v2/include/sec_adc.h
Normal file
32
drivers/battery_v2/include/sec_adc.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* sec_adc.h
|
||||
* Samsung Mobile Charger Header
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics, Inc.
|
||||
*
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SEC_ADC_H
|
||||
#define __SEC_ADC_H __FILE__
|
||||
|
||||
#include <linux/iio/consumer.h>
|
||||
#include "sec_battery.h"
|
||||
#include "sec_charging_common.h"
|
||||
|
||||
#define VENDOR_UNKNOWN 0
|
||||
#define VENDOR_LSI 1
|
||||
#define VENDOR_QCOM 2
|
||||
#define RETRY_CNT 3
|
||||
|
||||
#endif /* __SEC_ADC_H */
|
||||
|
||||
658
drivers/battery_v2/include/sec_battery.h
Normal file
658
drivers/battery_v2/include/sec_battery.h
Normal file
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* sec_battery.h
|
||||
* Samsung Mobile Battery Header
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics, Inc.
|
||||
*
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SEC_BATTERY_H
|
||||
#define __SEC_BATTERY_H __FILE__
|
||||
|
||||
#include "sec_charging_common.h"
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/alarmtimer.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/qpnp/qpnp-adc.h>
|
||||
|
||||
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
|
||||
#include <linux/usb/manager/usb_typec_manager_notifier.h>
|
||||
#else
|
||||
#if defined(CONFIG_CCIC_NOTIFIER)
|
||||
#include <linux/ccic/ccic_notifier.h>
|
||||
#endif /* CONFIG_CCIC_NOTIFIER */
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
#include <linux/muic/muic.h>
|
||||
#include <linux/muic/muic_notifier.h>
|
||||
#else
|
||||
#include <linux/muic/muic.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BATTERY_NOTIFIER)
|
||||
#include <linux/battery/battery_notifier.h>
|
||||
#endif
|
||||
#if defined(CONFIG_VBUS_NOTIFIER)
|
||||
#include <linux/vbus_notifier.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BATTERY_CISD)
|
||||
#include "sec_cisd.h"
|
||||
#endif
|
||||
|
||||
#include "sec_adc.h"
|
||||
|
||||
#define SEC_BAT_CURRENT_EVENT_NONE 0x00000
|
||||
#define SEC_BAT_CURRENT_EVENT_AFC 0x00001
|
||||
#define SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE 0x00002
|
||||
#define SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL 0x00004
|
||||
#define SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING 0x00010
|
||||
#define SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING 0x00020
|
||||
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
|
||||
#define SEC_BAT_CURRENT_EVENT_USB_100MA 0x00040
|
||||
#else
|
||||
#define SEC_BAT_CURRENT_EVENT_USB_100MA 0x00000
|
||||
#endif
|
||||
#define SEC_BAT_CURRENT_EVENT_LOW_TEMP 0x00080
|
||||
#define SEC_BAT_CURRENT_EVENT_SWELLING_MODE (SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING | SEC_BAT_CURRENT_EVENT_LOW_TEMP | SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING)
|
||||
#define SEC_BAT_CURRENT_EVENT_USB_SUPER 0x00100
|
||||
#define SEC_BAT_CURRENT_EVENT_CHG_LIMIT 0x00200
|
||||
#define SEC_BAT_CURRENT_EVENT_CALL 0x00400
|
||||
#define SEC_BAT_CURRENT_EVENT_SLATE 0x00800
|
||||
#define SEC_BAT_CURRENT_EVENT_VBAT_OVP 0x01000
|
||||
#define SEC_BAT_CURRENT_EVENT_VSYS_OVP 0x02000
|
||||
#define SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK 0x04000
|
||||
#define SEC_BAT_CURRENT_EVENT_AICL 0x08000
|
||||
#define SEC_BAT_CURRENT_EVENT_HV_DISABLE 0x10000
|
||||
#define SEC_BAT_CURRENT_EVENT_SELECT_PDO 0x020000
|
||||
|
||||
#define SIOP_EVENT_NONE 0x0000
|
||||
#define SIOP_EVENT_WPC_CALL 0x0001
|
||||
|
||||
#if defined(CONFIG_SEC_FACTORY) // SEC_FACTORY
|
||||
#define STORE_MODE_CHARGING_MAX 80
|
||||
#define STORE_MODE_CHARGING_MIN 70
|
||||
#else // !SEC_FACTORY, STORE MODE
|
||||
#define STORE_MODE_CHARGING_MAX 70
|
||||
#define STORE_MODE_CHARGING_MIN 60
|
||||
#define STORE_MODE_CHARGING_MAX_VZW 35
|
||||
#define STORE_MODE_CHARGING_MIN_VZW 30
|
||||
#endif //(CONFIG_SEC_FACTORY)
|
||||
|
||||
#define ADC_CH_COUNT 10
|
||||
#define ADC_SAMPLE_COUNT 10
|
||||
|
||||
#define DEFAULT_HEALTH_CHECK_COUNT 5
|
||||
#define TEMP_HIGHLIMIT_DEFAULT 2000
|
||||
|
||||
#define SIOP_INPUT_LIMIT_CURRENT 1200
|
||||
#define SIOP_CHARGING_LIMIT_CURRENT 1000
|
||||
#define SIOP_WIRELESS_INPUT_LIMIT_CURRENT 530
|
||||
#define SIOP_WIRELESS_CHARGING_LIMIT_CURRENT 780
|
||||
#define SIOP_HV_WIRELESS_INPUT_LIMIT_CURRENT 700
|
||||
#define SIOP_HV_WIRELESS_CHARGING_LIMIT_CURRENT 600
|
||||
#define SIOP_STORE_HV_WIRELESS_CHARGING_LIMIT_CURRENT 450
|
||||
#define SIOP_HV_INPUT_LIMIT_CURRENT 1200
|
||||
#define SIOP_HV_CHARGING_LIMIT_CURRENT 1000
|
||||
#define SIOP_HV_12V_INPUT_LIMIT_CURRENT 535
|
||||
#define SIOP_HV_12V_CHARGING_LIMIT_CURRENT 1000
|
||||
|
||||
#define BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE 0x00000001
|
||||
#define BATT_MISC_EVENT_WIRELESS_BACKPACK_TYPE 0x00000002
|
||||
#define BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE 0x00000004
|
||||
#define BATT_MISC_EVENT_BATT_RESET_SOC 0x00000008
|
||||
#define BATT_MISC_EVENT_HICCUP_TYPE 0x00000020
|
||||
#define BATT_MISC_EVENT_WIRELESS_FOD 0x00000100
|
||||
#define BATT_MISC_EVENT_HEALTH_OVERHEATLIMIT 0x00100000
|
||||
|
||||
#define SEC_INPUT_VOLTAGE_0V 0
|
||||
#define SEC_INPUT_VOLTAGE_5V 5
|
||||
#define SEC_INPUT_VOLTAGE_9V 9
|
||||
#define SEC_INPUT_VOLTAGE_10V 10
|
||||
#define SEC_INPUT_VOLTAGE_12V 12
|
||||
|
||||
#define HV_CHARGER_STATUS_STANDARD1 12000 /* mW */
|
||||
#define HV_CHARGER_STATUS_STANDARD2 20000 /* mW */
|
||||
|
||||
#if defined(CONFIG_CCIC_NOTIFIER)
|
||||
struct sec_bat_pdic_info {
|
||||
unsigned int input_voltage;
|
||||
unsigned int input_current;
|
||||
unsigned int pdo_index;
|
||||
};
|
||||
struct sec_bat_pdic_list {
|
||||
struct sec_bat_pdic_info pd_info[8]; /* 5V ~ 12V */
|
||||
unsigned int now_pd_index;
|
||||
unsigned int max_pd_count;
|
||||
};
|
||||
#endif
|
||||
#if defined(CONFIG_BATTERY_SWELLING)
|
||||
enum swelling_mode_state {
|
||||
SWELLING_MODE_NONE = 0,
|
||||
SWELLING_MODE_CHARGING,
|
||||
SWELLING_MODE_FULL,
|
||||
};
|
||||
#endif
|
||||
struct adc_sample_info {
|
||||
unsigned int cnt;
|
||||
int total_adc;
|
||||
int average_adc;
|
||||
int adc_arr[ADC_SAMPLE_COUNT];
|
||||
int index;
|
||||
};
|
||||
|
||||
struct sec_ttf_data;
|
||||
|
||||
struct sec_battery_info {
|
||||
struct device *dev;
|
||||
sec_battery_platform_data_t *pdata;
|
||||
struct sec_ttf_data *ttf_d;
|
||||
|
||||
/* power supply used in Android */
|
||||
struct power_supply *psy_bat;
|
||||
struct power_supply *psy_usb;
|
||||
struct power_supply *psy_ac;
|
||||
struct power_supply *psy_wireless;
|
||||
struct power_supply *psy_ps;
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
struct power_supply *psy_pogo;
|
||||
#endif
|
||||
unsigned int irq;
|
||||
|
||||
int pd_usb_attached;
|
||||
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
|
||||
struct notifier_block usb_typec_nb;
|
||||
#else
|
||||
#if defined(CONFIG_CCIC_NOTIFIER)
|
||||
struct notifier_block pdic_nb;
|
||||
#endif
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
struct notifier_block batt_nb;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CCIC_NOTIFIER)
|
||||
bool pdic_attach;
|
||||
bool pdic_ps_rdy;
|
||||
struct pdic_notifier_struct pdic_info;
|
||||
struct sec_bat_pdic_list pd_list;
|
||||
#endif
|
||||
bool update_pd_list;
|
||||
|
||||
#if defined(CONFIG_VBUS_NOTIFIER)
|
||||
struct notifier_block vbus_nb;
|
||||
int muic_vbus_status;
|
||||
#endif
|
||||
|
||||
bool is_sysovlo;
|
||||
bool is_vbatovlo;
|
||||
|
||||
bool safety_timer_set;
|
||||
bool lcd_status;
|
||||
bool skip_swelling;
|
||||
|
||||
int status;
|
||||
int health;
|
||||
bool present;
|
||||
|
||||
int voltage_now; /* cell voltage (mV) */
|
||||
int voltage_avg; /* average voltage (mV) */
|
||||
int voltage_ocv; /* open circuit voltage (mV) */
|
||||
int current_now; /* current (mA) */
|
||||
int inbat_adc; /* inbat adc */
|
||||
int current_avg; /* average current (mA) */
|
||||
int current_max; /* input current limit (mA) */
|
||||
int current_adc;
|
||||
|
||||
unsigned int capacity; /* SOC (%) */
|
||||
unsigned int input_voltage; /* CHGIN/WCIN input voltage (V) */
|
||||
unsigned int charge_power; /* charge power (mW) */
|
||||
unsigned int max_charge_power; /* max charge power (mW) */
|
||||
unsigned int pd_max_charge_power; /* max charge power for pd (mW) */
|
||||
unsigned int aicl_current;
|
||||
|
||||
struct mutex adclock;
|
||||
struct adc_sample_info adc_sample[ADC_CH_COUNT];
|
||||
|
||||
/* keep awake until monitor is done */
|
||||
struct wake_lock monitor_wake_lock;
|
||||
struct workqueue_struct *monitor_wqueue;
|
||||
struct delayed_work monitor_work;
|
||||
#ifdef CONFIG_SAMSUNG_BATTERY_FACTORY
|
||||
struct wake_lock lpm_wake_lock;
|
||||
#endif
|
||||
unsigned int polling_count;
|
||||
unsigned int polling_time;
|
||||
bool polling_in_sleep;
|
||||
bool polling_short;
|
||||
|
||||
struct delayed_work polling_work;
|
||||
struct alarm polling_alarm;
|
||||
ktime_t last_poll_time;
|
||||
|
||||
#if defined(CONFIG_BATTERY_CISD)
|
||||
struct cisd cisd;
|
||||
bool skip_cisd;
|
||||
bool usb_overheat_check;
|
||||
int prev_volt;
|
||||
int prev_temp;
|
||||
int prev_jig_on;
|
||||
int enable_update_data;
|
||||
int prev_chg_on;
|
||||
#endif
|
||||
|
||||
/* battery check */
|
||||
unsigned int check_count;
|
||||
/* ADC check */
|
||||
unsigned int check_adc_count;
|
||||
unsigned int check_adc_value;
|
||||
|
||||
/* health change check*/
|
||||
bool health_change;
|
||||
/* ovp-uvlo health check */
|
||||
int health_check_count;
|
||||
|
||||
/* time check */
|
||||
unsigned long charging_start_time;
|
||||
unsigned long charging_passed_time;
|
||||
unsigned long charging_next_time;
|
||||
unsigned long charging_fullcharged_time;
|
||||
|
||||
unsigned long wc_heating_start_time;
|
||||
unsigned long wc_heating_passed_time;
|
||||
unsigned int wc_heat_limit;
|
||||
|
||||
/* chg temperature check */
|
||||
unsigned int chg_limit;
|
||||
unsigned int chg_limit_recovery_cable;
|
||||
unsigned int vbus_chg_by_siop;
|
||||
unsigned int vbus_chg_by_full;
|
||||
unsigned int mix_limit;
|
||||
unsigned int vbus_limit;
|
||||
|
||||
/* temperature check */
|
||||
int temperature; /* battery temperature */
|
||||
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
|
||||
int temperature_test_battery;
|
||||
int temperature_test_usb;
|
||||
int temperature_test_wpc;
|
||||
int temperature_test_chg;
|
||||
int temperature_test_blkt;
|
||||
#endif
|
||||
int temper_amb; /* target temperature */
|
||||
int usb_temp;
|
||||
int chg_temp; /* charger temperature */
|
||||
int wpc_temp;
|
||||
int coil_temp;
|
||||
int slave_chg_temp;
|
||||
int blkt_temp; /* blanket temperature(instead of batt temp in mix_temp func for tablet model) */
|
||||
|
||||
int temp_adc;
|
||||
int temp_ambient_adc;
|
||||
int usb_temp_adc;
|
||||
int chg_temp_adc;
|
||||
int wpc_temp_adc;
|
||||
int coil_temp_adc;
|
||||
int slave_chg_temp_adc;
|
||||
int blkt_temp_adc;
|
||||
|
||||
int temp_highlimit_threshold;
|
||||
int temp_highlimit_recovery;
|
||||
int temp_high_threshold;
|
||||
int temp_high_recovery;
|
||||
int temp_low_threshold;
|
||||
int temp_low_recovery;
|
||||
|
||||
/* charging */
|
||||
unsigned int charging_mode;
|
||||
bool is_recharging;
|
||||
int wdt_kick_disable;
|
||||
|
||||
bool is_jig_on;
|
||||
int cable_type;
|
||||
int muic_cable_type;
|
||||
int extended_cable_type;
|
||||
|
||||
struct wake_lock cable_wake_lock;
|
||||
struct delayed_work cable_work;
|
||||
struct wake_lock vbus_wake_lock;
|
||||
struct delayed_work siop_work;
|
||||
struct wake_lock siop_wake_lock;
|
||||
struct wake_lock afc_wake_lock;
|
||||
struct delayed_work afc_work;
|
||||
#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE)
|
||||
struct delayed_work update_work;
|
||||
struct delayed_work fw_init_work;
|
||||
#endif
|
||||
struct delayed_work siop_event_work;
|
||||
struct wake_lock siop_event_wake_lock;
|
||||
struct delayed_work siop_level_work;
|
||||
struct wake_lock siop_level_wake_lock;
|
||||
struct delayed_work wc_headroom_work;
|
||||
struct wake_lock wc_headroom_wake_lock;
|
||||
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
||||
struct delayed_work batt_data_work;
|
||||
struct wake_lock batt_data_wake_lock;
|
||||
char *data_path;
|
||||
#endif
|
||||
#ifdef CONFIG_OF
|
||||
struct delayed_work parse_mode_dt_work;
|
||||
struct wake_lock parse_mode_dt_wake_lock;
|
||||
#endif
|
||||
struct delayed_work init_chg_work;
|
||||
|
||||
char batt_type[48];
|
||||
unsigned int full_check_cnt;
|
||||
unsigned int recharge_check_cnt;
|
||||
|
||||
struct mutex iolock;
|
||||
int input_current;
|
||||
int charging_current;
|
||||
int topoff_current;
|
||||
int wpc_vout_level;
|
||||
unsigned int current_event;
|
||||
|
||||
/* wireless charging enable */
|
||||
struct mutex wclock;
|
||||
int wc_enable;
|
||||
int wc_enable_cnt;
|
||||
int wc_enable_cnt_value;
|
||||
int led_cover;
|
||||
int wc_status;
|
||||
bool wc_cv_mode;
|
||||
bool wc_pack_max_curr;
|
||||
|
||||
int wire_status;
|
||||
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
/* pogo status */
|
||||
int pogo_status;
|
||||
#endif
|
||||
/* wearable charging */
|
||||
int ps_status;
|
||||
int ps_enable;
|
||||
|
||||
/* test mode */
|
||||
int test_mode;
|
||||
bool factory_mode;
|
||||
bool store_mode;
|
||||
|
||||
/* MTBF test for CMCC */
|
||||
bool is_hc_usb;
|
||||
|
||||
int siop_level;
|
||||
int siop_event;
|
||||
int siop_prev_event;
|
||||
int stability_test;
|
||||
int eng_not_full_status;
|
||||
|
||||
bool skip_chg_temp_check;
|
||||
bool skip_wpc_temp_check;
|
||||
bool wpc_temp_mode;
|
||||
#if defined(CONFIG_BATTERY_SWELLING)
|
||||
unsigned int swelling_mode;
|
||||
#endif
|
||||
#if defined(CONFIG_AFC_CHARGER_MODE)
|
||||
char *hv_chg_name;
|
||||
#endif
|
||||
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
|
||||
struct delayed_work slowcharging_work;
|
||||
#endif
|
||||
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
||||
int batt_cycle;
|
||||
#endif
|
||||
#if defined(CONFIG_STEP_CHARGING)
|
||||
unsigned int step_charging_type;
|
||||
unsigned int step_charging_charge_power;
|
||||
int step_charging_status;
|
||||
int step_charging_step;
|
||||
#endif
|
||||
#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY)
|
||||
bool cooldown_mode;
|
||||
#endif
|
||||
struct mutex misclock;
|
||||
unsigned int misc_event;
|
||||
unsigned int prev_misc_event;
|
||||
struct delayed_work misc_event_work;
|
||||
struct wake_lock misc_event_wake_lock;
|
||||
struct mutex batt_handlelock;
|
||||
struct mutex current_eventlock;
|
||||
struct mutex typec_notylock;
|
||||
|
||||
unsigned int hiccup_status;
|
||||
|
||||
bool stop_timer;
|
||||
unsigned long prev_safety_time;
|
||||
unsigned long expired_time;
|
||||
unsigned long cal_safety_time;
|
||||
int fg_reset;
|
||||
};
|
||||
ssize_t sec_bat_show_attrs(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
ssize_t sec_bat_store_attrs(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
#define SEC_BATTERY_ATTR(_name) \
|
||||
{ \
|
||||
.attr = {.name = #_name, .mode = 0664}, \
|
||||
.show = sec_bat_show_attrs, \
|
||||
.store = sec_bat_store_attrs, \
|
||||
}
|
||||
/* event check */
|
||||
#define EVENT_NONE (0)
|
||||
#define EVENT_2G_CALL (0x1 << 0)
|
||||
#define EVENT_3G_CALL (0x1 << 1)
|
||||
#define EVENT_MUSIC (0x1 << 2)
|
||||
#define EVENT_VIDEO (0x1 << 3)
|
||||
#define EVENT_BROWSER (0x1 << 4)
|
||||
#define EVENT_HOTSPOT (0x1 << 5)
|
||||
#define EVENT_CAMERA (0x1 << 6)
|
||||
#define EVENT_CAMCORDER (0x1 << 7)
|
||||
#define EVENT_DATA_CALL (0x1 << 8)
|
||||
#define EVENT_WIFI (0x1 << 9)
|
||||
#define EVENT_WIBRO (0x1 << 10)
|
||||
#define EVENT_LTE (0x1 << 11)
|
||||
#define EVENT_LCD (0x1 << 12)
|
||||
#define EVENT_GPS (0x1 << 13)
|
||||
enum {
|
||||
BATT_RESET_SOC = 0,
|
||||
BATT_READ_RAW_SOC,
|
||||
BATT_READ_ADJ_SOC,
|
||||
BATT_TYPE,
|
||||
BATT_VFOCV,
|
||||
BATT_VOL_ADC,
|
||||
BATT_VOL_ADC_CAL,
|
||||
BATT_VOL_AVER,
|
||||
BATT_VOL_ADC_AVER,
|
||||
BATT_CURRENT_UA_NOW,
|
||||
BATT_CURRENT_UA_AVG,
|
||||
BATT_FILTER_CFG,
|
||||
BATT_TEMP,
|
||||
BATT_TEMP_ADC,
|
||||
BATT_TEMP_AVER,
|
||||
BATT_TEMP_ADC_AVER,
|
||||
USB_TEMP,
|
||||
USB_TEMP_ADC,
|
||||
BATT_CHG_TEMP,
|
||||
BATT_CHG_TEMP_ADC,
|
||||
SLAVE_CHG_TEMP,
|
||||
SLAVE_CHG_TEMP_ADC,
|
||||
BLKT_TEMP,
|
||||
BLKT_TEMP_ADC,
|
||||
BATT_VF_ADC,
|
||||
BATT_SLATE_MODE,
|
||||
BATT_LP_CHARGING,
|
||||
SIOP_ACTIVATED,
|
||||
SIOP_LEVEL,
|
||||
SIOP_EVENT,
|
||||
BATT_CHARGING_SOURCE,
|
||||
FG_REG_DUMP,
|
||||
FG_RESET_CAP,
|
||||
FG_CAPACITY,
|
||||
FG_ASOC,
|
||||
AUTH,
|
||||
CHG_CURRENT_ADC,
|
||||
WC_ADC,
|
||||
WC_STATUS,
|
||||
WC_ENABLE,
|
||||
WC_CONTROL,
|
||||
WC_CONTROL_CNT,
|
||||
LED_COVER,
|
||||
HV_CHARGER_STATUS,
|
||||
HV_WC_CHARGER_STATUS,
|
||||
HV_CHARGER_SET,
|
||||
FACTORY_MODE,
|
||||
STORE_MODE,
|
||||
UPDATE,
|
||||
TEST_MODE,
|
||||
BATT_EVENT_CALL,
|
||||
BATT_EVENT_2G_CALL,
|
||||
BATT_EVENT_TALK_GSM,
|
||||
BATT_EVENT_3G_CALL,
|
||||
BATT_EVENT_TALK_WCDMA,
|
||||
BATT_EVENT_MUSIC,
|
||||
BATT_EVENT_VIDEO,
|
||||
BATT_EVENT_BROWSER,
|
||||
BATT_EVENT_HOTSPOT,
|
||||
BATT_EVENT_CAMERA,
|
||||
BATT_EVENT_CAMCORDER,
|
||||
BATT_EVENT_DATA_CALL,
|
||||
BATT_EVENT_WIFI,
|
||||
BATT_EVENT_WIBRO,
|
||||
BATT_EVENT_LTE,
|
||||
BATT_EVENT_LCD,
|
||||
BATT_EVENT_GPS,
|
||||
BATT_EVENT,
|
||||
BATT_TEMP_TABLE,
|
||||
BATT_HIGH_CURRENT_USB,
|
||||
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
|
||||
TEST_CHARGE_CURRENT,
|
||||
#endif
|
||||
SET_STABILITY_TEST,
|
||||
BATT_CAPACITY_MAX,
|
||||
BATT_INBAT_VOLTAGE,
|
||||
BATT_INBAT_VOLTAGE_OCV,
|
||||
CHECK_SLAVE_CHG,
|
||||
BATT_INBAT_WIRELESS_CS100,
|
||||
HMT_TA_CONNECTED,
|
||||
HMT_TA_CHARGE,
|
||||
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
||||
FG_CYCLE,
|
||||
FG_FULL_VOLTAGE,
|
||||
FG_FULLCAPNOM,
|
||||
BATTERY_CYCLE,
|
||||
#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE)
|
||||
BATT_AFTER_MANUFACTURED,
|
||||
#endif
|
||||
#endif
|
||||
BATT_WPC_TEMP,
|
||||
BATT_WPC_TEMP_ADC,
|
||||
BATT_COIL_TEMP,
|
||||
BATT_COIL_TEMP_ADC,
|
||||
BATT_WIRELESS_MST_SWITCH_TEST,
|
||||
#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE)
|
||||
BATT_WIRELESS_FIRMWARE_UPDATE,
|
||||
OTP_FIRMWARE_RESULT,
|
||||
WC_IC_GRADE,
|
||||
OTP_FIRMWARE_VER_BIN,
|
||||
OTP_FIRMWARE_VER,
|
||||
TX_FIRMWARE_RESULT,
|
||||
TX_FIRMWARE_VER,
|
||||
BATT_TX_STATUS,
|
||||
#endif
|
||||
WC_VOUT,
|
||||
WC_VRECT,
|
||||
#if defined(CONFIG_WIRELESS_CHARGER_HIGH_VOLTAGE)
|
||||
BATT_HV_WIRELESS_STATUS,
|
||||
BATT_HV_WIRELESS_PAD_CTRL,
|
||||
#endif
|
||||
WC_OP_FREQ,
|
||||
WC_CMD_INFO,
|
||||
BATT_TUNE_FLOAT_VOLTAGE,
|
||||
BATT_TUNE_INPUT_CHARGE_CURRENT,
|
||||
BATT_TUNE_FAST_CHARGE_CURRENT,
|
||||
BATT_TUNE_UI_TERM_CURRENT_1ST,
|
||||
BATT_TUNE_UI_TERM_CURRENT_2ND,
|
||||
BATT_TUNE_TEMP_HIGH_NORMAL,
|
||||
BATT_TUNE_TEMP_HIGH_REC_NORMAL,
|
||||
BATT_TUNE_TEMP_LOW_NORMAL,
|
||||
BATT_TUNE_TEMP_LOW_REC_NORMAL,
|
||||
BATT_TUNE_CHG_TEMP_HIGH,
|
||||
BATT_TUNE_CHG_TEMP_REC,
|
||||
BATT_TUNE_CHG_LIMMIT_CUR,
|
||||
BATT_TUNE_COIL_TEMP_HIGH,
|
||||
BATT_TUNE_COIL_TEMP_REC,
|
||||
BATT_TUNE_COIL_LIMMIT_CUR,
|
||||
|
||||
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
||||
BATT_UPDATE_DATA,
|
||||
#endif
|
||||
|
||||
BATT_MISC_EVENT,
|
||||
BATT_EXT_DEV_CHG,
|
||||
BATT_WDT_CONTROL,
|
||||
MODE,
|
||||
CHECK_PS_READY,
|
||||
BATT_CHIP_ID,
|
||||
CISD_FULLCAPREP_MAX,
|
||||
#if defined(CONFIG_BATTERY_CISD)
|
||||
CISD_DATA,
|
||||
CISD_DATA_JSON,
|
||||
CISD_DATA_D_JSON,
|
||||
CISD_WIRE_COUNT,
|
||||
CISD_WC_DATA,
|
||||
CISD_WC_DATA_JSON,
|
||||
PREV_BATTERY_DATA,
|
||||
PREV_BATTERY_INFO,
|
||||
#endif
|
||||
SAFETY_TIMER_SET,
|
||||
BATT_SWELLING_CONTROL,
|
||||
SAFETY_TIMER_INFO,
|
||||
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
|
||||
BATT_TEMP_TEST,
|
||||
#endif
|
||||
BATT_CURRENT_EVENT,
|
||||
CHARGE_OTG_CONTROL,
|
||||
CHARGE_UNO_CONTROL,
|
||||
};
|
||||
enum {
|
||||
EXT_DEV_NONE = 0,
|
||||
EXT_DEV_GAMEPAD_CHG,
|
||||
EXT_DEV_GAMEPAD_OTG,
|
||||
};
|
||||
|
||||
extern unsigned int lpcharge;
|
||||
|
||||
extern void select_pdo(int num);
|
||||
extern int adc_read(struct sec_battery_info *battery, int channel);
|
||||
extern void adc_init(struct platform_device *pdev, struct sec_battery_info *battery);
|
||||
extern void adc_exit(struct sec_battery_info *battery);
|
||||
extern void sec_cable_init(struct platform_device *pdev, struct sec_battery_info *battery);
|
||||
extern int sec_bat_get_adc_data(struct sec_battery_info *battery, int adc_ch, int count);
|
||||
extern int sec_bat_get_charger_type_adc(struct sec_battery_info *battery);
|
||||
extern bool sec_bat_get_value_by_adc(struct sec_battery_info *battery, enum sec_battery_adc_channel channel, union power_supply_propval *value);
|
||||
extern int sec_bat_get_adc_value(struct sec_battery_info *battery, int channel);
|
||||
extern bool sec_bat_check_vf_adc(struct sec_battery_info *battery);
|
||||
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
||||
extern int sec_battery_update_data(const char* file_path);
|
||||
#endif
|
||||
#if defined(CONFIG_BATTERY_CISD)
|
||||
extern bool sec_bat_cisd_check(struct sec_battery_info *battery);
|
||||
extern void sec_battery_cisd_init(struct sec_battery_info *battery);
|
||||
extern void set_cisd_pad_data(struct sec_battery_info *battery, const char* buf);
|
||||
#endif
|
||||
bool sec_bat_hv_wc_normal_mode_check(struct sec_battery_info *battery);
|
||||
#endif /* __SEC_BATTERY_H */
|
||||
67
drivers/battery_v2/include/sec_battery_ttf.h
Normal file
67
drivers/battery_v2/include/sec_battery_ttf.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* sec_battery.h
|
||||
* Samsung Mobile Battery Header
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics, Inc.
|
||||
*
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SEC_BATTERY_TTF_H
|
||||
#define __SEC_BATTERY_TTF_H __FILE__
|
||||
|
||||
struct sec_cv_slope {
|
||||
int fg_current;
|
||||
int soc;
|
||||
int time;
|
||||
};
|
||||
|
||||
struct sec_battery_info;
|
||||
|
||||
struct sec_ttf_data {
|
||||
void *pdev;
|
||||
int timetofull;
|
||||
|
||||
unsigned int ttf_hv_12v_charge_current;
|
||||
unsigned int ttf_hv_charge_current;
|
||||
unsigned int ttf_hv_12v_wireless_charge_current;
|
||||
unsigned int ttf_hv_wireless_charge_current;
|
||||
unsigned int ttf_wireless_charge_current;
|
||||
unsigned int ttf_dc25_charge_current;
|
||||
unsigned int ttf_dc45_charge_current;
|
||||
unsigned int ttf_predict_wc20_charge_current;
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
unsigned int ttf_pogo_charge_current;
|
||||
#endif
|
||||
|
||||
unsigned int max_charging_current;
|
||||
unsigned int pd_charging_charge_power;
|
||||
|
||||
struct sec_cv_slope *cv_data;
|
||||
int cv_data_length;
|
||||
unsigned int ttf_capacity;
|
||||
|
||||
struct delayed_work timetofull_work;
|
||||
};
|
||||
|
||||
int sec_calc_ttf(struct sec_battery_info *battery, unsigned int ttf_curr);
|
||||
extern void sec_bat_calc_time_to_full(struct sec_battery_info *battery);
|
||||
extern void sec_bat_time_to_full_work(struct work_struct *work);
|
||||
extern void ttf_init(struct sec_battery_info *battery);
|
||||
extern void ttf_work_start(struct sec_battery_info *battery);
|
||||
extern int ttf_display(struct sec_battery_info *battery);
|
||||
#ifdef CONFIG_OF
|
||||
int sec_ttf_parse_dt(struct sec_battery_info *battery);
|
||||
#endif
|
||||
|
||||
#endif /* __SEC_BATTERY_H */
|
||||
1143
drivers/battery_v2/include/sec_charging_common.h
Normal file
1143
drivers/battery_v2/include/sec_charging_common.h
Normal file
File diff suppressed because it is too large
Load Diff
314
drivers/battery_v2/include/sec_cisd.h
Normal file
314
drivers/battery_v2/include/sec_cisd.h
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* sec_cisd.h
|
||||
* Samsung Mobile Charger Header
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics, Inc.
|
||||
*
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef __SEC_CISD_H
|
||||
#define __SEC_CISD_H __FILE__
|
||||
|
||||
#define CISD_STATE_NONE 0x00
|
||||
#define CISD_STATE_CAP_OVERFLOW 0x01
|
||||
#define CISD_STATE_VOLT_DROP 0x02
|
||||
#define CISD_STATE_SOC_DROP 0x04
|
||||
#define CISD_STATE_RESET 0x08
|
||||
#define CISD_STATE_LEAK_A 0x10
|
||||
#define CISD_STATE_LEAK_B 0x20
|
||||
#define CISD_STATE_LEAK_C 0x40
|
||||
#define CISD_STATE_LEAK_D 0x80
|
||||
#define CISD_STATE_OVER_VOLTAGE 0x100
|
||||
#define CISD_STATE_LEAK_E 0x200
|
||||
#define CISD_STATE_LEAK_F 0x400
|
||||
#define CISD_STATE_LEAK_G 0x800
|
||||
|
||||
#define is_cisd_check_type(cable_type) ( \
|
||||
cable_type == SEC_BATTERY_CABLE_TA || \
|
||||
cable_type == SEC_BATTERY_CABLE_9V_TA || \
|
||||
cable_type == SEC_BATTERY_CABLE_9V_UNKNOWN || \
|
||||
cable_type == SEC_BATTERY_CABLE_9V_ERR || \
|
||||
cable_type == SEC_BATTERY_CABLE_PDIC)
|
||||
|
||||
#if 0
|
||||
enum cisd_data {
|
||||
CISD_DATA_FULL_COUNT = 0,
|
||||
CISD_DATA_CAP_MAX,
|
||||
CISD_DATA_CAP_MIN,
|
||||
CISD_DATA_CAP_ONCE,
|
||||
CISD_DATA_LEAKAGE_A,
|
||||
CISD_DATA_LEAKAGE_B,
|
||||
CISD_DATA_LEAKAGE_C,
|
||||
CISD_DATA_LEAKAGE_D,
|
||||
CISD_DATA_CAP_PER_TIME,
|
||||
CISD_DATA_ERRCAP_LOW,
|
||||
CISD_DATA_ERRCAP_HIGH,
|
||||
|
||||
CISD_DATA_OVER_VOLTAGE,
|
||||
CISD_DATA_LEAKAGE_E,
|
||||
CISD_DATA_LEAKAGE_F,
|
||||
CISD_DATA_LEAKAGE_G,
|
||||
CISD_DATA_RECHARGING_TIME,
|
||||
CISD_DATA_VALERT_COUNT,
|
||||
CISD_DATA_CYCLE,
|
||||
CISD_DATA_WIRE_COUNT,
|
||||
CISD_DATA_WIRELESS_COUNT,
|
||||
CISD_DATA_HIGH_TEMP_SWELLING,
|
||||
|
||||
CISD_DATA_LOW_TEMP_SWELLING,
|
||||
CISD_DATA_SWELLING_CHARGING_COUNT,
|
||||
CISD_DATA_SAFETY_TIMER_3,
|
||||
CISD_DATA_SAFETY_TIMER_5,
|
||||
CISD_DATA_SAFETY_TIMER_10,
|
||||
CISD_DATA_AICL_COUNT,
|
||||
CISD_DATA_BATT_TEMP_MAX,
|
||||
CISD_DATA_BATT_TEMP_MIN,
|
||||
CISD_DATA_CHG_TEMP_MAX,
|
||||
CISD_DATA_CHG_TEMP_MIN,
|
||||
|
||||
CISD_DATA_WPC_TEMP_MAX,
|
||||
CISD_DATA_WPC_TEMP_MIN,
|
||||
CISD_UNSAFE_VOLTAGE,
|
||||
CISD_UNSAFE_TEMPERATURE,
|
||||
CISD_SAFETY_TIMER,
|
||||
CISD_VSYS_OVP,
|
||||
CISD_VBAT_OVP,
|
||||
CISD_WATER_DETECT,
|
||||
CISD_AFC_FAIL,
|
||||
|
||||
CISD_DATA_MAX,
|
||||
};
|
||||
#endif
|
||||
|
||||
enum cisd_data {
|
||||
CISD_DATA_RESET_ALG = 0,
|
||||
|
||||
CISD_DATA_ALG_INDEX,
|
||||
CISD_DATA_FULL_COUNT,
|
||||
CISD_DATA_CAP_MAX,
|
||||
CISD_DATA_CAP_MIN,
|
||||
CISD_DATA_RECHARGING_COUNT,
|
||||
CISD_DATA_VALERT_COUNT,
|
||||
CISD_DATA_CYCLE,
|
||||
CISD_DATA_WIRE_COUNT,
|
||||
CISD_DATA_WIRELESS_COUNT,
|
||||
CISD_DATA_HIGH_TEMP_SWELLING,
|
||||
|
||||
CISD_DATA_LOW_TEMP_SWELLING,
|
||||
CISD_DATA_SWELLING_CHARGING_COUNT,
|
||||
CISD_DATA_SWELLING_FULL_CNT,
|
||||
CISD_DATA_SWELLING_RECOVERY_CNT,
|
||||
CISD_DATA_AICL_COUNT,
|
||||
CISD_DATA_BATT_TEMP_MAX,
|
||||
CISD_DATA_BATT_TEMP_MIN,
|
||||
CISD_DATA_CHG_TEMP_MAX,
|
||||
CISD_DATA_CHG_TEMP_MIN,
|
||||
CISD_DATA_WPC_TEMP_MAX,
|
||||
|
||||
CISD_DATA_WPC_TEMP_MIN,
|
||||
CISD_DATA_USB_TEMP_MAX,
|
||||
CISD_DATA_USB_TEMP_MIN,
|
||||
CISD_DATA_CHG_BATT_TEMP_MAX,
|
||||
CISD_DATA_CHG_BATT_TEMP_MIN,
|
||||
CISD_DATA_CHG_CHG_TEMP_MAX,
|
||||
CISD_DATA_CHG_CHG_TEMP_MIN,
|
||||
CISD_DATA_CHG_WPC_TEMP_MAX,
|
||||
CISD_DATA_CHG_WPC_TEMP_MIN,
|
||||
CISD_DATA_CHG_USB_TEMP_MAX,
|
||||
|
||||
CISD_DATA_CHG_USB_TEMP_MIN,
|
||||
CISD_DATA_USB_OVERHEAT_CHARGING, /* 32 */
|
||||
CISD_DATA_UNSAFETY_VOLTAGE,
|
||||
CISD_DATA_UNSAFETY_TEMPERATURE,
|
||||
CISD_DATA_SAFETY_TIMER,
|
||||
CISD_DATA_VSYS_OVP,
|
||||
CISD_DATA_VBAT_OVP,
|
||||
CISD_DATA_AFC_FAIL,
|
||||
CISD_DATA_BUCK_OFF,
|
||||
CISD_DATA_WATER_DETECT,
|
||||
|
||||
CISD_DATA_DROP_VALUE,
|
||||
|
||||
CISD_DATA_MAX,
|
||||
};
|
||||
|
||||
enum cisd_data_per_day {
|
||||
CISD_DATA_FULL_COUNT_PER_DAY = CISD_DATA_MAX,
|
||||
|
||||
CISD_DATA_CAP_MAX_PER_DAY,
|
||||
CISD_DATA_CAP_MIN_PER_DAY,
|
||||
CISD_DATA_RECHARGING_COUNT_PER_DAY,
|
||||
CISD_DATA_VALERT_COUNT_PER_DAY,
|
||||
CISD_DATA_WIRE_COUNT_PER_DAY,
|
||||
CISD_DATA_WIRELESS_COUNT_PER_DAY,
|
||||
CISD_DATA_HIGH_TEMP_SWELLING_PER_DAY,
|
||||
CISD_DATA_LOW_TEMP_SWELLING_PER_DAY,
|
||||
CISD_DATA_SWELLING_CHARGING_COUNT_PER_DAY,
|
||||
CISD_DATA_SWELLING_FULL_CNT_PER_DAY,
|
||||
|
||||
CISD_DATA_SWELLING_RECOVERY_CNT_PER_DAY,
|
||||
CISD_DATA_AICL_COUNT_PER_DAY,
|
||||
CISD_DATA_BATT_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_BATT_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_CHG_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_CHG_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_WPC_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_WPC_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_USB_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_USB_TEMP_MIN_PER_DAY,
|
||||
|
||||
CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY,
|
||||
CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY,
|
||||
CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY,
|
||||
CISD_DATA_UNSAFE_VOLTAGE_PER_DAY,
|
||||
|
||||
CISD_DATA_UNSAFE_TEMPERATURE_PER_DAY,
|
||||
CISD_DATA_SAFETY_TIMER_PER_DAY, /* 32 */
|
||||
CISD_DATA_VSYS_OVP_PER_DAY,
|
||||
CISD_DATA_VBAT_OVP_PER_DAY,
|
||||
CISD_DATA_AFC_FAIL_PER_DAY,
|
||||
CISD_DATA_BUCK_OFF_PER_DAY,
|
||||
CISD_DATA_WATER_DETECT_PER_DAY,
|
||||
CISD_DATA_DROP_VALUE_PER_DAY,
|
||||
|
||||
CISD_DATA_MAX_PER_DAY,
|
||||
};
|
||||
|
||||
enum {
|
||||
WC_DATA_INDEX = 0,
|
||||
WC_SNGL_NOBLE,
|
||||
WC_SNGL_VEHICLE,
|
||||
WC_SNGL_MINI,
|
||||
WC_SNGL_ZERO,
|
||||
WC_SNGL_DREAM,
|
||||
WC_STAND_HERO,
|
||||
WC_STAND_DREAM,
|
||||
WC_EXT_PACK,
|
||||
WC_EXT_PACK_TA,
|
||||
|
||||
WC_DATA_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CISD_CABLE_INDEX = 0,
|
||||
CISD_CABLE_TA,
|
||||
CISD_CABLE_AFC,
|
||||
CISD_CABLE_AFC_FAIL,
|
||||
CISD_CABLE_QC,
|
||||
CISD_CABLE_QC_FAIL,
|
||||
CISD_CABLE_PD,
|
||||
CISD_CABLE_PD_HIGH,
|
||||
|
||||
CISD_CABLE_TYPE_MAX,
|
||||
};
|
||||
|
||||
extern const char *cisd_data_str[];
|
||||
extern const char *cisd_data_str_d[];
|
||||
|
||||
#define PAD_INDEX_STRING "INDEX"
|
||||
#define PAD_INDEX_VALUE 1
|
||||
#define PAD_JSON_STRING "PAD_0x"
|
||||
#define MAX_PAD_ID 0xFF
|
||||
|
||||
struct pad_data {
|
||||
unsigned int id;
|
||||
unsigned int count;
|
||||
|
||||
struct pad_data* prev;
|
||||
struct pad_data* next;
|
||||
};
|
||||
|
||||
struct cisd {
|
||||
unsigned int cisd_alg_index;
|
||||
unsigned int state;
|
||||
|
||||
unsigned int delay_time;
|
||||
int diff_volt_now;
|
||||
int diff_cap_now;
|
||||
int curr_cap_max;
|
||||
int err_cap_max_thrs;
|
||||
int err_cap_high_thr;
|
||||
int err_cap_low_thr;
|
||||
int overflow_cap_thr;
|
||||
unsigned int cc_delay_time;
|
||||
unsigned int full_delay_time;
|
||||
unsigned int lcd_off_delay_time;
|
||||
unsigned int recharge_delay_time;
|
||||
unsigned int diff_time;
|
||||
unsigned long cc_start_time;
|
||||
unsigned long full_start_time;
|
||||
unsigned long lcd_off_start_time;
|
||||
unsigned long overflow_start_time;
|
||||
unsigned long charging_end_time;
|
||||
unsigned long charging_end_time_2;
|
||||
unsigned int recharge_count;
|
||||
unsigned int recharge_count_2;
|
||||
unsigned int recharge_count_thres;
|
||||
unsigned long leakage_e_time;
|
||||
unsigned long leakage_f_time;
|
||||
unsigned long leakage_g_time;
|
||||
int current_max_thres;
|
||||
int charging_current_thres;
|
||||
int current_avg_thres;
|
||||
|
||||
unsigned int ab_vbat_max_count;
|
||||
unsigned int ab_vbat_check_count;
|
||||
unsigned int max_voltage_thr;
|
||||
|
||||
/* Big Data Field */
|
||||
int capacity_now;
|
||||
int data[CISD_DATA_MAX_PER_DAY];
|
||||
int cable_data[CISD_CABLE_TYPE_MAX];
|
||||
|
||||
struct mutex padlock;
|
||||
struct pad_data* pad_array;
|
||||
unsigned int pad_count;
|
||||
|
||||
#if defined(CONFIG_QH_ALGORITHM)
|
||||
unsigned long prev_time;
|
||||
unsigned long qh_valid_time;
|
||||
int prev_qh_value;
|
||||
int prev_qh_vfsoc;
|
||||
int qh_value_now;
|
||||
int qh_vfsoc_now;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct cisd *gcisd;
|
||||
static inline void set_cisd_data(int type, int value)
|
||||
{
|
||||
if (gcisd && (type >= CISD_DATA_RESET_ALG && type < CISD_DATA_MAX_PER_DAY))
|
||||
gcisd->data[type] = value;
|
||||
}
|
||||
static inline int get_cisd_data(int type)
|
||||
{
|
||||
if (!gcisd || (type < CISD_DATA_RESET_ALG || type >= CISD_DATA_MAX_PER_DAY))
|
||||
return -1;
|
||||
|
||||
return gcisd->data[type];
|
||||
}
|
||||
static inline void increase_cisd_count(int type)
|
||||
{
|
||||
if (gcisd && (type >= CISD_DATA_RESET_ALG && type < CISD_DATA_MAX_PER_DAY))
|
||||
gcisd->data[type]++;
|
||||
}
|
||||
|
||||
void init_cisd_pad_data(struct cisd *cisd);
|
||||
void count_cisd_pad_data(struct cisd *cisd, unsigned int pad_id);
|
||||
|
||||
#endif /* __SEC_CISD_H */
|
||||
462
drivers/battery_v2/sec_adc.c
Normal file
462
drivers/battery_v2/sec_adc.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* sec_adc.c
|
||||
* Samsung Mobile Battery Driver
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "include/sec_adc.h"
|
||||
|
||||
#define DEBUG
|
||||
|
||||
static struct qpnp_vadc_chip *adc_client;
|
||||
|
||||
struct adc_list {
|
||||
const char* name;
|
||||
struct iio_channel *channel;
|
||||
struct qpnp_vadc_result prev_value;
|
||||
//bool is_used;
|
||||
};
|
||||
|
||||
static struct adc_list batt_adc_list[SEC_BAT_ADC_CHANNEL_NUM] = {
|
||||
{.name = "adc-cable"},
|
||||
{.name = "adc-bat"},
|
||||
{.name = "adc-temp"},
|
||||
{.name = "adc-temp"},
|
||||
{.name = "adc-full"},
|
||||
{.name = "adc-volt"},
|
||||
{.name = "adc-chg-temp"},
|
||||
{.name = "adc-in-bat"},
|
||||
{.name = "adc-dischg"},
|
||||
{.name = "adc-dischg-ntc"},
|
||||
{.name = "adc-wpc-temp"},
|
||||
{.name = "adc-slave-chg-temp"},
|
||||
{.name = "adc-usb-temp"},
|
||||
};
|
||||
static void sec_bat_adc_ap_init(struct platform_device *pdev,
|
||||
struct sec_battery_info *battery)
|
||||
{
|
||||
adc_client = qpnp_get_vadc(battery->dev, "sec-battery");
|
||||
|
||||
if (IS_ERR(adc_client)) {
|
||||
int rc;
|
||||
rc = PTR_ERR(adc_client);
|
||||
if (rc != -EPROBE_DEFER)
|
||||
pr_err("%s: Fail to get vadc %d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
|
||||
static void sec_bat_read_adc(struct qpnp_vadc_chip *vadc, int channel,
|
||||
struct qpnp_vadc_result *result, int adc_channel)
|
||||
{
|
||||
int ret = 0;
|
||||
int retry_cnt = RETRY_CNT;
|
||||
|
||||
do {
|
||||
ret = qpnp_vadc_read(vadc, channel, result);
|
||||
retry_cnt--;
|
||||
} while ((retry_cnt > 0) && ret);
|
||||
|
||||
if (retry_cnt <= 0) {
|
||||
pr_err("%s: Error in ADC(%d) retry_cnt(%d)\n", __func__, adc_channel, retry_cnt);
|
||||
result->adc_code = batt_adc_list[adc_channel].prev_value.adc_code;
|
||||
result->physical = batt_adc_list[adc_channel].prev_value.physical;
|
||||
} else {
|
||||
batt_adc_list[adc_channel].prev_value.adc_code = result->adc_code;
|
||||
batt_adc_list[adc_channel].prev_value.physical = result->physical;
|
||||
}
|
||||
pr_debug("%s, channel: 0x%x, adc: %d, physical: %lld\n", __func__, channel, result->adc_code, result->physical);
|
||||
}
|
||||
|
||||
static int sec_bat_adc_ap_read(struct sec_battery_info *battery, int channel)
|
||||
{
|
||||
struct qpnp_vadc_result results;
|
||||
int data = -1;
|
||||
|
||||
switch (channel)
|
||||
{
|
||||
case SEC_BAT_ADC_CHANNEL_TEMP:
|
||||
sec_bat_read_adc(adc_client, battery->pdata->temp_adc_channel, &results, channel);
|
||||
data = results.adc_code;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT:
|
||||
data = 33000;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_BAT_CHECK:
|
||||
sec_bat_read_adc(adc_client, battery->pdata->batt_channel, &results, channel);
|
||||
data = results.adc_code;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_USB_TEMP:
|
||||
sec_bat_read_adc(adc_client, battery->pdata->usb_temp_adc_channel, &results, channel);
|
||||
data = results.adc_code;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_CHG_TEMP:
|
||||
sec_bat_read_adc(adc_client, battery->pdata->chg_temp_adc_channel, &results, channel);
|
||||
data = results.adc_code;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_WPC_TEMP:
|
||||
sec_bat_read_adc(adc_client, battery->pdata->temp_adc_channel, &results, channel);
|
||||
data = results.adc_code;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_INBAT_VOLTAGE:
|
||||
sec_bat_read_adc(adc_client, VBAT_SNS, &results, channel);
|
||||
data = ((int)results.physical)/1000;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_BLKT_TEMP:
|
||||
sec_bat_read_adc(adc_client, battery->pdata->blkt_temp_adc_channel, &results, channel);
|
||||
data = results.adc_code;
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void sec_bat_adc_ap_exit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void sec_bat_adc_none_init(struct platform_device *pdev)
|
||||
{
|
||||
}
|
||||
|
||||
static int sec_bat_adc_none_read(int channel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sec_bat_adc_none_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void sec_bat_adc_ic_init(struct platform_device *pdev)
|
||||
{
|
||||
}
|
||||
|
||||
static int sec_bat_adc_ic_read(int channel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sec_bat_adc_ic_exit(void)
|
||||
{
|
||||
}
|
||||
static int adc_read_type(struct sec_battery_info *battery, int channel)
|
||||
{
|
||||
int adc = 0;
|
||||
|
||||
switch (battery->pdata->temp_adc_type)
|
||||
{
|
||||
case SEC_BATTERY_ADC_TYPE_NONE :
|
||||
adc = sec_bat_adc_none_read(channel);
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_AP :
|
||||
adc = sec_bat_adc_ap_read(battery, channel);
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_IC :
|
||||
adc = sec_bat_adc_ic_read(channel);
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_NUM :
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
pr_debug("[%s] [%d] ADC = %d\n", __func__, channel, adc);
|
||||
return adc;
|
||||
|
||||
}
|
||||
|
||||
static void adc_init_type(struct platform_device *pdev,
|
||||
struct sec_battery_info *battery)
|
||||
{
|
||||
switch (battery->pdata->temp_adc_type)
|
||||
{
|
||||
case SEC_BATTERY_ADC_TYPE_NONE :
|
||||
sec_bat_adc_none_init(pdev);
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_AP :
|
||||
sec_bat_adc_ap_init(pdev, battery);
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_IC :
|
||||
sec_bat_adc_ic_init(pdev);
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_NUM :
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void adc_exit_type(struct sec_battery_info *battery)
|
||||
{
|
||||
switch (battery->pdata->temp_adc_type)
|
||||
{
|
||||
case SEC_BATTERY_ADC_TYPE_NONE :
|
||||
sec_bat_adc_none_exit();
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_AP :
|
||||
sec_bat_adc_ap_exit();
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_IC :
|
||||
sec_bat_adc_ic_exit();
|
||||
break;
|
||||
case SEC_BATTERY_ADC_TYPE_NUM :
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int sec_bat_get_adc_data(struct sec_battery_info *battery,
|
||||
int adc_ch, int count)
|
||||
{
|
||||
int adc_data = 0;
|
||||
int adc_max = 0;
|
||||
int adc_min = 0;
|
||||
int adc_total = 0;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
mutex_lock(&battery->adclock);
|
||||
#ifdef CONFIG_OF
|
||||
adc_data = adc_read_type(battery, adc_ch);
|
||||
#else
|
||||
adc_data = adc_read(battery->pdata, adc_ch);
|
||||
#endif
|
||||
mutex_unlock(&battery->adclock);
|
||||
|
||||
if (adc_data < 0)
|
||||
goto err;
|
||||
|
||||
if (i != 0) {
|
||||
if (adc_data > adc_max)
|
||||
adc_max = adc_data;
|
||||
else if (adc_data < adc_min)
|
||||
adc_min = adc_data;
|
||||
} else {
|
||||
adc_max = adc_data;
|
||||
adc_min = adc_data;
|
||||
}
|
||||
adc_total += adc_data;
|
||||
}
|
||||
|
||||
return (adc_total - adc_max - adc_min) / (count - 2);
|
||||
err:
|
||||
return adc_data;
|
||||
}
|
||||
|
||||
int adc_read(struct sec_battery_info *battery, int channel)
|
||||
{
|
||||
int adc = 0;
|
||||
|
||||
adc = adc_read_type(battery, channel);
|
||||
|
||||
dev_dbg(battery->dev, "[%s]adc = %d\n", __func__, adc);
|
||||
|
||||
return adc;
|
||||
}
|
||||
|
||||
int sec_bat_get_adc_value(
|
||||
struct sec_battery_info *battery, int channel)
|
||||
{
|
||||
int adc = 0;
|
||||
|
||||
adc = sec_bat_get_adc_data(battery, channel,
|
||||
battery->pdata->adc_check_count);
|
||||
|
||||
if (adc < 0) {
|
||||
dev_err(battery->dev,
|
||||
"%s: Error in ADC\n", __func__);
|
||||
return adc;
|
||||
}
|
||||
|
||||
return adc;
|
||||
}
|
||||
|
||||
int sec_bat_get_charger_type_adc
|
||||
(struct sec_battery_info *battery)
|
||||
{
|
||||
/* It is true something valid is
|
||||
connected to the device for charging.
|
||||
By default this something is considered to be USB.*/
|
||||
int result = SEC_BATTERY_CABLE_USB;
|
||||
|
||||
int adc = 0;
|
||||
int i = 0;
|
||||
|
||||
/* Do NOT check cable type when cable_switch_check() returns false
|
||||
* and keep current cable type
|
||||
*/
|
||||
if (battery->pdata->cable_switch_check &&
|
||||
!battery->pdata->cable_switch_check())
|
||||
return battery->cable_type;
|
||||
|
||||
adc = sec_bat_get_adc_value(battery,
|
||||
SEC_BAT_ADC_CHANNEL_CABLE_CHECK);
|
||||
|
||||
/* Do NOT check cable type when cable_switch_normal() returns false
|
||||
* and keep current cable type
|
||||
*/
|
||||
if (battery->pdata->cable_switch_normal &&
|
||||
!battery->pdata->cable_switch_normal())
|
||||
return battery->cable_type;
|
||||
|
||||
for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++)
|
||||
if ((adc > battery->pdata->cable_adc_value[i].min) &&
|
||||
(adc < battery->pdata->cable_adc_value[i].max))
|
||||
break;
|
||||
if (i >= SEC_BATTERY_CABLE_MAX)
|
||||
dev_err(battery->dev,
|
||||
"%s : default USB\n", __func__);
|
||||
else
|
||||
result = i;
|
||||
|
||||
dev_dbg(battery->dev, "%s : result(%d), adc(%d)\n",
|
||||
__func__, result, adc);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sec_bat_get_value_by_adc(
|
||||
struct sec_battery_info *battery,
|
||||
enum sec_battery_adc_channel channel,
|
||||
union power_supply_propval *value)
|
||||
{
|
||||
int temp = 0;
|
||||
int temp_adc;
|
||||
int low = 0;
|
||||
int high = 0;
|
||||
int mid = 0;
|
||||
const sec_bat_adc_table_data_t *temp_adc_table = {0 , };
|
||||
unsigned int temp_adc_table_size = 0;
|
||||
|
||||
temp_adc = sec_bat_get_adc_value(battery, channel);
|
||||
if (temp_adc < 0)
|
||||
return true;
|
||||
|
||||
switch (channel) {
|
||||
case SEC_BAT_ADC_CHANNEL_TEMP:
|
||||
temp_adc_table = battery->pdata->temp_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->temp_adc_table_size;
|
||||
battery->temp_adc = temp_adc;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT:
|
||||
temp_adc_table = battery->pdata->temp_amb_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->temp_amb_adc_table_size;
|
||||
battery->temp_ambient_adc = temp_adc;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_USB_TEMP:
|
||||
temp_adc_table = battery->pdata->usb_temp_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->usb_temp_adc_table_size;
|
||||
battery->usb_temp_adc = temp_adc;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_CHG_TEMP:
|
||||
temp_adc_table = battery->pdata->chg_temp_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->chg_temp_adc_table_size;
|
||||
battery->chg_temp_adc = temp_adc;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_WPC_TEMP: /* Coil Therm */
|
||||
temp_adc_table = battery->pdata->wpc_temp_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->wpc_temp_adc_table_size;
|
||||
battery->wpc_temp_adc = temp_adc;
|
||||
battery->coil_temp_adc = temp_adc;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP:
|
||||
temp_adc_table = battery->pdata->slave_chg_temp_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->slave_chg_temp_adc_table_size;
|
||||
battery->slave_chg_temp_adc = temp_adc;
|
||||
break;
|
||||
case SEC_BAT_ADC_CHANNEL_BLKT_TEMP:
|
||||
temp_adc_table = battery->pdata->blkt_temp_adc_table;
|
||||
temp_adc_table_size =
|
||||
battery->pdata->blkt_temp_adc_table_size;
|
||||
battery->blkt_temp_adc = temp_adc;
|
||||
break;
|
||||
default:
|
||||
dev_err(battery->dev,
|
||||
"%s: Invalid Property\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (temp_adc_table[0].adc >= temp_adc) {
|
||||
temp = temp_adc_table[0].data;
|
||||
goto temp_by_adc_goto;
|
||||
} else if (temp_adc_table[temp_adc_table_size-1].adc <= temp_adc) {
|
||||
temp = temp_adc_table[temp_adc_table_size-1].data;
|
||||
goto temp_by_adc_goto;
|
||||
}
|
||||
|
||||
high = temp_adc_table_size - 1;
|
||||
|
||||
while (low <= high) {
|
||||
mid = (low + high) / 2;
|
||||
if (temp_adc_table[mid].adc > temp_adc)
|
||||
high = mid - 1;
|
||||
else if (temp_adc_table[mid].adc < temp_adc)
|
||||
low = mid + 1;
|
||||
else {
|
||||
temp = temp_adc_table[mid].data;
|
||||
goto temp_by_adc_goto;
|
||||
}
|
||||
}
|
||||
|
||||
temp = temp_adc_table[high].data;
|
||||
temp += ((temp_adc_table[low].data - temp_adc_table[high].data) *
|
||||
(temp_adc - temp_adc_table[high].adc)) /
|
||||
(temp_adc_table[low].adc - temp_adc_table[high].adc);
|
||||
|
||||
temp_by_adc_goto:
|
||||
value->intval = temp;
|
||||
|
||||
dev_info(battery->dev,
|
||||
"%s:[%d] Temp(%d), Temp-ADC(%d)\n",
|
||||
__func__,channel, temp, temp_adc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sec_bat_check_vf_adc(struct sec_battery_info *battery)
|
||||
{
|
||||
int adc = 0;
|
||||
|
||||
adc = sec_bat_get_adc_data(battery,
|
||||
SEC_BAT_ADC_CHANNEL_BAT_CHECK,
|
||||
battery->pdata->adc_check_count);
|
||||
if (adc < 0) {
|
||||
dev_err(battery->dev, "%s: VF ADC error\n", __func__);
|
||||
adc = battery->check_adc_value;
|
||||
} else
|
||||
battery->check_adc_value = adc;
|
||||
|
||||
dev_info(battery->dev, "%s: adc (%d)\n", __func__, battery->check_adc_value);
|
||||
if ((battery->check_adc_value <= battery->pdata->check_adc_max) &&
|
||||
(battery->check_adc_value >= battery->pdata->check_adc_min)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void adc_init(struct platform_device *pdev, struct sec_battery_info *battery)
|
||||
{
|
||||
adc_init_type(pdev, battery);
|
||||
}
|
||||
|
||||
void adc_exit(struct sec_battery_info *battery)
|
||||
{
|
||||
adc_exit_type(battery);
|
||||
}
|
||||
|
||||
10051
drivers/battery_v2/sec_battery.c
Normal file
10051
drivers/battery_v2/sec_battery.c
Normal file
File diff suppressed because it is too large
Load Diff
312
drivers/battery_v2/sec_battery_ttf.c
Normal file
312
drivers/battery_v2/sec_battery_ttf.c
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* sec_battery_ttf.c
|
||||
* Samsung Mobile Battery Driver
|
||||
*
|
||||
* Copyright (C) 2019 Samsung Electronics
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "include/sec_battery.h"
|
||||
#include "include/sec_battery_ttf.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_CALC_TIME_TO_FULL)
|
||||
int sec_calc_ttf(struct sec_battery_info *battery, unsigned int ttf_curr)
|
||||
{
|
||||
struct sec_cv_slope *cv_data = battery->ttf_d->cv_data;
|
||||
int i, cc_time = 0, cv_time = 0;
|
||||
int soc = battery->capacity;
|
||||
int charge_current = ttf_curr;
|
||||
int design_cap = battery->ttf_d->ttf_capacity;
|
||||
union power_supply_propval value = {0, };
|
||||
|
||||
value.intval = SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE;
|
||||
psy_do_property(battery->pdata->fuelgauge_name, get,
|
||||
POWER_SUPPLY_PROP_CAPACITY, value);
|
||||
soc = value.intval;
|
||||
|
||||
if (!cv_data || (ttf_curr <= 0)) {
|
||||
pr_info("%s: no cv_data or val: %d\n", __func__, ttf_curr);
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < battery->ttf_d->cv_data_length; i++) {
|
||||
if (charge_current >= cv_data[i].fg_current)
|
||||
break;
|
||||
}
|
||||
i = i >= battery->ttf_d->cv_data_length ? battery->ttf_d->cv_data_length - 1 : i;
|
||||
if (cv_data[i].soc < soc) {
|
||||
for (i = 0; i < battery->ttf_d->cv_data_length; i++) {
|
||||
if (soc <= cv_data[i].soc)
|
||||
break;
|
||||
}
|
||||
cv_time =
|
||||
((cv_data[i - 1].time - cv_data[i].time) * (cv_data[i].soc - soc)
|
||||
/ (cv_data[i].soc - cv_data[i - 1].soc)) + cv_data[i].time;
|
||||
} else { /* CC mode || NONE */
|
||||
cv_time = cv_data[i].time;
|
||||
cc_time =
|
||||
design_cap * (cv_data[i].soc - soc) / ttf_curr * 3600 / 1000;
|
||||
pr_debug("%s: cc_time: %d\n", __func__, cc_time);
|
||||
if (cc_time < 0)
|
||||
cc_time = 0;
|
||||
}
|
||||
|
||||
pr_info("%s: cap: %d, soc: %4d, T: %6d, avg: %4d, cv soc: %4d, i: %4d, val: %d\n",
|
||||
__func__, design_cap, soc, cv_time + cc_time,
|
||||
battery->current_avg, cv_data[i].soc, i, ttf_curr);
|
||||
|
||||
if (cv_time + cc_time >= 0)
|
||||
return cv_time + cc_time + 60;
|
||||
else
|
||||
return 60; /* minimum 1minutes */
|
||||
}
|
||||
|
||||
void sec_bat_calc_time_to_full(struct sec_battery_info * battery)
|
||||
{
|
||||
if (delayed_work_pending(&battery->ttf_d->timetofull_work)) {
|
||||
pr_info("%s: keep time_to_full(%5d sec)\n", __func__, battery->ttf_d->timetofull);
|
||||
} else if (battery->status == POWER_SUPPLY_STATUS_CHARGING ||
|
||||
(battery->status == POWER_SUPPLY_STATUS_FULL && battery->capacity != 100)) {
|
||||
|
||||
int charge = 0;
|
||||
|
||||
if (is_hv_wire_12v_type(battery->cable_type) ||
|
||||
battery->max_charge_power >= (battery->ttf_d->pd_charging_charge_power + 5000)) { /* 20000mW */
|
||||
charge = battery->ttf_d->ttf_hv_12v_charge_current;
|
||||
} else if (is_hv_wire_type(battery->cable_type) || (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_TA) ||
|
||||
/* if max_charge_power could support over than max_charging_current, calculate based on ttf_hv_charge_current */
|
||||
battery->max_charge_power >= (battery->ttf_d->max_charging_current * 5)) {
|
||||
charge = battery->ttf_d->ttf_hv_charge_current;
|
||||
} else if (is_hv_wireless_type(battery->cable_type) ||
|
||||
battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) {
|
||||
if (sec_bat_hv_wc_normal_mode_check(battery))
|
||||
charge = battery->ttf_d->ttf_wireless_charge_current;
|
||||
else
|
||||
charge = battery->ttf_d->ttf_hv_wireless_charge_current;
|
||||
} else if (is_nv_wireless_type(battery->cable_type)) {
|
||||
charge = battery->ttf_d->ttf_wireless_charge_current;
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
} else if (battery->cable_type == SEC_BATTERY_CABLE_POGO) {
|
||||
charge = battery->ttf_d->ttf_pogo_charge_current;
|
||||
#endif
|
||||
} else {
|
||||
charge = (battery->max_charge_power / 5) > battery->pdata->charging_current[battery->cable_type].fast_charging_current ?
|
||||
battery->pdata->charging_current[battery->cable_type].fast_charging_current : (battery->max_charge_power / 5);
|
||||
}
|
||||
battery->ttf_d->timetofull = sec_calc_ttf(battery, charge);
|
||||
dev_info(battery->dev, "%s: T: %5d sec, passed time: %5ld, current: %d\n",
|
||||
__func__, battery->ttf_d->timetofull, battery->charging_passed_time, charge);
|
||||
} else {
|
||||
battery->ttf_d->timetofull = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
int sec_ttf_parse_dt(struct sec_battery_info *battery)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct sec_ttf_data *pdata = battery->ttf_d;
|
||||
sec_battery_platform_data_t *bpdata = battery->pdata;
|
||||
int ret = 0, len = 0;
|
||||
const u32 *p;
|
||||
|
||||
pdata->pdev = battery;
|
||||
np = of_find_node_by_name(NULL, "battery");
|
||||
if (!np) {
|
||||
pr_info("%s: np NULL\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,ttf_hv_12v_charge_current",
|
||||
&pdata->ttf_hv_12v_charge_current);
|
||||
if (ret) {
|
||||
pdata->ttf_hv_12v_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_12V_TA].fast_charging_current;
|
||||
pr_info("%s: ttf_hv_12v_charge_current is Empty, Defualt value %d \n",
|
||||
__func__, pdata->ttf_hv_12v_charge_current);
|
||||
}
|
||||
ret = of_property_read_u32(np, "battery,ttf_hv_charge_current",
|
||||
&pdata->ttf_hv_charge_current);
|
||||
if (ret) {
|
||||
pdata->ttf_hv_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_9V_TA].fast_charging_current;
|
||||
pr_info("%s: ttf_hv_charge_current is Empty, Defualt value %d \n",
|
||||
__func__, pdata->ttf_hv_charge_current);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,ttf_hv_wireless_charge_current",
|
||||
&pdata->ttf_hv_wireless_charge_current);
|
||||
if (ret) {
|
||||
pr_info("%s: ttf_hv_wireless_charge_current is Empty, Defualt value 0 \n", __func__);
|
||||
pdata->ttf_hv_wireless_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_HV_WIRELESS].fast_charging_current - 300;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,ttf_wireless_charge_current",
|
||||
&pdata->ttf_wireless_charge_current);
|
||||
if (ret) {
|
||||
pr_info("%s: ttf_wireless_charge_current is Empty, Defualt value 0 \n", __func__);
|
||||
pdata->ttf_wireless_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_WIRELESS].input_current_limit;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USE_POGO)
|
||||
ret = of_property_read_u32(np, "battery,ttf_pogo_charge_current",
|
||||
&pdata->ttf_pogo_charge_current);
|
||||
if (ret) {
|
||||
pr_info("%s: ttf_pogo_charge_current is Empty, default value 0 \n", __func__);
|
||||
pdata->ttf_pogo_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_POGO].input_current_limit;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = of_property_read_u32(np, "battery,pd_charging_charge_power",
|
||||
&pdata->pd_charging_charge_power);
|
||||
if (ret) {
|
||||
pr_err("%s: pd_charging_charge_power is Empty\n", __func__);
|
||||
pdata->pd_charging_charge_power = 15000;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,max_charging_current",
|
||||
&pdata->max_charging_current);
|
||||
if (ret) {
|
||||
pr_err("%s: max_charging_current is Empty\n", __func__);
|
||||
pdata->max_charging_current = 3000;
|
||||
}
|
||||
/* temporary dt setting */
|
||||
ret = of_property_read_u32(np, "battery,ttf_predict_wc20_charge_current",
|
||||
&pdata->ttf_predict_wc20_charge_current);
|
||||
if (ret) {
|
||||
pr_info("%s: ttf_predict_wc20_charge_current is Empty, Default value 0\n", __func__);
|
||||
pdata->ttf_predict_wc20_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_WIRELESS].input_current_limit;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,ttf_dc25_charge_current",
|
||||
&pdata->ttf_dc25_charge_current);
|
||||
if (ret) {
|
||||
pr_info("%s: ttf_dc25_charge_current is Empty, Default value 0 \n", __func__);
|
||||
pdata->ttf_dc25_charge_current =
|
||||
bpdata->charging_current[SEC_BATTERY_CABLE_9V_TA].fast_charging_current;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,ttf_dc45_charge_current",
|
||||
&pdata->ttf_dc45_charge_current);
|
||||
if (ret) {
|
||||
pr_info("%s: ttf_dc45_charge_current is Empty, Default value 0 \n", __func__);
|
||||
pdata->ttf_dc45_charge_current = pdata->ttf_dc25_charge_current;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "battery,ttf_capacity",
|
||||
&pdata->ttf_capacity);
|
||||
if (ret < 0) {
|
||||
pr_err("%s error reading capacity_calculation_type %d\n", __func__, ret);
|
||||
pdata->ttf_capacity = bpdata->battery_full_capacity;
|
||||
}
|
||||
|
||||
p = of_get_property(np, "battery,cv_data", &len);
|
||||
if (p) {
|
||||
pdata->cv_data = kzalloc(len, GFP_KERNEL);
|
||||
pdata->cv_data_length = len / sizeof(struct sec_cv_slope);
|
||||
pr_err("%s: len= %ld, length= %d, %d\n", __func__,
|
||||
sizeof(int) * len, len, pdata->cv_data_length);
|
||||
ret = of_property_read_u32_array(np, "battery,cv_data",
|
||||
(u32 *)pdata->cv_data, len / sizeof(u32));
|
||||
if (ret) {
|
||||
pr_err("%s: failed to read battery->cv_data: %d\n",
|
||||
__func__, ret);
|
||||
kfree(pdata->cv_data);
|
||||
pdata->cv_data = NULL;
|
||||
}
|
||||
} else {
|
||||
pr_err("%s: there is not cv_data\n", __func__);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sec_bat_time_to_full_work(struct work_struct *work)
|
||||
{
|
||||
struct sec_ttf_data *dev = container_of(work,
|
||||
struct sec_ttf_data, timetofull_work.work);
|
||||
struct sec_battery_info *battery = dev->pdev;
|
||||
union power_supply_propval value = {0, };
|
||||
|
||||
psy_do_property(battery->pdata->charger_name, get,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
||||
battery->current_max = value.intval;
|
||||
|
||||
value.intval = SEC_BATTERY_CURRENT_MA;
|
||||
psy_do_property(battery->pdata->fuelgauge_name, get,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW, value);
|
||||
battery->current_now = value.intval;
|
||||
|
||||
value.intval = SEC_BATTERY_CURRENT_MA;
|
||||
psy_do_property(battery->pdata->fuelgauge_name, get,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG, value);
|
||||
battery->current_avg = value.intval;
|
||||
|
||||
sec_bat_calc_time_to_full(battery);
|
||||
dev_info(battery->dev, "%s:\n", __func__);
|
||||
if (battery->voltage_now > 0)
|
||||
battery->voltage_now--;
|
||||
|
||||
power_supply_changed(battery->psy_bat);
|
||||
}
|
||||
|
||||
void ttf_work_start(struct sec_battery_info *battery)
|
||||
{
|
||||
if (lpcharge) {
|
||||
cancel_delayed_work(&battery->ttf_d->timetofull_work);
|
||||
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
|
||||
int work_delay = 0;
|
||||
|
||||
if (!is_wireless_type(battery->cable_type)) {
|
||||
work_delay = battery->pdata->pre_afc_work_delay;
|
||||
} else {
|
||||
work_delay = battery->pdata->pre_wc_afc_work_delay;
|
||||
}
|
||||
queue_delayed_work(battery->monitor_wqueue,
|
||||
&battery->ttf_d->timetofull_work, msecs_to_jiffies(work_delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ttf_display(struct sec_battery_info *battery)
|
||||
{
|
||||
if (battery->capacity == 100)
|
||||
return 0;
|
||||
|
||||
if (((battery->status == POWER_SUPPLY_STATUS_CHARGING) ||
|
||||
(battery->status == POWER_SUPPLY_STATUS_FULL && battery->capacity != 100)) &&
|
||||
!battery->swelling_mode)
|
||||
return battery->ttf_d->timetofull;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ttf_init(struct sec_battery_info *battery)
|
||||
{
|
||||
battery->ttf_d = kzalloc(sizeof(struct sec_ttf_data),
|
||||
GFP_KERNEL);
|
||||
if (!battery->ttf_d) {
|
||||
pr_err("Failed to allocate memory\n");
|
||||
}
|
||||
sec_ttf_parse_dt(battery);
|
||||
battery->ttf_d->timetofull = -1;
|
||||
|
||||
INIT_DELAYED_WORK(&battery->ttf_d->timetofull_work, sec_bat_time_to_full_work);
|
||||
}
|
||||
#else
|
||||
int sec_calc_ttf(struct sec_battery_info *battery, unsigned int ttf_curr) { return -ENODEV; }
|
||||
void sec_bat_calc_time_to_full(struct sec_battery_info *battery) { }
|
||||
void sec_bat_time_to_full_work(struct work_struct *work) { }
|
||||
void ttf_init(struct sec_battery_info *battery) { }
|
||||
void ttf_work_start(struct sec_battery_info *battery) { }
|
||||
int ttf_display(struct sec_battery_info *battery) { return 0; }
|
||||
#ifdef CONFIG_OF
|
||||
int sec_ttf_parse_dt(struct sec_battery_info *battery) { return -ENODEV; }
|
||||
#endif
|
||||
#endif
|
||||
482
drivers/battery_v2/sec_cisd.c
Normal file
482
drivers/battery_v2/sec_cisd.c
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* sec_cisd.c
|
||||
* Samsung Mobile Battery Driver
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "include/sec_battery.h"
|
||||
#include "include/sec_cisd.h"
|
||||
|
||||
#if defined(CONFIG_SEC_ABC)
|
||||
#include <linux/sti/abc_common.h>
|
||||
#endif
|
||||
|
||||
const char* cisd_data_str[] ={
|
||||
"RESET_ALG", "ALG_INDEX", "FULL_CNT", "CAP_MAX", "CAP_MIN", "RECHARGING_CNT", "VALERT_CNT",
|
||||
"BATT_CYCLE", "WIRE_CNT", "WIRELESS_CNT", "HIGH_SWELLING_CNT", "LOW_SWELLING_CNT",
|
||||
"SWELLING_CHARGING", "SWELLING_FULL_CNT", "SWELLING_RECOVERY_CNT", "AICL_CNT", "BATT_THM_MAX",
|
||||
"BATT_THM_MIN", "CHG_THM_MAX", "CHG_THM_MIN", "WPC_THM_MAX", "WPC_THM_MIN", "USB_THM_MAX", "USB_THM_MIN",
|
||||
"CHG_BATT_THM_MAX", "CHG_BATT_THM_MIN", "CHG_CHG_THM_MAX", "CHG_CHG_THM_MIN", "CHG_WPC_THM_MAX",
|
||||
"CHG_WPC_THM_MIN", "CHG_USB_THM_MAX", "CHG_USB_THM_MIN", "USB_OVERHEAT_CHARGING", "UNSAFETY_VOLT",
|
||||
"UNSAFETY_TEMP", "SAFETY_TIMER", "VSYS_OVP", "VBAT_OVP", "AFC_FAIL", "BUCK_OFF", "WATER_DET", "DROP_SENSOR"
|
||||
};
|
||||
const char* cisd_data_str_d[] = {
|
||||
"FULL_CNT_D", "CAP_MAX_D", "CAP_MIN_D", "RECHARGING_CNT_D", "VALERT_CNT_D", "WIRE_CNT_D", "WIRELESS_CNT_D",
|
||||
"HIGH_SWELLING_CNT_D", "LOW_SWELLING_CNT_D", "SWELLING_CHARGING_D", "SWELLING_FULL_CNT_D",
|
||||
"SWELLING_RECOVERY_CNT_D", "AICL_CNT_D", "BATT_THM_MAX_D", "BATT_THM_MIN_D", "CHG_THM_MAX_D",
|
||||
"CHG_THM_MIN_D", "WPC_THM_MAX_D", "WPC_THM_MIN_D", "USB_THM_MAX_D", "USB_THM_MIN_D",
|
||||
"CHG_BATT_THM_MAX_D", "CHG_BATT_THM_MIN_D", "CHG_CHG_THM_MAX_D", "CHG_CHG_THM_MIN_D",
|
||||
"CHG_WPC_THM_MAX_D", "CHG_WPC_THM_MIN_D", "CHG_USB_THM_MAX_D", "CHG_USB_THM_MIN_D",
|
||||
"USB_OVERHEAT_CHARGING_D", "UNSAFETY_VOLT_D", "UNSAFETY_TEMP_D", "SAFETY_TIMER_D", "VSYS_OVP_D",
|
||||
"VBAT_OVP_D", "AFC_FAIL_D", "BUCK_OFF_D", "WATER_DET_D", "DROP_SENSOR_D"
|
||||
};
|
||||
|
||||
bool sec_bat_cisd_check(struct sec_battery_info *battery)
|
||||
{
|
||||
union power_supply_propval incur_val = {0, };
|
||||
union power_supply_propval chgcur_val = {0, };
|
||||
union power_supply_propval capcurr_val = {0, };
|
||||
union power_supply_propval vbat_val = {0, };
|
||||
struct cisd *pcisd = &battery->cisd;
|
||||
bool ret = false;
|
||||
|
||||
if (battery->factory_mode || battery->is_jig_on || battery->skip_cisd) {
|
||||
dev_dbg(battery->dev, "%s: No need to check in factory mode\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((battery->status == POWER_SUPPLY_STATUS_CHARGING) ||
|
||||
(battery->status == POWER_SUPPLY_STATUS_FULL)) {
|
||||
|
||||
/* check abnormal vbat */
|
||||
pcisd->ab_vbat_check_count = battery->voltage_now > pcisd->max_voltage_thr ?
|
||||
pcisd->ab_vbat_check_count + 1 : 0;
|
||||
|
||||
if ((pcisd->ab_vbat_check_count >= pcisd->ab_vbat_max_count) &&
|
||||
!(pcisd->state & CISD_STATE_OVER_VOLTAGE)) {
|
||||
dev_info(battery->dev, "%s : [CISD] Battery Over Voltage Protction !! vbat(%d)mV\n",
|
||||
__func__, battery->voltage_now);
|
||||
vbat_val.intval = true;
|
||||
psy_do_property("battery", set, (enum power_supply_property) POWER_SUPPLY_EXT_PROP_VBAT_OVP ,
|
||||
vbat_val);
|
||||
pcisd->data[CISD_DATA_VBAT_OVP]++;
|
||||
pcisd->data[CISD_DATA_VBAT_OVP_PER_DAY]++;
|
||||
pcisd->state |= CISD_STATE_OVER_VOLTAGE;
|
||||
#if defined(CONFIG_SEC_ABC)
|
||||
sec_abc_send_event("MODULE=battery@ERROR=over_voltage");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* get actual input current */
|
||||
psy_do_property(battery->pdata->charger_name, get,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG, incur_val);
|
||||
|
||||
/* get actual charging current */
|
||||
psy_do_property(battery->pdata->charger_name, get,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, chgcur_val);
|
||||
|
||||
if (battery->temperature > pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX] = battery->temperature;
|
||||
if (battery->temperature < pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN] = battery->temperature;
|
||||
|
||||
if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX] = battery->chg_temp;
|
||||
if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN] = battery->chg_temp;
|
||||
|
||||
if (battery->wpc_temp > pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX] = battery->wpc_temp;
|
||||
if (battery->wpc_temp < pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN] = battery->wpc_temp;
|
||||
|
||||
if (battery->usb_temp > pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX] = battery->usb_temp;
|
||||
if (battery->usb_temp < pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN] = battery->usb_temp;
|
||||
|
||||
if (battery->temperature > pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = battery->temperature;
|
||||
if (battery->temperature < pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = battery->temperature;
|
||||
|
||||
if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = battery->chg_temp;
|
||||
if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = battery->chg_temp;
|
||||
|
||||
if (battery->wpc_temp > pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = battery->wpc_temp;
|
||||
if (battery->wpc_temp < pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = battery->wpc_temp;
|
||||
|
||||
if (battery->usb_temp > pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY] = battery->usb_temp;
|
||||
if (battery->usb_temp < pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY] = battery->usb_temp;
|
||||
|
||||
if (battery->usb_temp > 800 && !battery->usb_overheat_check) {
|
||||
battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING]++;
|
||||
battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY]++;
|
||||
battery->usb_overheat_check = true;
|
||||
}
|
||||
|
||||
dev_info(battery->dev, "%s: [CISD] iavg: %d, incur: %d, chgcur: %d,\n"
|
||||
"cc_T: %ld, lcd_off_T: %ld, passed_T: %ld, full_T: %ld, chg_end_T: %ld, cisd: 0x%x\n",__func__,
|
||||
battery->current_avg, incur_val.intval, chgcur_val.intval,
|
||||
pcisd->cc_start_time, pcisd->lcd_off_start_time, battery->charging_passed_time,
|
||||
battery->charging_fullcharged_time, pcisd->charging_end_time, pcisd->state);
|
||||
} else {
|
||||
/* discharging */
|
||||
if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
|
||||
/* check abnormal vbat */
|
||||
pcisd->ab_vbat_check_count = battery->voltage_now > pcisd->max_voltage_thr ?
|
||||
pcisd->ab_vbat_check_count + 1 : 0;
|
||||
|
||||
if ((pcisd->ab_vbat_check_count >= pcisd->ab_vbat_max_count) &&
|
||||
!(pcisd->state & CISD_STATE_OVER_VOLTAGE)) {
|
||||
pcisd->data[CISD_DATA_VBAT_OVP]++;
|
||||
pcisd->data[CISD_DATA_VBAT_OVP_PER_DAY]++;
|
||||
pcisd->state |= CISD_STATE_OVER_VOLTAGE;
|
||||
#if defined(CONFIG_SEC_ABC)
|
||||
sec_abc_send_event("MODULE=battery@ERROR=over_voltage");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
capcurr_val.intval = SEC_BATTERY_CAPACITY_FULL;
|
||||
psy_do_property(battery->pdata->fuelgauge_name, get,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW, capcurr_val);
|
||||
if (capcurr_val.intval == -1) {
|
||||
dev_info(battery->dev, "%s: [CISD] FG I2C fail. skip cisd check \n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (capcurr_val.intval > pcisd->data[CISD_DATA_CAP_MAX])
|
||||
pcisd->data[CISD_DATA_CAP_MAX] = capcurr_val.intval;
|
||||
if (capcurr_val.intval < pcisd->data[CISD_DATA_CAP_MIN])
|
||||
pcisd->data[CISD_DATA_CAP_MIN] = capcurr_val.intval;
|
||||
|
||||
if (capcurr_val.intval > pcisd->data[CISD_DATA_CAP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CAP_MAX_PER_DAY] = capcurr_val.intval;
|
||||
if (capcurr_val.intval < pcisd->data[CISD_DATA_CAP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CAP_MIN_PER_DAY] = capcurr_val.intval;
|
||||
}
|
||||
|
||||
if (battery->temperature > pcisd->data[CISD_DATA_BATT_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_BATT_TEMP_MAX] = battery->temperature;
|
||||
if (battery->temperature < battery->cisd.data[CISD_DATA_BATT_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_BATT_TEMP_MIN] = battery->temperature;
|
||||
|
||||
if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_CHG_TEMP_MAX] = battery->chg_temp;
|
||||
if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_CHG_TEMP_MIN] = battery->chg_temp;
|
||||
|
||||
if (battery->wpc_temp > pcisd->data[CISD_DATA_WPC_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_WPC_TEMP_MAX] = battery->wpc_temp;
|
||||
if (battery->wpc_temp < battery->cisd.data[CISD_DATA_WPC_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_WPC_TEMP_MIN] = battery->wpc_temp;
|
||||
|
||||
if (battery->usb_temp > pcisd->data[CISD_DATA_USB_TEMP_MAX])
|
||||
pcisd->data[CISD_DATA_USB_TEMP_MAX] = battery->usb_temp;
|
||||
if (battery->usb_temp < pcisd->data[CISD_DATA_USB_TEMP_MIN])
|
||||
pcisd->data[CISD_DATA_USB_TEMP_MIN] = battery->usb_temp;
|
||||
|
||||
if (battery->temperature > pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = battery->temperature;
|
||||
if (battery->temperature < pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = battery->temperature;
|
||||
|
||||
if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = battery->chg_temp;
|
||||
if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = battery->chg_temp;
|
||||
|
||||
if (battery->wpc_temp > pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = battery->wpc_temp;
|
||||
if (battery->wpc_temp < pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = battery->wpc_temp;
|
||||
|
||||
if (battery->usb_temp > pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY])
|
||||
pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY] = battery->usb_temp;
|
||||
if (battery->usb_temp < pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY])
|
||||
pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY] = battery->usb_temp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct cisd *gcisd;
|
||||
void sec_battery_cisd_init(struct sec_battery_info *battery)
|
||||
{
|
||||
union power_supply_propval capfull_val;
|
||||
|
||||
battery->cisd.state = CISD_STATE_NONE;
|
||||
|
||||
battery->cisd.delay_time = 600; /* 10 min */
|
||||
battery->cisd.diff_volt_now = 40;
|
||||
battery->cisd.diff_cap_now = 5;
|
||||
|
||||
capfull_val.intval = SEC_BATTERY_CAPACITY_FULL;
|
||||
psy_do_property(battery->pdata->fuelgauge_name, get,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW, capfull_val);
|
||||
battery->cisd.curr_cap_max = capfull_val.intval;
|
||||
battery->cisd.err_cap_high_thr = battery->pdata->cisd_cap_high_thr;
|
||||
battery->cisd.err_cap_low_thr = battery->pdata->cisd_cap_low_thr;
|
||||
battery->cisd.cc_delay_time = 3600; /* 60 min */
|
||||
battery->cisd.lcd_off_delay_time = 10200; /* 170 min */
|
||||
battery->cisd.full_delay_time = 3600; /* 60 min */
|
||||
battery->cisd.recharge_delay_time = 9000; /* 150 min */
|
||||
battery->cisd.cc_start_time = 0;
|
||||
battery->cisd.full_start_time = 0;
|
||||
battery->cisd.lcd_off_start_time = 0;
|
||||
battery->cisd.overflow_start_time = 0;
|
||||
battery->cisd.charging_end_time = 0;
|
||||
battery->cisd.charging_end_time_2 = 0;
|
||||
battery->cisd.recharge_count = 0;
|
||||
battery->cisd.recharge_count_2 = 0;
|
||||
battery->cisd.recharge_count_thres = 2;
|
||||
battery->cisd.leakage_e_time = 3600; /* 60 min */
|
||||
battery->cisd.leakage_f_time = 7200; /* 120 min */
|
||||
battery->cisd.leakage_g_time = 14400; /* 240 min */
|
||||
battery->cisd.current_max_thres = 1600;
|
||||
battery->cisd.charging_current_thres = 1000;
|
||||
battery->cisd.current_avg_thres = 1000;
|
||||
|
||||
battery->cisd.data[CISD_DATA_ALG_INDEX] = battery->pdata->cisd_alg_index;
|
||||
battery->cisd.data[CISD_DATA_FULL_COUNT] = 1;
|
||||
battery->cisd.data[CISD_DATA_BATT_TEMP_MAX] = -300;
|
||||
battery->cisd.data[CISD_DATA_CHG_TEMP_MAX] = -300;
|
||||
battery->cisd.data[CISD_DATA_WPC_TEMP_MAX] = -300;
|
||||
battery->cisd.data[CISD_DATA_BATT_TEMP_MIN] = 1000;
|
||||
battery->cisd.data[CISD_DATA_CHG_TEMP_MIN] = 1000;
|
||||
battery->cisd.data[CISD_DATA_WPC_TEMP_MIN] = 1000;
|
||||
battery->cisd.data[CISD_DATA_CAP_MIN] = 0xFFFF;
|
||||
|
||||
battery->cisd.data[CISD_DATA_FULL_COUNT_PER_DAY] = 1;
|
||||
battery->cisd.data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = -300;
|
||||
battery->cisd.data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = -300;
|
||||
battery->cisd.data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = -300;
|
||||
battery->cisd.data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = 1000;
|
||||
battery->cisd.data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = 1000;
|
||||
battery->cisd.data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = 1000;
|
||||
battery->cisd.data[CISD_DATA_CAP_MIN] = 0xFFFF;
|
||||
|
||||
battery->cisd.data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = -300;
|
||||
battery->cisd.data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = -300;
|
||||
battery->cisd.data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = -300;
|
||||
battery->cisd.data[CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY] = 1000;
|
||||
battery->cisd.data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = 1000;
|
||||
battery->cisd.data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = 1000;
|
||||
|
||||
battery->cisd.capacity_now = capfull_val.intval;
|
||||
battery->cisd.overflow_cap_thr = capfull_val.intval > battery->pdata->cisd_cap_limit ?
|
||||
capfull_val.intval : battery->pdata->cisd_cap_limit;
|
||||
|
||||
battery->cisd.ab_vbat_max_count = 2; /* should be 1 */
|
||||
battery->cisd.ab_vbat_check_count = 0;
|
||||
battery->cisd.max_voltage_thr = battery->pdata->max_voltage_thr;
|
||||
battery->cisd.cisd_alg_index = 6;
|
||||
pr_info("%s: cisd.err_cap_high_thr:%d, cisd.err_cap_low_thr:%d, cisd.overflow_cap_thr:%d\n", __func__,
|
||||
battery->cisd.err_cap_high_thr, battery->cisd.err_cap_low_thr, battery->cisd.overflow_cap_thr);
|
||||
|
||||
/* set cisd pointer */
|
||||
gcisd = &battery->cisd;
|
||||
|
||||
/* initialize pad data */
|
||||
mutex_init(&battery->cisd.padlock);
|
||||
init_cisd_pad_data(&battery->cisd);
|
||||
}
|
||||
|
||||
static struct pad_data* create_pad_data(unsigned int pad_id, unsigned int pad_count)
|
||||
{
|
||||
struct pad_data* temp_data;
|
||||
|
||||
temp_data = kzalloc(sizeof(struct pad_data), GFP_KERNEL);
|
||||
if (temp_data == NULL)
|
||||
return NULL;
|
||||
|
||||
temp_data->id = pad_id;
|
||||
temp_data->count = pad_count;
|
||||
temp_data->prev = temp_data->next = NULL;
|
||||
|
||||
return temp_data;
|
||||
}
|
||||
|
||||
static struct pad_data* find_pad_data_by_id(struct cisd* cisd, unsigned int pad_id)
|
||||
{
|
||||
struct pad_data* temp_data = cisd->pad_array->next;
|
||||
|
||||
if (cisd->pad_count <= 0 || temp_data == NULL)
|
||||
return NULL;
|
||||
|
||||
while ((temp_data->id != pad_id) &&
|
||||
((temp_data = temp_data->next) != NULL));
|
||||
|
||||
return temp_data;
|
||||
}
|
||||
|
||||
static void add_pad_data(struct cisd* cisd, unsigned int pad_id, unsigned int pad_count)
|
||||
{
|
||||
struct pad_data* temp_data = cisd->pad_array->next;
|
||||
struct pad_data* pad_data;
|
||||
|
||||
if (pad_id == 0 || pad_id >= MAX_PAD_ID)
|
||||
return;
|
||||
|
||||
pad_data = create_pad_data(pad_id, pad_count);
|
||||
if (pad_data == NULL)
|
||||
return;
|
||||
|
||||
pr_info("%s: id(0x%x), count(%d)\n", __func__, pad_id, pad_count);
|
||||
while (temp_data) {
|
||||
if (temp_data->id > pad_id) {
|
||||
temp_data->prev->next = pad_data;
|
||||
pad_data->prev = temp_data->prev;
|
||||
pad_data->next = temp_data;
|
||||
temp_data->prev = pad_data;
|
||||
cisd->pad_count++;
|
||||
return;
|
||||
}
|
||||
temp_data = temp_data->next;
|
||||
}
|
||||
|
||||
pr_info("%s: failed to add pad_data(%d, %d)\n",
|
||||
__func__, pad_id, pad_count);
|
||||
kfree(pad_data);
|
||||
}
|
||||
|
||||
void init_cisd_pad_data(struct cisd* cisd)
|
||||
{
|
||||
struct pad_data* temp_data = cisd->pad_array;
|
||||
|
||||
mutex_lock(&cisd->padlock);
|
||||
while (temp_data) {
|
||||
struct pad_data* next_data = temp_data->next;
|
||||
|
||||
kfree(temp_data);
|
||||
temp_data = next_data;
|
||||
}
|
||||
|
||||
/* create dummy data */
|
||||
cisd->pad_count = 0;
|
||||
cisd->pad_array = create_pad_data(0, 0);
|
||||
if (cisd->pad_array == NULL)
|
||||
return;
|
||||
temp_data = create_pad_data(MAX_PAD_ID, 0);
|
||||
if (temp_data == NULL) {
|
||||
kfree(cisd->pad_array);
|
||||
return;
|
||||
}
|
||||
cisd->pad_array->next = temp_data;
|
||||
temp_data->prev = cisd->pad_array;
|
||||
mutex_unlock(&cisd->padlock);
|
||||
}
|
||||
|
||||
void count_cisd_pad_data(struct cisd* cisd, unsigned int pad_id)
|
||||
{
|
||||
struct pad_data* pad_data;
|
||||
|
||||
mutex_lock(&cisd->padlock);
|
||||
if ((pad_data = find_pad_data_by_id(cisd, pad_id)) != NULL)
|
||||
pad_data->count++;
|
||||
else
|
||||
add_pad_data(cisd, pad_id, 1);
|
||||
mutex_unlock(&cisd->padlock);
|
||||
}
|
||||
|
||||
static unsigned int convert_wc_index_to_pad_id(unsigned int wc_index)
|
||||
{
|
||||
switch (wc_index) {
|
||||
case WC_SNGL_NOBLE:
|
||||
return WC_PAD_ID_SNGL_NOBLE;
|
||||
case WC_SNGL_VEHICLE:
|
||||
return WC_PAD_ID_SNGL_VEHICLE;
|
||||
case WC_SNGL_MINI:
|
||||
return WC_PAD_ID_SNGL_MINI;
|
||||
case WC_SNGL_ZERO:
|
||||
return WC_PAD_ID_SNGL_ZERO;
|
||||
case WC_SNGL_DREAM:
|
||||
return WC_PAD_ID_SNGL_DREAM;
|
||||
case WC_STAND_HERO:
|
||||
return WC_PAD_ID_STAND_HERO;
|
||||
case WC_STAND_DREAM:
|
||||
return WC_PAD_ID_STAND_DREAM;
|
||||
case WC_EXT_PACK:
|
||||
return WC_PAD_ID_EXT_BATT_PACK;
|
||||
case WC_EXT_PACK_TA:
|
||||
return WC_PAD_ID_EXT_BATT_PACK_TA;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_cisd_pad_data(struct sec_battery_info *battery, const char* buf)
|
||||
{
|
||||
struct cisd* pcisd = &battery->cisd;
|
||||
unsigned int pad_index, pad_total_count, pad_id, pad_count;
|
||||
struct pad_data* pad_data;
|
||||
int i, x;
|
||||
|
||||
pr_info("%s: %s\n", __func__, buf);
|
||||
if (sscanf(buf, "%10d%n", &pad_index, &x) <= 0) {
|
||||
pr_info("%s: failed to read pad index\n", __func__);
|
||||
return;
|
||||
}
|
||||
buf += (size_t)x;
|
||||
pr_info("%s: stored pad_index(%d)\n", __func__, pad_index);
|
||||
|
||||
if (pcisd->pad_count > 0)
|
||||
init_cisd_pad_data(pcisd);
|
||||
|
||||
if (!pad_index) {
|
||||
for (i = WC_DATA_INDEX + 1; i < WC_DATA_MAX; i++) {
|
||||
if (sscanf(buf, "%10d%n", &pad_count, &x) <= 0)
|
||||
break;
|
||||
buf += (size_t)x;
|
||||
|
||||
if (pad_count > 0) {
|
||||
pad_id = convert_wc_index_to_pad_id(i);
|
||||
|
||||
mutex_lock(&pcisd->padlock);
|
||||
if ((pad_data = find_pad_data_by_id(pcisd, pad_id)) != NULL)
|
||||
pad_data->count = pad_count;
|
||||
else
|
||||
add_pad_data(pcisd, pad_id, pad_count);
|
||||
mutex_unlock(&pcisd->padlock);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((sscanf(buf + 1, "%10d%n", &pad_total_count, &x) <= 0) ||
|
||||
(pad_total_count >= MAX_PAD_ID))
|
||||
return;
|
||||
buf += (size_t)(x + 1);
|
||||
|
||||
pr_info("%s: add pad data(count: %d)\n", __func__, pad_total_count);
|
||||
for (i = 0; i < pad_total_count; i++) {
|
||||
if (sscanf(buf, " 0x%02x:%10d%n", &pad_id, &pad_count, &x) != 2) {
|
||||
pr_info("%s: failed to read pad data(0x%x, %d, %d)!!!re-init pad data\n",
|
||||
__func__, pad_id, pad_count, x);
|
||||
init_cisd_pad_data(pcisd);
|
||||
break;
|
||||
}
|
||||
buf += (size_t)x;
|
||||
|
||||
mutex_lock(&pcisd->padlock);
|
||||
if ((pad_data = find_pad_data_by_id(pcisd, pad_id)) != NULL)
|
||||
pad_data->count = pad_count;
|
||||
else
|
||||
add_pad_data(pcisd, pad_id, pad_count);
|
||||
mutex_unlock(&pcisd->padlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2013
drivers/battery_v2/sm5705_charger.c
Normal file
2013
drivers/battery_v2/sm5705_charger.c
Normal file
File diff suppressed because it is too large
Load Diff
225
drivers/battery_v2/sm5705_charger_oper.c
Normal file
225
drivers/battery_v2/sm5705_charger_oper.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* drivers/battery/sm5705_charger_oper.c
|
||||
*
|
||||
* SM5705 Charger Operation Mode controller
|
||||
*
|
||||
* Copyright (C) 2015 Siliconmitus Technology Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "include/charger/sm5705_charger.h"
|
||||
#include "include/charger/sm5705_charger_oper.h"
|
||||
|
||||
enum {
|
||||
BST_OUT_4000mV = 0x0,
|
||||
BST_OUT_4100mV = 0x1,
|
||||
BST_OUT_4200mV = 0x2,
|
||||
BST_OUT_4300mV = 0x3,
|
||||
BST_OUT_4400mV = 0x4,
|
||||
BST_OUT_4500mV = 0x5,
|
||||
BST_OUT_4600mV = 0x6,
|
||||
BST_OUT_4700mV = 0x7,
|
||||
BST_OUT_4800mV = 0x8,
|
||||
BST_OUT_4900mV = 0x9,
|
||||
BST_OUT_5000mV = 0xA,
|
||||
BST_OUT_5100mV = 0xB,
|
||||
};
|
||||
enum {
|
||||
OTG_CURRENT_500mA = 0x0,
|
||||
OTG_CURRENT_700mA = 0x1,
|
||||
OTG_CURRENT_900mA = 0x2,
|
||||
OTG_CURRENT_1500mA = 0x3,
|
||||
};
|
||||
#define SM5705_OPERATION_MODE_MASK 0x07
|
||||
#define SM5705_BSTOUT_MASK 0x0F
|
||||
#define SM5705_OTGCURRENT_MASK 0xC
|
||||
struct sm5705_charger_oper_table_info {
|
||||
unsigned char status;
|
||||
unsigned char oper_mode;
|
||||
unsigned char BST_OUT;
|
||||
unsigned char OTG_CURRENT;
|
||||
};
|
||||
struct sm5705_charger_oper_info {
|
||||
struct i2c_client *i2c;
|
||||
|
||||
bool suspend_mode;
|
||||
int max_table_num;
|
||||
struct sm5705_charger_oper_table_info current_table;
|
||||
};
|
||||
static struct sm5705_charger_oper_info oper_info;
|
||||
/**
|
||||
* (VBUS in/out) (WPC in/out) (FLASH on/off) (TORCH on/off) (OTG cable in/out) (Power Sharing cable in/out)
|
||||
**/
|
||||
static struct sm5705_charger_oper_table_info sm5705_charger_operation_mode_table[] = {
|
||||
/* Charger mode : Charging ON */
|
||||
{ make_OP_STATUS(0,0,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(1,0,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(1,1,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(1,0,0,1,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(1,1,0,1,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(0,1,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(1,0,0,0,1,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_1500mA},
|
||||
/* Charger mode : Flash Boost */
|
||||
{ make_OP_STATUS(0,0,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(0,0,1,0,1,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_1500mA},
|
||||
{ make_OP_STATUS(0,0,1,0,0,1), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA},
|
||||
{ make_OP_STATUS(0,1,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(0,1,1,0,1,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_1500mA},
|
||||
{ make_OP_STATUS(0,1,1,0,0,1), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA},
|
||||
{ make_OP_STATUS(1,0,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(1,1,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(0,0,0,1,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
/* Charger mode : Wireless OTG & Charger ON */
|
||||
{ make_OP_STATUS(0,1,0,1,1,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_1500mA},
|
||||
{ make_OP_STATUS(0,1,0,1,0,1), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
||||
{ make_OP_STATUS(0,1,0,1,0,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
||||
{ make_OP_STATUS(0,1,0,0,1,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_1500mA},
|
||||
{ make_OP_STATUS(0,1,0,0,0,1), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
||||
/* Charger mode : USB OTG */
|
||||
{ make_OP_STATUS(0,0,0,1,1,0), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_1500mA},
|
||||
{ make_OP_STATUS(0,0,0,1,0,1), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
||||
{ make_OP_STATUS(0,0,0,0,1,0), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_1500mA},
|
||||
{ make_OP_STATUS(0,0,0,0,0,1), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
||||
};
|
||||
|
||||
/**
|
||||
* SM5705 Charger operation mode controller relative I2C setup
|
||||
*/
|
||||
|
||||
static int sm5705_charger_oper_set_mode(struct i2c_client *i2c, unsigned char mode)
|
||||
{
|
||||
return sm5705_update_reg(i2c, SM5705_REG_CNTL, mode, SM5705_OPERATION_MODE_MASK);
|
||||
}
|
||||
static int sm5705_charger_oper_set_BSTOUT(struct i2c_client *i2c, unsigned char BSTOUT)
|
||||
{
|
||||
return sm5705_update_reg(i2c, SM5705_REG_FLEDCNTL6, BSTOUT, SM5705_BSTOUT_MASK);
|
||||
}
|
||||
static int sm5705_charger_oper_set_OTG_CURRENT(struct i2c_client *i2c, unsigned char OTG_CURRENT)
|
||||
{
|
||||
return sm5705_update_reg(i2c, SM5705_REG_CHGCNTL6, OTG_CURRENT << 2, SM5705_OTGCURRENT_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM5705 Charger operation mode controller API functions.
|
||||
*/
|
||||
|
||||
static inline unsigned char _update_status(int event_type, bool enable)
|
||||
{
|
||||
if (event_type > SM5705_CHARGER_OP_EVENT_VBUS)
|
||||
return oper_info.current_table.status;
|
||||
|
||||
if (enable)
|
||||
return (oper_info.current_table.status | (1 << event_type));
|
||||
else
|
||||
return (oper_info.current_table.status & ~(1 << event_type));
|
||||
}
|
||||
static inline void sm5705_charger_oper_change_state(unsigned char new_status)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < oper_info.max_table_num; ++i) {
|
||||
if (new_status == sm5705_charger_operation_mode_table[i].status)
|
||||
break;
|
||||
}
|
||||
if (i == oper_info.max_table_num) {
|
||||
pr_err("sm5705-charger: %s: can't find matched Charger Operation Mode Table (status = 0x%x)\n", __func__, new_status);
|
||||
return;
|
||||
}
|
||||
if (oper_info.suspend_mode) {
|
||||
pr_info("sm5705-charger: %s: skip setting by suspend mode(MODE: %d)\n",
|
||||
__func__, oper_info.current_table.oper_mode);
|
||||
goto skip_oper_change_state;
|
||||
}
|
||||
if (sm5705_charger_operation_mode_table[i].BST_OUT != oper_info.current_table.BST_OUT) {
|
||||
sm5705_charger_oper_set_BSTOUT(oper_info.i2c, sm5705_charger_operation_mode_table[i].BST_OUT);
|
||||
oper_info.current_table.BST_OUT = sm5705_charger_operation_mode_table[i].BST_OUT;
|
||||
}
|
||||
if (sm5705_charger_operation_mode_table[i].OTG_CURRENT != oper_info.current_table.OTG_CURRENT) {
|
||||
sm5705_charger_oper_set_OTG_CURRENT(oper_info.i2c, sm5705_charger_operation_mode_table[i].OTG_CURRENT);
|
||||
oper_info.current_table.OTG_CURRENT = sm5705_charger_operation_mode_table[i].OTG_CURRENT;
|
||||
}
|
||||
if (sm5705_call_fg_device_id() < 5) {
|
||||
/* USB_OTG to CHG_ON work-around for BAT_REG stabilize */
|
||||
if (oper_info.current_table.oper_mode == SM5705_CHARGER_OP_MODE_USB_OTG && \
|
||||
sm5705_charger_operation_mode_table[i].oper_mode == SM5705_CHARGER_OP_MODE_CHG_ON) {
|
||||
pr_info("sm5705-charger: %s: trans op_mode:suspend for BAT_REG stabilize (time=100ms)\n", __func__);
|
||||
sm5705_charger_oper_set_mode(oper_info.i2c, SM5705_CHARGER_OP_MODE_SUSPEND);
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
if (sm5705_charger_operation_mode_table[i].oper_mode != oper_info.current_table.oper_mode) {
|
||||
sm5705_charger_oper_set_mode(oper_info.i2c, sm5705_charger_operation_mode_table[i].oper_mode);
|
||||
oper_info.current_table.oper_mode = sm5705_charger_operation_mode_table[i].oper_mode;
|
||||
}
|
||||
skip_oper_change_state:
|
||||
oper_info.current_table.status = new_status;
|
||||
pr_info("sm5705-charger: %s: New table[%d] info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x\n", \
|
||||
__func__, i, oper_info.current_table.status, oper_info.current_table.oper_mode, oper_info.current_table.BST_OUT, oper_info.current_table.OTG_CURRENT);
|
||||
}
|
||||
|
||||
int sm5705_charger_oper_push_event(int event_type, bool enable)
|
||||
{
|
||||
unsigned char new_status;
|
||||
|
||||
if (oper_info.i2c == NULL) {
|
||||
pr_err("sm5705-charger: %s: required sm5705 charger operation table initialize\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
pr_info("sm5705-charger: %s: event_type=%d, enable=%d\n", __func__, event_type, enable);
|
||||
if (event_type == SM5705_CHARGER_OP_EVENT_SUSPEND_MODE) {
|
||||
oper_info.suspend_mode = enable;
|
||||
if (oper_info.suspend_mode) {
|
||||
oper_info.current_table.oper_mode = SM5705_CHARGER_OP_MODE_SUSPEND;
|
||||
sm5705_charger_oper_set_mode(oper_info.i2c, SM5705_CHARGER_OP_MODE_SUSPEND);
|
||||
} else if (oper_info.current_table.oper_mode == SM5705_CHARGER_OP_MODE_SUSPEND)
|
||||
sm5705_charger_oper_change_state(oper_info.current_table.status);
|
||||
goto out;
|
||||
}
|
||||
new_status = _update_status(event_type, enable);
|
||||
if (new_status == oper_info.current_table.status)
|
||||
goto out;
|
||||
|
||||
sm5705_charger_oper_change_state(new_status);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sm5705_charger_oper_push_event);
|
||||
int sm5705_charger_oper_table_init(struct i2c_client *i2c)
|
||||
{
|
||||
if (i2c == NULL) {
|
||||
pr_err("sm5705-charger: %s: invalid i2c client handler=n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
oper_info.i2c = i2c;
|
||||
/* set default operation mode condition */
|
||||
oper_info.suspend_mode = false;
|
||||
oper_info.max_table_num = ARRAY_SIZE(sm5705_charger_operation_mode_table);
|
||||
oper_info.current_table.status = make_OP_STATUS(0, 0, 0, 0, 0, 0);
|
||||
oper_info.current_table.oper_mode = SM5705_CHARGER_OP_MODE_CHG_ON;
|
||||
oper_info.current_table.BST_OUT = BST_OUT_4500mV;
|
||||
oper_info.current_table.OTG_CURRENT = OTG_CURRENT_500mA;
|
||||
sm5705_charger_oper_set_mode(oper_info.i2c, oper_info.current_table.oper_mode);
|
||||
sm5705_charger_oper_set_BSTOUT(oper_info.i2c, oper_info.current_table.BST_OUT);
|
||||
sm5705_charger_oper_set_OTG_CURRENT(oper_info.i2c, oper_info.current_table.OTG_CURRENT);
|
||||
pr_info("sm5705-charger: %s: current table info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x\n", \
|
||||
__func__, oper_info.current_table.status, oper_info.current_table.oper_mode, oper_info.current_table.BST_OUT, oper_info.current_table.OTG_CURRENT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sm5705_charger_oper_table_init);
|
||||
int sm5705_charger_oper_get_current_status(void)
|
||||
{
|
||||
return oper_info.current_table.status;
|
||||
}
|
||||
EXPORT_SYMBOL(sm5705_charger_oper_get_current_status);
|
||||
int sm5705_charger_oper_get_current_op_mode(void)
|
||||
{
|
||||
return oper_info.current_table.oper_mode;
|
||||
}
|
||||
EXPORT_SYMBOL(sm5705_charger_oper_get_current_op_mode);
|
||||
2582
drivers/battery_v2/sm5705_fuelgauge.c
Normal file
2582
drivers/battery_v2/sm5705_fuelgauge.c
Normal file
File diff suppressed because it is too large
Load Diff
61
drivers/ccic/Kconfig
Normal file
61
drivers/ccic/Kconfig
Normal file
@@ -0,0 +1,61 @@
|
||||
#
|
||||
# CCIC devices
|
||||
#
|
||||
|
||||
comment "CCIC configs"
|
||||
|
||||
config CCIC_NOTIFIER
|
||||
bool "CCIC notifier support"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
the CCIC attached device status change notification.
|
||||
|
||||
config CCIC_S2MM005
|
||||
bool "CCIC S2MM005"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
s2mm005 ccic full version chipset
|
||||
|
||||
config CCIC_ALTERNATE_MODE
|
||||
bool "support CCIC alternate mode"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
alternate mode
|
||||
|
||||
config CCIC_LPM_ENABLE
|
||||
bool "Support LPM ENABLE"
|
||||
depends on CCIC_S2MM005
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
lpm enable
|
||||
|
||||
config CCIC_WATER_DETECT
|
||||
bool "support WATER DETECT"
|
||||
depends on CCIC_S2MM005
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
water detect Enable
|
||||
|
||||
config CCIC_MANUAL_UPDATE
|
||||
bool "support CCIC manual update"
|
||||
depends on CCIC_S2MM005
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
ccic manual update
|
||||
|
||||
config CCIC_S2MM005_ANALOG_AUDIO
|
||||
bool "Support type-C analog audio"
|
||||
depends on CCIC_S2MM005
|
||||
default n
|
||||
help
|
||||
If you say yes here you will get support for
|
||||
water detect Enable
|
||||
7
drivers/ccic/Makefile
Normal file
7
drivers/ccic/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for ccic devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CCIC_NOTIFIER) += ccic_notifier.o ccic_sysfs.o ccic_alternate.o
|
||||
obj-$(CONFIG_CCIC_S2MM005) += s2mm005_fw.o s2mm005_cc.o s2mm005_pd.o s2mm005.o
|
||||
obj-$(CONFIG_CCIC_ALTERNATE_MODE) += ccic_misc.o
|
||||
1982
drivers/ccic/ccic_alternate.c
Normal file
1982
drivers/ccic/ccic_alternate.c
Normal file
File diff suppressed because it is too large
Load Diff
253
drivers/ccic/ccic_misc.c
Normal file
253
drivers/ccic/ccic_misc.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* driver/ccic/ccic_misc.c - S2MM005 CCIC MISC driver
|
||||
*
|
||||
* Copyright (C) 2017 Samsung Electronics
|
||||
* Author: Wookwang Lee <wookwang.lee@samsung.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
//serial_acm.c
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/poll.h>
|
||||
#include "ccic_misc.h"
|
||||
|
||||
static struct ccic_misc_dev *c_dev;
|
||||
|
||||
#define MAX_BUF 255
|
||||
#define DEXDOCK_PRODUCT_ID 0xA020
|
||||
#define NODE_OF_MISC "ccic_misc"
|
||||
#define CCIC_IOCTL_UVDM _IOWR('C', 0, struct uvdm_data)
|
||||
|
||||
static inline int _lock(atomic_t *excl)
|
||||
{
|
||||
if (atomic_inc_return(excl) == 1) {
|
||||
return 0;
|
||||
} else {
|
||||
atomic_dec(excl);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _unlock(atomic_t *excl)
|
||||
{
|
||||
atomic_dec(excl);
|
||||
}
|
||||
|
||||
static int ccic_misc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s + open success\n", __func__);
|
||||
if (!c_dev) {
|
||||
pr_err("%s - error : c_dev is NULL\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (_lock(&c_dev->open_excl)) {
|
||||
pr_err("%s - error : device busy\n", __func__);
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (!samsung_uvdm_ready()) {
|
||||
// check if there is some connection
|
||||
_unlock(&c_dev->open_excl);
|
||||
pr_err("%s - error : uvdm is not ready\n", __func__);
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
pr_info("%s - open success\n", __func__);
|
||||
|
||||
return 0;
|
||||
err1:
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccic_misc_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (c_dev)
|
||||
_unlock(&c_dev->open_excl);
|
||||
samsung_uvdm_close();
|
||||
pr_info("%s - close success\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_uvdm_message(void *data, int size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("%s - size : %d\n", __func__, size);
|
||||
ret = samsung_uvdm_out_request_message(data, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int receive_uvdm_message(void *data, int size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("%s - size : %d\n", __func__, size);
|
||||
ret = samsung_uvdm_in_request_message(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long
|
||||
ccic_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
void *buf = NULL;
|
||||
|
||||
if (_lock(&c_dev->ioctl_excl)) {
|
||||
pr_err("%s - error : ioctl busy - cmd : %d\n", __func__, cmd);
|
||||
ret = -EBUSY;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CCIC_IOCTL_UVDM:
|
||||
pr_info("%s - CCIC_IOCTL_UVDM cmd\n", __func__);
|
||||
if (copy_from_user(&c_dev->u_data, (void __user *) arg,
|
||||
sizeof(struct uvdm_data))) {
|
||||
ret = -EIO;
|
||||
pr_err("%s - copy_from_user error\n", __func__);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
buf = kzalloc(MAX_BUF, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -EINVAL;
|
||||
pr_err("%s - kzalloc error\n", __func__);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (c_dev->u_data.size > MAX_BUF) {
|
||||
ret = -ENOMEM;
|
||||
pr_err("%s - user data size is %d error\n", __func__, c_dev->u_data.size);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (c_dev->u_data.dir == DIR_OUT) {
|
||||
if (copy_from_user(buf, c_dev->u_data.pData,\
|
||||
c_dev->u_data.size)) {
|
||||
ret = -EIO;
|
||||
pr_err("%s - copy_from_user error\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
ret = send_uvdm_message(buf, c_dev->u_data.size);
|
||||
if (ret <= 0) {
|
||||
pr_err("%s - send_uvdm_message error\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
ret = receive_uvdm_message(buf, c_dev->u_data.size);
|
||||
if (ret <= 0) {
|
||||
pr_err("%s - receive_uvdm_message error\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
if (copy_to_user((void __user *)c_dev->u_data.pData,
|
||||
buf, ret)) {
|
||||
ret = -EIO;
|
||||
pr_err("%s - copy_to_user error\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("%s - unknown ioctl cmd : %d\n", __func__, cmd);
|
||||
ret = -ENOIOCTLCMD;
|
||||
goto err;
|
||||
}
|
||||
err:
|
||||
kfree(buf);
|
||||
err1:
|
||||
_unlock(&c_dev->ioctl_excl);
|
||||
err2:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long
|
||||
ccic_misc_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
pr_info("%s - cmd : %d\n", __func__, cmd);
|
||||
ret = ccic_misc_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations ccic_misc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ccic_misc_open,
|
||||
.release = ccic_misc_close,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = ccic_misc_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ccic_misc_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct miscdevice ccic_misc_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = NODE_OF_MISC,
|
||||
.fops = &ccic_misc_fops,
|
||||
};
|
||||
|
||||
int ccic_misc_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = misc_register(&ccic_misc_device);
|
||||
if (ret) {
|
||||
pr_err("%s - return error : %d\n", __func__, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
c_dev = kzalloc(sizeof(struct ccic_misc_dev), GFP_KERNEL);
|
||||
if (!c_dev) {
|
||||
ret = -ENOMEM;
|
||||
pr_err("%s - kzalloc failed : %d\n", __func__, ret);
|
||||
goto err1;
|
||||
}
|
||||
atomic_set(&c_dev->open_excl, 0);
|
||||
atomic_set(&c_dev->ioctl_excl, 0);
|
||||
|
||||
pr_info("%s - register success\n", __func__);
|
||||
return 0;
|
||||
err1:
|
||||
misc_deregister(&ccic_misc_device);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ccic_misc_init);
|
||||
|
||||
void ccic_misc_exit(void)
|
||||
{
|
||||
pr_info("%s() called\n", __func__);
|
||||
if (!c_dev)
|
||||
return;
|
||||
kfree(c_dev);
|
||||
misc_deregister(&ccic_misc_device);
|
||||
}
|
||||
EXPORT_SYMBOL(ccic_misc_exit);
|
||||
105
drivers/ccic/ccic_misc.h
Normal file
105
drivers/ccic/ccic_misc.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* driver/ccic/ccic_misc.h - S2MM005 CCIC MISC driver
|
||||
*
|
||||
* Copyright (C) 2017 Samsung Electronics
|
||||
* Author: Wookwang Lee <wookwang.lee@samsung.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
enum uvdm_data_type {
|
||||
TYPE_SHORT = 0,
|
||||
TYPE_LONG,
|
||||
};
|
||||
|
||||
enum uvdm_direction_type {
|
||||
DIR_OUT = 0,
|
||||
DIR_IN,
|
||||
};
|
||||
#if 0
|
||||
typedef union sec_uvdm_header {
|
||||
uint32_t data;
|
||||
struct {
|
||||
uint8_t bdata[4];
|
||||
} BYTES;
|
||||
struct {
|
||||
uint32_t data:8,
|
||||
total_number_of_uvdm_set:4,
|
||||
direction:1,
|
||||
command_type:2,
|
||||
data_type:1,
|
||||
pid:16;
|
||||
} BITS;
|
||||
} U_SEC_UVDM_HEADER;
|
||||
|
||||
typedef U_SEC_UVDM_HEADER U_SEC_UVDM_RESPONSE_HEADER;
|
||||
|
||||
typedef union sec_tx_data_header {
|
||||
uint32_t data;
|
||||
struct {
|
||||
uint8_t bdata[4];
|
||||
} BYTES;
|
||||
struct {
|
||||
uint32_t data_size_of_current_set:8,
|
||||
total_data_size:8,
|
||||
reserved:12,
|
||||
order_of_current_uvdm_set:4;
|
||||
} BITS;
|
||||
} U_SEC_TX_DATA_HEADER;
|
||||
|
||||
typedef union sec_data_tx_tailer {
|
||||
uint32_t data;
|
||||
struct {
|
||||
uint8_t bdata[4];
|
||||
} BYTES;
|
||||
struct {
|
||||
uint32_t checksum:16,
|
||||
reserved:16;
|
||||
} BITS;
|
||||
} U_SEC_DATA_TX_TAILER;
|
||||
|
||||
typedef union sec_data_rx_header {
|
||||
uint32_t data;
|
||||
struct {
|
||||
uint8_t bdata[4];
|
||||
} BYTES;
|
||||
struct {
|
||||
uint32_t reserved:18,
|
||||
result_value:2,
|
||||
received_data_size_of_current_set:8,
|
||||
order_of_current_uvdm_set:4;
|
||||
} BITS;
|
||||
} U_SEC_DATA_RX_HEADER;
|
||||
#endif
|
||||
struct uvdm_data {
|
||||
unsigned short pid; /* Product ID */
|
||||
char type; /* uvdm_data_type */
|
||||
char dir; /* uvdm_direction_type */
|
||||
unsigned int size; /* data size */
|
||||
void __user *pData; /* data pointer */
|
||||
};
|
||||
|
||||
struct ccic_misc_dev {
|
||||
struct uvdm_data u_data;
|
||||
atomic_t open_excl;
|
||||
atomic_t ioctl_excl;
|
||||
int (*uvdm_write)(void *data, int size);
|
||||
int (*uvdm_read)(void *data, int size);
|
||||
};
|
||||
|
||||
extern ssize_t samsung_uvdm_out_request_message(void *data, size_t size);
|
||||
extern int samsung_uvdm_in_request_message(void *data);
|
||||
extern int samsung_uvdm_ready(void);
|
||||
extern void samsung_uvdm_close(void);
|
||||
321
drivers/ccic/ccic_notifier.c
Normal file
321
drivers/ccic/ccic_notifier.c
Normal file
@@ -0,0 +1,321 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/ccic/ccic_notifier.h>
|
||||
#include <linux/sec_class.h>
|
||||
#include <linux/ccic/ccic_sysfs.h>
|
||||
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
||||
#include <linux/battery/battery_notifier.h>
|
||||
#endif
|
||||
#include <linux/usb_notify.h>
|
||||
|
||||
#define DEBUG
|
||||
#define SET_CCIC_NOTIFIER_BLOCK(nb, fn, dev) do { \
|
||||
(nb)->notifier_call = (fn); \
|
||||
(nb)->priority = (dev); \
|
||||
} while (0)
|
||||
|
||||
#define DESTROY_CCIC_NOTIFIER_BLOCK(nb) \
|
||||
SET_CCIC_NOTIFIER_BLOCK(nb, NULL, -1)
|
||||
|
||||
static struct ccic_notifier_struct ccic_notifier;
|
||||
|
||||
struct device *ccic_device;
|
||||
static int ccic_notifier_init_done = 0;
|
||||
int ccic_notifier_init(void);
|
||||
|
||||
char CCIC_NOTI_DEST_Print[CCIC_NOTI_DEST_NUM][10] =
|
||||
{
|
||||
{"INITIAL"},
|
||||
{"USB"},
|
||||
{"BATTERY"},
|
||||
{"PDIC"},
|
||||
{"MUIC"},
|
||||
{"CCIC"},
|
||||
{"MANAGER"},
|
||||
{"DP"},
|
||||
{"DPUSB"},
|
||||
{"ALL"},
|
||||
};
|
||||
|
||||
char CCIC_NOTI_ID_Print[CCIC_NOTI_ID_NUM][20] =
|
||||
{
|
||||
{"ID_INITIAL"},
|
||||
{"ID_ATTACH"},
|
||||
{"ID_RID"},
|
||||
{"ID_USB"},
|
||||
{"ID_POWER_STATUS"},
|
||||
{"ID_WATER"},
|
||||
{"ID_VCONN"},
|
||||
{"ID_DP_CONNECT"},
|
||||
{"ID_DP_HPD"},
|
||||
{"ID_DP_LINK_CONF"},
|
||||
{"ID_DP_USB"},
|
||||
{"ID_ROLE_SWAP"},
|
||||
{"ID_FAC"},
|
||||
{"ID_PIN_STATUS"},
|
||||
};
|
||||
|
||||
char CCIC_NOTI_RID_Print[CCIC_NOTI_RID_NUM][15] =
|
||||
{
|
||||
{"RID_UNDEFINED"},
|
||||
{"RID_000K"},
|
||||
{"RID_001K"},
|
||||
{"RID_255K"},
|
||||
{"RID_301K"},
|
||||
{"RID_523K"},
|
||||
{"RID_619K"},
|
||||
{"RID_OPEN"},
|
||||
};
|
||||
|
||||
char CCIC_NOTI_USB_STATUS_Print[CCIC_NOTI_USB_STATUS_NUM][20] =
|
||||
{
|
||||
{"USB_DETACH"},
|
||||
{"USB_ATTACH_DFP"},
|
||||
{"USB_ATTACH_UFP"},
|
||||
{"USB_ATTACH_DRP"},
|
||||
{"USB_ATTACH_NO_USB"},
|
||||
};
|
||||
|
||||
int ccic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
|
||||
ccic_notifier_device_t listener)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d register\n", __func__, listener);
|
||||
|
||||
/* Check if CCIC Notifier is ready. */
|
||||
if(!ccic_notifier_init_done)
|
||||
ccic_notifier_init();
|
||||
|
||||
if (!ccic_device) {
|
||||
pr_err("%s: Not Initialized...\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SET_CCIC_NOTIFIER_BLOCK(nb, notifier, listener);
|
||||
ret = blocking_notifier_chain_register(&(ccic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
|
||||
__func__, ret);
|
||||
|
||||
/* current ccic's attached_device status notify */
|
||||
nb->notifier_call(nb, 0,
|
||||
&(ccic_notifier.ccic_template));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ccic_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&(ccic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
|
||||
__func__, ret);
|
||||
DESTROY_CCIC_NOTIFIER_BLOCK(nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccic_uevent_work(int id, int state)
|
||||
{
|
||||
char *water[2] = { "CCIC=WATER", NULL };
|
||||
char *dry[2] = { "CCIC=DRY", NULL };
|
||||
char *vconn[2] = { "CCIC=VCONN", NULL };
|
||||
#if defined(CONFIG_SEC_FACTORY)
|
||||
char ccicrid[15] = {0,};
|
||||
char *rid[2] = {ccicrid, NULL};
|
||||
char ccicFacErr[20] = {0,};
|
||||
char *facErr[2] = {ccicFacErr, NULL};
|
||||
#endif
|
||||
|
||||
pr_info("usb: %s: id=%s state=%d\n", __func__, CCIC_NOTI_ID_Print[id], state);
|
||||
|
||||
switch (id) {
|
||||
case CCIC_NOTIFY_ID_WATER:
|
||||
if (state)
|
||||
kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, water);
|
||||
else
|
||||
kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, dry);
|
||||
break;
|
||||
case CCIC_NOTIFY_ID_VCONN:
|
||||
kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, vconn);
|
||||
break;
|
||||
#if defined(CONFIG_SEC_FACTORY)
|
||||
case CCIC_NOTIFY_ID_RID:
|
||||
snprintf(ccicrid, sizeof(ccicrid), "%s",
|
||||
(state < CCIC_NOTI_RID_NUM) ? CCIC_NOTI_RID_Print[state] : CCIC_NOTI_RID_Print[0]);
|
||||
kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, rid);
|
||||
break;
|
||||
case CCIC_NOTIFY_ID_FAC:
|
||||
snprintf(ccicFacErr, sizeof(ccicFacErr), "%s:%d",
|
||||
"ERR_STATE", state);
|
||||
kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, facErr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ccic's attached_device attach broadcast */
|
||||
int ccic_notifier_notify(CC_NOTI_TYPEDEF *p_noti, void *pd, int pdic_attach)
|
||||
{
|
||||
int ret = 0;
|
||||
ccic_notifier.ccic_template = *p_noti;
|
||||
|
||||
switch (p_noti->id) {
|
||||
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
||||
case CCIC_NOTIFY_ID_POWER_STATUS: // PDIC_NOTIFY_EVENT_PD_SINK
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x "
|
||||
"attach:%02x cable_type:%02x rprd:%01x\n", __func__,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->cable_type,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->rprd);
|
||||
|
||||
if (pd != NULL) {
|
||||
if (!((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach &&
|
||||
((struct pdic_notifier_struct *)pd)->event != PDIC_NOTIFY_EVENT_CCIC_ATTACH) {
|
||||
((struct pdic_notifier_struct *)pd)->event = PDIC_NOTIFY_EVENT_DETACH;
|
||||
}
|
||||
ccic_notifier.ccic_template.pd = pd;
|
||||
|
||||
pr_info("%s: PD event:%d, num:%d, sel:%d \n", __func__,
|
||||
((struct pdic_notifier_struct *)pd)->event,
|
||||
((struct pdic_notifier_struct *)pd)->sink_status.available_pdo_num,
|
||||
((struct pdic_notifier_struct *)pd)->sink_status.selected_pdo_num);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case CCIC_NOTIFY_ID_ATTACH:
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x "
|
||||
"attach:%02x cable_type:%02x rprd:%01x\n", __func__,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->cable_type,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->rprd);
|
||||
break;
|
||||
case CCIC_NOTIFY_ID_RID:
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x rid:%02x\n", __func__,
|
||||
((CC_NOTI_RID_TYPEDEF *)p_noti)->src,
|
||||
((CC_NOTI_RID_TYPEDEF *)p_noti)->dest,
|
||||
((CC_NOTI_RID_TYPEDEF *)p_noti)->id,
|
||||
((CC_NOTI_RID_TYPEDEF *)p_noti)->rid);
|
||||
#if defined(CONFIG_SEC_FACTORY)
|
||||
ccic_uevent_work(CCIC_NOTIFY_ID_RID,((CC_NOTI_RID_TYPEDEF *)p_noti)->rid);
|
||||
#endif
|
||||
break;
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
case CCIC_NOTIFY_ID_FAC:
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x ErrState:%02x\n", __func__,
|
||||
p_noti->src, p_noti->dest, p_noti->id, p_noti->sub1);
|
||||
ccic_uevent_work(CCIC_NOTIFY_ID_FAC, p_noti->sub1);
|
||||
return 0;
|
||||
#endif
|
||||
case CCIC_NOTIFY_ID_WATER:
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x attach:%02x\n", __func__,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach);
|
||||
ccic_uevent_work(CCIC_NOTIFY_ID_WATER, ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach);
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
return 0;
|
||||
#endif
|
||||
break;
|
||||
case CCIC_NOTIFY_ID_VCONN:
|
||||
ccic_uevent_work(CCIC_NOTIFY_ID_VCONN, 0);
|
||||
break;
|
||||
case CCIC_NOTIFY_ID_ROLE_SWAP:
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x sub1:%02x\n", __func__,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id,
|
||||
((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach);
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: src:%01x dest:%01x id:%02x "
|
||||
"sub1:%d sub2:%02x sub3:%02x\n", __func__,
|
||||
((CC_NOTI_TYPEDEF *)p_noti)->src,
|
||||
((CC_NOTI_TYPEDEF *)p_noti)->dest,
|
||||
((CC_NOTI_TYPEDEF *)p_noti)->id,
|
||||
((CC_NOTI_TYPEDEF *)p_noti)->sub1,
|
||||
((CC_NOTI_TYPEDEF *)p_noti)->sub2,
|
||||
((CC_NOTI_TYPEDEF *)p_noti)->sub3);
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
|
||||
store_usblog_notify(NOTIFY_CCIC_EVENT, (void *)p_noti, NULL);
|
||||
#endif
|
||||
ret = blocking_notifier_call_chain(&(ccic_notifier.notifier_call_chain),
|
||||
p_noti->id, &(ccic_notifier.ccic_template));
|
||||
|
||||
|
||||
switch (ret) {
|
||||
case NOTIFY_STOP_MASK:
|
||||
case NOTIFY_BAD:
|
||||
pr_err("%s: notify error occur(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
case NOTIFY_DONE:
|
||||
case NOTIFY_OK:
|
||||
pr_info("%s: notify done(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int ccic_notifier_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
if(ccic_notifier_init_done)
|
||||
{
|
||||
pr_err("%s already registered\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
ccic_notifier_init_done = 1;
|
||||
ccic_device = sec_device_create(0, NULL, "ccic");
|
||||
|
||||
if (IS_ERR(ccic_device)) {
|
||||
pr_err("%s Failed to create device(switch)!\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* create sysfs group */
|
||||
ret = sysfs_create_group(&ccic_device->kobj, &ccic_sysfs_group);
|
||||
if (ret) {
|
||||
pr_err("%s: ccic sysfs fail, ret %d", __func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&(ccic_notifier.notifier_call_chain));
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ccic_notifier_exit(void)
|
||||
{
|
||||
pr_info("%s: exit\n", __func__);
|
||||
}
|
||||
|
||||
device_initcall(ccic_notifier_init);
|
||||
module_exit(ccic_notifier_exit);
|
||||
662
drivers/ccic/ccic_sysfs.c
Normal file
662
drivers/ccic/ccic_sysfs.c
Normal file
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* ccic_sysfs.c
|
||||
*
|
||||
* Copyright (C) 2016 Samsung Electronics
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ccic/ccic_sysfs.h>
|
||||
#include <linux/ccic/s2mm005.h>
|
||||
#include <linux/ccic/s2mm005_ext.h>
|
||||
#include <linux/ccic/s2mm005_fw.h>
|
||||
#include <linux/ccic/ccic_alternate.h>
|
||||
|
||||
|
||||
static ssize_t s2mm005_cur_ver_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_version chip_swver;
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
|
||||
pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__,
|
||||
chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
|
||||
|
||||
usbpd_data->firm_ver[0] = chip_swver.main[2];
|
||||
usbpd_data->firm_ver[1] = chip_swver.main[1];
|
||||
usbpd_data->firm_ver[2] = chip_swver.main[0];
|
||||
usbpd_data->firm_ver[3] = chip_swver.boot;
|
||||
|
||||
return sprintf(buf, "%02X %02X %02X %02X\n",
|
||||
usbpd_data->firm_ver[0], usbpd_data->firm_ver[1],
|
||||
usbpd_data->firm_ver[2], usbpd_data->firm_ver[3]);
|
||||
|
||||
}
|
||||
static DEVICE_ATTR(cur_version, 0664, s2mm005_cur_ver_show, NULL);
|
||||
|
||||
static ssize_t s2mm005_src_ver_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
struct s2mm005_version fw_swver;
|
||||
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id,
|
||||
&fw_swver, usbpd_data->firm_ver[3], usbpd_data->hw_rev);
|
||||
return sprintf(buf, "%02X %02X %02X %02X\n",
|
||||
fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot);
|
||||
}
|
||||
static DEVICE_ATTR(src_version, 0664, s2mm005_src_ver_show, NULL);
|
||||
|
||||
static ssize_t s2mm005_show_manual_lpm_mode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", usbpd_data->manual_lpm_mode);
|
||||
}
|
||||
static ssize_t s2mm005_store_manual_lpm_mode(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int mode;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &mode);
|
||||
pr_info("usb: %s mode=%d\n", __func__, mode);
|
||||
|
||||
switch(mode){
|
||||
case 0:
|
||||
/* Disable Low Power Mode for App (SW JIGON Disable) */
|
||||
s2mm005_manual_JIGON(usbpd_data, 0);
|
||||
usbpd_data->manual_lpm_mode = 0;
|
||||
break;
|
||||
case 1:
|
||||
/* Enable Low Power Mode for App (SW JIGON Enable) */
|
||||
s2mm005_manual_JIGON(usbpd_data, 1);
|
||||
usbpd_data->manual_lpm_mode = 1;
|
||||
break;
|
||||
case 2:
|
||||
/* SW JIGON Enable */
|
||||
s2mm005_manual_JIGON(usbpd_data, 1);
|
||||
// s2mm005_manual_LPM(usbpd_data, 0x1);
|
||||
usbpd_data->manual_lpm_mode = 1;
|
||||
break;
|
||||
default:
|
||||
/* SW JIGON Disable */
|
||||
s2mm005_manual_JIGON(usbpd_data, 0);
|
||||
usbpd_data->manual_lpm_mode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(lpm_mode, 0664,
|
||||
s2mm005_show_manual_lpm_mode, s2mm005_store_manual_lpm_mode);
|
||||
|
||||
static ssize_t ccic_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", usbpd_data->pd_state);
|
||||
}
|
||||
static DEVICE_ATTR(state, 0664, ccic_state_show, NULL);
|
||||
|
||||
#if defined(CONFIG_SEC_FACTORY)
|
||||
static ssize_t ccic_rid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", usbpd_data->cur_rid);
|
||||
}
|
||||
static DEVICE_ATTR(rid, 0664, ccic_rid_show, NULL);
|
||||
|
||||
static ssize_t s2mm005_store_control_option_command(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int cmd;
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &cmd);
|
||||
pr_info("usb: %s mode=%d\n", __func__, cmd);
|
||||
|
||||
s2mm005_control_option_command(usbpd_data, cmd);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(ccic_control_option, 0664, NULL, s2mm005_store_control_option_command);
|
||||
|
||||
static ssize_t ccic_booting_dry_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
pr_info("%s booting_run_dry=%d\n", __func__,
|
||||
usbpd_data->fac_booting_dry_check);
|
||||
|
||||
return sprintf(buf, "%d\n", (usbpd_data->fac_booting_dry_check));
|
||||
}
|
||||
static DEVICE_ATTR(booting_dry, 0444, ccic_booting_dry_show, NULL);
|
||||
#endif
|
||||
|
||||
static int ccic_firmware_update_built_in(struct device *dev)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
struct s2mm005_version chip_swver, fw_swver, hwver;
|
||||
|
||||
s2mm005_get_chip_hwversion(usbpd_data, &hwver);
|
||||
pr_err("%s CHIP HWversion %2x %2x %2x %2x\n", __func__,
|
||||
hwver.main[2] , hwver.main[1], hwver.main[0], hwver.boot);
|
||||
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
|
||||
pr_err("%s CHIP SWversion %2x %2x %2x %2x - before\n", __func__,
|
||||
chip_swver.main[2] , chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
|
||||
s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id,
|
||||
&fw_swver, chip_swver.boot, usbpd_data->hw_rev);
|
||||
pr_err("%s SRC SWversion:%2x,%2x,%2x,%2x\n",__func__,
|
||||
fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot);
|
||||
|
||||
pr_err("%s: FW UPDATE boot:%01d hw_rev:%02d\n", __func__, chip_swver.boot, usbpd_data->hw_rev);
|
||||
|
||||
if(chip_swver.main[0] == fw_swver.main[0]) {
|
||||
pr_err("%s: FW version is same. Stop FW update. src:%2x chip:%2x\n",
|
||||
__func__, chip_swver.main[0], fw_swver.main[0]);
|
||||
} else
|
||||
s2mm005_flash_fw(usbpd_data, chip_swver.boot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccic_firmware_update_ums(struct device *dev)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
unsigned char *fw_data;
|
||||
struct s2mm005_fw *fw_hd;
|
||||
struct file *fp;
|
||||
mm_segment_t old_fs;
|
||||
long fw_size, nread;
|
||||
int error = 0;
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
fp = filp_open(CCIC_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: failed to open %s.\n", __func__,
|
||||
CCIC_DEFAULT_UMS_FW);
|
||||
error = -ENOENT;
|
||||
goto open_err;
|
||||
}
|
||||
|
||||
fw_size = fp->f_path.dentry->d_inode->i_size;
|
||||
|
||||
if (0 < fw_size) {
|
||||
fw_data = kzalloc(fw_size, GFP_KERNEL);
|
||||
nread = vfs_read(fp, (char __user *)fw_data, fw_size, &fp->f_pos);
|
||||
|
||||
pr_info("%s: start, file path %s, size %ld Bytes\n",
|
||||
__func__, CCIC_DEFAULT_UMS_FW, fw_size);
|
||||
filp_close(fp, NULL);
|
||||
|
||||
if (nread != fw_size) {
|
||||
pr_err("%s: failed to read firmware file, nread %ld Bytes\n",
|
||||
__func__, nread);
|
||||
error = -EIO;
|
||||
} else {
|
||||
fw_hd = (struct s2mm005_fw*)fw_data;
|
||||
pr_info("CCIC FW ver - cur:%02X %02X %02X %02X / bin:%02X %02X %02X %02X\n",
|
||||
usbpd_data->firm_ver[0], usbpd_data->firm_ver[1], usbpd_data->firm_ver[2], usbpd_data->firm_ver[3],
|
||||
fw_hd->boot, fw_hd->main[0], fw_hd->main[1], fw_hd->main[2]);
|
||||
|
||||
if(fw_hd->boot == usbpd_data->firm_ver[3]) {
|
||||
if (s2mm005_flash_fw(usbpd_data, FLASH_WRITE_UMS) >= 0)
|
||||
goto done;
|
||||
} else {
|
||||
pr_err("error : Didn't match to CCIC FW firmware version\n");
|
||||
error = -EINVAL;
|
||||
}
|
||||
}
|
||||
if (error < 0)
|
||||
pr_err("%s: failed update firmware\n", __func__);
|
||||
done:
|
||||
kfree(fw_data);
|
||||
}
|
||||
|
||||
open_err:
|
||||
set_fs(old_fs);
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t ccic_store_firmware_status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
u8 val = 0;
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
s2mm005_read_byte_flash(usbpd_data->i2c, FLASH_STATUS_0x24, &val, 1);
|
||||
pr_err("%s flash mode: %s\n", __func__, flashmode_to_string(val));
|
||||
|
||||
return sprintf(buf, "%s\n", flashmode_to_string(val));
|
||||
}
|
||||
static DEVICE_ATTR(fw_update_status, 0444, ccic_store_firmware_status_show, NULL);
|
||||
|
||||
static ssize_t ccic_store_firmware_update(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
struct s2mm005_version version;
|
||||
int mode = 0, ret = 1;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &mode);
|
||||
pr_info("%s mode=%d\n", __func__, mode);
|
||||
|
||||
s2mm005_get_chip_swversion(usbpd_data, &version);
|
||||
pr_err("%s CHIP SWversion %2x %2x %2x %2x - before\n", __func__,
|
||||
version.main[2] , version.main[1], version.main[0], version.boot);
|
||||
|
||||
/* Factory cmd for firmware update
|
||||
* argument represent what is source of firmware like below.
|
||||
*
|
||||
* 0 : [BUILT_IN] Getting firmware from source.
|
||||
* 1 : [UMS] Getting firmware from sd card.
|
||||
*/
|
||||
switch (mode) {
|
||||
case BUILT_IN:
|
||||
ret = ccic_firmware_update_built_in(dev);
|
||||
break;
|
||||
case UMS:
|
||||
ret = ccic_firmware_update_ums(dev);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Not support command[%d]\n",
|
||||
__func__, mode);
|
||||
break;
|
||||
}
|
||||
|
||||
s2mm005_get_chip_swversion(usbpd_data, &version);
|
||||
pr_err("%s CHIP SWversion %2x %2x %2x %2x - after\n", __func__,
|
||||
version.main[2] , version.main[1], version.main[0], version.boot);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(fw_update, 0220, NULL, ccic_store_firmware_update);
|
||||
|
||||
|
||||
static ssize_t ccic_set_gpio(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int mode;
|
||||
u8 W_DATA[2];
|
||||
u8 REG_ADD;
|
||||
u8 R_DATA;
|
||||
int i;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &mode);
|
||||
pr_info("usb: %s mode=%d\n", __func__, mode);
|
||||
|
||||
/* for Wake up*/
|
||||
for(i=0; i<5; i++){
|
||||
R_DATA = 0x00;
|
||||
REG_ADD = 0x8;
|
||||
s2mm005_read_byte(usbpd_data->i2c, REG_ADD, &R_DATA, 1); //dummy read
|
||||
}
|
||||
udelay(10);
|
||||
|
||||
switch(mode){
|
||||
case 0:
|
||||
/* SBU1/SBU2 set as open-drain status*/
|
||||
// SBU1/2 Open command ON
|
||||
REG_ADD = 0x10;
|
||||
W_DATA[0] = 0x03;
|
||||
W_DATA[1] = 0x85;
|
||||
s2mm005_write_byte(usbpd_data->i2c, REG_ADD, &W_DATA[0], 2);
|
||||
break;
|
||||
case 1:
|
||||
/* SBU1/SBU2 set as default status */
|
||||
// SBU1/2 Open command OFF
|
||||
REG_ADD = 0x10;
|
||||
W_DATA[0] = 0x03;
|
||||
W_DATA[1] = 0x86;
|
||||
s2mm005_write_byte(usbpd_data->i2c, REG_ADD, &W_DATA[0], 2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t ccic_get_gpio(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
u8 W_DATA[4];
|
||||
u8 REG_ADD;
|
||||
u8 R_DATA;
|
||||
int i;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* for Wake up*/
|
||||
for(i=0; i<5; i++){
|
||||
R_DATA = 0x00;
|
||||
REG_ADD = 0x8;
|
||||
s2mm005_read_byte(usbpd_data->i2c, REG_ADD, &R_DATA, 1); //dummy read
|
||||
}
|
||||
udelay(10);
|
||||
|
||||
W_DATA[0] =0x2;
|
||||
W_DATA[1] =0x10;
|
||||
|
||||
W_DATA[2] =0x84;
|
||||
W_DATA[3] =0x10;
|
||||
|
||||
s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 4);
|
||||
s2mm005_read_byte(usbpd_data->i2c, 0x14, &R_DATA, 1);
|
||||
|
||||
pr_err("%s SBU1 status = %2x , SBU2 status = %2x \n", __func__,
|
||||
(R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5);
|
||||
|
||||
return sprintf(buf, "%d %d\n", (R_DATA & 0x10) >> 4,(R_DATA & 0x20) >> 5);
|
||||
}
|
||||
static DEVICE_ATTR(control_gpio, 0664, ccic_get_gpio, ccic_set_gpio);
|
||||
|
||||
static ssize_t ccic_water_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
|
||||
if(!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
pr_info("%s water=%d, run_dry=%d\n", __func__,
|
||||
usbpd_data->water_det, usbpd_data->run_dry);
|
||||
|
||||
return sprintf(buf, "%d\n", (usbpd_data->water_det | !usbpd_data->run_dry));
|
||||
}
|
||||
static DEVICE_ATTR(water, 0444, ccic_water_show, NULL);
|
||||
|
||||
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
||||
static ssize_t ccic_send_samsung_uVDM_message(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ret = send_samsung_unstructured_vdm_message(usbpd_data, buf, size);
|
||||
if( ret < 0 )
|
||||
return ret;
|
||||
else
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(samsung_uvdm, 0220, NULL, ccic_send_samsung_uVDM_message);
|
||||
|
||||
static ssize_t ccic_send_uVDM_message(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int cmd = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &cmd);
|
||||
pr_info("%s cmd=%d\n", __func__, cmd);
|
||||
|
||||
send_unstructured_vdm_message(usbpd_data, cmd);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(uvdm, 0220, NULL, ccic_send_uVDM_message);
|
||||
|
||||
static ssize_t ccic_send_dna_audio_uVDM_message(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int cmd = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &cmd);
|
||||
pr_info("%s cmd=%d\n", __func__, cmd);
|
||||
|
||||
send_dna_audio_unstructured_vdm_message(usbpd_data, cmd);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(dna_audio_uvdm, 0220, NULL, ccic_send_dna_audio_uVDM_message);
|
||||
|
||||
static ssize_t ccic_send_dex_fan_uVDM_message(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int cmd = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &cmd);
|
||||
pr_info("%s cmd=%d\n", __func__, cmd);
|
||||
|
||||
send_dex_fan_unstructured_vdm_message(usbpd_data, cmd);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(dex_fan_uvdm, 0220, NULL, ccic_send_dex_fan_uVDM_message);
|
||||
|
||||
static ssize_t ccic_send_attention_message(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int cmd = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &cmd);
|
||||
pr_info("%s cmd=%d\n", __func__, cmd);
|
||||
|
||||
send_attention_message(usbpd_data, cmd);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(attention, 0220, NULL, ccic_send_attention_message);
|
||||
static ssize_t ccic_send_role_swap_message(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int cmd = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("usbpd_data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sscanf(buf, "%d", &cmd);
|
||||
pr_info("%s cmd=%d\n", __func__, cmd);
|
||||
|
||||
send_role_swap_message(usbpd_data, cmd);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(role_swap, 0220, NULL, ccic_send_role_swap_message);
|
||||
|
||||
static ssize_t ccic_acc_device_version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
pr_info("%s 0x%04x\n", __func__, usbpd_data->Device_Version);
|
||||
|
||||
return sprintf(buf, "%04x\n", usbpd_data->Device_Version);
|
||||
}
|
||||
static DEVICE_ATTR(acc_device_version, 0444, ccic_acc_device_version_show,NULL);
|
||||
|
||||
static ssize_t ccic_usbpd_ids_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int retval = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
retval = sprintf(buf, "%04x:%04x\n",
|
||||
le16_to_cpu(usbpd_data->Vendor_ID),
|
||||
le16_to_cpu(usbpd_data->Product_ID));
|
||||
pr_info("usb: %s : %s",
|
||||
__func__, buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
static DEVICE_ATTR(usbpd_ids, 0444, ccic_usbpd_ids_show, NULL);
|
||||
|
||||
static ssize_t ccic_usbpd_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = dev_get_drvdata(dev);
|
||||
int retval = 0;
|
||||
|
||||
if (!usbpd_data) {
|
||||
pr_err("%s usbpd_data is null!!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
retval = sprintf(buf, "%d\n", usbpd_data->acc_type);
|
||||
pr_info("usb: %s : %d",
|
||||
__func__, usbpd_data->acc_type);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(usbpd_type, 0444, ccic_usbpd_type_show, NULL);
|
||||
#endif
|
||||
|
||||
static struct attribute *ccic_attributes[] = {
|
||||
&dev_attr_cur_version.attr,
|
||||
&dev_attr_src_version.attr,
|
||||
&dev_attr_lpm_mode.attr,
|
||||
&dev_attr_state.attr,
|
||||
#if defined(CONFIG_SEC_FACTORY)
|
||||
&dev_attr_rid.attr,
|
||||
&dev_attr_ccic_control_option.attr,
|
||||
&dev_attr_booting_dry.attr,
|
||||
#endif
|
||||
&dev_attr_fw_update.attr,
|
||||
&dev_attr_fw_update_status.attr,
|
||||
&dev_attr_control_gpio.attr,
|
||||
&dev_attr_water.attr,
|
||||
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
||||
&dev_attr_uvdm.attr,
|
||||
&dev_attr_dna_audio_uvdm.attr,
|
||||
&dev_attr_dex_fan_uvdm.attr,
|
||||
&dev_attr_samsung_uvdm.attr,
|
||||
&dev_attr_attention.attr,
|
||||
&dev_attr_role_swap.attr,
|
||||
&dev_attr_acc_device_version.attr,
|
||||
&dev_attr_usbpd_ids.attr,
|
||||
&dev_attr_usbpd_type.attr,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct attribute_group ccic_sysfs_group = {
|
||||
.attrs = ccic_attributes,
|
||||
};
|
||||
173
drivers/ccic/pdic_notifier.c
Normal file
173
drivers/ccic/pdic_notifier.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/ccic/pdic_notifier.h>
|
||||
|
||||
#define SET_PDIC_NOTIFIER_BLOCK(nb, fn, dev) do { \
|
||||
(nb)->notifier_call = (fn); \
|
||||
(nb)->priority = (dev); \
|
||||
} while (0)
|
||||
|
||||
#define DESTROY_PDIC_NOTIFIER_BLOCK(nb) \
|
||||
SET_PDIC_NOTIFIER_BLOCK(nb, NULL, -1)
|
||||
|
||||
static struct s2mu004_pdic_notifier_struct pdic_notifier;
|
||||
|
||||
int s2mu004_pdic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
|
||||
muic_notifier_device_t listener)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d register\n", __func__, listener);
|
||||
|
||||
SET_PDIC_NOTIFIER_BLOCK(nb, notifier, listener);
|
||||
ret = blocking_notifier_chain_register(&(pdic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
|
||||
__func__, ret);
|
||||
|
||||
/* current pdic's attached_device status notify */
|
||||
nb->notifier_call(nb, pdic_notifier.cmd,
|
||||
&(pdic_notifier.attached_dev));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int s2mu004_pdic_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&(pdic_notifier.notifier_call_chain), nb);
|
||||
if (ret < 0)
|
||||
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
|
||||
__func__, ret);
|
||||
DESTROY_PDIC_NOTIFIER_BLOCK(nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s2mu004_pdic_notifier_notify(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s: CMD=%d, DATA=%d\n", __func__, pdic_notifier.cmd,
|
||||
pdic_notifier.attached_dev);
|
||||
|
||||
ret = blocking_notifier_call_chain(&(pdic_notifier.notifier_call_chain),
|
||||
pdic_notifier.cmd, &(pdic_notifier.attached_dev));
|
||||
|
||||
switch (ret) {
|
||||
case NOTIFY_STOP_MASK:
|
||||
case NOTIFY_BAD:
|
||||
pr_err("%s: notify error occur(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
case NOTIFY_DONE:
|
||||
case NOTIFY_OK:
|
||||
pr_info("%s: notify done(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void s2mu004_pdic_notifier_attach_attached_jig_dev(muic_attached_dev_t new_dev)
|
||||
{
|
||||
pr_info("%s: (%d)\n", __func__, new_dev);
|
||||
|
||||
pdic_notifier.cmd = PDIC_MUIC_NOTIFY_CMD_JIG_ATTACH;
|
||||
pdic_notifier.attached_dev = new_dev;
|
||||
|
||||
/* pdic's attached_device attach broadcast */
|
||||
s2mu004_pdic_notifier_notify();
|
||||
}
|
||||
#if 0
|
||||
void s2mu004_pdic_notifier_detach_attached_jig_dev(muic_attached_dev_t cur_dev)
|
||||
{
|
||||
pr_info("%s: (%d)\n", __func__, cur_dev);
|
||||
|
||||
pdic_notifier.cmd = PDIC_MUIC_NOTIFY_CMD_JIG_DETACH;
|
||||
|
||||
if (pdic_notifier.attached_dev != cur_dev)
|
||||
pr_warn("%s: attached_dev of pdic_notifier(%d) != pdic_data(%d)\n",
|
||||
__func__, pdic_notifier.attached_dev, cur_dev);
|
||||
|
||||
if (pdic_notifier.attached_dev != ATTACHED_DEV_NONE_MUIC) {
|
||||
/* pdic's attached_device detach broadcast */
|
||||
s2mu004_pdic_notifier_notify();
|
||||
}
|
||||
|
||||
pdic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
}
|
||||
#endif
|
||||
void s2mu004_pdic_notifier_attach_attached_dev(muic_attached_dev_t new_dev)
|
||||
{
|
||||
pr_info("%s: (%d)\n", __func__, new_dev);
|
||||
|
||||
pdic_notifier.cmd = MUIC_NOTIFY_CMD_ATTACH;
|
||||
pdic_notifier.attached_dev = new_dev;
|
||||
|
||||
/* pdic's attached_device attach broadcast */
|
||||
s2mu004_pdic_notifier_notify();
|
||||
}
|
||||
|
||||
void s2mu004_pdic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev)
|
||||
{
|
||||
pr_info("%s: (%d)\n", __func__, cur_dev);
|
||||
|
||||
pdic_notifier.cmd = MUIC_NOTIFY_CMD_DETACH;
|
||||
|
||||
if (pdic_notifier.attached_dev != cur_dev)
|
||||
pr_warn("%s: attached_dev of pdic_notifier(%d) != pdic_data(%d)\n",
|
||||
__func__, pdic_notifier.attached_dev, cur_dev);
|
||||
|
||||
if (pdic_notifier.attached_dev != ATTACHED_DEV_NONE_MUIC) {
|
||||
/* pdic's attached_device detach broadcast */
|
||||
s2mu004_pdic_notifier_notify();
|
||||
}
|
||||
|
||||
pdic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
}
|
||||
|
||||
void s2mu004_pdic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev)
|
||||
{
|
||||
pr_info("%s: (%d)\n", __func__, new_dev);
|
||||
|
||||
pdic_notifier.cmd = MUIC_NOTIFY_CMD_LOGICALLY_ATTACH;
|
||||
pdic_notifier.attached_dev = new_dev;
|
||||
|
||||
/* pdic's attached_device attach broadcast */
|
||||
s2mu004_pdic_notifier_notify();
|
||||
}
|
||||
|
||||
void s2mu004_pdic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev)
|
||||
{
|
||||
pr_info("%s: (%d)\n", __func__, cur_dev);
|
||||
|
||||
pdic_notifier.cmd = MUIC_NOTIFY_CMD_LOGICALLY_DETACH;
|
||||
pdic_notifier.attached_dev = cur_dev;
|
||||
|
||||
/* pdic's attached_device detach broadcast */
|
||||
s2mu004_pdic_notifier_notify();
|
||||
|
||||
pdic_notifier.attached_dev = ATTACHED_DEV_NONE_MUIC;
|
||||
}
|
||||
|
||||
static int __init s2mu004_pdic_notifier_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&(pdic_notifier.notifier_call_chain));
|
||||
pdic_notifier.cmd = MUIC_NOTIFY_CMD_DETACH;
|
||||
pdic_notifier.attached_dev = ATTACHED_DEV_UNKNOWN_MUIC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(s2mu004_pdic_notifier_init);
|
||||
|
||||
1396
drivers/ccic/s2mm005.c
Normal file
1396
drivers/ccic/s2mm005.c
Normal file
File diff suppressed because it is too large
Load Diff
1339
drivers/ccic/s2mm005_cc.c
Normal file
1339
drivers/ccic/s2mm005_cc.c
Normal file
File diff suppressed because it is too large
Load Diff
607
drivers/ccic/s2mm005_fw.c
Normal file
607
drivers/ccic/s2mm005_fw.c
Normal file
@@ -0,0 +1,607 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ccic/s2mm005.h>
|
||||
#include <linux/ccic/s2mm005_ext.h>
|
||||
#include <linux/ccic/s2mm005_fw.h>
|
||||
#include <linux/ccic/ccic_sysfs.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_BOOT5.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_BOOT6.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_0x0B_BOOT7.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_0x0C_BOOT8.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_0x11_BOOT8.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_0x12_BOOT8.h>
|
||||
#include <linux/ccic/BOOT_FLASH_FW_0x14_BOOT8.h>
|
||||
|
||||
|
||||
#include <linux/ccic/BOOT_SRAM_FW.h>
|
||||
|
||||
#define S2MM005_FIRMWARE_PATH "usbpd/s2mm005.bin"
|
||||
|
||||
#define FW_CHECK_RETRY 5
|
||||
#define VALID_FW_BOOT_VERSION(fw_boot) (fw_boot == 0x7)
|
||||
#define VALID_FW_MAIN_VERSION(fw_main) \
|
||||
(!((fw_main[0] == 0xff) && (fw_main[1] == 0xff)) \
|
||||
&& !((fw_main[0] == 0x00) && (fw_main[1] == 0x00)))
|
||||
|
||||
const char *flashmode_to_string(u32 mode)
|
||||
{
|
||||
switch (mode) {
|
||||
#define FLASH_MODE_STR(mode) case mode: return(#mode)
|
||||
FLASH_MODE_STR(FLASH_MODE_NORMAL);
|
||||
FLASH_MODE_STR(FLASH_MODE_FLASH);
|
||||
FLASH_MODE_STR(FLASH_MODE_ERASE);
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
int s2mm005_sram_write(const struct i2c_client *i2c)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_msg msg[1];
|
||||
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
|
||||
|
||||
pr_err("%s size:%d\n", __func__, BOOT_SRAM_FW_SIZE);
|
||||
mutex_lock(&usbpd_data->i2c_mutex);
|
||||
msg[0].addr = 0x3B; /* Slave addr 0x76 */
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = BOOT_SRAM_FW_SIZE;
|
||||
msg[0].buf = (u8 *)&BOOT_SRAM_FW[0];
|
||||
|
||||
ret = i2c_transfer(i2c->adapter, msg, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&i2c->dev, "i2c write fail error %d\n", ret);
|
||||
mutex_unlock(&usbpd_data->i2c_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void s2mm005_write_flash(const struct i2c_client *i2c,
|
||||
unsigned int fAddr, unsigned int fData) {
|
||||
u8 data[8];
|
||||
|
||||
data[0] = 0x42;
|
||||
data[1] = 0x04; /* long write */
|
||||
|
||||
data[2] = (u8)(fAddr & 0xFF);
|
||||
data[3] = (u8)((fAddr >> 8) & 0xFF);
|
||||
|
||||
data[4] = (uint8_t)(fData & 0xFF);
|
||||
data[5] = (uint8_t)((fData>>8) & 0xFF);
|
||||
|
||||
data[6] = (uint8_t)((fData>>16) & 0xFF);
|
||||
data[7] = (uint8_t)((fData>>24) & 0xFF);
|
||||
/* pr_info("Flash Write Address :0x%08X Data:0x%08X\n", fAddr, fData);*/
|
||||
s2mm005_write_byte(i2c, 0x10, &data[0], 8);
|
||||
}
|
||||
|
||||
void s2mm005_verify_flash(const struct i2c_client *i2c,
|
||||
uint32_t fAddr, uint32_t *fData) {
|
||||
uint16_t REG_ADD;
|
||||
uint8_t W_DATA[8];
|
||||
uint8_t R_DATA[4];
|
||||
|
||||
uint32_t fRead[3];
|
||||
|
||||
uint32_t fCnt;
|
||||
|
||||
for (fCnt = 0; fCnt < 2; fCnt++) {
|
||||
W_DATA[0] = 0x42;
|
||||
W_DATA[1] = 0x40; /* Long Read */
|
||||
|
||||
W_DATA[2] = (uint8_t)(fAddr & 0xFF);
|
||||
W_DATA[3] = (uint8_t)((fAddr>>8) & 0xFF);
|
||||
|
||||
|
||||
REG_ADD = 0x10;
|
||||
udelay(10);
|
||||
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 4);
|
||||
REG_ADD = 0x14;
|
||||
udelay(10);
|
||||
s2mm005_read_byte_flash(i2c, REG_ADD, &R_DATA[0], 4);
|
||||
|
||||
fRead[fCnt] = 0;
|
||||
|
||||
fRead[fCnt] |= R_DATA[0];
|
||||
fRead[fCnt] |= (R_DATA[1]<<8);
|
||||
fRead[fCnt] |= (R_DATA[2]<<16);
|
||||
fRead[fCnt] |= (R_DATA[3]<<24);
|
||||
|
||||
}
|
||||
|
||||
if (fRead[0] == fRead[1]) {
|
||||
*fData = fRead[0];
|
||||
/* pr_err("> Flash Read Address : 0x%08X Data : 0x%08X\n",fAddr, *fData); */
|
||||
} else {
|
||||
W_DATA[0] = 0x42;
|
||||
W_DATA[1] = 0x40; /* Long Read */
|
||||
|
||||
W_DATA[2] = (uint8_t)(fAddr & 0xFF);
|
||||
W_DATA[3] = (uint8_t)((fAddr>>8) & 0xFF);
|
||||
|
||||
|
||||
REG_ADD = 0x10;
|
||||
udelay(10);
|
||||
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 4);
|
||||
REG_ADD = 0x14;
|
||||
udelay(10);
|
||||
s2mm005_read_byte_flash(i2c, REG_ADD, &R_DATA[0], 4);
|
||||
|
||||
fRead[fCnt] = 0;
|
||||
|
||||
fRead[2] |= R_DATA[0];
|
||||
fRead[2] |= (R_DATA[1]<<8);
|
||||
fRead[2] |= (R_DATA[2]<<16);
|
||||
fRead[2] |= (R_DATA[3]<<24);
|
||||
|
||||
if (fRead[2] == fRead[0]) {
|
||||
*fData = fRead[0];
|
||||
pr_err("> Flash Read[0] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData);
|
||||
} else if (fRead[2] == fRead[1]) {
|
||||
*fData = fRead[1];
|
||||
pr_err("> Flash Read[1] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData);
|
||||
} else {
|
||||
*fData = 0;
|
||||
pr_err("> Flash Read[FAIL] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int s2mm005_flash_write(struct s2mm005_data *usbpd_data, unsigned char *fw_data)
|
||||
{
|
||||
struct i2c_client *i2c = usbpd_data->i2c;
|
||||
u8 val, reg, fError;
|
||||
uint32_t *pFlash_FW, *pFlash_FWCS;
|
||||
uint32_t LopCnt, fAddr, fData, fRData, sLopCnt;
|
||||
uint32_t recheck_count = 0;
|
||||
struct s2mm005_fw *fw_hd;
|
||||
unsigned int size;
|
||||
|
||||
reg = FLASH_WRITE_0x42;
|
||||
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
||||
reg = FLASH_WRITING_BYTE_SIZE_0x4;
|
||||
s2mm005_write_byte(i2c, CMD_HOST_0x11, ®, 1);
|
||||
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
||||
|
||||
pFlash_FW = (uint32_t *)(fw_data + 12);
|
||||
pFlash_FWCS = (uint32_t *)fw_data;
|
||||
fw_hd = (struct s2mm005_fw*)fw_data;
|
||||
size = fw_hd -> size;
|
||||
if(fw_hd -> boot < 6)
|
||||
sLopCnt = 0x1000/4;
|
||||
else if (fw_hd -> boot == 6)
|
||||
sLopCnt = 0x8000/4;
|
||||
else if (fw_hd -> boot == 7)
|
||||
sLopCnt = 0x7000/4;
|
||||
else if (fw_hd -> boot >= 8)
|
||||
sLopCnt = 0x5000/4;
|
||||
|
||||
/* Flash write */
|
||||
for (LopCnt = sLopCnt; LopCnt < (size/4); LopCnt++) {
|
||||
fAddr = LopCnt*4;
|
||||
fData = pFlash_FW[LopCnt];
|
||||
udelay(10);
|
||||
s2mm005_write_flash(i2c, fAddr, fData);
|
||||
}
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
/* Partial verify */
|
||||
while(1) {
|
||||
for (LopCnt = sLopCnt; LopCnt < (size/4); LopCnt++) {
|
||||
fError = 1;
|
||||
fAddr = LopCnt*4;
|
||||
fData = pFlash_FW[LopCnt];
|
||||
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
||||
if (fData != fRData) {
|
||||
recheck_count++;
|
||||
LopCnt = (fAddr & 0xffffff00) / 4;
|
||||
sLopCnt = LopCnt;
|
||||
fError = 0;
|
||||
pr_err("%s partial verify fail!! recheck count : %d\n", __func__, recheck_count);
|
||||
pr_err("Verify Error Address = 0x%08X WData = 0x%08X VData = 0x%08X\n", fAddr, fData, fRData);
|
||||
s2mm005_write_flash(i2c, fAddr, fData);
|
||||
msleep(20);
|
||||
if(recheck_count == 1000)
|
||||
return -EFLASH_VERIFY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fError)
|
||||
break;
|
||||
}
|
||||
pr_err("%s verify success!! recheck count : %d\n", __func__, recheck_count);
|
||||
|
||||
if (LopCnt >= (size / 4)) {
|
||||
if (fw_hd -> boot >= 6) {
|
||||
/* SW version from header */
|
||||
recheck_count = 0;
|
||||
while(1) {
|
||||
recheck_count++;
|
||||
fAddr = 0xEFF0;
|
||||
pr_err("SW version = 0x%08X\n", pFlash_FWCS[0]);
|
||||
fData = pFlash_FWCS[0];
|
||||
s2mm005_write_flash(i2c, fAddr, fData);
|
||||
fRData = 0;
|
||||
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
||||
if(fData == fRData)
|
||||
break;
|
||||
else {
|
||||
if (recheck_count == 30)
|
||||
return -EFLASH_WRITE_SWVERSION;
|
||||
}
|
||||
}
|
||||
/* Size from header */
|
||||
recheck_count = 0;
|
||||
while(1) {
|
||||
recheck_count++;
|
||||
fAddr = 0xEFF4;
|
||||
pr_err("SW Size = 0x%08X\n", pFlash_FWCS[2]);
|
||||
fData = pFlash_FWCS[2];
|
||||
s2mm005_write_flash(i2c, fAddr, fData);
|
||||
fRData = 0;
|
||||
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
||||
if(fData == fRData)
|
||||
break;
|
||||
else {
|
||||
if (recheck_count == 30)
|
||||
return -EFLASH_WRITE_SIZE;
|
||||
}
|
||||
}
|
||||
/* CRC Check sum */
|
||||
recheck_count = 0;
|
||||
while(1) {
|
||||
recheck_count++;
|
||||
fAddr = 0xEFF8;
|
||||
pr_err("SW CheckSum = 0x%08X\n", pFlash_FWCS[((size + 16) / 4) - 1]);
|
||||
fData = pFlash_FWCS[((size + 16) / 4) - 1];
|
||||
s2mm005_write_flash(i2c, fAddr, fData);
|
||||
fRData = 0;
|
||||
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
||||
if(fData == fRData)
|
||||
break;
|
||||
else {
|
||||
if (recheck_count == 30)
|
||||
return -EFLASH_WRITE_CRC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* flash done */
|
||||
recheck_count = 0;
|
||||
while(1)
|
||||
{
|
||||
recheck_count++;
|
||||
fAddr = 0xEFFC;
|
||||
fData = 0x1;
|
||||
s2mm005_write_flash(i2c, fAddr, fData);
|
||||
fRData = 0;
|
||||
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
||||
pr_err("0xeffc = %d\n", fRData);
|
||||
if(fData == fRData)
|
||||
break;
|
||||
else {
|
||||
if (recheck_count == 30)
|
||||
return -EFLASH_WRITE_DONE;
|
||||
}
|
||||
}
|
||||
pr_err("%s flash write succesfully done!!\n", __func__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s2mm005_flash_ready(struct s2mm005_data *usbpd_data)
|
||||
{
|
||||
struct i2c_client *i2c = usbpd_data->i2c;
|
||||
u8 W_DATA[5];
|
||||
|
||||
/* FLASH_READY */
|
||||
W_DATA[0] = 0x02;
|
||||
W_DATA[1] = 0x01;
|
||||
W_DATA[2] = 0x30;
|
||||
W_DATA[3] = 0x50;
|
||||
W_DATA[4] = 0x01;
|
||||
s2mm005_write_byte(i2c, CMD_MODE_0x10, &W_DATA[0], 5);
|
||||
}
|
||||
|
||||
int s2mm005_flash(struct s2mm005_data *usbpd_data, unsigned int input)
|
||||
{
|
||||
struct i2c_client *i2c = usbpd_data->i2c;
|
||||
u8 val, reg;
|
||||
int ret = 0;
|
||||
int retry = 0;
|
||||
struct s2mm005_fw *fw_hd;
|
||||
struct file *fp;
|
||||
mm_segment_t old_fs;
|
||||
long fw_size, nread;
|
||||
int irq_gpio_status;
|
||||
FLASH_STATE_Type Flash_DATA;
|
||||
|
||||
switch (input) {
|
||||
case FLASH_MODE_ENTER: { /* enter flash mode */
|
||||
/* FLASH_READY */
|
||||
s2mm005_flash_ready(usbpd_data);
|
||||
do {
|
||||
/* FLASH_MODE */
|
||||
reg = FLASH_MODE_ENTER_0x10;
|
||||
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
||||
usleep_range(50 * 1000, 50 * 1000);
|
||||
/* If irq status is not clear, CCIC can not enter flash mode. */
|
||||
irq_gpio_status = gpio_get_value(usbpd_data->irq_gpio);
|
||||
dev_info(&i2c->dev, "%s IRQ0:%02d\n", __func__, irq_gpio_status);
|
||||
if(!irq_gpio_status) {
|
||||
s2mm005_int_clear(usbpd_data); // interrupt clear
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
}
|
||||
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
||||
pr_err("%s %s retry %d\n", __func__, flashmode_to_string(val), retry);
|
||||
usleep_range(50*1000, 50*1000);
|
||||
|
||||
s2mm005_read_byte(i2c, 0x24, Flash_DATA.BYTE, 4);
|
||||
dev_info(&i2c->dev, "Flash_State:0x%02X Reserved:0x%06X\n",
|
||||
Flash_DATA.BITS.Flash_State, Flash_DATA.BITS.Reserved);
|
||||
|
||||
retry++;
|
||||
if(retry == 10) {
|
||||
/* RESET */
|
||||
s2mm005_reset(usbpd_data);
|
||||
msleep(3000);
|
||||
s2mm005_flash_ready(usbpd_data);
|
||||
} else if (retry == 20) {
|
||||
panic("Flash mode change fail!\n");
|
||||
}
|
||||
} while (val != FLASH_MODE_FLASH);
|
||||
break;
|
||||
}
|
||||
case FLASH_ERASE: { /* erase flash */
|
||||
reg = FLASH_ERASE_0x44;
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
||||
msleep(200);
|
||||
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
||||
pr_err("flash mode : %s\n", flashmode_to_string(val));
|
||||
break;
|
||||
}
|
||||
case FLASH_WRITE7: { /* write flash & verify */
|
||||
switch (usbpd_data->s2mm005_fw_product_id) {
|
||||
case PRODUCT_NUM_ASTAR_DREAMLITE:
|
||||
default:
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x0B_BOOT7[0]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FLASH_WRITE8: { /* write flash & verify */
|
||||
switch (usbpd_data->s2mm005_fw_product_id) {
|
||||
case PRODUCT_NUM_KELLY:
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x0C_BOOT8[0]);
|
||||
break;
|
||||
case PRODUCT_NUM_A8S:
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x11_BOOT8[0]);
|
||||
break;
|
||||
case PRODUCT_NUM_TABS4LV:
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x12_BOOT8[0]);
|
||||
break;
|
||||
case PRODUCT_NUM_GTACTIVEXL:
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x14_BOOT8[0]);
|
||||
break;
|
||||
default:
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x12_BOOT8[0]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FLASH_WRITE_UMS: {
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
fp = filp_open(CCIC_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: failed to open %s.\n", __func__,
|
||||
CCIC_DEFAULT_UMS_FW);
|
||||
ret = -ENOENT;
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fw_size = fp->f_path.dentry->d_inode->i_size;
|
||||
if (0 < fw_size) {
|
||||
unsigned char *fw_data;
|
||||
fw_data = kzalloc(fw_size, GFP_KERNEL);
|
||||
nread = vfs_read(fp, (char __user *)fw_data,
|
||||
fw_size, &fp->f_pos);
|
||||
|
||||
pr_err("%s: start, file path %s, size %ld Bytes\n",
|
||||
__func__, CCIC_DEFAULT_UMS_FW, fw_size);
|
||||
|
||||
if (nread != fw_size) {
|
||||
pr_err("%s: failed to read firmware file, nread %ld Bytes\n",
|
||||
__func__, nread);
|
||||
ret = -EIO;
|
||||
} else {
|
||||
fw_hd = (struct s2mm005_fw*)fw_data;
|
||||
pr_err("%02X %02X %02X %02X size:%05d\n", fw_hd->boot, fw_hd->main[0], fw_hd->main[1], fw_hd->main[2], fw_hd->size);
|
||||
/* TODO : DISABLE IRQ */
|
||||
/* TODO : FW UPDATE */
|
||||
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)fw_data);
|
||||
/* TODO : ENABLE IRQ */
|
||||
}
|
||||
kfree(fw_data);
|
||||
}
|
||||
|
||||
filp_close(fp, NULL);
|
||||
set_fs(old_fs);
|
||||
break;
|
||||
}
|
||||
case FLASH_MODE_EXIT: { /* exit flash mode */
|
||||
reg = FLASH_MODE_EXIT_0x20;
|
||||
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
||||
usleep_range(15 * 1000, 15 * 1000);
|
||||
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
||||
pr_err("flash mode : %s\n", flashmode_to_string(val));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
pr_err("Flash value does not matched menu\n");
|
||||
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void s2mm005_get_fw_version(int product_id,
|
||||
struct s2mm005_version *version, u8 boot_version, u32 hw_rev)
|
||||
{
|
||||
struct s2mm005_fw *fw_hd = NULL;
|
||||
switch (boot_version) {
|
||||
case 5:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_BOOT5;
|
||||
break;
|
||||
case 6:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_BOOT6;
|
||||
break;
|
||||
case 7:
|
||||
switch (product_id) {
|
||||
case PRODUCT_NUM_ASTAR_DREAMLITE:
|
||||
default:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x0B_BOOT7;
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
switch (product_id) {
|
||||
case PRODUCT_NUM_KELLY:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x0C_BOOT8;
|
||||
break;
|
||||
case PRODUCT_NUM_A8S:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x11_BOOT8;
|
||||
break;
|
||||
case PRODUCT_NUM_TABS4LV:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x12_BOOT8;
|
||||
break;
|
||||
case PRODUCT_NUM_GTACTIVEXL:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x14_BOOT8;
|
||||
break;
|
||||
default:
|
||||
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x12_BOOT8;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(fw_hd != NULL)
|
||||
{
|
||||
version->boot = fw_hd->boot;
|
||||
version->main[0] = fw_hd->main[0];
|
||||
version->main[1] = fw_hd->main[1];
|
||||
version->main[2] = fw_hd->main[2];
|
||||
} else {
|
||||
version->boot = 0;
|
||||
version->main[0] = 0;
|
||||
version->main[1] = 0;
|
||||
version->main[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void s2mm005_get_chip_hwversion(struct s2mm005_data *usbpd_data,
|
||||
struct s2mm005_version *version)
|
||||
{
|
||||
struct i2c_client *i2c = usbpd_data->i2c;
|
||||
|
||||
s2mm005_read_byte_flash(i2c, 0x0, (u8 *)&version->boot, 1);
|
||||
s2mm005_read_byte_flash(i2c, 0x1, (u8 *)&version->main, 3);
|
||||
s2mm005_read_byte_flash(i2c, 0x4, (u8 *)&version->ver2, 4);
|
||||
}
|
||||
|
||||
void s2mm005_get_chip_swversion(struct s2mm005_data *usbpd_data,
|
||||
struct s2mm005_version *version)
|
||||
{
|
||||
struct i2c_client *i2c = usbpd_data->i2c;
|
||||
int i;
|
||||
|
||||
for(i=0; i < FW_CHECK_RETRY; i++) {
|
||||
s2mm005_read_byte_flash(i2c, 0x8, (u8 *)&version->boot, 1);
|
||||
if(VALID_FW_BOOT_VERSION(version->boot))
|
||||
break;
|
||||
}
|
||||
for(i=0; i < FW_CHECK_RETRY; i++) {
|
||||
s2mm005_read_byte_flash(i2c, 0x9, (u8 *)&version->main, 3);
|
||||
if(VALID_FW_MAIN_VERSION(version->main))
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < FW_CHECK_RETRY; i++)
|
||||
s2mm005_read_byte_flash(i2c, 0xc, (u8 *)&version->ver2, 4);
|
||||
}
|
||||
|
||||
int s2mm005_check_version(struct s2mm005_version *version1,
|
||||
struct s2mm005_version *version2)
|
||||
{
|
||||
if (version1->boot != version2->boot) {
|
||||
return FLASH_FW_VER_BOOT;
|
||||
}
|
||||
if (memcmp(version1->main, version2->main, 3)) {
|
||||
return FLASH_FW_VER_MAIN;
|
||||
}
|
||||
|
||||
return FLASH_FW_VER_MATCH;
|
||||
}
|
||||
|
||||
int s2mm005_flash_fw(struct s2mm005_data *usbpd_data, unsigned int input)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 val = 0;
|
||||
|
||||
if( usbpd_data->fw_product_id != usbpd_data->s2mm005_fw_product_id)
|
||||
{
|
||||
pr_err("FW_UPDATE fail, product number is different (%d)(%d) \n", usbpd_data->fw_product_id,usbpd_data->s2mm005_fw_product_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_err("FW_UPDATE %d\n", input);
|
||||
switch (input) {
|
||||
case FLASH_WRITE5:
|
||||
case FLASH_WRITE6:
|
||||
case FLASH_WRITE7:
|
||||
case FLASH_WRITE8:
|
||||
case FLASH_WRITE: {
|
||||
s2mm005_flash(usbpd_data, FLASH_MODE_ENTER);
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_flash(usbpd_data, FLASH_ERASE);
|
||||
msleep(200);
|
||||
ret = s2mm005_flash(usbpd_data, input);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_flash(usbpd_data, FLASH_MODE_EXIT);
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_reset(usbpd_data);
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
break;
|
||||
}
|
||||
case FLASH_WRITE_UMS: {
|
||||
s2mm005_read_byte_flash(usbpd_data->i2c, FLASH_STATUS_0x24, &val, 1);
|
||||
if(val != FLASH_MODE_NORMAL) {
|
||||
pr_err("Can't CCIC FW update: cause by %s\n", flashmode_to_string(val));
|
||||
}
|
||||
disable_irq(usbpd_data->irq);
|
||||
s2mm005_manual_LPM(usbpd_data, 0x7); // LP Off
|
||||
msleep(3000);
|
||||
s2mm005_flash(usbpd_data, FLASH_MODE_ENTER);
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_flash(usbpd_data, FLASH_ERASE);
|
||||
msleep(200);
|
||||
ret = s2mm005_flash(usbpd_data, input);
|
||||
if (ret < 0)
|
||||
panic("infinite write fail!\n");
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_flash(usbpd_data, FLASH_MODE_EXIT);
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_reset(usbpd_data);
|
||||
usleep_range(10 * 1000, 10 * 1000);
|
||||
s2mm005_manual_LPM(usbpd_data, 0x6); // LP On
|
||||
enable_irq(usbpd_data->irq);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
444
drivers/ccic/s2mm005_pd.c
Normal file
444
drivers/ccic/s2mm005_pd.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* driver/../s2mm005.c - S2MM005 USB PD function driver
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/ccic/s2mm005_ext.h>
|
||||
#include <linux/power_supply.h>
|
||||
#if defined(CONFIG_BATTERY_NOTIFIER)
|
||||
#include <linux/battery/battery_notifier.h>
|
||||
#endif
|
||||
#include <linux/usb_notify.h>
|
||||
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
||||
#include <linux/ccic/ccic_alternate.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO
|
||||
extern int muic_GPIO_control(int gpio);
|
||||
#endif
|
||||
struct pdic_notifier_struct pd_noti;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// function definition
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void select_pdo(int num);
|
||||
void s2mm005_select_pdo(int num);
|
||||
void select_pdo(int num);
|
||||
void (*fp_select_pdo)(int num);
|
||||
void vbus_turn_on_ctrl(bool enable);
|
||||
void process_pd(void *data, u8 plug_attach_done, u8 *pdic_attach, MSG_IRQ_STATUS_Type *MSG_IRQ_State);
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PD function will be merged
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static inline struct power_supply *get_power_supply_by_name(char *name)
|
||||
{
|
||||
if (!name)
|
||||
return (struct power_supply *)NULL;
|
||||
else
|
||||
return power_supply_get_by_name(name);
|
||||
}
|
||||
|
||||
void s2mm005_select_pdo(int num)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = pd_noti.pusbpd;
|
||||
uint8_t CMD_DATA[3];
|
||||
|
||||
if (pd_noti.sink_status.selected_pdo_num == num) {
|
||||
pr_info("%s num(%d)\n", __func__, num);
|
||||
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY,
|
||||
CCIC_NOTIFY_ID_POWER_STATUS, 1/*attach*/, 0, 0);
|
||||
return;
|
||||
} else if (num > pd_noti.sink_status.available_pdo_num)
|
||||
pd_noti.sink_status.selected_pdo_num = pd_noti.sink_status.available_pdo_num;
|
||||
else if (num < 1)
|
||||
pd_noti.sink_status.selected_pdo_num = 1;
|
||||
else
|
||||
pd_noti.sink_status.selected_pdo_num = num;
|
||||
pr_info(" %s : PDO(%d) is selected to change\n", __func__, pd_noti.sink_status.selected_pdo_num);
|
||||
|
||||
CMD_DATA[0] = 0x3;
|
||||
CMD_DATA[1] = 0x3;
|
||||
CMD_DATA[2] = pd_noti.sink_status.selected_pdo_num;
|
||||
s2mm005_write_byte(pd_noti.pusbpd->i2c, REG_I2C_SLV_CMD, &CMD_DATA[0], 3);
|
||||
|
||||
CMD_DATA[0] = 0x3;
|
||||
CMD_DATA[1] = 0x2;
|
||||
CMD_DATA[2] = State_PE_SNK_Wait_for_Capabilities;
|
||||
s2mm005_write_byte(pd_noti.pusbpd->i2c, REG_I2C_SLV_CMD, &CMD_DATA[0], 3);
|
||||
}
|
||||
|
||||
void select_pdo(int num)
|
||||
{
|
||||
if (fp_select_pdo)
|
||||
fp_select_pdo(num);
|
||||
}
|
||||
|
||||
void vbus_turn_on_ctrl(bool enable)
|
||||
{
|
||||
struct power_supply *psy_otg;
|
||||
union power_supply_propval val;
|
||||
int on = !!enable;
|
||||
int ret = 0;
|
||||
|
||||
#if defined(CONFIG_USB_HOST_NOTIFY)
|
||||
struct otg_notify *o_notify = get_otg_notify();
|
||||
bool must_block_host = 0;
|
||||
bool unsupport_host = 0;
|
||||
if (o_notify)
|
||||
must_block_host = is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST);
|
||||
|
||||
pr_info("%s : enable=%d, must_block_host=%d\n",
|
||||
__func__, enable, must_block_host);
|
||||
|
||||
if (enable) {
|
||||
if (o_notify)
|
||||
unsupport_host = !is_usb_host(o_notify);
|
||||
pr_info("%s : unsupport_host=%d\n", __func__, unsupport_host);
|
||||
|
||||
if (must_block_host || unsupport_host) {
|
||||
enable = false;
|
||||
pr_info("%s : turn off vbus because of blocked host\n",
|
||||
__func__);
|
||||
}
|
||||
} else {
|
||||
// don't turn off because of blocked (already off)
|
||||
if (must_block_host)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
pr_info("%s %d, enable=%d\n", __func__, __LINE__, enable);
|
||||
psy_otg = get_power_supply_by_name("otg");
|
||||
if (psy_otg) {
|
||||
val.intval = enable;
|
||||
ret = psy_otg->desc->set_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val);
|
||||
} else {
|
||||
pr_err("%s: Fail to get psy battery\n", __func__);
|
||||
}
|
||||
if (ret) {
|
||||
pr_err("%s: fail to set power_suppy ONLINE property(%d)\n",
|
||||
__func__, ret);
|
||||
} else {
|
||||
pr_info("otg accessory power = %d\n", on);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int s2mm005_src_capacity_information(const struct i2c_client *i2c, uint32_t *RX_SRC_CAPA_MSG,
|
||||
PDIC_SINK_STATUS * pd_sink_status, uint8_t *do_power_nego)
|
||||
{
|
||||
uint32_t RdCnt;
|
||||
uint32_t PDO_cnt;
|
||||
uint32_t PDO_sel;
|
||||
int available_pdo_num = 0;
|
||||
int num_of_obj = 0;
|
||||
|
||||
MSG_HEADER_Type *MSG_HDR;
|
||||
SRC_FIXED_SUPPLY_Typedef *MSG_FIXED_SUPPLY;
|
||||
SRC_VAR_SUPPLY_Typedef *MSG_VAR_SUPPLY;
|
||||
SRC_BAT_SUPPLY_Typedef *MSG_BAT_SUPPLY;
|
||||
|
||||
dev_info(&i2c->dev, "\n");
|
||||
for(RdCnt=0;RdCnt<8;RdCnt++)
|
||||
{
|
||||
dev_info(&i2c->dev, "Rd_SRC_CAPA_%d : 0x%X\n", RdCnt, RX_SRC_CAPA_MSG[RdCnt]);
|
||||
}
|
||||
|
||||
MSG_HDR = (MSG_HEADER_Type *)&RX_SRC_CAPA_MSG[0];
|
||||
dev_info(&i2c->dev, "=======================================\n");
|
||||
dev_info(&i2c->dev, " MSG Header\n");
|
||||
|
||||
dev_info(&i2c->dev, " Rsvd_msg_header : %d\n",MSG_HDR->Rsvd_msg_header );
|
||||
dev_info(&i2c->dev, " Number_of_obj : %d\n",MSG_HDR->Number_of_obj );
|
||||
dev_info(&i2c->dev, " Message_ID : %d\n",MSG_HDR->Message_ID );
|
||||
dev_info(&i2c->dev, " Port_Power_Role : %d\n",MSG_HDR->Port_Power_Role );
|
||||
dev_info(&i2c->dev, " Specification_Revision : %d\n",MSG_HDR->Specification_Revision );
|
||||
dev_info(&i2c->dev, " Port_Data_Role : %d\n",MSG_HDR->Port_Data_Role );
|
||||
dev_info(&i2c->dev, " Rsvd2_msg_header : %d\n",MSG_HDR->Rsvd2_msg_header );
|
||||
dev_info(&i2c->dev, " Message_Type : %d\n",MSG_HDR->Message_Type );
|
||||
|
||||
num_of_obj = MSG_HDR->Number_of_obj > MAX_PDO_NUM ? MAX_PDO_NUM : MSG_HDR->Number_of_obj;
|
||||
for (PDO_cnt = 0; PDO_cnt < num_of_obj; PDO_cnt++)
|
||||
{
|
||||
PDO_sel = (RX_SRC_CAPA_MSG[PDO_cnt + 1] >> 30) & 0x3;
|
||||
dev_info(&i2c->dev, " =================\n");
|
||||
dev_info(&i2c->dev, " PDO_Num : %d\n", (PDO_cnt + 1));
|
||||
|
||||
if(PDO_sel == 0) // *MSG_FIXED_SUPPLY
|
||||
{
|
||||
MSG_FIXED_SUPPLY = (SRC_FIXED_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
|
||||
if(MSG_FIXED_SUPPLY->Voltage_Unit <= (AVAILABLE_VOLTAGE/UNIT_FOR_VOLTAGE))
|
||||
available_pdo_num = PDO_cnt + 1;
|
||||
if (!(*do_power_nego) &&
|
||||
(pd_sink_status->power_list[PDO_cnt+1].max_voltage != MSG_FIXED_SUPPLY->Voltage_Unit * UNIT_FOR_VOLTAGE ||
|
||||
pd_sink_status->power_list[PDO_cnt+1].max_current != MSG_FIXED_SUPPLY->Maximum_Current * UNIT_FOR_CURRENT))
|
||||
*do_power_nego = 1;
|
||||
pd_sink_status->power_list[PDO_cnt+1].max_voltage = MSG_FIXED_SUPPLY->Voltage_Unit * UNIT_FOR_VOLTAGE;
|
||||
pd_sink_status->power_list[PDO_cnt+1].max_current = MSG_FIXED_SUPPLY->Maximum_Current * UNIT_FOR_CURRENT;
|
||||
|
||||
dev_info(&i2c->dev, " PDO_Parameter(FIXED_SUPPLY) : %d\n",MSG_FIXED_SUPPLY->PDO_Parameter );
|
||||
dev_info(&i2c->dev, " Dual_Role_Power : %d\n",MSG_FIXED_SUPPLY->Dual_Role_Power );
|
||||
dev_info(&i2c->dev, " USB_Suspend_Support : %d\n",MSG_FIXED_SUPPLY->USB_Suspend_Support );
|
||||
dev_info(&i2c->dev, " Externally_POW : %d\n",MSG_FIXED_SUPPLY->Externally_POW );
|
||||
dev_info(&i2c->dev, " USB_Comm_Capable : %d\n",MSG_FIXED_SUPPLY->USB_Comm_Capable );
|
||||
dev_info(&i2c->dev, " Data_Role_Swap : %d\n",MSG_FIXED_SUPPLY->Data_Role_Swap );
|
||||
dev_info(&i2c->dev, " Reserved : %d\n",MSG_FIXED_SUPPLY->Reserved );
|
||||
dev_info(&i2c->dev, " Peak_Current : %d\n",MSG_FIXED_SUPPLY->Peak_Current );
|
||||
dev_info(&i2c->dev, " Voltage_Unit : %d\n",MSG_FIXED_SUPPLY->Voltage_Unit );
|
||||
dev_info(&i2c->dev, " Maximum_Current : %d\n",MSG_FIXED_SUPPLY->Maximum_Current );
|
||||
}
|
||||
else if(PDO_sel == 2) // *MSG_VAR_SUPPLY
|
||||
{
|
||||
MSG_VAR_SUPPLY = (SRC_VAR_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
|
||||
|
||||
dev_info(&i2c->dev, " PDO_Parameter(VAR_SUPPLY) : %d\n",MSG_VAR_SUPPLY->PDO_Parameter );
|
||||
dev_info(&i2c->dev, " Maximum_Voltage : %d\n",MSG_VAR_SUPPLY->Maximum_Voltage );
|
||||
dev_info(&i2c->dev, " Minimum_Voltage : %d\n",MSG_VAR_SUPPLY->Minimum_Voltage );
|
||||
dev_info(&i2c->dev, " Maximum_Current : %d\n",MSG_VAR_SUPPLY->Maximum_Current );
|
||||
}
|
||||
else if(PDO_sel == 1) // *MSG_BAT_SUPPLY
|
||||
{
|
||||
MSG_BAT_SUPPLY = (SRC_BAT_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
|
||||
|
||||
dev_info(&i2c->dev, " PDO_Parameter(BAT_SUPPLY) : %d\n",MSG_BAT_SUPPLY->PDO_Parameter );
|
||||
dev_info(&i2c->dev, " Maximum_Voltage : %d\n",MSG_BAT_SUPPLY->Maximum_Voltage );
|
||||
dev_info(&i2c->dev, " Minimum_Voltage : %d\n",MSG_BAT_SUPPLY->Minimum_Voltage );
|
||||
dev_info(&i2c->dev, " Maximum_Allow_Power : %d\n",MSG_BAT_SUPPLY->Maximum_Allow_Power );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* the number of available pdo list */
|
||||
pd_sink_status->available_pdo_num = available_pdo_num;
|
||||
dev_info(&i2c->dev, "=======================================\n");
|
||||
return available_pdo_num;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Processing message role
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void process_message_role(void *data)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = data;
|
||||
struct otg_notify *o_notify = get_otg_notify();
|
||||
int is_dfp = 0;
|
||||
int is_src = 0;
|
||||
|
||||
// 1. read pd state
|
||||
is_dfp = usbpd_data->func_state & (0x1 << 26) ? 1 : 0;
|
||||
is_src = usbpd_data->func_state & (0x1 << 25) ? 1 : 0;
|
||||
pr_info("%s func_state :0x%X, is_dfp : %d, is_src : %d\n", __func__,
|
||||
usbpd_data->func_state, is_dfp, is_src);
|
||||
pr_info("%s current port data_role : %d, power_role : %d\n", __func__,
|
||||
usbpd_data->typec_data_role, usbpd_data->typec_power_role);
|
||||
|
||||
if (is_src == usbpd_data->typec_power_role) {
|
||||
pr_info("%s skip. already power role is set.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MUIC_SM5705_SWITCH_CONTROL_GPIO
|
||||
pr_info("%s call muic_GPIO_control(1) to keep usb path\n", __func__);
|
||||
muic_GPIO_control(1);
|
||||
#endif
|
||||
// 2. process power role
|
||||
if (usbpd_data->func_state != State_PE_PRS_SNK_SRC_Source_on) {
|
||||
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
||||
if (is_src && (usbpd_data->power_role == DUAL_ROLE_PROP_PR_SNK)) {
|
||||
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY,
|
||||
CCIC_NOTIFY_ID_ATTACH, 0, 0, 0);
|
||||
}
|
||||
#elif defined (CONFIG_TYPEC)
|
||||
if (is_src && (usbpd_data->typec_power_role == TYPEC_SINK)) {
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC;
|
||||
pd_noti.sink_status.selected_pdo_num = 0;
|
||||
pd_noti.sink_status.available_pdo_num = 0;
|
||||
pd_noti.sink_status.current_pdo_num = 0;
|
||||
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY,
|
||||
CCIC_NOTIFY_ID_POWER_STATUS, 0, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_USB_HOST_NOTIFY)
|
||||
if (is_src)
|
||||
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1);
|
||||
else
|
||||
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
||||
#endif
|
||||
vbus_turn_on_ctrl(is_src);
|
||||
|
||||
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
||||
usbpd_data->power_role = is_src ?
|
||||
DUAL_ROLE_PROP_PR_SRC : DUAL_ROLE_PROP_PR_SNK;
|
||||
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_PDIC,
|
||||
CCIC_NOTIFY_ID_ROLE_SWAP, 0, 0, 0);
|
||||
#elif defined (CONFIG_TYPEC)
|
||||
usbpd_data->typec_power_role = is_src ? TYPEC_SOURCE : TYPEC_SINK;
|
||||
typec_set_pwr_role(usbpd_data->port, usbpd_data->typec_power_role);
|
||||
#endif
|
||||
|
||||
if (usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_PR) {
|
||||
pr_info("%s : power role is changed %s\n",
|
||||
__func__, is_src ? "SOURCE" : "SINK");
|
||||
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
|
||||
complete(&usbpd_data->typec_reverse_completion);
|
||||
pr_info("%s typec_reverse_completion!\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void process_pd(void *data, u8 plug_attach_done, u8 *pdic_attach, MSG_IRQ_STATUS_Type *MSG_IRQ_State)
|
||||
{
|
||||
struct s2mm005_data *usbpd_data = data;
|
||||
struct i2c_client *i2c = usbpd_data->i2c;
|
||||
uint16_t REG_ADD;
|
||||
uint8_t rp_currentlvl, is_src, i;
|
||||
REQUEST_FIXED_SUPPLY_STRUCT_Typedef *request_power_number;
|
||||
|
||||
pr_info("%s\n",__func__);
|
||||
|
||||
if (usbpd_data->short_detected)
|
||||
rp_currentlvl = RP_CURRENT_ABNORMAL;
|
||||
else
|
||||
rp_currentlvl = ((usbpd_data->func_state >> 27) & 0x3);
|
||||
|
||||
is_src = (usbpd_data->func_state & (0x1 << 25) ? 1 : 0);
|
||||
dev_info(&i2c->dev, "rp_currentlvl:0x%02X, is_source:0x%02X\n", rp_currentlvl, is_src);
|
||||
|
||||
if (MSG_IRQ_State->BITS.Ctrl_Flag_PR_Swap)
|
||||
{
|
||||
usbpd_data->is_pr_swap++;
|
||||
dev_info(&i2c->dev, "PR_Swap requested to %s, receive\n", is_src ? "SOURCE" : "SINK");
|
||||
process_message_role(usbpd_data);
|
||||
} else if (usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_PR) {
|
||||
dev_info(&i2c->dev, "PR_Swap requested to %s, send\n", is_src ? "SOURCE" : "SINK");
|
||||
process_message_role(usbpd_data);
|
||||
} else ;
|
||||
|
||||
if (MSG_IRQ_State->BITS.Data_Flag_SRC_Capability)
|
||||
{
|
||||
uint8_t ReadMSG[32];
|
||||
int available_pdo_num;
|
||||
uint8_t do_power_nego = 0;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK;
|
||||
|
||||
REG_ADD = REG_RX_SRC_CAPA_MSG;
|
||||
s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32);
|
||||
available_pdo_num = s2mm005_src_capacity_information(i2c, (uint32_t *)ReadMSG, &pd_noti.sink_status, &do_power_nego);
|
||||
|
||||
REG_ADD = REG_TX_REQUEST_MSG;
|
||||
s2mm005_read_byte(i2c, REG_ADD, ReadMSG, 32);
|
||||
request_power_number = (REQUEST_FIXED_SUPPLY_STRUCT_Typedef *)&ReadMSG[4];
|
||||
|
||||
pr_info(" %s : Object_posision(%d), available_pdo_num(%d), selected_pdo_num(%d) \n", __func__,
|
||||
request_power_number->Object_Position, available_pdo_num, pd_noti.sink_status.selected_pdo_num);
|
||||
pd_noti.sink_status.current_pdo_num = request_power_number->Object_Position;
|
||||
|
||||
if(available_pdo_num > 0)
|
||||
{
|
||||
if(request_power_number->Object_Position != pd_noti.sink_status.selected_pdo_num)
|
||||
{
|
||||
if (pd_noti.sink_status.selected_pdo_num == 0)
|
||||
{
|
||||
pr_info(" %s : PDO is not selected yet by default\n", __func__);
|
||||
pd_noti.sink_status.selected_pdo_num = pd_noti.sink_status.current_pdo_num;
|
||||
}
|
||||
} else {
|
||||
if (do_power_nego) {
|
||||
pr_info(" %s : PDO(%d) is selected, but power negotiation is requested\n",
|
||||
__func__, pd_noti.sink_status.selected_pdo_num);
|
||||
pd_noti.sink_status.selected_pdo_num = 0;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK_CAP;
|
||||
} else {
|
||||
pr_info(" %s : PDO(%d) is selected, but same with previous list, so skip\n",
|
||||
__func__, pd_noti.sink_status.selected_pdo_num);
|
||||
}
|
||||
}
|
||||
*pdic_attach = 1;
|
||||
} else {
|
||||
pr_info(" %s : PDO is not selected\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
if (MSG_IRQ_State->BITS.Ctrl_Flag_Get_Sink_Cap)
|
||||
{
|
||||
pr_info(" %s : SRC requested SINK Cap\n", __func__);
|
||||
}
|
||||
|
||||
/* notify to battery */
|
||||
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
||||
if (plug_attach_done) {
|
||||
if (*pdic_attach) {
|
||||
/* PD charger is detected by PDIC */
|
||||
} else if (!is_src && (usbpd_data->pd_state == State_PE_SNK_Wait_for_Capabilities ||
|
||||
usbpd_data->pd_state == State_ErrorRecovery) &&
|
||||
rp_currentlvl != pd_noti.sink_status.rp_currentlvl &&
|
||||
rp_currentlvl >= RP_CURRENT_LEVEL_DEFAULT) {
|
||||
if (rp_currentlvl == RP_CURRENT_LEVEL3) {
|
||||
/* 5V/3A RP charger is detected by CCIC */
|
||||
pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL3;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
|
||||
} else if (rp_currentlvl == RP_CURRENT_LEVEL2) {
|
||||
/* 5V/1.5A RP charger is detected by CCIC */
|
||||
pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL2;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
|
||||
} else if (rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT) {
|
||||
/* 5V/0.5A RP charger is detected by CCIC */
|
||||
pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_DEFAULT;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
|
||||
} else if (rp_currentlvl == RP_CURRENT_ABNORMAL) {
|
||||
/* ABNORMAL RP charger is detected by CCIC */
|
||||
pd_noti.sink_status.rp_currentlvl = RP_CURRENT_ABNORMAL;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
|
||||
} else
|
||||
return;
|
||||
} else
|
||||
return;
|
||||
#ifdef CONFIG_SEC_FACTORY
|
||||
pr_info(" %s : debug pdic_attach(%d) event(%d)\n", __func__, *pdic_attach, pd_noti.event);
|
||||
#endif
|
||||
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_POWER_STATUS, *pdic_attach, 0, 0);
|
||||
} else {
|
||||
for (i = 0; i < MAX_PDO_NUM + 1; i++) {
|
||||
pd_noti.sink_status.power_list[i].max_current = 0;
|
||||
pd_noti.sink_status.power_list[i].max_voltage = 0;
|
||||
}
|
||||
pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE;
|
||||
pd_noti.sink_status.available_pdo_num = 0;
|
||||
pd_noti.sink_status.selected_pdo_num = 0;
|
||||
pd_noti.sink_status.current_pdo_num = 0;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_DETACH;
|
||||
}
|
||||
#else
|
||||
if(plug_attach_done)
|
||||
{
|
||||
/* PD notify */
|
||||
if(*pdic_attach)
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK;
|
||||
else
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
|
||||
}
|
||||
else
|
||||
{
|
||||
pd_noti.sink_status.selected_pdo_num = 0;
|
||||
pd_noti.event = PDIC_NOTIFY_EVENT_DETACH;
|
||||
}
|
||||
pdic_notifier_call(pd_noti);
|
||||
#endif
|
||||
}
|
||||
@@ -340,6 +340,8 @@ struct fastrpc_apps {
|
||||
bool secure_flag;
|
||||
spinlock_t ctxlock;
|
||||
struct smq_invoke_ctx *ctxtable[FASTRPC_CTX_MAX];
|
||||
int tx_counter;
|
||||
int rx_counter;
|
||||
};
|
||||
|
||||
struct fastrpc_mmap {
|
||||
@@ -1360,6 +1362,8 @@ static void context_free(struct smq_invoke_ctx *ctx)
|
||||
if (me->ctxtable[i] == ctx) {
|
||||
handle = me->ctxtable[i]->handle;
|
||||
ptr = me->ctxtable[i]->ptr;
|
||||
if(ctx->fl->cid == 0x2)
|
||||
me->tx_counter++;
|
||||
me->ctxtable[i] = NULL;
|
||||
break;
|
||||
}
|
||||
@@ -2065,6 +2069,7 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
||||
int err = 0, cid = -1, interrupted = 0;
|
||||
struct timespec invoket = {0};
|
||||
int64_t *perf_counter = NULL;
|
||||
struct fastrpc_apps *me = &gfa;
|
||||
|
||||
cid = fl->cid;
|
||||
VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS);
|
||||
@@ -3138,6 +3143,8 @@ static void fastrpc_glink_notify_rx(void *handle, const void *priv,
|
||||
spin_unlock_irqrestore(&me->ctxlock, irq_flags);
|
||||
goto bail;
|
||||
}
|
||||
if (me->ctxtable[index]->fl->cid == 0x2)
|
||||
me->rx_counter++;
|
||||
me->ctxtable[index]->handle = handle;
|
||||
me->ctxtable[index]->ptr = ptr;
|
||||
spin_unlock_irqrestore(&me->ctxlock, irq_flags);
|
||||
@@ -3417,6 +3424,8 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer,
|
||||
char *fileinfo = NULL;
|
||||
char single_line[UL_SIZE] = "----------------";
|
||||
char title[UL_SIZE] = "=========================";
|
||||
single_line[UL_SIZE-1]='\0';
|
||||
title[UL_SIZE-1]='\0';
|
||||
|
||||
fileinfo = kzalloc(DEBUGFS_SIZE, GFP_KERNEL);
|
||||
if (!fileinfo)
|
||||
@@ -4704,6 +4713,8 @@ static int __init fastrpc_device_init(void)
|
||||
me->dev = NULL;
|
||||
me->glink = true;
|
||||
me->secure_flag = false;
|
||||
me->tx_counter = 0;
|
||||
me->rx_counter = 0;
|
||||
VERIFY(err, 0 == platform_driver_register(&fastrpc_driver));
|
||||
if (err)
|
||||
goto register_bail;
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
#include <trace/events/power.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#if defined(CONFIG_COMMON_CLK)
|
||||
@@ -94,6 +97,8 @@ struct clk_core {
|
||||
unsigned long *rate_max;
|
||||
int num_rate_max;
|
||||
};
|
||||
extern unsigned int sec_debug_level(void);
|
||||
bool is_dbg_level_low;
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/clk.h>
|
||||
@@ -1811,6 +1816,8 @@ static int clk_change_rate(struct clk_core *core)
|
||||
}
|
||||
|
||||
trace_clk_set_rate(core, core->new_rate);
|
||||
sec_debug_clock_rate_log(core->name, core->new_rate,
|
||||
raw_smp_processor_id());
|
||||
|
||||
/* Enforce vdd requirements for new frequency. */
|
||||
if (core->prepare_count) {
|
||||
@@ -1847,6 +1854,8 @@ static int clk_change_rate(struct clk_core *core)
|
||||
}
|
||||
|
||||
trace_clk_set_rate_complete(core, core->new_rate);
|
||||
sec_debug_clock_rate_complete_log(core->name, core->new_rate,
|
||||
raw_smp_processor_id());
|
||||
|
||||
/* Release vdd requirements for old frequency. */
|
||||
if (core->prepare_count)
|
||||
@@ -2365,7 +2374,7 @@ EXPORT_SYMBOL_GPL(clk_list_frequency);
|
||||
|
||||
static struct dentry *rootdir;
|
||||
static int inited = 0;
|
||||
static u32 debug_suspend;
|
||||
static u32 debug_suspend = 1;
|
||||
static DEFINE_MUTEX(clk_debug_lock);
|
||||
static HLIST_HEAD(clk_debug_list);
|
||||
|
||||
@@ -3077,6 +3086,12 @@ static int __init clk_debug_init(void)
|
||||
|
||||
rootdir = debugfs_create_dir("clk", NULL);
|
||||
|
||||
//ANDROID_DEBUG_LEVEL_LOW 0x4f4c
|
||||
if (sec_debug_level() == 0x4f4c)
|
||||
is_dbg_level_low = true;
|
||||
else
|
||||
is_dbg_level_low = false;
|
||||
|
||||
if (!rootdir)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <dt-bindings/clock/qcom,cpucc-sdm845.h>
|
||||
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
|
||||
|
||||
#include <linux/sec_debug_partition.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-voter.h"
|
||||
@@ -296,6 +298,59 @@ static int clk_cpu_determine_rate(struct clk_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_APPS_CLK_LOGGING
|
||||
|
||||
typedef struct {
|
||||
uint64_t ktime;
|
||||
uint64_t qtime;
|
||||
uint64_t rate;
|
||||
} apps_clk_log_t;
|
||||
|
||||
#define MAX_CLK_LOG_CNT (10)
|
||||
|
||||
typedef struct {
|
||||
uint32_t max_cnt;
|
||||
uint32_t index;
|
||||
apps_clk_log_t log[MAX_CLK_LOG_CNT];
|
||||
} cpuclk_log_t;
|
||||
|
||||
cpuclk_log_t cpuclk_log[3] = {
|
||||
[0] = {.max_cnt = MAX_CLK_LOG_CNT,},
|
||||
[1] = {.max_cnt = MAX_CLK_LOG_CNT,},
|
||||
[2] = {.max_cnt = MAX_CLK_LOG_CNT,},
|
||||
};
|
||||
|
||||
static void clk_osm_add_log(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct clk_osm *c = policy->driver_data;
|
||||
cpuclk_log_t *clk = NULL;
|
||||
apps_clk_log_t *log = NULL;
|
||||
uint64_t idx = 0;
|
||||
uint32_t cluster = 0;
|
||||
|
||||
cluster = policy->cpu / 4;
|
||||
if (!WARN(cluster >= 2, "%s : invalid cluster_num(%u), dbg_name(%s)\n",
|
||||
__func__, cluster, clk_hw_get_name(&c->hw))) {
|
||||
if (cluster == 0)
|
||||
clk = &cpuclk_log[PWR_CLUSTER];
|
||||
else
|
||||
clk = &cpuclk_log[PERF_CLUSTER];
|
||||
idx = clk->index;
|
||||
log = &clk->log[idx];
|
||||
log->ktime = local_clock();
|
||||
log->qtime = arch_counter_get_cntvct();
|
||||
log->rate = policy->freq_table[index].frequency;
|
||||
clk->index = (clk->index + 1) % MAX_CLK_LOG_CNT;
|
||||
}
|
||||
}
|
||||
|
||||
void *clk_osm_get_log_addr(void)
|
||||
{
|
||||
return (void *)&cpuclk_log;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_osm_get_log_addr);
|
||||
#endif
|
||||
|
||||
static int l3_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -350,6 +405,22 @@ static int l3_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
cpuclk->rate = rate;
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_APPS_CLK_LOGGING
|
||||
{
|
||||
cpuclk_log_t *clk = NULL;
|
||||
apps_clk_log_t *log = NULL;
|
||||
uint64_t idx = 0;
|
||||
|
||||
clk = &cpuclk_log[L3];
|
||||
idx = clk->index;
|
||||
log = &clk->log[idx];
|
||||
log->ktime = local_clock();
|
||||
log->qtime = arch_counter_get_cntvct();
|
||||
log->rate = rate;
|
||||
clk->index = (clk->index + 1) % MAX_CLK_LOG_CNT;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -715,6 +786,9 @@ osm_cpufreq_target_index(struct cpufreq_policy *policy, unsigned int index)
|
||||
struct clk_osm *c = policy->driver_data;
|
||||
|
||||
osm_set_index(c, index);
|
||||
#ifdef CONFIG_SEC_DEBUG_APPS_CLK_LOGGING
|
||||
clk_osm_add_log(policy, index);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,9 @@ static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = {
|
||||
};
|
||||
|
||||
static const struct freq_tbl ftbl_disp_cc_mdss_dp_aux_clk_src[] = {
|
||||
#if defined(CONFIG_DISPLAY_SAMSUNG)
|
||||
F(12800000, P_BI_TCXO, 1.5, 0, 0),
|
||||
#endif
|
||||
F(19200000, P_BI_TCXO, 1, 0, 0),
|
||||
{ }
|
||||
};
|
||||
|
||||
@@ -966,13 +966,14 @@ static struct clk_rcg2 gcc_sdcc1_apps_clk_src = {
|
||||
};
|
||||
|
||||
static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
|
||||
F(300000, P_BI_TCXO, 32, 1, 2),
|
||||
F(400000, P_BI_TCXO, 12, 1, 4),
|
||||
F(9600000, P_BI_TCXO, 2, 0, 0),
|
||||
F(19200000, P_BI_TCXO, 1, 0, 0),
|
||||
F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0),
|
||||
F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
|
||||
F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
|
||||
F(201500000, P_GPLL4_OUT_MAIN, 4, 0, 0),
|
||||
F(179100000, P_GPLL4_OUT_MAIN, 4.5, 0, 0),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -163,8 +163,14 @@ static int qti_ice_setting_config(struct request *req,
|
||||
setting->encr_bypass = false;
|
||||
break;
|
||||
case READ:
|
||||
if (!ice_fde_flag || (ice_fde_flag & QCOM_ICE_DECRYPT))
|
||||
setting->decr_bypass = false;
|
||||
if (!ice_fde_flag || (ice_fde_flag & QCOM_ICE_DECRYPT)) {
|
||||
if (unlikely(req->cmd_flags & REQ_BYPASS)) {
|
||||
setting->encr_bypass = true;
|
||||
setting->decr_bypass = true;
|
||||
} else {
|
||||
setting->decr_bypass = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Should I say BUG_ON */
|
||||
|
||||
238
drivers/debug/Kconfig
Normal file
238
drivers/debug/Kconfig
Normal file
@@ -0,0 +1,238 @@
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
comment "Samsung Debugging Feature"
|
||||
menuconfig SEC_DEBUG
|
||||
bool "Samsung TN Ramdump Feature"
|
||||
default n
|
||||
help
|
||||
Samsung TN Ramdump Feature.
|
||||
Enables collection of ram dump.
|
||||
Enables task history, debug level etc.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
if SEC_DEBUG
|
||||
|
||||
config SEC_DEBUG_PRINTK_NOCACHE
|
||||
bool "Samsung non-cached kernel printk"
|
||||
default y
|
||||
help
|
||||
Samsung non-cached kernel printk
|
||||
This ensures that printk is never stale
|
||||
which is a good feature when debugging without
|
||||
hardware debuggers. If unsure, keep it turned on.
|
||||
|
||||
config SEC_DEBUG_SCHED_LOG
|
||||
bool "Samsung Scheduler Logging Feature"
|
||||
default n
|
||||
help
|
||||
Samsung Scheduler Logging Feature for Debug use.
|
||||
Enables task scheduling history.
|
||||
Enables IRQ scheduling history.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
config SEC_DEBUG_SEMAPHORE_LOG
|
||||
bool "Samsung Semaphore Logging Feature"
|
||||
default n
|
||||
help
|
||||
Samsung Semaphore Logging Feature for Debug use.
|
||||
Enables semaphore debugging statistics.
|
||||
Enables logging.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
config SEC_FILE_LEAK_DEBUG
|
||||
bool "Samsung File Leak Debugging Feature"
|
||||
default n
|
||||
help
|
||||
Samsung File Leak Debugging Feature for Debug use.
|
||||
Enables the forced panic mode when EMFILE Eror occurs.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
config SEC_LOGGER_BUFFER_EXPANSION
|
||||
bool "Samsung Logger Buffer Expansion Feature"
|
||||
default n
|
||||
help
|
||||
This is used to expand buffers of logger.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
config SEC_LOGGER_BUFFER_EXPANSION_SIZE
|
||||
int "Samsung Logger Buffer Expansion Size(MB)"
|
||||
depends on SEC_LOGGER_BUFFER_EXPANSION
|
||||
default 2
|
||||
help
|
||||
This is used to expand main buffer of logger(MB).
|
||||
|
||||
config SEC_DEBUG_USER
|
||||
bool "Panic on Userspace fault"
|
||||
default y
|
||||
help
|
||||
Panic on Userspace fault
|
||||
This feature enables collection of ram dump,
|
||||
on user fault.
|
||||
Enabled native code debugging.
|
||||
|
||||
config SEC_DEBUG_IRQ_EXIT_LOG
|
||||
bool "Temporary Logging for IRQ delay"
|
||||
default n
|
||||
help
|
||||
Verbose Logging for IRQ delay.
|
||||
Helps indetification of irq enter and exit.
|
||||
This is to track the current state of IRQ execution.
|
||||
This is enabled in defconfig file.
|
||||
|
||||
config SEC_DEBUG_MSG_LOG
|
||||
bool "Message Log for ram dump debug"
|
||||
default n
|
||||
help
|
||||
Verbose Logging for ram dump analysis.
|
||||
Collects kernel debug log.
|
||||
Log is collected in the no-cache area.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
config SEC_DEBUG_SUMMARY
|
||||
bool "Debug summary"
|
||||
depends on SEC_DEBUG_SCHED_LOG
|
||||
default n
|
||||
help
|
||||
Subsystems debug summary feature.
|
||||
When enabled provides kernel logs, modem logs, RPM registers,
|
||||
Schedule and IRQ logs at the time of crash along with the
|
||||
reason for crash, which can be extracted as a html in RAM dump mode.
|
||||
|
||||
config SEC_DEBUG_DCVS_LOG
|
||||
bool "Temporary Logging for DCVS"
|
||||
default n
|
||||
help
|
||||
DCVS Logging Feature for Debug use.
|
||||
The ACPU clock rate changes will be logged as a part
|
||||
of secdbg_log structure along with the CPU time stamp.
|
||||
The previous frequency and the new frequency for both the CPU along
|
||||
with the CPU time stamp will be logged.
|
||||
|
||||
config SEC_DEBUG_POWER_LOG
|
||||
bool "Temporary Logging for MSM POWER"
|
||||
default n
|
||||
help
|
||||
POWER Logging Feature for Debug use.
|
||||
The power and clock gating will be logged as a part
|
||||
of secdbg_log structure along with the CPU time stamp.
|
||||
|
||||
config SEC_DEBUG_FUELGAUGE_LOG
|
||||
bool "Temporary Logging for FuelGauge"
|
||||
default n
|
||||
help
|
||||
FuelGauge Logging Feature for Debug use.
|
||||
The FuelGauge values are logged as a part
|
||||
of secdbg_log structure along with the CPU time stamp.
|
||||
The voltage and soc values along with the CPU time will be logged.
|
||||
|
||||
config SEC_DEBUG_LOW_LOG
|
||||
bool "Kernel Message Logging for Debug Level Low"
|
||||
default n
|
||||
help
|
||||
Kernel Message Logging Feature for Debug use.
|
||||
The Kernel Messages are logged using file I/O
|
||||
when an exception occurs, when the debug level is low.
|
||||
The file I/O is added in kernel driver level
|
||||
so that kernel messages are logged on next reboot.
|
||||
|
||||
config SEC_DEBUG_MDM_FILE_INFO
|
||||
bool "MDM filename and line number in UPLOAD mode"
|
||||
default n
|
||||
help
|
||||
This feature enables display of MDM info in upload mode.
|
||||
This feature enabled SSR in debug level low.
|
||||
Collects MDM ram dump and then calls panic.
|
||||
UPLOAD mode has MDM dump info to show it on LCD.
|
||||
|
||||
config SEC_DEBUG_DOUBLE_FREE
|
||||
bool "Enable double free detection"
|
||||
default n
|
||||
help
|
||||
Detect erraneous codes that frees a kmalloced node
|
||||
twice. When kfree(p) is called, p is not freed right away.
|
||||
Instead, it is pushed into a circular buffer. What it frees
|
||||
is the oldest pointer that was pushed into the buffer.
|
||||
If someone tries to free the same pointer p, *p
|
||||
is read and checked for a magic code that is written
|
||||
when it was first freed. If it matches, the whole
|
||||
circular buffer is searched. Panic is be called when
|
||||
the match is found.
|
||||
|
||||
config SEC_DEBUG_FORCE_ERROR
|
||||
bool "enable force error"
|
||||
default n
|
||||
help
|
||||
This option enable to force error by sysfs
|
||||
|
||||
config SEC_DEBUG_SEC_WDOG_BITE
|
||||
bool "Samsung fore secure bite simulation"
|
||||
default n
|
||||
depends on QCOM_SCM && SEC_DEBUG_FORCE_ERROR
|
||||
help
|
||||
simulation for secure bite.
|
||||
This feature is enabled in defconfig.
|
||||
|
||||
config SEC_LOG_LAST_KMSG
|
||||
bool "Enable /proc/last_kmsg support: if EMBEDDED"
|
||||
default n
|
||||
help
|
||||
This option enables /proc/last_kmsg support.
|
||||
|
||||
config SEC_DEBUG_NOCACHE_LOG_IN_LEVEL_LOW
|
||||
bool "Enable nocache logging in debug level LOW"
|
||||
default n
|
||||
help
|
||||
Enable nocache logging in debug level LOW.
|
||||
|
||||
config SEC_SSR_DEBUG_LEVEL_CHK
|
||||
bool "PERIPHERAL SECURE check"
|
||||
default n
|
||||
help
|
||||
To check the authentication of peripheral image.
|
||||
|
||||
config USER_RESET_DEBUG
|
||||
bool "reset reason debug feature in user version"
|
||||
default n
|
||||
help
|
||||
This option provides reset history log in user version.
|
||||
This option enable proc/reset_reason support
|
||||
|
||||
config USER_RESET_DEBUG_TEST
|
||||
bool "reset reason debug test feature in eng version"
|
||||
depends on USER_RESET_DEBUG
|
||||
default n
|
||||
help
|
||||
This option enable for test in eng version(KP, DP, TP, WP)
|
||||
|
||||
config SEC_PERIPHERAL_SECURE_CHK
|
||||
bool "PERIPHERAL SECURE check"
|
||||
default n
|
||||
depends on MSM_PIL
|
||||
help
|
||||
This option enables checking the authentication of peripheral image.
|
||||
|
||||
config SEC_DEBUG_PWDT
|
||||
bool "Platform Watchdog check"
|
||||
default n
|
||||
help
|
||||
To check Platform Watchdog thread status
|
||||
endif
|
||||
|
||||
config SEC_QUEST
|
||||
bool "Samsung QUEST Feature"
|
||||
default n
|
||||
help
|
||||
Samsung QUEST Feature, to test chipset quality
|
||||
|
||||
config SEC_QUEST_UEFI
|
||||
bool "Samsung QUEST UEFI Feature"
|
||||
default n
|
||||
help
|
||||
Samsung QUEST UEFI Feature, to test chipset quality
|
||||
|
||||
config SEC_SKP
|
||||
bool "Samsung SKP Feature"
|
||||
default n
|
||||
help
|
||||
Samsung SKP Feature, to test chipset quality
|
||||
|
||||
14
drivers/debug/Makefile
Normal file
14
drivers/debug/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
obj-$(CONFIG_SEC_DEBUG) += sec_debug.o sec_kcompat.o \
|
||||
sec_crashkey.o \
|
||||
sec_crashkey_long.o \
|
||||
sec_key_notifier.o \
|
||||
sec_debug_partition.o
|
||||
obj-$(CONFIG_SEC_DEBUG_SCHED_LOG) += sec_debug_sched_log.o
|
||||
obj-$(CONFIG_USER_RESET_DEBUG) += sec_debug_user_reset.o
|
||||
obj-$(CONFIG_SEC_DEBUG_FORCE_ERROR) += sec_debug_force_err.o
|
||||
obj-$(CONFIG_SEC_DEBUG_DOUBLE_FREE) += sec_debug-dfd.o
|
||||
obj-$(CONFIG_SEC_DEBUG_SUMMARY) += sec_debug_summary.o
|
||||
obj-$(CONFIG_SEC_QUEST) += sec_quest.o
|
||||
obj-$(CONFIG_SEC_QUEST) += spinlock_test.o
|
||||
259
drivers/debug/sec_crashkey.c
Normal file
259
drivers/debug/sec_crashkey.c
Normal file
@@ -0,0 +1,259 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/samsung/debug/sec_crashkey.c
|
||||
*
|
||||
* COPYRIGHT(C) 2019 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
|
||||
#include "sec_debug_internal.h"
|
||||
#include "sec_key_notifier.h"
|
||||
|
||||
#define EVENT_KEY_PRESS(__keycode) \
|
||||
{ .keycode = __keycode, .down = true, }
|
||||
#define EVENT_KEY_RELEASE(__keycode) \
|
||||
{ .keycode = __keycode, .down = false, }
|
||||
#define EVENT_KEY_PRESS_AND_RELEASE(__keycode) \
|
||||
EVENT_KEY_PRESS(__keycode), \
|
||||
EVENT_KEY_RELEASE(__keycode)
|
||||
|
||||
struct event_pattern {
|
||||
unsigned int keycode;
|
||||
bool down;
|
||||
};
|
||||
|
||||
struct event_state {
|
||||
struct ratelimit_state rs;
|
||||
struct event_pattern *desired_pattern;
|
||||
struct event_pattern *received_pattern;
|
||||
size_t nr_pattern;
|
||||
const char *msg;
|
||||
int interval;
|
||||
size_t sequence;
|
||||
};
|
||||
|
||||
static struct event_pattern crashkey_pattern[] = {
|
||||
EVENT_KEY_PRESS(KEY_VOLUMEDOWN),
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER),
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER),
|
||||
};
|
||||
|
||||
static struct event_pattern received_pattern[ARRAY_SIZE(crashkey_pattern)];
|
||||
|
||||
static struct event_state crashkey_state = {
|
||||
.desired_pattern = crashkey_pattern,
|
||||
.received_pattern = received_pattern,
|
||||
.nr_pattern = ARRAY_SIZE(crashkey_pattern),
|
||||
.msg = UPLOAD_MSG_CRASH_KEY,
|
||||
.interval = 1 * HZ,
|
||||
};
|
||||
|
||||
static struct event_pattern crashkey_user_pattern[] = {
|
||||
EVENT_KEY_PRESS(KEY_VOLUMEDOWN), /* initial trigger */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 1 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 2 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 3 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 4 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 5 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 6 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 7 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 8 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 9 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 1 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 2 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 3 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 4 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_VOLUMEUP), /* 5 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 1 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 2 */
|
||||
EVENT_KEY_PRESS_AND_RELEASE(KEY_POWER), /* 3 */
|
||||
};
|
||||
|
||||
static struct event_pattern received_user_pattern[ARRAY_SIZE(crashkey_user_pattern)];
|
||||
|
||||
static struct event_state crashkey_user_state = {
|
||||
.desired_pattern = crashkey_user_pattern,
|
||||
.received_pattern = received_user_pattern,
|
||||
.nr_pattern = ARRAY_SIZE(crashkey_user_pattern),
|
||||
.msg = UPLOAD_MSG_USER_CRASH_KEY,
|
||||
.interval = 7 * HZ,
|
||||
};
|
||||
|
||||
static struct event_state *key_event_state;
|
||||
|
||||
static __always_inline int __crashkey_test_pattern(size_t len)
|
||||
{
|
||||
return memcmp(key_event_state->desired_pattern,
|
||||
key_event_state->received_pattern,
|
||||
len * sizeof(struct event_pattern));
|
||||
}
|
||||
|
||||
static __always_inline void __crashkey_clear_received_pattern(void)
|
||||
{
|
||||
key_event_state->sequence = 0;
|
||||
memset(key_event_state->received_pattern, 0x0,
|
||||
key_event_state->nr_pattern * sizeof(struct event_pattern));
|
||||
}
|
||||
|
||||
static int sec_crashkey_notifier_call(struct notifier_block *this,
|
||||
unsigned long type, void *data)
|
||||
{
|
||||
struct sec_key_notifier_param *param = data;
|
||||
size_t idx = key_event_state->sequence;
|
||||
|
||||
if (idx >= key_event_state->nr_pattern)
|
||||
goto clear_state;
|
||||
|
||||
key_event_state->received_pattern[idx].keycode = param->keycode;
|
||||
key_event_state->received_pattern[idx].down = !!param->down;
|
||||
key_event_state->sequence++;
|
||||
|
||||
if (!__ratelimit(&(key_event_state->rs))) {
|
||||
if (!__crashkey_test_pattern(key_event_state->nr_pattern)) {
|
||||
#ifdef CONFIG_SEC_USER_RESET_DEBUG
|
||||
sec_debug_store_extc_idx(false);
|
||||
#endif
|
||||
panic(key_event_state->msg);
|
||||
} else
|
||||
goto clear_state;
|
||||
} else if (__crashkey_test_pattern(key_event_state->sequence))
|
||||
goto clear_state;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
|
||||
clear_state:
|
||||
__crashkey_clear_received_pattern();
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block sec_crashkey_notifier = {
|
||||
.notifier_call = sec_crashkey_notifier_call,
|
||||
};
|
||||
|
||||
static unsigned int *carashkey_used_event;
|
||||
static size_t crashkey_nr_used_event;
|
||||
|
||||
static int __init __crashkey_init_used_event(void)
|
||||
{
|
||||
bool is_new;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
carashkey_used_event = kmalloc_array(key_event_state->nr_pattern,
|
||||
sizeof(*carashkey_used_event), GFP_KERNEL);
|
||||
if (!carashkey_used_event)
|
||||
return -ENOMEM;
|
||||
|
||||
carashkey_used_event[0] =
|
||||
key_event_state->desired_pattern[0].keycode;
|
||||
crashkey_nr_used_event = 1;
|
||||
|
||||
for (i = 1; i < key_event_state->nr_pattern; i++) {
|
||||
for (j = 0, is_new = true; j < crashkey_nr_used_event; j++) {
|
||||
if (carashkey_used_event[j] ==
|
||||
key_event_state->desired_pattern[i].keycode)
|
||||
is_new = false;
|
||||
}
|
||||
|
||||
if (is_new)
|
||||
carashkey_used_event[crashkey_nr_used_event++] =
|
||||
key_event_state->desired_pattern[i].keycode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __sec_crashkey_parse_dt_replace_keymap(void)
|
||||
{
|
||||
struct device_node *parent, *node;
|
||||
int err;
|
||||
size_t i;
|
||||
u32 resin_keycode, pwr_keycode;
|
||||
|
||||
parent = of_find_node_by_path("/soc");
|
||||
if (!parent)
|
||||
goto no_dt;
|
||||
|
||||
node = of_find_node_by_name(parent, "sec_key_crash");
|
||||
if (!node)
|
||||
goto no_dt;
|
||||
|
||||
err = of_property_read_u32(node, "resin-keycode", &resin_keycode);
|
||||
if (err)
|
||||
goto no_dt;
|
||||
|
||||
err = of_property_read_u32(node, "pwr-keycode", &pwr_keycode);
|
||||
if (err)
|
||||
goto no_dt;
|
||||
|
||||
for (i = 0; i < key_event_state->nr_pattern; i++) {
|
||||
struct event_pattern *desired_pattern =
|
||||
&key_event_state->desired_pattern[i];
|
||||
|
||||
switch (desired_pattern->keycode) {
|
||||
case KEY_VOLUMEDOWN:
|
||||
if (resin_keycode != KEY_VOLUMEDOWN)
|
||||
desired_pattern->keycode = resin_keycode;
|
||||
break;
|
||||
case KEY_POWER:
|
||||
if (pwr_keycode != KEY_POWER)
|
||||
desired_pattern->keycode = pwr_keycode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pr_info("use dt keymap");
|
||||
return;
|
||||
|
||||
no_dt:
|
||||
pr_info("use default keymap");
|
||||
}
|
||||
|
||||
static int __init sec_crashkey_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!sec_debug_is_enabled())
|
||||
key_event_state = &crashkey_user_state;
|
||||
else
|
||||
key_event_state = &crashkey_state;
|
||||
|
||||
__sec_crashkey_parse_dt_replace_keymap();
|
||||
|
||||
err = __crashkey_init_used_event();
|
||||
if (err) {
|
||||
pr_warn("crashkey can not be enabled! (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ratelimit_state_init(&(key_event_state->rs),
|
||||
key_event_state->interval,
|
||||
key_event_state->nr_pattern - 1);
|
||||
|
||||
sec_kn_register_notifier(&sec_crashkey_notifier,
|
||||
carashkey_used_event, crashkey_nr_used_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall_sync(sec_crashkey_init);
|
||||
206
drivers/debug/sec_crashkey_long.c
Normal file
206
drivers/debug/sec_crashkey_long.c
Normal file
@@ -0,0 +1,206 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/samsung/debug/sec_crashkey_long.c
|
||||
*
|
||||
* COPYRIGHT(C) 2019 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
#include <linux/sec_crashkey.h>
|
||||
|
||||
#include "sec_debug_internal.h"
|
||||
#include "sec_key_notifier.h"
|
||||
#include "sec_kcompat.h"
|
||||
|
||||
#define EXPIRE_MSEC 6600
|
||||
|
||||
static unsigned int crashkey_long_used_event[] = {
|
||||
KEY_VOLUMEDOWN,
|
||||
KEY_POWER,
|
||||
};
|
||||
|
||||
static size_t crashkey_long_nr_used_event =
|
||||
ARRAY_SIZE(crashkey_long_used_event);
|
||||
|
||||
struct long_pressed_event_state {
|
||||
unsigned long *bitmap_recieved;
|
||||
size_t sz_bitmap;
|
||||
};
|
||||
|
||||
static struct long_pressed_event_state __long_key_state;
|
||||
static struct long_pressed_event_state *long_key_state = &__long_key_state;
|
||||
|
||||
static inline void __reset_pon_s2_ctrl_reset(void)
|
||||
{
|
||||
qpnp_control_s2_reset_onoff(0);
|
||||
udelay(1000);
|
||||
qpnp_control_s2_reset_onoff(1);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
|
||||
static void sec_crashkey_long_timer_handler(struct timer_list *tl)
|
||||
#else
|
||||
static void sec_crashkey_long_timer_handler(unsigned long data)
|
||||
#endif
|
||||
{
|
||||
__pr_err("*** Force trigger kernel panic before triggering hard reset ***\n");
|
||||
|
||||
__reset_pon_s2_ctrl_reset();
|
||||
panic(UPLOAD_MSG_LONG_KEY_PRESS);
|
||||
}
|
||||
|
||||
static struct timer_list sec_crashkey_long_tl;
|
||||
|
||||
static int sec_crashkey_long_notifier_call(struct notifier_block *this,
|
||||
unsigned long type, void *data)
|
||||
{
|
||||
struct sec_key_notifier_param *param = data;
|
||||
int i;
|
||||
bool matching;
|
||||
|
||||
if (param->down)
|
||||
set_bit(param->keycode, long_key_state->bitmap_recieved);
|
||||
else
|
||||
clear_bit(param->keycode, long_key_state->bitmap_recieved);
|
||||
|
||||
for (i = 0, matching = true; i < crashkey_long_nr_used_event; i++) {
|
||||
if (!test_bit(crashkey_long_used_event[i], long_key_state->bitmap_recieved)) {
|
||||
matching = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matching) {
|
||||
if (sec_debug_get_upload_cause() == UPLOAD_CAUSE_INIT)
|
||||
sec_debug_set_upload_cause(UPLOAD_CAUSE_POWER_LONG_PRESS);
|
||||
|
||||
if (unlikely(sec_debug_is_enabled())) {
|
||||
if (!timer_pending(&sec_crashkey_long_tl)) {
|
||||
timer_setup(&sec_crashkey_long_tl,
|
||||
sec_crashkey_long_timer_handler, 0);
|
||||
mod_timer(&sec_crashkey_long_tl,
|
||||
jiffies + msecs_to_jiffies(EXPIRE_MSEC));
|
||||
pr_info("long key timer - start");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sec_debug_get_upload_cause() == UPLOAD_CAUSE_POWER_LONG_PRESS)
|
||||
sec_debug_set_upload_cause(UPLOAD_CAUSE_INIT);
|
||||
|
||||
if (timer_pending(&sec_crashkey_long_tl)) {
|
||||
del_timer(&sec_crashkey_long_tl);
|
||||
pr_info("long key timer - cancel");
|
||||
}
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block sec_crashkey_long_notifier = {
|
||||
.notifier_call = sec_crashkey_long_notifier_call,
|
||||
};
|
||||
|
||||
/* for retail group's request */
|
||||
#if defined(CONFIG_SEC_PM)
|
||||
void do_keyboard_notifier(int onoff)
|
||||
{
|
||||
pr_info("%s: onoff(%d)\n", __func__, onoff);
|
||||
|
||||
if (onoff)
|
||||
sec_kn_register_notifier(&sec_crashkey_long_notifier,
|
||||
crashkey_long_used_event, crashkey_long_nr_used_event);
|
||||
else
|
||||
sec_kn_unregister_notifier(&sec_crashkey_long_notifier,
|
||||
crashkey_long_used_event, crashkey_long_nr_used_event);
|
||||
}
|
||||
EXPORT_SYMBOL(do_keyboard_notifier);
|
||||
#endif
|
||||
|
||||
static void __sec_crashkey_parse_dt_replace_keymap(void)
|
||||
{
|
||||
struct device_node *parent, *node;
|
||||
int err;
|
||||
size_t i;
|
||||
u32 resin_keycode, pwr_keycode;
|
||||
|
||||
parent = of_find_node_by_path("/soc");
|
||||
if (!parent)
|
||||
goto no_dt;
|
||||
|
||||
node = of_find_node_by_name(parent, "sec_key_crash");
|
||||
if (!node)
|
||||
goto no_dt;
|
||||
|
||||
err = of_property_read_u32(node, "resin-keycode", &resin_keycode);
|
||||
if (err)
|
||||
goto no_dt;
|
||||
|
||||
err = of_property_read_u32(node, "pwr-keycode", &pwr_keycode);
|
||||
if (err)
|
||||
goto no_dt;
|
||||
|
||||
for (i = 0; i < crashkey_long_nr_used_event; i++) {
|
||||
unsigned int *keycode = &crashkey_long_used_event[i];
|
||||
|
||||
switch (*keycode) {
|
||||
case KEY_VOLUMEDOWN:
|
||||
if (resin_keycode != KEY_VOLUMEDOWN)
|
||||
*keycode = resin_keycode;
|
||||
break;
|
||||
case KEY_POWER:
|
||||
if (pwr_keycode != KEY_POWER)
|
||||
*keycode = pwr_keycode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pr_info("use dt keymap");
|
||||
return;
|
||||
|
||||
no_dt:
|
||||
pr_info("use default keymap");
|
||||
}
|
||||
|
||||
static int sec_crashkey_long_init(void)
|
||||
{
|
||||
unsigned long *bitmap_recieved;
|
||||
|
||||
bitmap_recieved = bitmap_zalloc(KEY_MAX, GFP_KERNEL);
|
||||
if (!bitmap_recieved) {
|
||||
kfree(bitmap_recieved);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
__sec_crashkey_parse_dt_replace_keymap();
|
||||
|
||||
long_key_state->bitmap_recieved = bitmap_recieved;
|
||||
long_key_state->sz_bitmap =
|
||||
BITS_TO_LONGS(KEY_MAX) * sizeof(unsigned long);
|
||||
|
||||
sec_kn_register_notifier(&sec_crashkey_long_notifier,
|
||||
crashkey_long_used_event, crashkey_long_nr_used_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall_sync(sec_crashkey_long_init);
|
||||
407
drivers/debug/sec_debug-dfd.c
Normal file
407
drivers/debug/sec_debug-dfd.c
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2013 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/shrinker.h>
|
||||
#include <linux/circ_buf.h>
|
||||
|
||||
static int dfd_enable(const char *val, struct kernel_param *kp);
|
||||
module_param_call(enable, dfd_enable, NULL, NULL,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
/* Double free detector(dfd) can use lots of memory because
|
||||
* it needs to hold on the freed slabs, otherwise
|
||||
* the the freeing node will be put into the kmem cache
|
||||
* of it's size and the slab allocator will soon re-allocate
|
||||
* this slab when the slab of that size is requested.
|
||||
* So to alleviate the pressure of the other shrinkers when
|
||||
* there is a memory pressure, enable DFD_HAS_SHRINKER below.
|
||||
*/
|
||||
#define DFD_HAS_SHRINKER
|
||||
|
||||
#ifdef DFD_HAS_SHRINKER
|
||||
/* Using DFD shrinker will keep the DFD buffer entries low
|
||||
* but at a cost. The page allocator will often go into
|
||||
* the slowpath and try to reclaim pages and eventually call
|
||||
* the shrinker.
|
||||
* If you want to avoid this overhead, enable below feature
|
||||
* to completely flush out the circular buffer and disable the
|
||||
* DFD when there is a memory pressure */
|
||||
//#define DFD_SHRINKER_DISABLE_DFD_ON_MEM_PRESSURE
|
||||
#endif
|
||||
|
||||
#define KFREE_HOOK_BYPASS_MASK 0x1
|
||||
/* The average size of a slab object is about 256 bytes.
|
||||
* 1<<15 number of slab objects take about 8MB to 10MB
|
||||
* (This average was mesaured with a min slab size of 64) */
|
||||
#define KFREE_CIRC_BUF_SIZE (1<<15)
|
||||
#define KFREE_FREE_MAGIC 0x65655266
|
||||
|
||||
static int dfd_panic = 1;
|
||||
static int dfd_disabled;
|
||||
|
||||
static DEFINE_SPINLOCK(dfd_list_lock);
|
||||
|
||||
struct dfd_node {
|
||||
void *addr;
|
||||
void *caller;
|
||||
};
|
||||
|
||||
struct dfd_node_list {
|
||||
int head;
|
||||
int tail;
|
||||
struct dfd_node entry[KFREE_CIRC_BUF_SIZE];
|
||||
};
|
||||
|
||||
struct dfd_node_list dfd_node_list;
|
||||
|
||||
static int __init setup_dfd_panic_disable(char *str)
|
||||
{
|
||||
dfd_panic = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("dfd_panic_disable", setup_dfd_panic_disable);
|
||||
|
||||
/* the caller must hold the dfd_list_lock */
|
||||
static void *circ_buf_lookup(struct dfd_node_list *circ_buf, void *addr)
|
||||
{
|
||||
int i;
|
||||
for (i = circ_buf->tail; i != circ_buf->head ;
|
||||
i = (i + 1) & (KFREE_CIRC_BUF_SIZE - 1)) {
|
||||
if (circ_buf->entry[i].addr == addr)
|
||||
return &circ_buf->entry[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* the caller must hold the dfd_list_lock and must check
|
||||
* for the buffer status before calling */
|
||||
static void *circ_buf_get(struct dfd_node_list *circ_buf)
|
||||
{
|
||||
void *entry;
|
||||
entry = &circ_buf->entry[circ_buf->tail];
|
||||
smp_rmb();
|
||||
circ_buf->tail = (circ_buf->tail + 1) &
|
||||
(KFREE_CIRC_BUF_SIZE - 1);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* the caller must hold the dfd_list_lock and must check
|
||||
* for the buffer status before calling */
|
||||
static void *circ_buf_put(struct dfd_node_list *circ_buf,
|
||||
struct dfd_node *entry)
|
||||
{
|
||||
memcpy(&circ_buf->entry[circ_buf->head], entry, sizeof(*entry));
|
||||
smp_wmb();
|
||||
circ_buf->head = (circ_buf->head + 1) &
|
||||
(KFREE_CIRC_BUF_SIZE - 1);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int dfd_flush(void)
|
||||
{
|
||||
struct dfd_node *pentry;
|
||||
unsigned long cnt;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE);
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
pr_debug("cnt=%lu\n", cnt);
|
||||
|
||||
do_flush:
|
||||
while (cnt) {
|
||||
void *tofree = NULL;
|
||||
/* we want to keep the lock region as short as possible
|
||||
* so we will re-read the buf count every loop */
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE);
|
||||
if (cnt == 0) {
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
break;
|
||||
}
|
||||
if ((pentry = circ_buf_get(&dfd_node_list)) != NULL)
|
||||
tofree = pentry->addr;
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
if (tofree)
|
||||
kfree((void *)((unsigned long)tofree |
|
||||
KFREE_HOOK_BYPASS_MASK));
|
||||
cnt--;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE);
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
|
||||
if (!dfd_disabled)
|
||||
goto out;
|
||||
|
||||
if (cnt)
|
||||
goto do_flush;
|
||||
|
||||
out:
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int dfd_enable(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
if (!strncmp(val, "1", 1)) {
|
||||
dfd_disabled = 0;
|
||||
pr_info("double free detection is enabled\n");
|
||||
} else if (!strncmp(val, "0", 1)) {
|
||||
dfd_disabled = 1;
|
||||
dfd_flush();
|
||||
pr_info("double free detection is disabled\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DFD_HAS_SHRINKER
|
||||
int dfd_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
{
|
||||
#ifndef DFD_SHRINKER_DISABLE_DFD_ON_MEM_PRESSURE
|
||||
struct dfd_node *pentry;
|
||||
unsigned long nr = sc->nr_to_scan;
|
||||
#endif
|
||||
unsigned long flags;
|
||||
unsigned long nr_objs;
|
||||
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
nr_objs = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE);
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
|
||||
/* nothing to reclaim from here */
|
||||
if (nr_objs == 0) {
|
||||
nr_objs = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef DFD_SHRINKER_DISABLE_DFD_ON_MEM_PRESSURE
|
||||
/* disable double free detection. This will flush
|
||||
* the entire circular buffer out. */
|
||||
dfd_disable();
|
||||
#else
|
||||
/* return max slab objects freeable */
|
||||
if (nr == 0)
|
||||
return nr_objs;
|
||||
|
||||
if (nr > nr_objs)
|
||||
nr = nr_objs;
|
||||
|
||||
pr_debug("nr_objs=%lu\n", nr_objs);
|
||||
while (nr) {
|
||||
unsigned long cnt;
|
||||
void *tofree = NULL;
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
cnt = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE);
|
||||
if (cnt > 0) {
|
||||
if ((pentry = circ_buf_get(&dfd_node_list)) != NULL)
|
||||
tofree = pentry->addr;
|
||||
}
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
if (tofree)
|
||||
kfree((void *)((unsigned long)tofree |
|
||||
KFREE_HOOK_BYPASS_MASK));
|
||||
nr--;
|
||||
}
|
||||
#endif
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
nr_objs = CIRC_CNT(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE);
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
if (nr_objs == 0) {
|
||||
pr_info("nothing more to reclaim from here!\n");
|
||||
nr_objs = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
return nr_objs;
|
||||
}
|
||||
|
||||
static struct shrinker dfd_shrinker = {
|
||||
.shrink = dfd_shrink,
|
||||
.seeks = DEFAULT_SEEKS
|
||||
};
|
||||
|
||||
static int __init dfd_shrinker_init(void)
|
||||
{
|
||||
register_shrinker(&dfd_shrinker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dfd_shrinker_exit(void)
|
||||
{
|
||||
unregister_shrinker(&dfd_shrinker);
|
||||
}
|
||||
|
||||
module_init(dfd_shrinker_init);
|
||||
module_exit(dfd_shrinker_exit);
|
||||
#endif
|
||||
|
||||
static inline int dfd_check_magic_any(void *addr)
|
||||
{
|
||||
return (((unsigned int *)addr)[0] == KFREE_FREE_MAGIC ||
|
||||
((unsigned int *)addr)[1] == KFREE_FREE_MAGIC ||
|
||||
((unsigned int *)addr)[2] == KFREE_FREE_MAGIC ||
|
||||
((unsigned int *)addr)[3] == KFREE_FREE_MAGIC);
|
||||
}
|
||||
|
||||
static inline int dfd_check_magic_all(void *addr)
|
||||
{
|
||||
return (((unsigned int *)addr)[0] == KFREE_FREE_MAGIC &&
|
||||
((unsigned int *)addr)[1] == KFREE_FREE_MAGIC &&
|
||||
((unsigned int *)addr)[2] == KFREE_FREE_MAGIC &&
|
||||
((unsigned int *)addr)[3] == KFREE_FREE_MAGIC);
|
||||
}
|
||||
|
||||
static inline void dfd_set_magic(void *addr)
|
||||
{
|
||||
BUILD_BUG_ON(KMALLOC_MIN_SIZE < 16);
|
||||
((unsigned long *)addr)[0] = KFREE_FREE_MAGIC;
|
||||
((unsigned long *)addr)[1] = KFREE_FREE_MAGIC;
|
||||
((unsigned long *)addr)[2] = KFREE_FREE_MAGIC;
|
||||
((unsigned long *)addr)[3] = KFREE_FREE_MAGIC;
|
||||
}
|
||||
|
||||
static inline void dfd_clear_magic(void *addr)
|
||||
{
|
||||
BUILD_BUG_ON(KMALLOC_MIN_SIZE < 16);
|
||||
((unsigned long *)addr)[0] = 0;
|
||||
((unsigned long *)addr)[1] = 0;
|
||||
((unsigned long *)addr)[2] = 0;
|
||||
((unsigned long *)addr)[3] = 0;
|
||||
}
|
||||
|
||||
static void __hexdump(void *mem, unsigned long size)
|
||||
{
|
||||
#define WORDS_PER_LINE 4
|
||||
#define WORD_SIZE 4
|
||||
#define LINE_SIZE (WORDS_PER_LINE * WORD_SIZE)
|
||||
#define LINE_BUF_SIZE (WORDS_PER_LINE * WORD_SIZE * 3 \
|
||||
+ WORDS_PER_LINE + 4)
|
||||
unsigned long addr;
|
||||
char linebuf[LINE_BUF_SIZE];
|
||||
int numline = size / LINE_SIZE;
|
||||
int i;
|
||||
for (i = 0; i < numline; i++) {
|
||||
addr = (unsigned long)mem + i * LINE_SIZE;
|
||||
hex_dump_to_buffer((const void *)addr,
|
||||
LINE_SIZE, LINE_SIZE,
|
||||
WORD_SIZE, linebuf, sizeof(linebuf), 1);
|
||||
pr_info(" %lx : %s\n", addr, linebuf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void *kfree_hook(void *p, void *caller)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct dfd_node *match = NULL;
|
||||
void *tofree = NULL;
|
||||
unsigned long addr = (unsigned long)p;
|
||||
struct dfd_node entry;
|
||||
struct dfd_node *pentry;
|
||||
|
||||
if (!virt_addr_valid(addr)) {
|
||||
/* there are too many NULL pointers so don't print for NULL */
|
||||
if (addr)
|
||||
pr_debug("trying to free an invalid addr %lx" \
|
||||
"from %pS\n", addr, caller);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (addr & KFREE_HOOK_BYPASS_MASK || dfd_disabled) {
|
||||
/* return original address to free */
|
||||
return (void *)(addr&~(KFREE_HOOK_BYPASS_MASK));
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dfd_list_lock, flags);
|
||||
|
||||
if (dfd_node_list.head == 0)
|
||||
pr_debug("circular buffer head rounded to zero.");
|
||||
|
||||
/* We can detect all the double free in the circular buffer time frame
|
||||
* if we scan the whole circular buffer all the time, but to minimize
|
||||
* the performance degradation we will just check for the magic values
|
||||
* (the number of magic values can be up to KMALLOC_MIN_SIZE/4) */
|
||||
if (dfd_check_magic_any(p)) {
|
||||
/* memory that is to be freed may originally have had magic
|
||||
* value, so search the whole circ buf for an actual match */
|
||||
match = circ_buf_lookup(&dfd_node_list, p);
|
||||
if (!match) {
|
||||
pr_debug("magic set but not in circ buf\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
pr_err("0x%08lx was already freed by %pS()\n",
|
||||
(unsigned long)p, match->caller);
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
if (dfd_panic)
|
||||
panic("double free detected!");
|
||||
/* if we don't panic we just return without adding this entry
|
||||
* to the circular buffer. This means that this kfree is ommited
|
||||
* and we are just forgiving the double free */
|
||||
dump_stack();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mark free magic on the freeing node */
|
||||
dfd_set_magic(p);
|
||||
|
||||
/* do an actual kfree for the oldest entry
|
||||
* if the circular buffer is full */
|
||||
if (CIRC_SPACE(dfd_node_list.head, dfd_node_list.tail,
|
||||
KFREE_CIRC_BUF_SIZE) == 0) {
|
||||
pentry = circ_buf_get(&dfd_node_list);
|
||||
if (pentry)
|
||||
tofree = pentry->addr;
|
||||
}
|
||||
|
||||
/* add the new entry to the circular buffer */
|
||||
entry.addr = p;
|
||||
entry.caller = caller;
|
||||
circ_buf_put(&dfd_node_list, &entry);
|
||||
if (tofree) {
|
||||
if (unlikely(!dfd_check_magic_all(tofree))) {
|
||||
pr_emerg("Use after free detected on the node " \
|
||||
"0x%lx which was freed by %pS.\n", \
|
||||
(unsigned long)tofree,
|
||||
pentry->caller);
|
||||
__hexdump((void *)tofree, KMALLOC_MIN_SIZE);
|
||||
pr_err("\n");
|
||||
}
|
||||
dfd_clear_magic(tofree);
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
/* do the real kfree */
|
||||
kfree((void *)((unsigned long)tofree | KFREE_HOOK_BYPASS_MASK));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dfd_list_lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
1349
drivers/debug/sec_debug.c
Normal file
1349
drivers/debug/sec_debug.c
Normal file
File diff suppressed because it is too large
Load Diff
363
drivers/debug/sec_debug_force_err.c
Normal file
363
drivers/debug/sec_debug_force_err.c
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* drivers/debug/sec_debug_force_err.c
|
||||
*
|
||||
* COPYRIGHT(C) 2017 Samsung Electronics Co., Ltd. All Right Reserved.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <soc/qcom/scm.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
#include <linux/sec_debug_user_reset.h>
|
||||
|
||||
/* timeout for dog bark/bite */
|
||||
#define DELAY_TIME 20000
|
||||
|
||||
static int force_error(const char *val, const struct kernel_param *kp);
|
||||
module_param_call(force_error, force_error, NULL, NULL,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
static void __simulate_apps_wdog_bark(void)
|
||||
{
|
||||
pr_emerg("Simulating apps watch dog bark\n");
|
||||
preempt_disable();
|
||||
mdelay(DELAY_TIME);
|
||||
preempt_enable();
|
||||
/* if we reach here, simulation failed */
|
||||
pr_emerg("Simulation of apps watch dog bark failed\n");
|
||||
}
|
||||
|
||||
static void __simulate_apps_wdog_bite(void)
|
||||
{
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
int cpu;
|
||||
for_each_online_cpu(cpu) {
|
||||
if (0 == cpu)
|
||||
continue;
|
||||
cpu_down(cpu);
|
||||
}
|
||||
#endif
|
||||
pr_emerg("Simulating apps watch dog bite\n");
|
||||
local_irq_disable();
|
||||
mdelay(DELAY_TIME);
|
||||
local_irq_enable();
|
||||
/* if we reach here, simulation had failed */
|
||||
pr_emerg("Simualtion of apps watch dog bite failed\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_SEC_WDOG_BITE
|
||||
static void __simulate_secure_wdog_bite(void)
|
||||
{
|
||||
#define SCM_SVC_SEC_WDOG_TRIG 0x8
|
||||
struct scm_desc desc = {
|
||||
.args[0] = 0,
|
||||
.arginfo = SCM_ARGS(1),
|
||||
};
|
||||
|
||||
pr_emerg("simulating secure watch dog bite\n");
|
||||
if (!is_scm_armv8())
|
||||
scm_call_atomic2(SCM_SVC_BOOT,
|
||||
SCM_SVC_SEC_WDOG_TRIG, 0, 0);
|
||||
else
|
||||
scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT,
|
||||
SCM_SVC_SEC_WDOG_TRIG), &desc);
|
||||
/* if we hit, scm_call has failed */
|
||||
pr_emerg("simulation of secure watch dog bite failed\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USER_RESET_DEBUG_TEST
|
||||
extern void qpnp_pon_pmic_wd_trigger(void);
|
||||
static void __simulate_pmic_wdog_bite(void)
|
||||
{
|
||||
pr_emerg("simulating pmic watch dog bite\n");
|
||||
|
||||
qpnp_pon_pmic_wd_trigger();
|
||||
|
||||
while(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8974)
|
||||
/*
|
||||
* Misc data structures needed for simulating bus timeout in
|
||||
* camera
|
||||
*/
|
||||
|
||||
#define HANG_ADDRESS 0xfda10000
|
||||
|
||||
struct clk_pair {
|
||||
const char *dev;
|
||||
const char *clk;
|
||||
};
|
||||
|
||||
static struct clk_pair bus_timeout_camera_clocks_on[] = {
|
||||
/*
|
||||
* gcc_mmss_noc_cfg_ahb_clk should be on but right
|
||||
* now this clock is on by default and not accessable.
|
||||
* Update this table if gcc_mmss_noc_cfg_ahb_clk is
|
||||
* ever not enabled by default!
|
||||
*/
|
||||
{
|
||||
.dev = "fda0c000.qcom,cci",
|
||||
.clk = "camss_top_ahb_clk",
|
||||
},
|
||||
{
|
||||
.dev = "fda10000.qcom,vfe",
|
||||
.clk = "iface_clk",
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_pair bus_timeout_camera_clocks_off[] = {
|
||||
{
|
||||
.dev = "fda10000.qcom,vfe",
|
||||
.clk = "camss_vfe_vfe_clk",
|
||||
}
|
||||
};
|
||||
|
||||
static void bus_timeout_clk_access(struct clk_pair bus_timeout_clocks_off[],
|
||||
struct clk_pair bus_timeout_clocks_on[],
|
||||
int off_size, int on_size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* Yes, none of this cleans up properly but the goal here
|
||||
* is to trigger a hang which is going to kill the rest of
|
||||
* the system anyway
|
||||
*/
|
||||
|
||||
for (i = 0; i < on_size; i++) {
|
||||
struct clk *this_clock;
|
||||
|
||||
this_clock = clk_get_sys(bus_timeout_clocks_on[i].dev,
|
||||
bus_timeout_clocks_on[i].clk);
|
||||
if (!IS_ERR(this_clock))
|
||||
if (clk_prepare_enable(this_clock))
|
||||
pr_warn("Device %s: Clock %s not enabled",
|
||||
bus_timeout_clocks_on[i].clk,
|
||||
bus_timeout_clocks_on[i].dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < off_size; i++) {
|
||||
struct clk *this_clock;
|
||||
|
||||
this_clock = clk_get_sys(bus_timeout_clocks_off[i].dev,
|
||||
bus_timeout_clocks_off[i].clk);
|
||||
if (!IS_ERR(this_clock))
|
||||
clk_disable_unprepare(this_clock);
|
||||
}
|
||||
}
|
||||
|
||||
static void simulate_bus_hang(void)
|
||||
{
|
||||
/* This simulates bus timeout on camera */
|
||||
int ret;
|
||||
uint32_t dummy_value;
|
||||
uint32_t address = HANG_ADDRESS;
|
||||
void *hang_address;
|
||||
struct regulator *r;
|
||||
|
||||
/* simulate */
|
||||
hang_address = ioremap(address, SZ_4K);
|
||||
r = regulator_get(NULL, "gdsc_vfe");
|
||||
ret = IS_ERR(r);
|
||||
if (!ret)
|
||||
regulator_enable(r);
|
||||
else
|
||||
pr_emerg("Unable to get regulator reference\n");
|
||||
|
||||
bus_timeout_clk_access(bus_timeout_camera_clocks_off,
|
||||
bus_timeout_camera_clocks_on,
|
||||
ARRAY_SIZE(bus_timeout_camera_clocks_off),
|
||||
ARRAY_SIZE(bus_timeout_camera_clocks_on));
|
||||
|
||||
dummy_value = readl_relaxed(hang_address);
|
||||
mdelay(DELAY_TIME);
|
||||
/* if we hit here, test had failed */
|
||||
pr_emerg("Bus timeout test failed...0x%x\n", dummy_value);
|
||||
iounmap(hang_address);
|
||||
}
|
||||
#else /* defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8974) */
|
||||
static void simulate_bus_hang(void)
|
||||
{
|
||||
void __iomem *p;
|
||||
|
||||
pr_emerg("Generating Bus Hang!\n");
|
||||
p = ioremap_nocache(0xFC4B8000, 32);
|
||||
*(unsigned int *)p = *(unsigned int *)p;
|
||||
mb(); /* memory barriar to generate bus hang */
|
||||
pr_info("*p = %x\n", *(unsigned int *)p);
|
||||
pr_emerg("Clk may be enabled.Try again if it reaches here!\n");
|
||||
}
|
||||
#endif /* defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8974) */
|
||||
|
||||
static void __simulate_dabort(void)
|
||||
{
|
||||
char *buf = NULL;
|
||||
*buf = 0x0;
|
||||
}
|
||||
|
||||
|
||||
static void __simulate_pabort(void)
|
||||
{
|
||||
((void (*)(void))NULL)();
|
||||
}
|
||||
|
||||
static void __simulate_undef(void)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static void __simulate_dblfree(void)
|
||||
{
|
||||
unsigned int *ptr = kmalloc(sizeof(unsigned int), GFP_KERNEL);
|
||||
kfree(ptr);
|
||||
msleep(1000);
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static void __simulate_danglingref(void)
|
||||
{
|
||||
unsigned int *ptr = kmalloc(sizeof(unsigned int), GFP_KERNEL);
|
||||
kfree(ptr);
|
||||
*ptr = 0x1234;
|
||||
}
|
||||
|
||||
static void __simulate_lowmem(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; kmalloc(128 * 1024, GFP_KERNEL); i++)
|
||||
;
|
||||
pr_emerg("Allocated %zu KB!\n", i * 128);
|
||||
}
|
||||
|
||||
static void __simulate_memcorrupt(void)
|
||||
{
|
||||
unsigned int *ptr = kmalloc(sizeof(unsigned int), GFP_KERNEL);
|
||||
*ptr++ = 4;
|
||||
*ptr = 2;
|
||||
panic("MEMORY CORRUPTION");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FREE_PAGES_RDONLY
|
||||
static void __check_page_read(void)
|
||||
{
|
||||
struct page *page = alloc_pages(GFP_ATOMIC, 0);
|
||||
unsigned int *ptr = (unsigned int *)page_address(page);
|
||||
pr_emerg("Test with RD page config.");
|
||||
__free_pages(page, 0);
|
||||
*ptr = 0x12345678;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int force_error(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
size_t i;
|
||||
struct __magic {
|
||||
const char *val;
|
||||
const char *msg;
|
||||
void (*func)(void);
|
||||
} magic[] = {
|
||||
{ "apppdogbark",
|
||||
"Generating an apps wdog bark!",
|
||||
&__simulate_apps_wdog_bark },
|
||||
{ "appdogbite",
|
||||
"Generating an apps wdog bite!",
|
||||
&__simulate_apps_wdog_bite },
|
||||
{ "dabort",
|
||||
"Generating a data abort exception!",
|
||||
&__simulate_dabort },
|
||||
{ "pabort",
|
||||
"Generating a data abort exception!",
|
||||
&__simulate_pabort },
|
||||
{ "undef",
|
||||
"Generating a undefined instruction exception!",
|
||||
&__simulate_undef },
|
||||
{ "bushang",
|
||||
"Generating a Bus Hang!",
|
||||
&simulate_bus_hang },
|
||||
{ "dblfree",
|
||||
NULL,
|
||||
&__simulate_dblfree },
|
||||
{ "danglingref",
|
||||
NULL,
|
||||
&__simulate_danglingref },
|
||||
{ "lowmem",
|
||||
"Allocating memory until failure!",
|
||||
&__simulate_lowmem },
|
||||
{ "memcorrupt",
|
||||
NULL,
|
||||
&__simulate_memcorrupt },
|
||||
#ifdef CONFIG_SEC_DEBUG_SEC_WDOG_BITE
|
||||
{ "secdogbite",
|
||||
NULL,
|
||||
&__simulate_secure_wdog_bite },
|
||||
#endif
|
||||
#ifdef CONFIG_USER_RESET_DEBUG_TEST
|
||||
{ "KP",
|
||||
"Generating a data abort exception!",
|
||||
&__simulate_dabort },
|
||||
{ "DP",
|
||||
NULL,
|
||||
&force_watchdog_bark },
|
||||
{ "WP",
|
||||
NULL,
|
||||
&__simulate_secure_wdog_bite },
|
||||
{ "PMWD",
|
||||
NULL,
|
||||
&__simulate_pmic_wdog_bite },
|
||||
#endif
|
||||
#ifdef CONFIG_FREE_PAGES_RDONLY
|
||||
{ "pageRDcheck",
|
||||
NULL,
|
||||
&__check_page_read },
|
||||
#endif
|
||||
};
|
||||
|
||||
pr_emerg("!!!WARN forced error : %s\n", val);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(magic); i++) {
|
||||
size_t len = strlen(magic[i].val);
|
||||
|
||||
if (strncmp(val, magic[i].val, len))
|
||||
continue;
|
||||
|
||||
if (NULL != magic[i].msg)
|
||||
pr_emerg("%s\n", magic[i].msg);
|
||||
|
||||
magic[i].func();
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_emerg("No such error defined for now!\n");
|
||||
return 0;
|
||||
}
|
||||
52
drivers/debug/sec_debug_internal.h
Normal file
52
drivers/debug/sec_debug_internal.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* drivers/debug/sec_debug_internal.h
|
||||
*
|
||||
* COPYRIGHT(C) 2017-2018 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#ifndef __SEC_DEBUG_INTERNAL_H__
|
||||
#define __SEC_DEBUG_INTERNAL_H__
|
||||
|
||||
/* [[BEGIN>> sec_debug.c */
|
||||
extern void dump_all_task_info(void);
|
||||
extern void dump_cpu_stat(void);
|
||||
/* <<END]] sec_debug.c */
|
||||
|
||||
/* [[BEGIN>> sec_debug_sched_log.c */
|
||||
extern struct sec_debug_log *secdbg_log;
|
||||
extern phys_addr_t secdbg_paddr;
|
||||
extern size_t secdbg_size;
|
||||
/* <<END]] sec_debug_sched_log.c */
|
||||
|
||||
/* out-of-sec_debug */
|
||||
|
||||
/* drivers/power/reset/msm-poweroff.c */
|
||||
extern void set_dload_mode(int on);
|
||||
|
||||
/* kernel/init/version.c */
|
||||
#include <linux/utsname.h>
|
||||
extern struct uts_namespace init_uts_ns;
|
||||
|
||||
/* drivers/base/core.c */
|
||||
extern struct kset *devices_kset;
|
||||
|
||||
/* kernel/printk/printk.c */
|
||||
extern unsigned int get_sec_log_idx(void);
|
||||
|
||||
/* fake pr_xxx macros to prevent checkpatch fails */
|
||||
#define __printx printk
|
||||
#define __pr_info(fmt, ...) \
|
||||
__printx(KERN_INFO fmt, ##__VA_ARGS__)
|
||||
#define __pr_err(fmt, ...) \
|
||||
__printx(KERN_ERR fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif /* __SEC_DEBUG_INTERNAL_H__ */
|
||||
534
drivers/debug/sec_debug_partition.c
Normal file
534
drivers/debug/sec_debug_partition.c
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* drivers/debug/sec_debug_partition.c
|
||||
*
|
||||
* COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/sec_bsp.h>
|
||||
#include <linux/sec_debug.h>
|
||||
#include <linux/sec_debug_summary.h>
|
||||
#include <linux/sec_debug_user_reset.h>
|
||||
#include <linux/sec_debug_partition.h>
|
||||
|
||||
#define PRINT_MSG_CYCLE 20
|
||||
|
||||
/* single global instance */
|
||||
struct debug_partition_data_s sched_debug_data;
|
||||
|
||||
static int in_panic;
|
||||
static int driver_initialized;
|
||||
static int ap_health_initialized;
|
||||
static struct delayed_work dbg_partition_notify_work;
|
||||
static struct workqueue_struct *dbg_part_wq;
|
||||
static struct delayed_work ap_health_work;
|
||||
static ap_health_t ap_health_data;
|
||||
|
||||
static DEFINE_MUTEX(ap_health_work_lock);
|
||||
static DEFINE_MUTEX(debug_partition_mutex);
|
||||
static BLOCKING_NOTIFIER_HEAD(dbg_partition_notifier_list);
|
||||
|
||||
static void debug_partition_operation(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct file *filp;
|
||||
mm_segment_t fs;
|
||||
struct debug_partition_data_s *sched_data =
|
||||
container_of(work, struct debug_partition_data_s,
|
||||
debug_partition_work);
|
||||
int flag = (sched_data->direction == PARTITION_WR) ?
|
||||
(O_RDWR | O_SYNC) : O_RDONLY;
|
||||
static unsigned int err_cnt;
|
||||
|
||||
if (!sched_data->value) {
|
||||
pr_err("%p %x %d %d - value is NULL!!\n",
|
||||
sched_data->value, sched_data->offset,
|
||||
sched_data->size, sched_data->direction);
|
||||
sched_data->error = -ENODATA;
|
||||
goto sched_data_err;
|
||||
}
|
||||
|
||||
fs = get_fs();
|
||||
set_fs(get_ds());
|
||||
|
||||
sched_data->error = 0;
|
||||
|
||||
filp = filp_open(DEBUG_PARTITION_NAME, flag, 0);
|
||||
if (IS_ERR(filp)) {
|
||||
if (!(++err_cnt % PRINT_MSG_CYCLE))
|
||||
pr_err("filp_open failed: %ld[%u]\n",
|
||||
PTR_ERR(filp), err_cnt);
|
||||
sched_data->error = PTR_ERR(filp);
|
||||
goto filp_err;
|
||||
}
|
||||
|
||||
ret = vfs_llseek(filp, sched_data->offset, SEEK_SET);
|
||||
if (ret < 0) {
|
||||
pr_err("FAIL LLSEEK\n");
|
||||
sched_data->error = ret;
|
||||
goto llseek_err;
|
||||
}
|
||||
|
||||
if (sched_data->direction == PARTITION_RD)
|
||||
vfs_read(filp, (char __user *)sched_data->value,
|
||||
sched_data->size, &filp->f_pos);
|
||||
else if (sched_data->direction == PARTITION_WR)
|
||||
vfs_write(filp, (char __user *)sched_data->value,
|
||||
sched_data->size, &filp->f_pos);
|
||||
|
||||
llseek_err:
|
||||
filp_close(filp, NULL);
|
||||
filp_err:
|
||||
set_fs(fs);
|
||||
sched_data_err:
|
||||
complete(&sched_data->work);
|
||||
}
|
||||
|
||||
static void ap_health_work_write_fn(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct file *filp;
|
||||
mm_segment_t fs;
|
||||
unsigned long delay = 5 * HZ;
|
||||
static unsigned int err_cnt;
|
||||
|
||||
pr_info("start.\n");
|
||||
|
||||
if (!mutex_trylock(&ap_health_work_lock)) {
|
||||
pr_err("already locked.\n");
|
||||
delay = 2 * HZ;
|
||||
goto occupied_retry;
|
||||
}
|
||||
|
||||
fs = get_fs();
|
||||
set_fs(get_ds());
|
||||
|
||||
filp = filp_open(DEBUG_PARTITION_NAME, (O_RDWR | O_SYNC), 0);
|
||||
if (IS_ERR(filp)) {
|
||||
if (!(++err_cnt % PRINT_MSG_CYCLE))
|
||||
pr_err("filp_open failed: %ld[%u]\n",
|
||||
PTR_ERR(filp), err_cnt);
|
||||
goto openfail_retry;
|
||||
}
|
||||
|
||||
ret = vfs_llseek(filp, SEC_DEBUG_AP_HEALTH_OFFSET, SEEK_SET);
|
||||
if (ret < 0) {
|
||||
pr_err("FAIL LLSEEK\n");
|
||||
ret = false;
|
||||
goto seekfail_retry;
|
||||
}
|
||||
|
||||
vfs_write(filp, (char __user *)&ap_health_data,
|
||||
sizeof(ap_health_t), &filp->f_pos);
|
||||
|
||||
if (--ap_health_data.header.need_write)
|
||||
goto remained;
|
||||
|
||||
filp_close(filp, NULL);
|
||||
set_fs(fs);
|
||||
|
||||
mutex_unlock(&ap_health_work_lock);
|
||||
pr_info("end.\n");
|
||||
return;
|
||||
|
||||
remained:
|
||||
seekfail_retry:
|
||||
filp_close(filp, NULL);
|
||||
openfail_retry:
|
||||
set_fs(fs);
|
||||
mutex_unlock(&ap_health_work_lock);
|
||||
occupied_retry:
|
||||
queue_delayed_work(dbg_part_wq, &ap_health_work, delay);
|
||||
pr_info("end, will retry, wr(%u).\n",
|
||||
ap_health_data.header.need_write);
|
||||
}
|
||||
|
||||
static bool init_lcd_debug_data(void)
|
||||
{
|
||||
int ret = true, retry = 0;
|
||||
struct lcd_debug_t lcd_debug;
|
||||
|
||||
pr_info("%s start\n", __func__);
|
||||
|
||||
memset((void *)&lcd_debug, 0, sizeof(struct lcd_debug_t));
|
||||
|
||||
pr_info("%s lcd_debug size[%ld]\n", __func__, sizeof(struct lcd_debug_t));
|
||||
|
||||
do {
|
||||
if (retry++) {
|
||||
pr_err("%s : will retry...\n", __func__);
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
mutex_lock(&debug_partition_mutex);
|
||||
|
||||
sched_debug_data.value = &lcd_debug;
|
||||
sched_debug_data.offset = SEC_DEBUG_LCD_DEBUG_OFFSET;
|
||||
sched_debug_data.size = sizeof(struct lcd_debug_t);
|
||||
sched_debug_data.direction = PARTITION_WR;
|
||||
|
||||
schedule_work(&sched_debug_data.debug_partition_work);
|
||||
wait_for_completion(&sched_debug_data.work);
|
||||
|
||||
mutex_unlock(&debug_partition_mutex);
|
||||
} while (sched_debug_data.error);
|
||||
|
||||
pr_info("%s end\n",__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void init_ap_health_data(void)
|
||||
{
|
||||
pr_info("start\n");
|
||||
|
||||
memset((void *)&ap_health_data, 0, sizeof(ap_health_t));
|
||||
|
||||
ap_health_data.header.magic = AP_HEALTH_MAGIC;
|
||||
ap_health_data.header.version = AP_HEALTH_VER;
|
||||
ap_health_data.header.size = sizeof(ap_health_t);
|
||||
ap_health_data.spare_magic1 = AP_HEALTH_MAGIC;
|
||||
ap_health_data.spare_magic2 = AP_HEALTH_MAGIC;
|
||||
ap_health_data.spare_magic3 = AP_HEALTH_MAGIC;
|
||||
|
||||
while (1) {
|
||||
mutex_lock(&debug_partition_mutex);
|
||||
|
||||
sched_debug_data.value = &ap_health_data;
|
||||
sched_debug_data.offset = SEC_DEBUG_AP_HEALTH_OFFSET;
|
||||
sched_debug_data.size = sizeof(ap_health_t);
|
||||
sched_debug_data.direction = PARTITION_WR;
|
||||
|
||||
schedule_work(&sched_debug_data.debug_partition_work);
|
||||
wait_for_completion(&sched_debug_data.work);
|
||||
|
||||
mutex_unlock(&debug_partition_mutex);
|
||||
if (!sched_debug_data.error)
|
||||
break;
|
||||
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
pr_info("end\n");
|
||||
}
|
||||
|
||||
static void init_debug_partition(void)
|
||||
{
|
||||
struct debug_reset_header init_reset_header;
|
||||
|
||||
pr_info("start\n");
|
||||
|
||||
/*++ add here need init data ++*/
|
||||
init_ap_health_data();
|
||||
/*-- add here need init data --*/
|
||||
|
||||
while (1) {
|
||||
mutex_lock(&debug_partition_mutex);
|
||||
|
||||
memset(&init_reset_header, 0,
|
||||
sizeof(struct debug_reset_header));
|
||||
init_reset_header.magic = DEBUG_PARTITION_MAGIC;
|
||||
|
||||
sched_debug_data.value = &init_reset_header;
|
||||
sched_debug_data.offset = SEC_DEBUG_RESET_HEADER_OFFSET;
|
||||
sched_debug_data.size = sizeof(struct debug_reset_header);
|
||||
sched_debug_data.direction = PARTITION_WR;
|
||||
|
||||
schedule_work(&sched_debug_data.debug_partition_work);
|
||||
wait_for_completion(&sched_debug_data.work);
|
||||
|
||||
mutex_unlock(&debug_partition_mutex);
|
||||
|
||||
if (!sched_debug_data.error)
|
||||
break;
|
||||
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
pr_info("end\n");
|
||||
}
|
||||
|
||||
static void check_magic_data(void)
|
||||
{
|
||||
static int checked_magic;
|
||||
struct debug_reset_header partition_header = {0,};
|
||||
|
||||
if (checked_magic)
|
||||
return;
|
||||
|
||||
pr_info("start\n");
|
||||
|
||||
while (1) {
|
||||
mutex_lock(&debug_partition_mutex);
|
||||
|
||||
sched_debug_data.value = &partition_header;
|
||||
sched_debug_data.offset = SEC_DEBUG_RESET_HEADER_OFFSET;
|
||||
sched_debug_data.size = sizeof(struct debug_reset_header);
|
||||
sched_debug_data.direction = PARTITION_RD;
|
||||
|
||||
schedule_work(&sched_debug_data.debug_partition_work);
|
||||
wait_for_completion(&sched_debug_data.work);
|
||||
|
||||
mutex_unlock(&debug_partition_mutex);
|
||||
|
||||
if (!sched_debug_data.error)
|
||||
break;
|
||||
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
if (partition_header.magic != DEBUG_PARTITION_MAGIC)
|
||||
init_debug_partition();
|
||||
|
||||
checked_magic = 1;
|
||||
|
||||
pr_info("end\n");
|
||||
}
|
||||
|
||||
#define READ_DEBUG_PARTITION(_value, _offset, _size) \
|
||||
{ \
|
||||
mutex_lock(&debug_partition_mutex); \
|
||||
sched_debug_data.value = _value; \
|
||||
sched_debug_data.offset = _offset; \
|
||||
sched_debug_data.size = _size; \
|
||||
sched_debug_data.direction = PARTITION_RD; \
|
||||
schedule_work(&sched_debug_data.debug_partition_work); \
|
||||
wait_for_completion(&sched_debug_data.work); \
|
||||
mutex_unlock(&debug_partition_mutex); \
|
||||
}
|
||||
|
||||
bool read_debug_partition(enum debug_partition_index index, void *value)
|
||||
{
|
||||
check_magic_data();
|
||||
|
||||
switch (index) {
|
||||
case debug_index_reset_ex_info:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_EXTRA_INFO_OFFSET,
|
||||
SEC_DEBUG_EX_INFO_SIZE);
|
||||
break;
|
||||
case debug_index_reset_klog_info:
|
||||
case debug_index_reset_summary_info:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_RESET_HEADER_OFFSET,
|
||||
sizeof(struct debug_reset_header));
|
||||
break;
|
||||
case debug_index_reset_summary:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_RESET_SUMMARY_OFFSET,
|
||||
SEC_DEBUG_RESET_SUMMARY_SIZE);
|
||||
break;
|
||||
case debug_index_reset_klog:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_RESET_KLOG_OFFSET,
|
||||
SEC_DEBUG_RESET_KLOG_SIZE);
|
||||
break;
|
||||
case debug_index_reset_tzlog:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_RESET_TZLOG_OFFSET,
|
||||
SEC_DEBUG_RESET_TZLOG_SIZE);
|
||||
break;
|
||||
case debug_index_ap_health:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_AP_HEALTH_OFFSET,
|
||||
SEC_DEBUG_AP_HEALTH_SIZE);
|
||||
break;
|
||||
case debug_index_reset_extrc_info:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_RESET_EXTRC_OFFSET,
|
||||
SEC_DEBUG_RESET_EXTRC_SIZE);
|
||||
break;
|
||||
case debug_index_lcd_debug_info:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_LCD_DEBUG_OFFSET,
|
||||
sizeof(struct lcd_debug_t));
|
||||
break;
|
||||
case debug_index_modem_info:
|
||||
READ_DEBUG_PARTITION(value,
|
||||
SEC_DEBUG_RESET_MODEM_OFFSET,
|
||||
sizeof(struct sec_debug_summary_data_modem));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(read_debug_partition);
|
||||
|
||||
bool write_debug_partition(enum debug_partition_index index, void *value)
|
||||
{
|
||||
check_magic_data();
|
||||
|
||||
switch (index) {
|
||||
case debug_index_reset_klog_info:
|
||||
case debug_index_reset_summary_info:
|
||||
mutex_lock(&debug_partition_mutex);
|
||||
sched_debug_data.value =
|
||||
(struct debug_reset_header *)value;
|
||||
sched_debug_data.offset = SEC_DEBUG_RESET_HEADER_OFFSET;
|
||||
sched_debug_data.size =
|
||||
sizeof(struct debug_reset_header);
|
||||
sched_debug_data.direction = PARTITION_WR;
|
||||
schedule_work(&sched_debug_data.debug_partition_work);
|
||||
wait_for_completion(&sched_debug_data.work);
|
||||
mutex_unlock(&debug_partition_mutex);
|
||||
break;
|
||||
case debug_index_reset_ex_info:
|
||||
case debug_index_reset_summary:
|
||||
// do nothing.
|
||||
break;
|
||||
case debug_index_lcd_debug_info:
|
||||
mutex_lock(&debug_partition_mutex);
|
||||
sched_debug_data.value = (struct lcd_debug_t *)value;
|
||||
sched_debug_data.offset = SEC_DEBUG_LCD_DEBUG_OFFSET;
|
||||
sched_debug_data.size = sizeof(struct lcd_debug_t);
|
||||
sched_debug_data.direction = PARTITION_WR;
|
||||
schedule_work(&sched_debug_data.debug_partition_work);
|
||||
wait_for_completion(&sched_debug_data.work);
|
||||
mutex_unlock(&debug_partition_mutex);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(write_debug_partition);
|
||||
|
||||
ap_health_t *ap_health_data_read(void)
|
||||
{
|
||||
if (!driver_initialized)
|
||||
return NULL;
|
||||
|
||||
if (ap_health_initialized)
|
||||
goto out;
|
||||
|
||||
if (in_interrupt()) {
|
||||
pr_info("skip read opt.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
read_debug_partition(debug_index_ap_health, (void *)&ap_health_data);
|
||||
|
||||
if (ap_health_data.header.magic != AP_HEALTH_MAGIC ||
|
||||
ap_health_data.header.version != AP_HEALTH_VER ||
|
||||
ap_health_data.header.size != sizeof(ap_health_t) ||
|
||||
ap_health_data.spare_magic1 != AP_HEALTH_MAGIC ||
|
||||
ap_health_data.spare_magic2 != AP_HEALTH_MAGIC ||
|
||||
ap_health_data.spare_magic3 != AP_HEALTH_MAGIC ||
|
||||
is_boot_recovery()) {
|
||||
init_ap_health_data();
|
||||
init_lcd_debug_data();
|
||||
}
|
||||
|
||||
ap_health_initialized = 1;
|
||||
out:
|
||||
return &ap_health_data;
|
||||
}
|
||||
EXPORT_SYMBOL(ap_health_data_read);
|
||||
|
||||
int ap_health_data_write(ap_health_t *data)
|
||||
{
|
||||
if (!driver_initialized || !data || !ap_health_initialized)
|
||||
return -ENODATA;
|
||||
|
||||
data->header.need_write++;
|
||||
|
||||
if (!in_panic)
|
||||
queue_delayed_work(dbg_part_wq, &ap_health_work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ap_health_data_write);
|
||||
|
||||
int dbg_partition_notifier_register(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(
|
||||
&dbg_partition_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(dbg_partition_notifier_register);
|
||||
|
||||
static void debug_partition_do_notify(struct work_struct *work)
|
||||
{
|
||||
blocking_notifier_call_chain(&dbg_partition_notifier_list,
|
||||
DBG_PART_DRV_INIT_DONE, NULL);
|
||||
}
|
||||
|
||||
static int dbg_partition_panic_prepare(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
in_panic = 1;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block dbg_partition_panic_notifier_block = {
|
||||
.notifier_call = dbg_partition_panic_prepare,
|
||||
};
|
||||
|
||||
static int __init sec_debug_partition_init(void)
|
||||
{
|
||||
pr_info("start\n");
|
||||
|
||||
sched_debug_data.offset = 0;
|
||||
sched_debug_data.direction = 0;
|
||||
sched_debug_data.size = 0;
|
||||
sched_debug_data.value = NULL;
|
||||
|
||||
init_completion(&sched_debug_data.work);
|
||||
INIT_WORK(&sched_debug_data.debug_partition_work,
|
||||
debug_partition_operation);
|
||||
INIT_DELAYED_WORK(&dbg_partition_notify_work,
|
||||
debug_partition_do_notify);
|
||||
INIT_DELAYED_WORK(&ap_health_work, ap_health_work_write_fn);
|
||||
|
||||
dbg_part_wq = create_singlethread_workqueue("glink_lbsrv");
|
||||
if (!dbg_part_wq) {
|
||||
pr_err("fail to create dbg_part_wq!\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&dbg_partition_panic_notifier_block);
|
||||
|
||||
driver_initialized = DRV_INITIALIZED;
|
||||
schedule_delayed_work(&dbg_partition_notify_work, 2 * HZ);
|
||||
pr_info("end\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit sec_debug_partition_exit(void)
|
||||
{
|
||||
driver_initialized = DRV_UNINITIALIZED;
|
||||
cancel_work_sync(&sched_debug_data.debug_partition_work);
|
||||
cancel_delayed_work_sync(&dbg_partition_notify_work);
|
||||
pr_info("exit\n");
|
||||
}
|
||||
|
||||
module_init(sec_debug_partition_init);
|
||||
module_exit(sec_debug_partition_exit);
|
||||
461
drivers/debug/sec_debug_sched_log.c
Normal file
461
drivers/debug/sec_debug_sched_log.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* drivers/debug/sec_debug_sched_log.c
|
||||
*
|
||||
* COPYRIGHT(C) 2017-2018 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
|
||||
#include "sec_debug_internal.h"
|
||||
|
||||
/* TODO: set 'NO_ATOMIC_IDX' only for internal testing purpose */
|
||||
#ifdef NO_ATOMIC_IDX
|
||||
#define sec_debug_idx_inc_return(__idx) (++(*(__idx)))
|
||||
#define sec_debug_idx_set(__idx, __v) { (*(__idx)) = __v; }
|
||||
#else
|
||||
#define sec_debug_idx_inc_return(__idx) atomic_inc_return((__idx))
|
||||
#define sec_debug_idx_set(__idx, __v) atomic_sec((__idx), (__v))
|
||||
#endif
|
||||
|
||||
struct sec_debug_log *secdbg_log;
|
||||
|
||||
phys_addr_t secdbg_paddr;
|
||||
size_t secdbg_size;
|
||||
|
||||
static int __init sec_dbg_setup(char *str)
|
||||
{
|
||||
size_t size = (size_t)memparse(str, &str);
|
||||
|
||||
pr_info("str=%s\n", str);
|
||||
|
||||
if (size /*&& (size == roundup_pow_of_two(size))*/ && (*str == '@')) {
|
||||
secdbg_paddr = (phys_addr_t)memparse(++str, NULL);
|
||||
secdbg_size = size;
|
||||
}
|
||||
|
||||
pr_info("secdbg_paddr = 0x%llx\n", (unsigned long long)secdbg_paddr);
|
||||
pr_info("secdbg_size = 0x%zx\n", secdbg_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
__setup("sec_dbg=", sec_dbg_setup);
|
||||
|
||||
/* save last_pet and last_ns with these nice functions */
|
||||
void sec_debug_save_last_pet(unsigned long long last_pet)
|
||||
{
|
||||
if (likely(secdbg_log))
|
||||
secdbg_log->last_pet = last_pet;
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_save_last_pet);
|
||||
|
||||
void sec_debug_save_last_ns(unsigned long long last_ns)
|
||||
{
|
||||
if (likely(secdbg_log))
|
||||
atomic64_set(&(secdbg_log->last_ns), last_ns);
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_save_last_ns);
|
||||
|
||||
static inline long get_switch_state(bool preempt, struct task_struct *p)
|
||||
{
|
||||
return preempt ? TASK_RUNNING | TASK_STATE_MAX : p->state;
|
||||
}
|
||||
|
||||
static __always_inline void __sec_debug_task_sched_log(int cpu, bool preempt,
|
||||
struct task_struct *task, struct task_struct *prev,
|
||||
char *msg)
|
||||
{
|
||||
struct sched_log *sched_log;
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
if (unlikely(!task && !msg))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_sched[cpu]))
|
||||
& (SCHED_LOG_MAX - 1);
|
||||
sched_log = &secdbg_log->sched[cpu][i];
|
||||
|
||||
sched_log->time = cpu_clock(cpu);
|
||||
if (task) {
|
||||
strlcpy(sched_log->comm, task->comm, sizeof(sched_log->comm));
|
||||
sched_log->pid = task->pid;
|
||||
sched_log->pTask = task;
|
||||
sched_log->prio = task->prio;
|
||||
strlcpy(sched_log->prev_comm, prev->comm,
|
||||
sizeof(sched_log->prev_comm));
|
||||
|
||||
sched_log->prev_pid = prev->pid;
|
||||
sched_log->prev_state = get_switch_state(preempt, prev);
|
||||
sched_log->prev_prio = prev->prio;
|
||||
} else {
|
||||
strlcpy(sched_log->comm, msg, sizeof(sched_log->comm));
|
||||
sched_log->pid = current->pid;
|
||||
sched_log->pTask = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void sec_debug_irq_enterexit_log(unsigned int irq, u64 start_time)
|
||||
{
|
||||
struct irq_exit_log *irq_exit_log;
|
||||
int cpu = smp_processor_id();
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_irq_exit[cpu]))
|
||||
& (SCHED_LOG_MAX - 1);
|
||||
irq_exit_log = &secdbg_log->irq_exit[cpu][i];
|
||||
|
||||
irq_exit_log->time = start_time;
|
||||
irq_exit_log->end_time = cpu_clock(cpu);
|
||||
irq_exit_log->irq = irq;
|
||||
irq_exit_log->elapsed_time = irq_exit_log->end_time - start_time;
|
||||
irq_exit_log->pid = current->pid;
|
||||
}
|
||||
|
||||
void sec_debug_task_sched_log_short_msg(char *msg)
|
||||
{
|
||||
__sec_debug_task_sched_log(raw_smp_processor_id(),
|
||||
false, NULL, NULL, msg);
|
||||
}
|
||||
|
||||
void sec_debug_task_sched_log(int cpu, bool preempt,
|
||||
struct task_struct *task, struct task_struct *prev)
|
||||
{
|
||||
__sec_debug_task_sched_log(cpu, false, task, prev, NULL);
|
||||
}
|
||||
|
||||
void sec_debug_timer_log(unsigned int type, int int_lock, void *fn)
|
||||
{
|
||||
struct timer_log *timer_log;
|
||||
int cpu = smp_processor_id();
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_timer[cpu]))
|
||||
& (SCHED_LOG_MAX - 1);
|
||||
timer_log = &secdbg_log->timer_log[cpu][i];
|
||||
|
||||
timer_log->time = cpu_clock(cpu);
|
||||
timer_log->type = type;
|
||||
timer_log->int_lock = int_lock;
|
||||
timer_log->fn = (void *)fn;
|
||||
timer_log->pid = current->pid;
|
||||
}
|
||||
|
||||
void sec_debug_secure_log(u32 svc_id, u32 cmd_id)
|
||||
{
|
||||
struct secure_log *secure_log;
|
||||
static DEFINE_SPINLOCK(secdbg_securelock);
|
||||
unsigned long flags;
|
||||
int cpu;
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&secdbg_securelock, flags);
|
||||
|
||||
cpu = smp_processor_id();
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_secure[cpu]))
|
||||
& (TZ_LOG_MAX - 1);
|
||||
secure_log = &secdbg_log->secure[cpu][i];
|
||||
|
||||
secure_log->time = cpu_clock(cpu);
|
||||
secure_log->svc_id = svc_id;
|
||||
secure_log->cmd_id = cmd_id;
|
||||
secure_log->pid = current->pid;
|
||||
|
||||
spin_unlock_irqrestore(&secdbg_securelock, flags);
|
||||
}
|
||||
|
||||
void sec_debug_irq_sched_log(unsigned int irq, void *fn,
|
||||
char *name, unsigned int en)
|
||||
{
|
||||
struct irq_log *irq_log;
|
||||
int cpu = smp_processor_id();
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_irq[cpu]))
|
||||
& (SCHED_LOG_MAX - 1);
|
||||
irq_log = &secdbg_log->irq[cpu][i];
|
||||
|
||||
irq_log->time = cpu_clock(cpu);
|
||||
irq_log->irq = irq;
|
||||
irq_log->fn = (void *)fn;
|
||||
irq_log->name = name;
|
||||
irq_log->en = irqs_disabled();
|
||||
irq_log->preempt_count = preempt_count();
|
||||
irq_log->context = &cpu;
|
||||
irq_log->pid = current->pid;
|
||||
irq_log->entry_exit = en;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
|
||||
int sec_debug_msg_log(void *caller, const char *fmt, ...)
|
||||
{
|
||||
struct secmsg_log *secmsg_log;
|
||||
int cpu = smp_processor_id();
|
||||
int r;
|
||||
int i;
|
||||
va_list args;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return 0;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_secmsg[cpu]))
|
||||
& (MSG_LOG_MAX - 1);
|
||||
secmsg_log = &secdbg_log->secmsg[cpu][i];
|
||||
|
||||
secmsg_log->time = cpu_clock(cpu);
|
||||
va_start(args, fmt);
|
||||
r = vsnprintf(secmsg_log->msg, sizeof(secmsg_log->msg), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
secmsg_log->caller0 = __builtin_return_address(0);
|
||||
secmsg_log->caller1 = caller;
|
||||
secmsg_log->task = current->comm;
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif /* CONFIG_SEC_DEBUG_MSG_LOG */
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_AVC_LOG
|
||||
int sec_debug_avc_log(const char *fmt, ...)
|
||||
{
|
||||
struct secavc_log *secavc_log;
|
||||
int cpu = smp_processor_id();
|
||||
int r;
|
||||
int i;
|
||||
va_list args;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return 0;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_secavc[cpu]))
|
||||
& (AVC_LOG_MAX - 1);
|
||||
secavc_log = &secdbg_log->secavc[cpu][i];
|
||||
|
||||
va_start(args, fmt);
|
||||
r = vsnprintf(secavc_log->msg, sizeof(secavc_log->msg), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif /* CONFIG_SEC_DEBUG_AVC_LOG */
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_DCVS_LOG
|
||||
void sec_debug_dcvs_log(int cpu_no, unsigned int prev_freq,
|
||||
unsigned int new_freq)
|
||||
{
|
||||
struct dcvs_debug *dcvs_debug;
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->dcvs_log_idx[cpu_no]))
|
||||
& (DCVS_LOG_MAX - 1);
|
||||
dcvs_debug = &secdbg_log->dcvs_log[cpu_no][i];
|
||||
|
||||
dcvs_debug->cpu_no = cpu_no;
|
||||
dcvs_debug->prev_freq = prev_freq;
|
||||
dcvs_debug->new_freq = new_freq;
|
||||
dcvs_debug->time = cpu_clock(cpu_no);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_FUELGAUGE_LOG
|
||||
void sec_debug_fuelgauge_log(unsigned int voltage, unsigned short soc,
|
||||
unsigned short charging_status)
|
||||
{
|
||||
struct fuelgauge_debug *fuelgauge_debug;
|
||||
int cpu = smp_processor_id();
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->fg_log_idx))
|
||||
& (FG_LOG_MAX - 1);
|
||||
fuelgauge_debug = &secdbg_log->fg_log[i];
|
||||
|
||||
fuelgauge_debug->time = cpu_clock(cpu);
|
||||
fuelgauge_debug->voltage = voltage;
|
||||
fuelgauge_debug->soc = soc;
|
||||
fuelgauge_debug->charging_status = charging_status;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_POWER_LOG
|
||||
void sec_debug_cpu_lpm_log(int cpu, unsigned int index,
|
||||
bool success, int entry_exit)
|
||||
{
|
||||
struct power_log *power_log;
|
||||
int i;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_power[cpu]))
|
||||
& (POWER_LOG_MAX - 1);
|
||||
power_log = &secdbg_log->pwr_log[cpu][i];
|
||||
|
||||
power_log->time = cpu_clock(cpu);
|
||||
power_log->pid = current->pid;
|
||||
power_log->type = CPU_POWER_TYPE;
|
||||
|
||||
power_log->cpu.index = index;
|
||||
power_log->cpu.success = success;
|
||||
power_log->cpu.entry_exit = entry_exit;
|
||||
}
|
||||
|
||||
void sec_debug_cluster_lpm_log(const char *name, int index,
|
||||
unsigned long sync_cpus, unsigned long child_cpus,
|
||||
bool from_idle, int entry_exit)
|
||||
{
|
||||
struct power_log *power_log;
|
||||
int i;
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_power[cpu]))
|
||||
& (POWER_LOG_MAX - 1);
|
||||
power_log = &secdbg_log->pwr_log[cpu][i];
|
||||
|
||||
power_log->time = cpu_clock(cpu);
|
||||
power_log->pid = current->pid;
|
||||
power_log->type = CLUSTER_POWER_TYPE;
|
||||
|
||||
power_log->cluster.name = (char *) name;
|
||||
power_log->cluster.index = index;
|
||||
power_log->cluster.sync_cpus = sync_cpus;
|
||||
power_log->cluster.child_cpus = child_cpus;
|
||||
power_log->cluster.from_idle = from_idle;
|
||||
power_log->cluster.entry_exit = entry_exit;
|
||||
}
|
||||
|
||||
void sec_debug_clock_log(const char *name, unsigned int state,
|
||||
unsigned int cpu_id, int complete)
|
||||
{
|
||||
struct power_log *power_log;
|
||||
int i;
|
||||
int cpu = cpu_id;
|
||||
|
||||
if (unlikely(!secdbg_log))
|
||||
return;
|
||||
|
||||
i = sec_debug_idx_inc_return(&(secdbg_log->idx_power[cpu_id]))
|
||||
& (POWER_LOG_MAX - 1);
|
||||
power_log = &secdbg_log->pwr_log[cpu][i];
|
||||
|
||||
power_log->time = cpu_clock(cpu_id);
|
||||
power_log->pid = current->pid;
|
||||
power_log->type = CLOCK_RATE_TYPE;
|
||||
|
||||
power_log->clk_rate.name = (char *)name;
|
||||
power_log->clk_rate.state = state;
|
||||
power_log->clk_rate.cpu_id = cpu_id;
|
||||
power_log->clk_rate.complete = complete;
|
||||
}
|
||||
#endif /* CONFIG_SEC_DEBUG_POWER_LOG */
|
||||
|
||||
static int __init sec_debug_sched_log_init(void)
|
||||
{
|
||||
size_t i;
|
||||
struct sec_debug_log *vaddr;
|
||||
size_t size;
|
||||
|
||||
if (secdbg_paddr == 0 || secdbg_size == 0) {
|
||||
pr_info("sec debug buffer not provided. Using kmalloc..\n");
|
||||
size = sizeof(struct sec_debug_log);
|
||||
vaddr = kzalloc(size, GFP_KERNEL);
|
||||
} else {
|
||||
size = secdbg_size;
|
||||
vaddr = ioremap_wc(secdbg_paddr, secdbg_size);
|
||||
}
|
||||
|
||||
pr_info("vaddr=0x%p paddr=0x%llx size=0x%zx sizeof(struct sec_debug_log)=0x%zx\n",
|
||||
vaddr, (uint64_t)secdbg_paddr,
|
||||
secdbg_size, sizeof(struct sec_debug_log));
|
||||
|
||||
if ((!vaddr) || (sizeof(struct sec_debug_log) > size)) {
|
||||
pr_err("ERROR! init failed!\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
memset_io(vaddr->sched, 0x0, sizeof(vaddr->sched));
|
||||
memset_io(vaddr->irq, 0x0, sizeof(vaddr->irq));
|
||||
memset_io(vaddr->irq_exit, 0x0, sizeof(vaddr->irq_exit));
|
||||
memset_io(vaddr->timer_log, 0x0, sizeof(vaddr->timer_log));
|
||||
memset_io(vaddr->secure, 0x0, sizeof(vaddr->secure));
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
|
||||
memset_io(vaddr->secmsg, 0x0, sizeof(vaddr->secmsg));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_AVC_LOG
|
||||
memset_io(vaddr->secavc, 0x0, sizeof(vaddr->secavc));
|
||||
#endif
|
||||
|
||||
for (i = 0; i < num_possible_cpus(); i++) {
|
||||
sec_debug_idx_set(&(vaddr->idx_sched[i]), -1);
|
||||
sec_debug_idx_set(&(vaddr->idx_irq[i]), -1);
|
||||
sec_debug_idx_set(&(vaddr->idx_secure[i]), -1);
|
||||
sec_debug_idx_set(&(vaddr->idx_irq_exit[i]), -1);
|
||||
sec_debug_idx_set(&(vaddr->idx_timer[i]), -1);
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
|
||||
sec_debug_idx_set(&(vaddr->idx_secmsg[i]), -1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_AVC_LOG
|
||||
sec_debug_idx_set(&(vaddr->idx_secavc[i]), -1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_DCVS_LOG
|
||||
sec_debug_idx_set(&(vaddr->dcvs_log_idx[i]), -1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_POWER_LOG
|
||||
sec_debug_idx_set(&(vaddr->idx_power[i]), -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_FUELGAUGE_LOG
|
||||
sec_debug_idx_set(&(vaddr->fg_log_idx), -1);
|
||||
#endif
|
||||
|
||||
secdbg_log = vaddr;
|
||||
|
||||
pr_info("init done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall_sync(sec_debug_sched_log_init);
|
||||
629
drivers/debug/sec_debug_summary.c
Normal file
629
drivers/debug/sec_debug_summary.c
Normal file
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
* drivers/debug/sec_debug_summary.c
|
||||
*
|
||||
* driver supporting debug functions for Samsung device
|
||||
*
|
||||
* COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <soc/qcom/smem.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/memory.h>
|
||||
|
||||
#include <linux/sec_bsp.h>
|
||||
#include <linux/sec_debug.h>
|
||||
#include <linux/sec_debug_user_reset.h>
|
||||
#include <linux/sec_debug_summary.h>
|
||||
|
||||
#include "sec_debug_internal.h"
|
||||
|
||||
struct sec_debug_summary *secdbg_summary;
|
||||
struct sec_debug_summary_data_apss *secdbg_apss;
|
||||
static char build_root[] = __FILE__;
|
||||
|
||||
static uint32_t tzapps_start_addr;
|
||||
static uint32_t tzapps_size;
|
||||
|
||||
char *sec_debug_arch_desc;
|
||||
|
||||
static unsigned long cpu_buf_vaddr;
|
||||
static unsigned long cpu_buf_paddr;
|
||||
static unsigned long cpu_data_vaddr;
|
||||
static unsigned long cpu_data_paddr;
|
||||
static unsigned long max_cpu_ctx_size;
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
||||
unsigned int cpu_frequency[CONFIG_NR_CPUS];
|
||||
unsigned int cpu_volt[CONFIG_NR_CPUS];
|
||||
char cpu_state[CONFIG_NR_CPUS][VAR_NAME_MAX];
|
||||
EXPORT_SYMBOL(cpu_frequency);
|
||||
EXPORT_SYMBOL(cpu_volt);
|
||||
EXPORT_SYMBOL(cpu_state);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
#define ARM_PT_REG_PC pc
|
||||
#define ARM_PT_REG_LR regs[30]
|
||||
#else
|
||||
#define ARM_PT_REG_PC ARM_pc
|
||||
#define ARM_PT_REG_LR ARM_lr
|
||||
#endif
|
||||
|
||||
int sec_debug_save_die_info(const char *str, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_USER_RESET_DEBUG
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
#endif
|
||||
if (!secdbg_apss)
|
||||
return -ENOMEM;
|
||||
snprintf(secdbg_apss->excp.pc_sym, sizeof(secdbg_apss->excp.pc_sym),
|
||||
"%pS", (void *)regs->ARM_PT_REG_PC);
|
||||
snprintf(secdbg_apss->excp.lr_sym, sizeof(secdbg_apss->excp.lr_sym),
|
||||
"%pS", (void *)regs->ARM_PT_REG_LR);
|
||||
|
||||
#ifdef CONFIG_USER_RESET_DEBUG
|
||||
sec_debug_store_extc_idx(false);
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
if (p_ex_info->cpu == -1) {
|
||||
int slen;
|
||||
char *msg;
|
||||
|
||||
p_ex_info->cpu = smp_processor_id();
|
||||
snprintf(p_ex_info->task_name,
|
||||
sizeof(p_ex_info->task_name), "%s", current->comm);
|
||||
p_ex_info->ktime = local_clock();
|
||||
snprintf(p_ex_info->pc,
|
||||
sizeof(p_ex_info->pc), "%pS", (void *)regs->ARM_PT_REG_PC);
|
||||
snprintf(p_ex_info->lr,
|
||||
sizeof(p_ex_info->lr), "%pS", (void *)regs->ARM_PT_REG_LR);
|
||||
slen = snprintf(p_ex_info->panic_buf,
|
||||
sizeof(p_ex_info->panic_buf), "%s", str);
|
||||
|
||||
msg = p_ex_info->panic_buf;
|
||||
|
||||
if ((slen >= 1) && (msg[slen-1] == '\n'))
|
||||
msg[slen-1] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sec_debug_save_panic_info(const char *str, unsigned long caller)
|
||||
{
|
||||
#ifdef CONFIG_USER_RESET_DEBUG
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
#endif
|
||||
if (!secdbg_apss)
|
||||
return -ENOMEM;
|
||||
snprintf(secdbg_apss->excp.panic_caller,
|
||||
sizeof(secdbg_apss->excp.panic_caller), "%pS", (void *)caller);
|
||||
snprintf(secdbg_apss->excp.panic_msg,
|
||||
sizeof(secdbg_apss->excp.panic_msg), "%s", str);
|
||||
snprintf(secdbg_apss->excp.thread,
|
||||
sizeof(secdbg_apss->excp.thread), "%s:%d", current->comm,
|
||||
task_pid_nr(current));
|
||||
|
||||
#ifdef CONFIG_USER_RESET_DEBUG
|
||||
sec_debug_store_extc_idx(false);
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
if (p_ex_info->cpu == -1) {
|
||||
int slen;
|
||||
char *msg;
|
||||
|
||||
p_ex_info->cpu = smp_processor_id();
|
||||
snprintf(p_ex_info->task_name,
|
||||
sizeof(p_ex_info->task_name), "%s", current->comm);
|
||||
p_ex_info->ktime = local_clock();
|
||||
snprintf(p_ex_info->pc,
|
||||
sizeof(p_ex_info->pc), "%pS", (void *)(caller-0x4));
|
||||
snprintf(p_ex_info->lr,
|
||||
sizeof(p_ex_info->lr), "%pS", (void *)caller);
|
||||
slen = snprintf(p_ex_info->panic_buf,
|
||||
sizeof(p_ex_info->panic_buf), "%s", str);
|
||||
|
||||
msg = p_ex_info->panic_buf;
|
||||
|
||||
if ((slen >= 1) && (msg[slen-1] == '\n'))
|
||||
msg[slen-1] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sec_debug_summary_add_infomon(char *name, unsigned int size, phys_addr_t pa)
|
||||
{
|
||||
if (!secdbg_apss)
|
||||
return -ENOMEM;
|
||||
|
||||
if (secdbg_apss->info_mon.idx >= ARRAY_SIZE(secdbg_apss->info_mon.var))
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].name,
|
||||
name, sizeof(secdbg_apss->info_mon.var[0].name));
|
||||
secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].sizeof_type
|
||||
= size;
|
||||
secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].var_paddr = pa;
|
||||
|
||||
secdbg_apss->info_mon.idx++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sec_debug_summary_add_varmon(char *name, unsigned int size, phys_addr_t pa)
|
||||
{
|
||||
if (!secdbg_apss)
|
||||
return -ENOMEM;
|
||||
|
||||
if (secdbg_apss->var_mon.idx >= ARRAY_SIZE(secdbg_apss->var_mon.var))
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].name, name,
|
||||
sizeof(secdbg_apss->var_mon.var[0].name));
|
||||
secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].sizeof_type = size;
|
||||
secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].var_paddr = pa;
|
||||
|
||||
secdbg_apss->var_mon.idx++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_MDM_FILE_INFO
|
||||
void sec_set_mdm_summary_info(char *str_buf)
|
||||
{
|
||||
snprintf(secdbg_apss->mdmerr_info,
|
||||
sizeof(secdbg_apss->mdmerr_info), "%s", str_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ___build_root_init(char *str)
|
||||
{
|
||||
char *st, *ed;
|
||||
int len;
|
||||
ed = strstr(str, "/android/kernel");
|
||||
if (!ed || ed == str)
|
||||
return -1;
|
||||
*ed = '\0';
|
||||
st = strrchr(str, '/');
|
||||
if (!st)
|
||||
return -1;
|
||||
st++;
|
||||
len = (unsigned long)ed - (unsigned long)st + 1;
|
||||
memmove(str, st, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
||||
void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value)
|
||||
{
|
||||
if (SAVE_FREQ == flag)
|
||||
cpu_frequency[cpu] = value;
|
||||
else if (SAVE_VOLT == flag)
|
||||
cpu_volt[cpu] = (unsigned int)value;
|
||||
}
|
||||
#else
|
||||
void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void sec_debug_secure_app_addr_size(uint32_t addr, uint32_t size)
|
||||
{
|
||||
tzapps_start_addr = addr;
|
||||
tzapps_size = size;
|
||||
}
|
||||
|
||||
static int __init _set_kconst(struct sec_debug_summary_data_apss *p)
|
||||
{
|
||||
p->kconst.nr_cpus = NR_CPUS;
|
||||
p->kconst.per_cpu_offset.pa = virt_to_phys(__per_cpu_offset);
|
||||
p->kconst.per_cpu_offset.size = sizeof(__per_cpu_offset[0]);
|
||||
p->kconst.per_cpu_offset.count = sizeof(__per_cpu_offset) /
|
||||
sizeof(__per_cpu_offset[0]);
|
||||
p->kconst.phys_offset = PHYS_OFFSET;
|
||||
p->kconst.page_offset = PAGE_OFFSET;
|
||||
p->kconst.va_bits = VA_BITS;
|
||||
p->kconst.kimage_vaddr = kimage_vaddr;
|
||||
p->kconst.kimage_voffset = kimage_voffset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init summary_init_infomon(void)
|
||||
{
|
||||
if (___build_root_init(build_root) == 0)
|
||||
ADD_STR_TO_INFOMON(build_root);
|
||||
ADD_STR_TO_INFOMON(linux_banner);
|
||||
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
|
||||
sec_debug_summary_add_infomon("Kernel cmdline", -1,
|
||||
__pa(erased_command_line));
|
||||
#else
|
||||
sec_debug_summary_add_infomon("Kernel cmdline", -1,
|
||||
__pa(saved_command_line));
|
||||
#endif
|
||||
sec_debug_summary_add_infomon("Hardware name", -1,
|
||||
__pa(sec_debug_arch_desc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init summary_init_varmon(void)
|
||||
{
|
||||
uint64_t last_pet_paddr = 0;
|
||||
uint64_t last_ns_paddr = 0;
|
||||
|
||||
/* save paddrs of last_pet und last_ns */
|
||||
if (secdbg_paddr && secdbg_log) {
|
||||
last_pet_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, last_pet);
|
||||
last_ns_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, last_ns);
|
||||
sec_debug_summary_add_varmon("last_pet",
|
||||
sizeof((secdbg_log->last_pet)), last_pet_paddr);
|
||||
sec_debug_summary_add_varmon("last_ns",
|
||||
sizeof((secdbg_log->last_ns.counter)),
|
||||
last_ns_paddr);
|
||||
} else
|
||||
pr_emerg("**** secdbg_log or secdbg_paddr is not initialized ****\n");
|
||||
|
||||
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
|
||||
ADD_VAR_TO_VARMON(boot_reason);
|
||||
ADD_VAR_TO_VARMON(cold_boot);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
||||
for (i = 0; i < CONFIG_NR_CPUS; i++) {
|
||||
ADD_STR_ARRAY_TO_VARMON(cpu_state[i], i, CPU_STAT_CORE);
|
||||
ADD_ARRAY_TO_VARMON(cpu_frequency[i], i, CPU_FREQ_CORE);
|
||||
ADD_ARRAY_TO_VARMON(cpu_volt[i], i, CPU_VOLT_CORE);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init summary_init_sched_log(struct sec_debug_summary_data_apss *p)
|
||||
{
|
||||
if (!secdbg_paddr)
|
||||
return -ENOMEM;
|
||||
|
||||
p->sched_log.sched_idx_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, idx_sched);
|
||||
p->sched_log.sched_buf_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, sched);
|
||||
p->sched_log.sched_struct_sz =
|
||||
sizeof(struct sched_log);
|
||||
p->sched_log.sched_array_cnt = SCHED_LOG_MAX;
|
||||
|
||||
p->sched_log.irq_idx_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, idx_irq);
|
||||
p->sched_log.irq_buf_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, irq);
|
||||
p->sched_log.irq_struct_sz =
|
||||
sizeof(struct irq_log);
|
||||
p->sched_log.irq_array_cnt = SCHED_LOG_MAX;
|
||||
|
||||
p->sched_log.secure_idx_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, idx_secure);
|
||||
p->sched_log.secure_buf_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, secure);
|
||||
p->sched_log.secure_struct_sz =
|
||||
sizeof(struct secure_log);
|
||||
p->sched_log.secure_array_cnt = TZ_LOG_MAX;
|
||||
|
||||
p->sched_log.irq_exit_idx_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, idx_irq_exit);
|
||||
p->sched_log.irq_exit_buf_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, irq_exit);
|
||||
p->sched_log.irq_exit_struct_sz =
|
||||
sizeof(struct irq_exit_log);
|
||||
p->sched_log.irq_exit_array_cnt = SCHED_LOG_MAX;
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
|
||||
p->sched_log.msglog_idx_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, idx_secmsg);
|
||||
p->sched_log.msglog_buf_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, secmsg);
|
||||
p->sched_log.msglog_struct_sz =
|
||||
sizeof(struct secmsg_log);
|
||||
p->sched_log.msglog_array_cnt = MSG_LOG_MAX;
|
||||
#else
|
||||
p->sched_log.msglog_idx_paddr = 0;
|
||||
p->sched_log.msglog_buf_paddr = 0;
|
||||
p->sched_log.msglog_struct_sz = 0;
|
||||
p->sched_log.msglog_array_cnt = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long get_wdog_regsave_paddr(void)
|
||||
{
|
||||
return __pa(&cpu_buf_paddr);
|
||||
}
|
||||
unsigned int get_last_pet_paddr(void)
|
||||
{
|
||||
#if 0 // MUST BE CHECK
|
||||
return virt_to_phys(&wdog_dd->last_pet);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sec_debug_summary_bark_dump(unsigned long cpu_data,
|
||||
unsigned long pcpu_data, unsigned long cpu_buf,
|
||||
unsigned long pcpu_buf, unsigned long cpu_ctx_size)
|
||||
{
|
||||
cpu_data_vaddr = cpu_data;
|
||||
cpu_data_paddr = pcpu_data;
|
||||
cpu_buf_vaddr = cpu_buf;
|
||||
cpu_buf_paddr = pcpu_buf;
|
||||
max_cpu_ctx_size = cpu_ctx_size;
|
||||
}
|
||||
|
||||
static int sec_debug_summary_set_msm_dump_info(
|
||||
struct sec_debug_summary_data_apss *apss)
|
||||
{
|
||||
apss->cpu_reg.msm_dump_info.cpu_data_paddr = cpu_data_paddr;
|
||||
apss->cpu_reg.msm_dump_info.cpu_buf_paddr = cpu_buf_paddr;
|
||||
apss->cpu_reg.msm_dump_info.cpu_ctx_size = max_cpu_ctx_size;
|
||||
apss->cpu_reg.msm_dump_info.offset = 0x10;
|
||||
|
||||
pr_info("cpu_data_paddr = 0x%llx\n",
|
||||
apss->cpu_reg.msm_dump_info.cpu_data_paddr);
|
||||
pr_info("cpu_buf_paddr = 0x%llx\n",
|
||||
apss->cpu_reg.msm_dump_info.cpu_buf_paddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init summary_init_core_reg(struct sec_debug_summary_data_apss *p)
|
||||
{
|
||||
/* setup sec debug core reg info */
|
||||
p->cpu_reg.sec_debug_core_reg_paddr = virt_to_phys(&sec_debug_core_reg);
|
||||
|
||||
pr_info("sec_debug_core_reg_paddr = 0x%llx\n",
|
||||
p->cpu_reg.sec_debug_core_reg_paddr);
|
||||
|
||||
#ifdef CONFIG_QCOM_MEMORY_DUMP_V2
|
||||
/* setup qc core reg info */
|
||||
sec_debug_summary_set_msm_dump_info(p);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init summary_init_avc_log(struct sec_debug_summary_data_apss *p)
|
||||
{
|
||||
if (!secdbg_paddr)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_SEC_DEBUG_AVC_LOG
|
||||
p->avc_log.secavc_idx_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, idx_secavc);
|
||||
p->avc_log.secavc_buf_paddr = secdbg_paddr +
|
||||
offsetof(struct sec_debug_log, secavc);
|
||||
p->avc_log.secavc_struct_sz =
|
||||
sizeof(struct secavc_log);
|
||||
p->avc_log.secavc_array_cnt = AVC_LOG_MAX;
|
||||
#else
|
||||
p->avc_log.secavc_idx_paddr = 0;
|
||||
p->avc_log.secavc_buf_paddr = 0;
|
||||
p->avc_log.secavc_struct_sz = 0;
|
||||
p->avc_log.secavc_array_cnt = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sec_debug_is_modem_separate_debug_ssr(void)
|
||||
{
|
||||
return secdbg_summary->priv.modem.seperate_debug;
|
||||
}
|
||||
|
||||
#define SET_MEMBER_TYPE_INFO(PTR, TYPE, MEMBER) \
|
||||
{ \
|
||||
(PTR)->size = sizeof(((TYPE *)0)->MEMBER); \
|
||||
(PTR)->offset = offsetof(TYPE, MEMBER); \
|
||||
}
|
||||
|
||||
int summary_set_task_info(struct sec_debug_summary_data_apss *apss)
|
||||
{
|
||||
extern struct task_struct init_task;
|
||||
|
||||
apss->task.stack_size = THREAD_SIZE;
|
||||
apss->task.start_sp = THREAD_START_SP;
|
||||
apss->task.irq_stack.pcpu_stack = (uint64_t)&irq_stack;
|
||||
apss->task.irq_stack.size = IRQ_STACK_SIZE;
|
||||
apss->task.irq_stack.start_sp = IRQ_STACK_START_SP;
|
||||
|
||||
apss->task.ti.struct_size = sizeof(struct thread_info);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ti.flags, struct thread_info, flags);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.cpu, struct task_struct, cpu);
|
||||
|
||||
apss->task.ts.struct_size = sizeof(struct task_struct);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.state, struct task_struct, state);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.exit_state, struct task_struct,
|
||||
exit_state);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.stack, struct task_struct, stack);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.flags, struct task_struct, flags);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.on_cpu, struct task_struct, on_cpu);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.pid, struct task_struct, pid);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.comm, struct task_struct, comm);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.tasks_next, struct task_struct,
|
||||
tasks.next);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.thread_group_next,
|
||||
struct task_struct, thread_group.next);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.fp, struct task_struct,
|
||||
thread.cpu_context.fp);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.sp, struct task_struct,
|
||||
thread.cpu_context.sp);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.pc, struct task_struct,
|
||||
thread.cpu_context.pc);
|
||||
|
||||
#ifdef CONFIG_SCHED_INFO
|
||||
/* sched_info */
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__pcount,
|
||||
struct task_struct, sched_info.pcount);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__run_delay,
|
||||
struct task_struct,
|
||||
sched_info.run_delay);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__last_arrival,
|
||||
struct task_struct,
|
||||
sched_info.last_arrival);
|
||||
SET_MEMBER_TYPE_INFO(&apss->task.ts.sched_info__last_queued,
|
||||
struct task_struct,
|
||||
sched_info.last_queued);
|
||||
#endif
|
||||
|
||||
apss->task.init_task = (uint64_t)&init_task;
|
||||
apss->task.ropp.magic = 0x0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_PM
|
||||
void summary_set_lpm_info_cci(uint64_t paddr)
|
||||
{
|
||||
if (secdbg_apss) {
|
||||
pr_info("%s : 0x%llx\n", __func__, paddr);
|
||||
secdbg_apss->aplpm.p_cci = paddr;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void summary_set_lpm_info_cci(uint64_t phy_addr)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void * sec_debug_summary_get_modem(void)
|
||||
{
|
||||
if (secdbg_summary) {
|
||||
return (void *)&secdbg_summary->priv.modem;
|
||||
} else {
|
||||
pr_info("%s : secdbg_summary is null.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int __init sec_debug_summary_init(void)
|
||||
{
|
||||
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
||||
short i;
|
||||
#endif
|
||||
|
||||
pr_info("SMEM_ID_VENDOR2=0x%x size=0x%lx\n",
|
||||
(unsigned int)SMEM_ID_VENDOR2,
|
||||
sizeof(struct sec_debug_summary));
|
||||
|
||||
/* set summary address in smem for other subsystems to see */
|
||||
secdbg_summary = (struct sec_debug_summary *)smem_alloc(
|
||||
SMEM_ID_VENDOR2,
|
||||
sizeof(struct sec_debug_summary),
|
||||
0,
|
||||
SMEM_ANY_HOST_FLAG);
|
||||
|
||||
if (secdbg_summary == NULL) {
|
||||
pr_info("smem alloc failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(secdbg_summary, 0, (unsigned long)sizeof(secdbg_summary));
|
||||
|
||||
secdbg_summary->secure_app_start_addr = tzapps_start_addr;
|
||||
secdbg_summary->secure_app_size = tzapps_size;
|
||||
|
||||
secdbg_apss = &secdbg_summary->priv.apss;
|
||||
|
||||
secdbg_summary->apss = (struct sec_debug_summary_data_apss *)
|
||||
(smem_virt_to_phys(&secdbg_summary->priv.apss)&0xFFFFFFFF);
|
||||
secdbg_summary->rpm = (struct sec_debug_summary_data *)
|
||||
(smem_virt_to_phys(&secdbg_summary->priv.rpm)&0xFFFFFFFF);
|
||||
secdbg_summary->modem = (struct sec_debug_summary_data_modem *)
|
||||
(smem_virt_to_phys(&secdbg_summary->priv.modem)&0xFFFFFFFF);
|
||||
secdbg_summary->dsps = (struct sec_debug_summary_data *)
|
||||
(smem_virt_to_phys(&secdbg_summary->priv.dsps)&0xFFFFFFFF);
|
||||
|
||||
pr_info("apss(%lx) rpm(%lx) modem(%lx) dsps(%lx)\n",
|
||||
(unsigned long)secdbg_summary->apss,
|
||||
(unsigned long)secdbg_summary->rpm,
|
||||
(unsigned long)secdbg_summary->modem,
|
||||
(unsigned long)secdbg_summary->dsps);
|
||||
|
||||
|
||||
strlcpy(secdbg_apss->name, "APSS", sizeof(secdbg_apss->name) + 1);
|
||||
strlcpy(secdbg_apss->state, "Init", sizeof(secdbg_apss->state) + 1);
|
||||
secdbg_apss->nr_cpus = CONFIG_NR_CPUS;
|
||||
|
||||
sec_debug_summary_set_kloginfo(&secdbg_apss->log.first_idx_paddr,
|
||||
&secdbg_apss->log.next_idx_paddr,
|
||||
&secdbg_apss->log.log_paddr, &secdbg_apss->log.size_paddr);
|
||||
|
||||
secdbg_apss->tz_core_dump =
|
||||
(struct msm_dump_data **)get_wdog_regsave_paddr();
|
||||
|
||||
summary_init_infomon();
|
||||
|
||||
summary_init_varmon();
|
||||
|
||||
summary_init_sched_log(secdbg_apss);
|
||||
|
||||
summary_init_core_reg(secdbg_apss);
|
||||
|
||||
summary_init_avc_log(secdbg_apss);
|
||||
|
||||
sec_debug_summary_set_kallsyms_info(secdbg_apss);
|
||||
|
||||
_set_kconst(secdbg_apss);
|
||||
|
||||
#ifdef CONFIG_QCOM_RTB
|
||||
sec_debug_summary_set_rtb_info(secdbg_apss);
|
||||
#endif
|
||||
|
||||
summary_set_task_info(secdbg_apss);
|
||||
|
||||
summary_set_lpm_info_cluster(secdbg_apss);
|
||||
summary_set_lpm_info_runqueues(secdbg_apss);
|
||||
|
||||
summary_set_msm_memdump_info(secdbg_apss);
|
||||
|
||||
/* fill magic nubmer last to ensure data integrity when the magic
|
||||
* numbers are written
|
||||
*/
|
||||
secdbg_summary->magic[0] = SEC_DEBUG_SUMMARY_MAGIC0;
|
||||
secdbg_summary->magic[1] = SEC_DEBUG_SUMMARY_MAGIC1;
|
||||
secdbg_summary->magic[2] = SEC_DEBUG_SUMMARY_MAGIC2;
|
||||
secdbg_summary->magic[3] = SEC_DEBUG_SUMMARY_MAGIC3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsys_initcall_sync(sec_debug_summary_init);
|
||||
923
drivers/debug/sec_debug_user_reset.c
Normal file
923
drivers/debug/sec_debug_user_reset.c
Normal file
@@ -0,0 +1,923 @@
|
||||
/*
|
||||
* drivers/debug/sec_debug_user_reset.c
|
||||
*
|
||||
* COPYRIGHT(C) 2006-2018 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
|
||||
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
#include <linux/sec_debug_summary.h>
|
||||
#include <linux/sec_debug_user_reset.h>
|
||||
#include <linux/sec_debug_partition.h>
|
||||
|
||||
#include <linux/sec_param.h>
|
||||
#include <linux/sec_class.h>
|
||||
|
||||
#include "sec_debug_internal.h"
|
||||
|
||||
static char rr_str[][3] = {
|
||||
[USER_UPLOAD_CAUSE_SMPL] = "SP",
|
||||
[USER_UPLOAD_CAUSE_WTSR] = "WP",
|
||||
[USER_UPLOAD_CAUSE_WATCHDOG] = "DP",
|
||||
[USER_UPLOAD_CAUSE_PANIC] = "KP",
|
||||
[USER_UPLOAD_CAUSE_MANUAL_RESET] = "MP",
|
||||
[USER_UPLOAD_CAUSE_POWER_RESET] = "PP",
|
||||
[USER_UPLOAD_CAUSE_REBOOT] = "RP",
|
||||
[USER_UPLOAD_CAUSE_BOOTLOADER_REBOOT] = "BP",
|
||||
[USER_UPLOAD_CAUSE_POWER_ON] = "NP",
|
||||
[USER_UPLOAD_CAUSE_THERMAL] = "TP",
|
||||
[USER_UPLOAD_CAUSE_UNKNOWN] = "NP",
|
||||
};
|
||||
|
||||
char *klog_buf;
|
||||
uint32_t klog_size;
|
||||
char *klog_read_buf;
|
||||
struct debug_reset_header *klog_info;
|
||||
static DEFINE_MUTEX(klog_mutex);
|
||||
|
||||
char *summary_buf;
|
||||
struct debug_reset_header *summary_info;
|
||||
static DEFINE_MUTEX(summary_mutex);
|
||||
static unsigned reset_reason = 0xFFEEFFEE;
|
||||
|
||||
char *tzlog_buf;
|
||||
struct debug_reset_header *tzlog_info;
|
||||
static DEFINE_MUTEX(tzlog_mutex);
|
||||
|
||||
static int reset_write_cnt = -1;
|
||||
|
||||
uint32_t sec_debug_get_reset_reason(void)
|
||||
{
|
||||
return reset_reason;
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_get_reset_reason);
|
||||
|
||||
int sec_debug_get_reset_write_cnt(void)
|
||||
{
|
||||
return reset_write_cnt;
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_get_reset_write_cnt);
|
||||
|
||||
char *sec_debug_get_reset_reason_str(unsigned int reason)
|
||||
{
|
||||
if (reason < USER_UPLOAD_CAUSE_MIN || reason > USER_UPLOAD_CAUSE_MAX)
|
||||
reason = USER_UPLOAD_CAUSE_UNKNOWN;
|
||||
|
||||
return rr_str[reason];
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_get_reset_reason_str);
|
||||
|
||||
static void sec_debug_update_reset_reason(uint32_t debug_partition_rr)
|
||||
{
|
||||
static char updated = 0;
|
||||
|
||||
if (!updated) {
|
||||
reset_reason = debug_partition_rr;
|
||||
updated = 1;
|
||||
pr_info("partition[%d] result[%s]\n",
|
||||
debug_partition_rr,
|
||||
sec_debug_get_reset_reason_str(reset_reason));
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_reason_update_and_clear(void)
|
||||
{
|
||||
ap_health_t *p_health;
|
||||
uint32_t rr_data;
|
||||
|
||||
p_health = ap_health_data_read();
|
||||
if (p_health == NULL) {
|
||||
pr_err("p_health is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("done\n");
|
||||
rr_data = sec_debug_get_reset_reason();
|
||||
switch (rr_data) {
|
||||
case USER_UPLOAD_CAUSE_SMPL:
|
||||
p_health->daily_rr.sp++;
|
||||
p_health->rr.sp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_WTSR:
|
||||
p_health->daily_rr.wp++;
|
||||
p_health->rr.wp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_WATCHDOG:
|
||||
p_health->daily_rr.dp++;
|
||||
p_health->rr.dp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_PANIC:
|
||||
p_health->daily_rr.kp++;
|
||||
p_health->rr.kp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_MANUAL_RESET:
|
||||
p_health->daily_rr.mp++;
|
||||
p_health->rr.mp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_POWER_RESET:
|
||||
p_health->daily_rr.pp++;
|
||||
p_health->rr.pp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_REBOOT:
|
||||
p_health->daily_rr.rp++;
|
||||
p_health->rr.rp++;
|
||||
break;
|
||||
case USER_UPLOAD_CAUSE_THERMAL:
|
||||
p_health->daily_rr.tp++;
|
||||
p_health->rr.tp++;
|
||||
break;
|
||||
default:
|
||||
p_health->daily_rr.np++;
|
||||
p_health->rr.np++;
|
||||
}
|
||||
|
||||
p_health->last_rst_reason = 0;
|
||||
ap_health_data_write(p_health);
|
||||
}
|
||||
|
||||
static int set_reset_reason_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
uint32_t rr_data = sec_debug_get_reset_reason();
|
||||
static uint32_t rr_cnt_update = 1;
|
||||
|
||||
seq_printf(m, "%sON\n", sec_debug_get_reset_reason_str(rr_data));
|
||||
if (rr_cnt_update) {
|
||||
reset_reason_update_and_clear();
|
||||
rr_cnt_update = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sec_reset_reason_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, set_reset_reason_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations sec_reset_reason_proc_fops = {
|
||||
.open = sec_reset_reason_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static phys_addr_t sec_debug_reset_ex_info_paddr;
|
||||
static unsigned sec_debug_reset_ex_info_size;
|
||||
rst_exinfo_t *sec_debug_reset_ex_info;
|
||||
ex_info_fault_t ex_info_fault[NR_CPUS];
|
||||
|
||||
void sec_debug_store_extc_idx(bool prefix)
|
||||
{
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
if (p_ex_info->extc_idx == 0) {
|
||||
p_ex_info->extc_idx = get_sec_log_idx();
|
||||
if (prefix)
|
||||
p_ex_info->extc_idx += SEC_DEBUG_RESET_EXTRC_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_store_extc_idx);
|
||||
|
||||
void sec_debug_store_bug_string(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(p_ex_info->bug_buf,
|
||||
sizeof(p_ex_info->bug_buf), fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_store_bug_string);
|
||||
|
||||
void sec_debug_store_additional_dbg(enum extra_info_dbg_type type,
|
||||
unsigned int value, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
switch (type) {
|
||||
case DBG_0_GLAD_ERR:
|
||||
va_start(args, fmt);
|
||||
vsnprintf(p_ex_info->dbg0,
|
||||
sizeof(p_ex_info->dbg0), fmt, args);
|
||||
va_end(args);
|
||||
break;
|
||||
case DBG_1_UFS_ERR:
|
||||
va_start(args, fmt);
|
||||
vsnprintf(p_ex_info->ufs_err,
|
||||
sizeof(p_ex_info->ufs_err), fmt, args);
|
||||
va_end(args);
|
||||
break;
|
||||
case DBG_2_DISPLAY_ERR:
|
||||
va_start(args, fmt);
|
||||
vsnprintf(p_ex_info->display_err,
|
||||
sizeof(p_ex_info->display_err), fmt, args);
|
||||
va_end(args);
|
||||
break;
|
||||
case DBG_3_RESERVED ... DBG_5_RESERVED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_store_additional_dbg);
|
||||
|
||||
static void sec_debug_init_panic_extra_info(void)
|
||||
{
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
memset((void *)&sec_debug_reset_ex_info->kern_ex_info, 0,
|
||||
sizeof(sec_debug_reset_ex_info->kern_ex_info));
|
||||
p_ex_info->cpu = -1;
|
||||
pr_info("%s: ex_info memory initialized size[%ld]\n",
|
||||
__func__, sizeof(kern_exinfo_t));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init sec_debug_ex_info_setup(char *str)
|
||||
{
|
||||
unsigned size = memparse(str, &str);
|
||||
int ret;
|
||||
|
||||
if (size && (*str == '@')) {
|
||||
unsigned long long base = 0;
|
||||
|
||||
ret = kstrtoull(++str, 0, &base);
|
||||
if (ret) {
|
||||
pr_err("failed to parse sec_dbg_ex_info\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sec_debug_reset_ex_info_paddr = base;
|
||||
sec_debug_reset_ex_info_size =
|
||||
(size + 0x1000 - 1) & ~(0x1000 - 1);
|
||||
|
||||
pr_info("ex info phy=0x%llx, size=0x%x\n",
|
||||
(uint64_t)sec_debug_reset_ex_info_paddr,
|
||||
sec_debug_reset_ex_info_size);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
__setup("sec_dbg_ex_info=", sec_debug_ex_info_setup);
|
||||
|
||||
static int __init sec_debug_get_extra_info_region(void)
|
||||
{
|
||||
if (!sec_debug_reset_ex_info_paddr || !sec_debug_reset_ex_info_size)
|
||||
return -1;
|
||||
|
||||
sec_debug_reset_ex_info = ioremap_cache(sec_debug_reset_ex_info_paddr,
|
||||
sec_debug_reset_ex_info_size);
|
||||
|
||||
if (!sec_debug_reset_ex_info) {
|
||||
pr_err("Failed to remap nocache ex info region\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec_debug_init_panic_extra_info();
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall_sync(sec_debug_get_extra_info_region);
|
||||
|
||||
struct debug_reset_header *get_debug_reset_header(void)
|
||||
{
|
||||
struct debug_reset_header *header = NULL;
|
||||
static int get_state = DRH_STATE_INIT;
|
||||
|
||||
if (get_state == DRH_STATE_INVALID)
|
||||
return NULL;
|
||||
|
||||
header = kmalloc(sizeof(struct debug_reset_header), GFP_KERNEL);
|
||||
if (!header) {
|
||||
pr_err("%s : fail - kmalloc for debug_reset_header\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
if (!read_debug_partition(debug_index_reset_summary_info, header)) {
|
||||
pr_err("%s : fail - get param!! debug_reset_header\n", __func__);
|
||||
kfree(header);
|
||||
header = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (get_state != DRH_STATE_VALID) {
|
||||
if (header->write_times == header->read_times) {
|
||||
pr_err("%s : untrustworthy debug_reset_header\n", __func__);
|
||||
get_state = DRH_STATE_INVALID;
|
||||
kfree(header);
|
||||
header = NULL;
|
||||
return NULL;
|
||||
}
|
||||
reset_write_cnt = header->write_times;
|
||||
get_state = DRH_STATE_VALID;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static int set_debug_reset_header(struct debug_reset_header *header)
|
||||
{
|
||||
int ret = 0;
|
||||
static int set_state = DRH_STATE_INIT;
|
||||
|
||||
if (set_state == DRH_STATE_VALID) {
|
||||
pr_info("%s : debug_reset_header working well\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((header->write_times - 1) == header->read_times) {
|
||||
pr_info("%s : debug_reset_header working well\n", __func__);
|
||||
header->read_times++;
|
||||
} else {
|
||||
pr_info("%s : debug_reset_header read[%d] and write[%d] work sync error.\n",
|
||||
__func__, header->read_times, header->write_times);
|
||||
header->read_times = header->write_times;
|
||||
}
|
||||
|
||||
if (!write_debug_partition(debug_index_reset_summary_info, header)) {
|
||||
pr_err("%s : fail - set param!! debug_reset_header\n", __func__);
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
set_state = DRH_STATE_VALID;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sec_reset_summary_info_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (summary_buf != NULL)
|
||||
return true;
|
||||
|
||||
if (summary_info != NULL) {
|
||||
pr_err("%s : already memory alloc for summary_info\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
summary_info = get_debug_reset_header();
|
||||
if (summary_info == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (summary_info->summary_size > SEC_DEBUG_RESET_SUMMARY_SIZE) {
|
||||
pr_err("%s : summary_size has problem.\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto error_summary_info;
|
||||
}
|
||||
|
||||
summary_buf = vmalloc(SEC_DEBUG_RESET_SUMMARY_SIZE);
|
||||
if (!summary_buf) {
|
||||
pr_err("%s : fail - kmalloc for summary_buf\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto error_summary_info;
|
||||
}
|
||||
if (!read_debug_partition(debug_index_reset_summary, summary_buf)) {
|
||||
pr_err("%s : fail - get param!! summary data\n", __func__);
|
||||
ret = -ENOENT;
|
||||
goto error_summary_buf;
|
||||
}
|
||||
|
||||
pr_info("%s : w[%d] r[%d] idx[%d] size[%d]\n",
|
||||
__func__, summary_info->write_times, summary_info->read_times,
|
||||
summary_info->ap_klog_idx, summary_info->summary_size);
|
||||
|
||||
return ret;
|
||||
|
||||
error_summary_buf:
|
||||
vfree(summary_buf);
|
||||
error_summary_info:
|
||||
kfree(summary_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sec_reset_summary_completed(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = set_debug_reset_header(summary_info);
|
||||
|
||||
vfree(summary_buf);
|
||||
kfree(summary_info);
|
||||
|
||||
summary_info = NULL;
|
||||
summary_buf = NULL;
|
||||
pr_info("%s finish\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t sec_reset_summary_info_proc_read(struct file *file,
|
||||
char __user *buf, size_t len, loff_t *offset)
|
||||
{
|
||||
loff_t pos = *offset;
|
||||
ssize_t count;
|
||||
|
||||
mutex_lock(&summary_mutex);
|
||||
if (sec_reset_summary_info_init() < 0) {
|
||||
mutex_unlock(&summary_mutex);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if ((pos >= summary_info->summary_size) || (pos >= SEC_DEBUG_RESET_SUMMARY_SIZE)) {
|
||||
pr_info("%s : pos %lld, size %d\n", __func__, pos, summary_info->summary_size);
|
||||
sec_reset_summary_completed();
|
||||
mutex_unlock(&summary_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = min(len, (size_t)(summary_info->summary_size - pos));
|
||||
if (copy_to_user(buf, summary_buf + pos, count)) {
|
||||
mutex_unlock(&summary_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*offset += count;
|
||||
mutex_unlock(&summary_mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations sec_reset_summary_info_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = sec_reset_summary_info_proc_read,
|
||||
};
|
||||
|
||||
static int sec_reset_klog_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((klog_read_buf != NULL) && (klog_buf != NULL))
|
||||
return true;
|
||||
|
||||
if (klog_info != NULL) {
|
||||
pr_err("%s : already memory alloc for klog_info\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
klog_info = get_debug_reset_header();
|
||||
if (klog_info == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
klog_read_buf = vmalloc(SEC_DEBUG_RESET_KLOG_SIZE);
|
||||
if (!klog_read_buf) {
|
||||
pr_err("%s : fail - vmalloc for klog_read_buf\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto error_klog_info;
|
||||
}
|
||||
if (!read_debug_partition(debug_index_reset_klog, klog_read_buf)) {
|
||||
pr_err("%s : fail - get param!! summary data\n", __func__);
|
||||
ret = -ENOENT;
|
||||
goto error_klog_read_buf;
|
||||
}
|
||||
|
||||
pr_info("%s : idx[%d]\n", __func__, klog_info->ap_klog_idx);
|
||||
|
||||
klog_size = min((uint32_t)SEC_DEBUG_RESET_KLOG_SIZE, (uint32_t)klog_info->ap_klog_idx);
|
||||
|
||||
klog_buf = vmalloc(klog_size);
|
||||
if (!klog_buf) {
|
||||
pr_err("%s : fail - vmalloc for klog_buf\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto error_klog_read_buf;
|
||||
}
|
||||
|
||||
if (klog_size && klog_buf && klog_read_buf) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < klog_size; i++)
|
||||
klog_buf[i] = klog_read_buf[(klog_info->ap_klog_idx - klog_size + i) % SEC_DEBUG_RESET_KLOG_SIZE];
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_klog_read_buf:
|
||||
vfree(klog_read_buf);
|
||||
error_klog_info:
|
||||
kfree(klog_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sec_reset_klog_completed(void)
|
||||
{
|
||||
set_debug_reset_header(klog_info);
|
||||
|
||||
vfree(klog_buf);
|
||||
vfree(klog_read_buf);
|
||||
kfree(klog_info);
|
||||
|
||||
klog_info = NULL;
|
||||
klog_buf = NULL;
|
||||
klog_read_buf = NULL;
|
||||
klog_size = 0;
|
||||
|
||||
pr_info("%s finish\n", __func__);
|
||||
}
|
||||
|
||||
static ssize_t sec_reset_klog_proc_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *offset)
|
||||
{
|
||||
loff_t pos = *offset;
|
||||
ssize_t count;
|
||||
|
||||
mutex_lock(&klog_mutex);
|
||||
if (sec_reset_klog_init() < 0) {
|
||||
mutex_unlock(&klog_mutex);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (pos >= klog_size) {
|
||||
pr_info("%s : pos %lld, size %d\n", __func__, pos, klog_size);
|
||||
sec_reset_klog_completed();
|
||||
mutex_unlock(&klog_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = min(len, (size_t)(klog_size - pos));
|
||||
if (copy_to_user(buf, klog_buf + pos, count)) {
|
||||
mutex_unlock(&klog_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*offset += count;
|
||||
mutex_unlock(&klog_mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations sec_reset_klog_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = sec_reset_klog_proc_read,
|
||||
};
|
||||
|
||||
static int sec_reset_tzlog_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (tzlog_buf != NULL)
|
||||
return true;
|
||||
|
||||
if (tzlog_info != NULL) {
|
||||
pr_err("%s : already memory alloc for tzlog_info\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tzlog_info = get_debug_reset_header();
|
||||
if (tzlog_info == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (tzlog_info->stored_tzlog == 0) {
|
||||
pr_err("%s : The target didn't run SDI operation\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto error_tzlog_info;
|
||||
}
|
||||
|
||||
tzlog_buf = vmalloc(SEC_DEBUG_RESET_TZLOG_SIZE);
|
||||
if (!tzlog_buf) {
|
||||
pr_err("%s : fail - vmalloc for tzlog_read_buf\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto error_tzlog_info;
|
||||
}
|
||||
if (!read_debug_partition(debug_index_reset_tzlog, tzlog_buf)) {
|
||||
pr_err("%s : fail - get param!! tzlog data\n", __func__);
|
||||
ret = -ENOENT;
|
||||
goto error_tzlog_buf;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_tzlog_buf:
|
||||
vfree(tzlog_buf);
|
||||
error_tzlog_info:
|
||||
kfree(tzlog_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sec_reset_tzlog_completed(void)
|
||||
{
|
||||
set_debug_reset_header(tzlog_info);
|
||||
|
||||
vfree(tzlog_buf);
|
||||
kfree(tzlog_info);
|
||||
|
||||
tzlog_info = NULL;
|
||||
tzlog_buf = NULL;
|
||||
|
||||
pr_info("%s finish\n", __func__);
|
||||
}
|
||||
|
||||
static ssize_t sec_reset_tzlog_proc_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *offset)
|
||||
{
|
||||
loff_t pos = *offset;
|
||||
ssize_t count;
|
||||
|
||||
mutex_lock(&tzlog_mutex);
|
||||
if (sec_reset_tzlog_init() < 0) {
|
||||
mutex_unlock(&tzlog_mutex);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (pos >= SEC_DEBUG_RESET_TZLOG_SIZE) {
|
||||
pr_info("%s : pos %lld, size %d\n", __func__, pos, SEC_DEBUG_RESET_TZLOG_SIZE);
|
||||
sec_reset_tzlog_completed();
|
||||
mutex_unlock(&tzlog_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = min(len, (size_t)(SEC_DEBUG_RESET_TZLOG_SIZE - pos));
|
||||
if (copy_to_user(buf, tzlog_buf + pos, count)) {
|
||||
mutex_unlock(&tzlog_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*offset += count;
|
||||
mutex_unlock(&tzlog_mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations sec_reset_tzlog_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = sec_reset_tzlog_proc_read,
|
||||
};
|
||||
|
||||
static int set_store_lastkmsg_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct debug_reset_header *check_store = NULL;
|
||||
|
||||
if (check_store != NULL) {
|
||||
pr_err("%s : already memory alloc for check_store\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
check_store = get_debug_reset_header();
|
||||
if (check_store == NULL) {
|
||||
seq_printf(m, "0\n");
|
||||
} else {
|
||||
seq_printf(m, "1\n");
|
||||
}
|
||||
|
||||
if (check_store != NULL) {
|
||||
kfree(check_store);
|
||||
check_store = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sec_store_lastkmsg_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, set_store_lastkmsg_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations sec_store_lastkmsg_proc_fops = {
|
||||
.open = sec_store_lastkmsg_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void sec_restore_modem_reset_data(void)
|
||||
{
|
||||
void *p_modem = sec_debug_summary_get_modem();
|
||||
struct debug_reset_header *header = get_debug_reset_header();
|
||||
|
||||
if (!header) {
|
||||
pr_info("%s : updated nothing.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sec_debug_get_reset_reason() != USER_UPLOAD_CAUSE_PANIC) {
|
||||
pr_info("%s : it was not kernel panic.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_modem) {
|
||||
read_debug_partition(debug_index_modem_info, p_modem);
|
||||
pr_info("%s : complete.\n", __func__);
|
||||
} else {
|
||||
pr_info("%s : skip.\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void sec_debug_summary_modem_print(void)
|
||||
{
|
||||
if (sec_debug_get_reset_reason() != USER_UPLOAD_CAUSE_PANIC) {
|
||||
pr_info("%s : it was not kernel panic.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("0x%016lx\n",
|
||||
(unsigned long)sec_debug_summary_get_modem());
|
||||
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
|
||||
sec_debug_summary_get_modem(),
|
||||
0x190, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_summary_modem_print);
|
||||
|
||||
static int sec_reset_reason_dbg_part_notifier_callback(
|
||||
struct notifier_block *nfb, unsigned long action, void *data)
|
||||
{
|
||||
ap_health_t *p_health;
|
||||
uint32_t rr_data;
|
||||
|
||||
switch (action) {
|
||||
case DBG_PART_DRV_INIT_DONE:
|
||||
p_health = ap_health_data_read();
|
||||
if (!p_health)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
sec_debug_update_reset_reason(
|
||||
p_health->last_rst_reason);
|
||||
rr_data = sec_debug_get_reset_reason();
|
||||
sec_restore_modem_reset_data();
|
||||
break;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
|
||||
static struct notifier_block sec_reset_reason_dbg_part_notifier = {
|
||||
.notifier_call = sec_reset_reason_dbg_part_notifier_callback,
|
||||
};
|
||||
|
||||
static int __init sec_debug_reset_reason_init(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
entry = proc_create("reset_reason", S_IWUGO, NULL,
|
||||
&sec_reset_reason_proc_fops);
|
||||
if (unlikely(!entry))
|
||||
return -ENOMEM;
|
||||
|
||||
entry = proc_create("reset_summary", S_IWUGO, NULL,
|
||||
&sec_reset_summary_info_proc_fops);
|
||||
if (unlikely(!entry))
|
||||
return -ENOMEM;
|
||||
|
||||
entry = proc_create("reset_klog", S_IWUGO, NULL,
|
||||
&sec_reset_klog_proc_fops);
|
||||
if (unlikely(!entry))
|
||||
return -ENOMEM;
|
||||
|
||||
entry = proc_create("reset_tzlog", S_IWUGO, NULL,
|
||||
&sec_reset_tzlog_proc_fops);
|
||||
if (unlikely(!entry))
|
||||
return -ENOMEM;
|
||||
|
||||
entry = proc_create("store_lastkmsg", S_IWUGO, NULL,
|
||||
&sec_store_lastkmsg_proc_fops);
|
||||
if (unlikely(!entry))
|
||||
return -ENOMEM;
|
||||
|
||||
dbg_partition_notifier_register(&sec_reset_reason_dbg_part_notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(sec_debug_reset_reason_init);
|
||||
|
||||
static ssize_t show_recovery_cause(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char recovery_cause[256];
|
||||
|
||||
sec_get_param(param_index_reboot_recovery_cause, recovery_cause);
|
||||
pr_info("%s\n", recovery_cause);
|
||||
|
||||
return scnprintf(buf, sizeof(recovery_cause), "%s", recovery_cause);
|
||||
}
|
||||
|
||||
static ssize_t store_recovery_cause(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
char recovery_cause[256];
|
||||
|
||||
if (count > sizeof(recovery_cause)) {
|
||||
pr_err("input buffer length is out of range.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
snprintf(recovery_cause, sizeof(recovery_cause), "%s:%d ", current->comm, task_pid_nr(current));
|
||||
if (strlen(recovery_cause) + strlen(buf) >= sizeof(recovery_cause)) {
|
||||
pr_err("input buffer length is out of range.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
strncat(recovery_cause, buf, strlen(buf));
|
||||
sec_set_param(param_index_reboot_recovery_cause, recovery_cause);
|
||||
pr_info("%s\n", recovery_cause);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(recovery_cause, 0660, show_recovery_cause, store_recovery_cause);
|
||||
|
||||
static struct device *sec_debug_dev;
|
||||
|
||||
static int __init sec_debug_recovery_reason_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* create sysfs for reboot_recovery_cause */
|
||||
sec_debug_dev = sec_device_create(0, NULL, "sec_debug");
|
||||
if (IS_ERR(sec_debug_dev)) {
|
||||
pr_err("Failed to create device for sec_debug\n");
|
||||
return PTR_ERR(sec_debug_dev);
|
||||
}
|
||||
|
||||
ret = sysfs_create_file(&sec_debug_dev->kobj, &dev_attr_recovery_cause.attr);
|
||||
if (ret) {
|
||||
pr_err("Failed to create sysfs group for sec_debug\n");
|
||||
sec_device_destroy(sec_debug_dev->devt);
|
||||
sec_debug_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(sec_debug_recovery_reason_init);
|
||||
|
||||
void _sec_debug_store_backtrace(unsigned long where)
|
||||
{
|
||||
static int offset;
|
||||
unsigned int max_size = 0;
|
||||
_kern_ex_info_t *p_ex_info;
|
||||
|
||||
if (sec_debug_reset_ex_info) {
|
||||
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
|
||||
max_size = (unsigned long long int)&sec_debug_reset_ex_info->rpm_ex_info.info
|
||||
- (unsigned long long int)p_ex_info->backtrace;
|
||||
|
||||
if (max_size <= offset)
|
||||
return;
|
||||
|
||||
if (offset)
|
||||
offset += snprintf(p_ex_info->backtrace+offset,
|
||||
max_size-offset, " > ");
|
||||
|
||||
offset += snprintf(p_ex_info->backtrace+offset, max_size-offset,
|
||||
"%pS", (void *)where);
|
||||
}
|
||||
}
|
||||
|
||||
void sec_debug_backtrace(void)
|
||||
{
|
||||
static int once = 0;
|
||||
struct stackframe frame;
|
||||
int skip_callstack = 0;
|
||||
|
||||
if (!once++) {
|
||||
frame.fp = (unsigned long)__builtin_frame_address(0);
|
||||
frame.sp = current_stack_pointer;
|
||||
frame.pc = (unsigned long)sec_debug_backtrace;
|
||||
|
||||
while (1) {
|
||||
int ret;
|
||||
|
||||
ret = unwind_frame(current, &frame);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (skip_callstack++ > 3) {
|
||||
_sec_debug_store_backtrace(frame.pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(sec_debug_backtrace);
|
||||
|
||||
60
drivers/debug/sec_kcompat.c
Normal file
60
drivers/debug/sec_kcompat.c
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/samsung/sec_kcompat.c
|
||||
*
|
||||
* COPYRIGHT(C) 2019 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#if defined(CONFIG_MSM_SMEM)
|
||||
#include <soc/qcom/smem.h>
|
||||
#endif
|
||||
|
||||
#include "sec_kcompat.h"
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
|
||||
#if defined(CONFIG_MSM_SMEM)
|
||||
void * __weak qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
{
|
||||
void * ret;
|
||||
unsigned int size_tmp = 0;
|
||||
|
||||
ret = smem_get_entry(item, &size_tmp, SMEM_APPS, host);
|
||||
*size = (size_t)size_tmp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phys_addr_t __weak qcom_smem_virt_to_phys(void *p)
|
||||
{
|
||||
return smem_virt_to_phys(p) & 0xFFFFFFFFULL;
|
||||
}
|
||||
#endif /* CONFIG_MSM_SMEM */
|
||||
#endif /* KERNEL_VERSION(4,14,0) */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
|
||||
unsigned long * __weak bitmap_alloc(unsigned int nbits, gfp_t flags)
|
||||
{
|
||||
return kmalloc_array(BITS_TO_LONGS(nbits), sizeof(unsigned long),
|
||||
flags);
|
||||
}
|
||||
|
||||
unsigned long * __weak bitmap_zalloc(unsigned int nbits, gfp_t flags)
|
||||
{
|
||||
return bitmap_alloc(nbits, flags | __GFP_ZERO);
|
||||
}
|
||||
#endif /* KERNEL_VERSION(4,19,0) */
|
||||
34
drivers/debug/sec_kcompat.h
Normal file
34
drivers/debug/sec_kcompat.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef __SEC_KCOMPAT_H__
|
||||
#define __SEC_KCOMPAT_H__
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
#if defined(CONFIG_MSM_SMEM) && defined(CONFIG_QCOM_SMEM)
|
||||
#error "CONFIG_MSM_SMEM and CONFIG_QCOM_SMEM can not be enabled at the same time"
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
|
||||
|
||||
#define timer_setup(__timer, __fn, __data) setup_timer(__timer, __fn, __data)
|
||||
|
||||
#if defined(CONFIG_MSM_SMEM)
|
||||
|
||||
#if defined(QCOM_SMEM_HOST_ANY)
|
||||
#undef QCOM_SMEM_HOST_ANY
|
||||
#endif
|
||||
#define QCOM_SMEM_HOST_ANY SMEM_ANY_HOST_FLAG
|
||||
|
||||
void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
|
||||
extern phys_addr_t qcom_smem_virt_to_phys(void *p);
|
||||
#endif /* CONFIG_MSM_SMEM */
|
||||
|
||||
#endif /* KERNEL_VERSION(4,14,0) */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
|
||||
|
||||
extern unsigned long *bitmap_alloc(unsigned int nbits, gfp_t flags);
|
||||
extern unsigned long *bitmap_zalloc(unsigned int nbits, gfp_t flags);
|
||||
|
||||
#endif /* KERNEL_VERSION(4,19,0) */
|
||||
|
||||
#endif /* __SEC_KCOMPAT_H__ */
|
||||
170
drivers/debug/sec_key_notifier.c
Normal file
170
drivers/debug/sec_key_notifier.c
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/samsung/debug/sec_key_notifier.c
|
||||
*
|
||||
* COPYRIGHT(C) 2016-2019 Samsung Electronics Co., Ltd. All Right 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/sec_debug.h>
|
||||
|
||||
#include "sec_key_notifier.h"
|
||||
|
||||
static DEFINE_SPINLOCK(sec_kn_event_lock);
|
||||
|
||||
static ATOMIC_NOTIFIER_HEAD(sec_kn_notifier_list);
|
||||
|
||||
static atomic_t sec_kn_acceptable_event[KEY_MAX] __read_mostly;
|
||||
|
||||
static void inline update_acceptable_event(unsigned int event_code, bool is_add)
|
||||
{
|
||||
if (is_add)
|
||||
atomic_inc(&(sec_kn_acceptable_event[event_code]));
|
||||
else
|
||||
atomic_dec(&(sec_kn_acceptable_event[event_code]));
|
||||
}
|
||||
|
||||
int sec_kn_register_notifier(struct notifier_block *nb,
|
||||
const unsigned int *events, const size_t nr_events)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_events; i++)
|
||||
update_acceptable_event(events[i], true);
|
||||
|
||||
return atomic_notifier_chain_register(&sec_kn_notifier_list, nb);
|
||||
}
|
||||
|
||||
int sec_kn_unregister_notifier(struct notifier_block *nb,
|
||||
const unsigned int *events, const size_t nr_events)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_events; i++)
|
||||
update_acceptable_event(events[i], false);
|
||||
|
||||
return atomic_notifier_chain_unregister(&sec_kn_notifier_list, nb);
|
||||
}
|
||||
|
||||
static inline bool is_event_supported(unsigned int event_type,
|
||||
unsigned int event_code)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (event_type != EV_KEY || event_code >= KEY_MAX)
|
||||
return false;
|
||||
|
||||
ret = !!atomic_read(&(sec_kn_acceptable_event[event_code]));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sec_kn_event(struct input_handle *handle, unsigned int event_type,
|
||||
unsigned int event_code, int value)
|
||||
{
|
||||
struct sec_key_notifier_param param = {
|
||||
.keycode = event_code,
|
||||
.down = value,
|
||||
};
|
||||
|
||||
if (!is_event_supported(event_type, event_code))
|
||||
return;
|
||||
|
||||
spin_lock(&sec_kn_event_lock);
|
||||
|
||||
atomic_notifier_call_chain(&sec_kn_notifier_list, 0, ¶m);
|
||||
|
||||
spin_unlock(&sec_kn_event_lock);
|
||||
}
|
||||
|
||||
static int sec_kn_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
const struct input_device_id *id)
|
||||
{
|
||||
struct input_handle *handle;
|
||||
int error;
|
||||
|
||||
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
|
||||
handle->dev = dev;
|
||||
handle->handler = handler;
|
||||
handle->name = "sec_key_notifier";
|
||||
|
||||
error = input_register_handle(handle);
|
||||
if (error)
|
||||
goto err_free_handle;
|
||||
|
||||
error = input_open_device(handle);
|
||||
if (error)
|
||||
goto err_unregister_handle;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_handle:
|
||||
input_unregister_handle(handle);
|
||||
err_free_handle:
|
||||
kfree(handle);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void sec_kn_disconnect(struct input_handle *handle)
|
||||
{
|
||||
input_close_device(handle);
|
||||
input_unregister_handle(handle);
|
||||
kfree(handle);
|
||||
}
|
||||
|
||||
static const struct input_device_id sec_kn_ids[] = {
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct input_handler sec_kn_handler = {
|
||||
.event = sec_kn_event,
|
||||
.connect = sec_kn_connect,
|
||||
.disconnect = sec_kn_disconnect,
|
||||
.name = "sec_key_notifier",
|
||||
.id_table = sec_kn_ids,
|
||||
};
|
||||
|
||||
static int __init sec_kn_init(void)
|
||||
{
|
||||
int err;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < KEY_MAX; i++)
|
||||
atomic_set(&(sec_kn_acceptable_event[i]), 0);
|
||||
|
||||
spin_lock_init(&sec_kn_event_lock);
|
||||
|
||||
err = input_register_handler(&sec_kn_handler);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit sec_kn_exit(void)
|
||||
{
|
||||
input_unregister_handler(&sec_kn_handler);
|
||||
}
|
||||
|
||||
arch_initcall(sec_kn_init);
|
||||
module_exit(sec_kn_exit);
|
||||
35
drivers/debug/sec_key_notifier.h
Normal file
35
drivers/debug/sec_key_notifier.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* drivers/debug/sec_key_notifier.h
|
||||
*
|
||||
* COPYRIGHT(C) 2016 Samsung Electronics Co., Ltd. All Right Reserved.
|
||||
*
|
||||
* 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 __SEC_KEY_NOTIFIER_H__
|
||||
#define __SEC_KEY_NOTIFIER_H__
|
||||
|
||||
struct sec_key_notifier_param {
|
||||
unsigned int keycode;
|
||||
int down;
|
||||
};
|
||||
|
||||
int sec_kn_register_notifier(struct notifier_block *nb,
|
||||
const unsigned int *events, const size_t nr_events);
|
||||
|
||||
int sec_kn_unregister_notifier(struct notifier_block *nb,
|
||||
const unsigned int *events, const size_t nr_events);
|
||||
|
||||
#endif /* __SEC_KEY_NOTIFIER_H__ */
|
||||
1719
drivers/debug/sec_nad.c
Normal file
1719
drivers/debug/sec_nad.c
Normal file
File diff suppressed because it is too large
Load Diff
2457
drivers/debug/sec_quest.c
Normal file
2457
drivers/debug/sec_quest.c
Normal file
File diff suppressed because it is too large
Load Diff
522
drivers/debug/spinlock_test.c
Normal file
522
drivers/debug/spinlock_test.c
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* File is for internal use only, and not for distribution
|
||||
* If distribution is needed, OS review is required.
|
||||
* See OSRQCT-5220 for further information.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "spinlock-test: " fmt
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define MODULE_NAME "spinlock_test"
|
||||
#define CPU_NUM_MAX NR_CPUS
|
||||
#define TEST_ITERS_MAX 100
|
||||
#define INNER_TEST_TIME_SECS_DEFAULT 5
|
||||
#define INNER_TEST_TIME_SECS_MAX 100
|
||||
#define SPINLOCK_TEST_DELAY_MS 1000
|
||||
#define IRQ_DIALBE_TIME_MS (MSEC_PER_SEC/HZ)
|
||||
|
||||
static void spinwork_fn(struct work_struct *work);
|
||||
static struct delayed_work spinworks[CPU_NUM_MAX];
|
||||
static struct dentry *dent;
|
||||
static DEFINE_PER_CPU(int, cpu_test_idx);
|
||||
|
||||
static u32 start;
|
||||
static u32 num_test_iters;
|
||||
static u32 canceltest;
|
||||
static u32 cpu_start[CPU_NUM_MAX];
|
||||
static u32 inner_test_time_secs = INNER_TEST_TIME_SECS_DEFAULT;
|
||||
|
||||
static struct locks {
|
||||
spinlock_t lock1;
|
||||
spinlock_t lock2;
|
||||
} testlocks __cacheline_aligned;
|
||||
|
||||
/*
|
||||
* global_lock_static will be in the kernel static (BSS?) region.
|
||||
* global_inner_lock will be kmalloc'ed. The idea here is to use
|
||||
* different cache-lines for the two locks.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(global_lock_static);
|
||||
static spinlock_t *global_lock_heap;
|
||||
static spinlock_t *uncached_lock;
|
||||
static phys_addr_t phys;
|
||||
|
||||
enum {
|
||||
IRQS_DISABLED_TEST = 1,
|
||||
STACK_LOCK_TEST,
|
||||
TWOLOCKS_TEST,
|
||||
SIMPLE_TEST,
|
||||
UNCACHED_LOCK_TEST,
|
||||
NUM_OF_SPINLOCK_TEST = 5,
|
||||
};
|
||||
|
||||
static int set_inner_test_time(void *data, u64 val)
|
||||
{
|
||||
if (val != 0) {
|
||||
inner_test_time_secs = val < INNER_TEST_TIME_SECS_MAX ? \
|
||||
val : INNER_TEST_TIME_SECS_MAX;
|
||||
pr_debug("set_inner_test_time: %u\n", inner_test_time_secs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_inner_test_time(void *data, u64 *val)
|
||||
{
|
||||
*val = inner_test_time_secs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinlock_test_start(void);
|
||||
static int spinlock_test_stop(void);
|
||||
static int set_spinlock_start(void *data, u64 val)
|
||||
{
|
||||
int ret = 0;
|
||||
if (val > 0) {
|
||||
if (start) {
|
||||
pr_err("The last test is not done yet. Exitting...\n");
|
||||
return 0;
|
||||
}
|
||||
num_test_iters = val < TEST_ITERS_MAX ? val : TEST_ITERS_MAX;
|
||||
ret = spinlock_test_start();
|
||||
} else if (val == 0) {
|
||||
if (!start) {
|
||||
pr_err("No spinlock test is running. Exitting...\n");
|
||||
return 0;
|
||||
}
|
||||
ret = spinlock_test_stop();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_spinlock_start(void *data, u64 *val)
|
||||
{
|
||||
*val = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spinlock_start_ops,
|
||||
get_spinlock_start, set_spinlock_start, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(inner_test_time_ops,
|
||||
get_inner_test_time, set_inner_test_time, "%llu\n");
|
||||
|
||||
static int creat_spinlock_debugfs(void)
|
||||
{
|
||||
/*
|
||||
* Create a simple debugfs with the name of "spinlock-test",
|
||||
*
|
||||
* As this is a simple directory, no uevent will be sent to
|
||||
* userspace.
|
||||
*/
|
||||
struct dentry *phandle;
|
||||
|
||||
dent = debugfs_create_dir("spinlock-test", 0);
|
||||
if (IS_ERR(dent))
|
||||
return -ENOMEM;
|
||||
|
||||
phandle = debugfs_create_file("start", 0666, dent, 0, &spinlock_start_ops);
|
||||
if (!phandle || IS_ERR(phandle))
|
||||
goto err;
|
||||
|
||||
phandle = debugfs_create_file("inner_test_time", 0666, dent, 0, &inner_test_time_ops);
|
||||
if (!phandle || IS_ERR(phandle))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
debugfs_remove_recursive(dent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void sync_cpus(int test_idx, int cpu)
|
||||
{
|
||||
int ocpu, flag;
|
||||
per_cpu(cpu_test_idx, cpu) = test_idx;
|
||||
/*
|
||||
Wait until all other cpus finished the same test case
|
||||
within the same iteration.
|
||||
*/
|
||||
do {
|
||||
flag = 0;
|
||||
for_each_online_cpu(ocpu) {
|
||||
if (per_cpu(cpu_test_idx, ocpu) != test_idx ||
|
||||
cpu_start[ocpu] != cpu_start[cpu]) {
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (flag);
|
||||
}
|
||||
|
||||
static void irqs_disabled_spintest(int cpu)
|
||||
{
|
||||
unsigned long flags, irqflags;
|
||||
int incritical = 0;
|
||||
ktime_t test_time = ktime_add_us(ktime_get(),
|
||||
(inner_test_time_secs * USEC_PER_SEC));
|
||||
pr_info("(irq_disabled): Running test on CPU %d for %d seconds.\n", cpu,
|
||||
inner_test_time_secs);
|
||||
|
||||
while (ktime_compare(ktime_get(), test_time) < 0)
|
||||
{
|
||||
ktime_t irq_disable_test_time = ktime_add_us(ktime_get(),
|
||||
(IRQ_DIALBE_TIME_MS * USEC_PER_MSEC));
|
||||
local_irq_save(irqflags);
|
||||
while (ktime_compare(ktime_get(), irq_disable_test_time) < 0) {
|
||||
spin_lock_irqsave(&global_lock_static, flags);
|
||||
spin_lock(global_lock_heap);
|
||||
incritical++;
|
||||
spin_unlock(global_lock_heap);
|
||||
spin_unlock_irqrestore(&global_lock_static, flags);
|
||||
}
|
||||
local_irq_restore(irqflags);
|
||||
}
|
||||
|
||||
pr_info("(irqs disabled) CPU%d entered critical section %d times.\n",
|
||||
cpu, incritical);
|
||||
sync_cpus(IRQS_DISABLED_TEST, cpu);
|
||||
}
|
||||
|
||||
static void spintest_two_lock_single_cacheline(int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
int incritical = 0;
|
||||
ktime_t test_time = ktime_add_us(ktime_get(),
|
||||
(inner_test_time_secs * USEC_PER_SEC));
|
||||
pr_info("(two-locks): Running test on CPU %d for %d seconds.\n", cpu,
|
||||
inner_test_time_secs);
|
||||
|
||||
while (ktime_compare(ktime_get(), test_time) < 0)
|
||||
{
|
||||
spin_lock_irqsave(&testlocks.lock1, flags);
|
||||
spin_lock(&testlocks.lock2);
|
||||
incritical++;
|
||||
spin_unlock(&testlocks.lock2);
|
||||
spin_unlock_irqrestore(&testlocks.lock1, flags);
|
||||
}
|
||||
|
||||
pr_info("(two-locks): CPU%d entered critical section %d times.\n",
|
||||
cpu, incritical);
|
||||
sync_cpus(TWOLOCKS_TEST, cpu);
|
||||
}
|
||||
|
||||
static void spintest_simple(int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
int incritical = 0;
|
||||
ktime_t test_time = ktime_add_us(ktime_get(),
|
||||
(inner_test_time_secs * USEC_PER_SEC));
|
||||
pr_info("(simple): Running test on CPU %d for %d seconds.\n", cpu,
|
||||
inner_test_time_secs);
|
||||
|
||||
while (ktime_compare(ktime_get(), test_time) < 0)
|
||||
{
|
||||
spin_lock_irqsave(&global_lock_static, flags);
|
||||
incritical++;
|
||||
spin_unlock_irqrestore(&global_lock_static, flags);
|
||||
}
|
||||
|
||||
pr_info("(simple): CPU%d entered critical section %d times.\n",
|
||||
cpu, incritical);
|
||||
sync_cpus(SIMPLE_TEST, cpu);
|
||||
}
|
||||
|
||||
static void spintest_stack_lock(int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
int incritical = 0;
|
||||
ktime_t test_time = ktime_add_us(ktime_get(),
|
||||
(inner_test_time_secs * USEC_PER_SEC));
|
||||
spinlock_t stack_lock;
|
||||
spin_lock_init(&stack_lock);
|
||||
|
||||
pr_info("(stack-lock): Running test on CPU %d for %d seconds.\n", cpu,
|
||||
inner_test_time_secs);
|
||||
|
||||
while (ktime_compare(ktime_get(), test_time) < 0)
|
||||
{
|
||||
spin_lock_irqsave(&global_lock_static, flags);
|
||||
spin_lock(&stack_lock);
|
||||
incritical++;
|
||||
spin_unlock(&stack_lock);
|
||||
spin_unlock_irqrestore(&global_lock_static, flags);
|
||||
}
|
||||
|
||||
pr_info("(stack-lock): CPU%d entered critical section %d times.\n",
|
||||
cpu, incritical);
|
||||
sync_cpus(STACK_LOCK_TEST, cpu);
|
||||
}
|
||||
|
||||
static void spintest_uncached_lock_test(int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
int incritical = 0;
|
||||
ktime_t test_time = ktime_add_us(ktime_get(),
|
||||
(inner_test_time_secs * USEC_PER_SEC));
|
||||
pr_info("(uncached-lock): Running test on CPU %d for %d seconds.\n", cpu,
|
||||
inner_test_time_secs);
|
||||
|
||||
while (ktime_compare(ktime_get(), test_time) < 0)
|
||||
{
|
||||
spin_lock_irqsave(&global_lock_static, flags);
|
||||
spin_lock(uncached_lock);
|
||||
incritical++;
|
||||
spin_unlock(uncached_lock);
|
||||
spin_unlock_irqrestore(&global_lock_static, flags);
|
||||
}
|
||||
|
||||
pr_info("(uncached-lock): CPU%d entered critical section %d times.\n",
|
||||
cpu, incritical);
|
||||
|
||||
sync_cpus(UNCACHED_LOCK_TEST, cpu);
|
||||
}
|
||||
|
||||
static void spinwork_fn(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = (struct delayed_work *)work;
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
if (canceltest) {
|
||||
pr_err("test cancelled!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start interrupt disabled test. This should provide maximum
|
||||
* contention.
|
||||
*/
|
||||
irqs_disabled_spintest(cpu);
|
||||
pr_info("irqs_disabled_spintest is done (CPU:%d iteration:%d)\n",
|
||||
cpu, cpu_start[cpu]);
|
||||
msleep(SPINLOCK_TEST_DELAY_MS);
|
||||
|
||||
spintest_simple(cpu);
|
||||
pr_info("spintest_simple is done (CPU:%d iteration:%d)\n",
|
||||
cpu, cpu_start[cpu]);
|
||||
msleep(SPINLOCK_TEST_DELAY_MS);
|
||||
|
||||
/* Use a spinlock allocated on the stack */
|
||||
spintest_stack_lock(cpu);
|
||||
pr_info("spintest_stack_lock is done (CPU:%d iteration:%d)\n",
|
||||
cpu, cpu_start[cpu]);
|
||||
msleep(SPINLOCK_TEST_DELAY_MS);
|
||||
|
||||
spintest_two_lock_single_cacheline(cpu);
|
||||
pr_info("spintest_two_lock is done (CPU:%d iteration:%d)\n",
|
||||
cpu, cpu_start[cpu]);
|
||||
msleep(SPINLOCK_TEST_DELAY_MS);
|
||||
|
||||
spintest_uncached_lock_test(cpu);
|
||||
pr_info("spintest_uncached_lock_test is done (CPU:%d iteration:%d)\n",
|
||||
cpu, cpu_start[cpu]);
|
||||
msleep(SPINLOCK_TEST_DELAY_MS);
|
||||
|
||||
if (++cpu_start[cpu] < num_test_iters) {
|
||||
schedule_work_on(cpu, &dwork->work);
|
||||
}
|
||||
}
|
||||
|
||||
static int spinlock_test_start(void)
|
||||
{
|
||||
int i, flag;
|
||||
ktime_t test_time;
|
||||
unsigned long total_wait_time;
|
||||
|
||||
start = 1;
|
||||
canceltest = 0;
|
||||
pr_info("Starting spinlock test\n");
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
/* Online all the cores */
|
||||
for_each_present_cpu(i) {
|
||||
if (!cpu_online(i)) {
|
||||
cpu_up(i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(i) {
|
||||
cpu_start[i] = 0;
|
||||
INIT_DEFERRABLE_WORK(&spinworks[i], spinwork_fn);
|
||||
schedule_delayed_work_on(i, &spinworks[i], msecs_to_jiffies(150));
|
||||
pr_info("Scheduling spinlock test on cpu %d\n", i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimate and calculate total wait time according to
|
||||
* - number of toal test cases. (e.g irq_disable, stack spinlock, etc..)
|
||||
* - inner test time for each test case
|
||||
* - number of iterations for each test case
|
||||
* - sleep time during two test cases
|
||||
*/
|
||||
total_wait_time = (NUM_OF_SPINLOCK_TEST + 1) * (inner_test_time_secs * \
|
||||
num_test_iters + SPINLOCK_TEST_DELAY_MS / MSEC_PER_SEC);
|
||||
pr_info("Total test time might be ~%lu seconds\n", total_wait_time);
|
||||
|
||||
test_time = ktime_add_us(ktime_get(),
|
||||
(total_wait_time * USEC_PER_SEC));
|
||||
while (ktime_compare(ktime_get(), test_time) < 0) {
|
||||
flag = 0;
|
||||
for_each_online_cpu(i) {
|
||||
if (cpu_start[i] != num_test_iters) {
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
start = 0;
|
||||
break;
|
||||
}
|
||||
msleep(SPINLOCK_TEST_DELAY_MS);
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
pr_info("spinlock test is done successfully!\n");
|
||||
put_online_cpus();
|
||||
return 0;
|
||||
} else {
|
||||
pr_err("spinlock test Failed!\n");
|
||||
spinlock_test_stop();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int spinlock_test_stop(void)
|
||||
{
|
||||
int i;
|
||||
canceltest = 1;
|
||||
if (!start) {
|
||||
pr_err("No spinlock test is running. Exitting...\n");
|
||||
return -1;
|
||||
}
|
||||
for(i = 0; i < CPU_NUM_MAX; i++){
|
||||
if (spinworks[i].wq != NULL) {
|
||||
pr_info("canceling work oncpu %d\n", i);
|
||||
cancel_delayed_work_sync(&spinworks[i]);
|
||||
}
|
||||
}
|
||||
/* "start" might be updated by the test work thread */
|
||||
if (start)
|
||||
put_online_cpus();
|
||||
start = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinlock_test_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* Cancel the test if it's not done yet */
|
||||
if (start)
|
||||
spinlock_test_stop();
|
||||
|
||||
if (dent) {
|
||||
debugfs_remove_recursive(dent);
|
||||
dent = NULL;
|
||||
}
|
||||
if (global_lock_heap) {
|
||||
kfree(global_lock_heap);
|
||||
global_lock_heap = NULL;
|
||||
}
|
||||
|
||||
if (uncached_lock) {
|
||||
dma_free_coherent(&pdev->dev, sizeof(spinlock_t), uncached_lock, phys);
|
||||
uncached_lock = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinlock_test_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
if (num_possible_cpus() > CPU_NUM_MAX) {
|
||||
dev_err(&pdev->dev, "Number of possible cpus on this target \
|
||||
exceeds the limit %d\n", CPU_NUM_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
global_lock_heap = kzalloc(sizeof(spinlock_t), GFP_KERNEL);
|
||||
if (!global_lock_heap) {
|
||||
dev_err(&pdev->dev, "unable to alloc memory for global lock\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
|
||||
arch_setup_dma_ops(&pdev->dev, 0, U64_MAX, NULL, 0);
|
||||
#endif
|
||||
|
||||
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
|
||||
uncached_lock = dma_alloc_coherent(&pdev->dev, sizeof(spinlock_t), &phys, GFP_KERNEL);
|
||||
if (!uncached_lock) {
|
||||
dev_err(&pdev->dev ,"Failed to allocate Uncached memory for lock\n");
|
||||
kfree(global_lock_heap);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&testlocks.lock1);
|
||||
spin_lock_init(&testlocks.lock2);
|
||||
spin_lock_init(global_lock_heap);
|
||||
spin_lock_init(uncached_lock);
|
||||
|
||||
ret = creat_spinlock_debugfs();
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "create spinlock debugfs failed!\n");
|
||||
kfree(global_lock_heap);
|
||||
dma_free_coherent(&pdev->dev, sizeof(spinlock_t), uncached_lock, phys);
|
||||
uncached_lock = NULL;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver spinlock_test_driver = {
|
||||
.probe = spinlock_test_probe,
|
||||
.remove = spinlock_test_remove,
|
||||
.driver = {
|
||||
.name = MODULE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static void platform_spinlock_release(struct device* dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static struct platform_device spinlock_test_device = {
|
||||
.name = MODULE_NAME,
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.release = platform_spinlock_release,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init spinlock_test_init(void)
|
||||
{
|
||||
platform_device_register(&spinlock_test_device);
|
||||
return platform_driver_register(&spinlock_test_driver);
|
||||
}
|
||||
|
||||
static void __exit spinlock_test_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&spinlock_test_driver);
|
||||
platform_device_unregister(&spinlock_test_device);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("SPINLOCK TEST");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
module_init(spinlock_test_init);
|
||||
module_exit(spinlock_test_exit);
|
||||
@@ -224,7 +224,7 @@ struct extcon_cable {
|
||||
};
|
||||
|
||||
static struct class *extcon_class;
|
||||
#if defined(CONFIG_ANDROID)
|
||||
#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH)
|
||||
static struct class_compat *switch_class;
|
||||
#endif /* CONFIG_ANDROID */
|
||||
|
||||
@@ -1011,7 +1011,7 @@ static int create_extcon_class(void)
|
||||
return PTR_ERR(extcon_class);
|
||||
extcon_class->dev_groups = extcon_groups;
|
||||
|
||||
#if defined(CONFIG_ANDROID)
|
||||
#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH)
|
||||
switch_class = class_compat_register("switch");
|
||||
if (WARN(!switch_class, "cannot allocate"))
|
||||
return -ENOMEM;
|
||||
@@ -1237,7 +1237,7 @@ int extcon_dev_register(struct extcon_dev *edev)
|
||||
put_device(&edev->dev);
|
||||
goto err_dev;
|
||||
}
|
||||
#if defined(CONFIG_ANDROID)
|
||||
#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH)
|
||||
if (switch_class)
|
||||
ret = class_compat_create_link(switch_class, &edev->dev, NULL);
|
||||
#endif /* CONFIG_ANDROID */
|
||||
@@ -1333,7 +1333,7 @@ void extcon_dev_unregister(struct extcon_dev *edev)
|
||||
kfree(edev->cables);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ANDROID)
|
||||
#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH)
|
||||
if (switch_class)
|
||||
class_compat_remove_link(switch_class, &edev->dev, NULL);
|
||||
#endif
|
||||
@@ -1407,7 +1407,7 @@ module_init(extcon_class_init);
|
||||
|
||||
static void __exit extcon_class_exit(void)
|
||||
{
|
||||
#if defined(CONFIG_ANDROID)
|
||||
#if defined(CONFIG_ANDROID) && !IS_ENABLED(CONFIG_SWITCH)
|
||||
class_compat_unregister(switch_class);
|
||||
#endif
|
||||
class_destroy(extcon_class);
|
||||
|
||||
32
drivers/fingerprint/Kconfig
Executable file
32
drivers/fingerprint/Kconfig
Executable file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Sensor drivers configuration
|
||||
#
|
||||
menuconfig SENSORS_FINGERPRINT
|
||||
bool "Finger Print Sensor devices"
|
||||
help
|
||||
Say Y here, and a list of sensors drivers will be displayed.
|
||||
Everything that didn't fit into the other categories is here. This option
|
||||
doesn't affect the kernel.
|
||||
|
||||
if SENSORS_FINGERPRINT
|
||||
|
||||
config SENSORS_VFS8XXX
|
||||
tristate "VFS8XXX fingerprint sensor support"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for Synaptics's
|
||||
fingerprint sensor NAMSAN.
|
||||
|
||||
config SENSORS_ET5XX
|
||||
tristate "ET5XX fingerprint sensor support"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for Egistec's
|
||||
fingerprint sensor ET5XX.
|
||||
|
||||
config SENSORS_GW32X
|
||||
tristate "generic goodix fingerprint driver"
|
||||
default n
|
||||
help
|
||||
add support for goodix fingerprint driver.
|
||||
endif # SENSORS_FINGERPRINT
|
||||
12
drivers/fingerprint/Makefile
Executable file
12
drivers/fingerprint/Makefile
Executable file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Makefile for the sensors drivers.
|
||||
#
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
ccflags-y := $(KBUILD_FP_SENSOR_CFLAGS)
|
||||
|
||||
obj-$(CONFIG_SENSORS_FINGERPRINT) += fingerprint_sysfs.o
|
||||
obj-$(CONFIG_SENSORS_VFS8XXX) += vfs8xxx.o
|
||||
obj-$(CONFIG_SENSORS_ET5XX) += et5xx-spi.o et5xx-spi_data_transfer.o
|
||||
obj-$(CONFIG_SENSORS_GW32X) += gf_common.o gf_platform.o
|
||||
1398
drivers/fingerprint/et5xx-spi.c
Executable file
1398
drivers/fingerprint/et5xx-spi.c
Executable file
File diff suppressed because it is too large
Load Diff
1056
drivers/fingerprint/et5xx-spi_data_transfer.c
Executable file
1056
drivers/fingerprint/et5xx-spi_data_transfer.c
Executable file
File diff suppressed because it is too large
Load Diff
240
drivers/fingerprint/et5xx.h
Executable file
240
drivers/fingerprint/et5xx.h
Executable file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Samsung Electronics. 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 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.
|
||||
*/
|
||||
|
||||
#ifndef _ET5XX_LINUX_DIRVER_H_
|
||||
#define _ET5XX_LINUX_DIRVER_H_
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/wakelock.h>
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/pl330.h>
|
||||
#endif
|
||||
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include "../pinctrl/core.h"
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
/*#define ET5XX_SPI_DEBUG*/
|
||||
|
||||
#ifdef ET5XX_SPI_DEBUG
|
||||
#define DEBUG_PRINT(fmt, args...) pr_err(fmt, ## args)
|
||||
#else
|
||||
#define DEBUG_PRINT(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define VENDOR "EGISTEC"
|
||||
#define CHIP_ID "ET5XX"
|
||||
|
||||
/* assigned */
|
||||
#define ET5XX_MAJOR 152
|
||||
/* ... up to 256 */
|
||||
#define N_SPI_MINORS 32
|
||||
|
||||
#define OP_REG_R 0x20
|
||||
#define OP_REG_R_C 0x22
|
||||
#define OP_REG_R_C_BW 0x23
|
||||
#define OP_REG_W 0x24
|
||||
#define OP_REG_W_C 0x26
|
||||
#define OP_REG_W_C_BW 0x27
|
||||
#define OP_NVM_ON_R 0x40
|
||||
#define OP_NVM_ON_W 0x42
|
||||
#define OP_NVM_RE 0x44
|
||||
#define OP_NVM_WE 0x46
|
||||
#define OP_NVM_OFF 0x48
|
||||
#define OP_IMG_R 0x50
|
||||
#define OP_VDM_R 0x60
|
||||
#define OP_VDM_W 0x62
|
||||
#define BITS_PER_WORD 8
|
||||
|
||||
#define SLOW_BAUD_RATE 12500000
|
||||
|
||||
#define DRDY_IRQ_ENABLE 1
|
||||
#define DRDY_IRQ_DISABLE 0
|
||||
|
||||
#define ET5XX_INT_DETECTION_PERIOD 10
|
||||
#define ET5XX_DETECTION_THRESHOLD 10
|
||||
|
||||
#define FP_REGISTER_READ 0x01
|
||||
#define FP_REGISTER_WRITE 0x02
|
||||
#define FP_GET_ONE_IMG 0x03
|
||||
#define FP_SENSOR_RESET 0x04
|
||||
#define FP_POWER_CONTROL 0x05
|
||||
#define FP_SET_SPI_CLOCK 0x06
|
||||
#define FP_RESET_SET 0x07
|
||||
#define FP_REGISTER_BREAD 0x20
|
||||
#define FP_REGISTER_BWRITE 0x21
|
||||
#define FP_REGISTER_MREAD 0x22
|
||||
#define FP_REGISTER_MWRITE 0x23
|
||||
#define FP_REGISTER_BREAD_BACKWARD 0x24
|
||||
#define FP_REGISTER_BWRITE_BACKWARD 0x25
|
||||
#define FP_VDM_READ 0x30
|
||||
#define FP_VDM_WRITE 0x31
|
||||
#define FP_NVM_READ 0X40
|
||||
#define FP_NVM_WRITE 0x41
|
||||
#define FP_NVM_OFF 0x42
|
||||
#define FP_NVM_WRITEEX 0x43
|
||||
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
#define FP_DISABLE_SPI_CLOCK 0x10
|
||||
#define FP_CPU_SPEEDUP 0x11
|
||||
#define FP_SET_SENSOR_TYPE 0x14
|
||||
/* Do not use ioctl number 0x15 */
|
||||
#define FP_SET_LOCKSCREEN 0x16
|
||||
#define FP_SET_WAKE_UP_SIGNAL 0x17
|
||||
#endif
|
||||
#define FP_POWER_CONTROL_ET5XX 0x18
|
||||
#define FP_SENSOR_ORIENT 0x19
|
||||
#define FP_SPI_VALUE 0x1a
|
||||
#define FP_IOCTL_RESERVED_01 0x1b
|
||||
#define FP_IOCTL_RESERVED_02 0x1c
|
||||
|
||||
|
||||
|
||||
/* trigger signal initial routine */
|
||||
#define INT_TRIGGER_INIT 0xa4
|
||||
/* trigger signal close routine */
|
||||
#define INT_TRIGGER_CLOSE 0xa5
|
||||
/* read trigger status */
|
||||
#define INT_TRIGGER_READ 0xa6
|
||||
/* polling trigger status */
|
||||
#define INT_TRIGGER_POLLING 0xa7
|
||||
/* polling abort */
|
||||
#define INT_TRIGGER_ABORT 0xa8
|
||||
/* Sensor Registers */
|
||||
#define FDATA_ET5XX_ADDR 0x00
|
||||
#define FSTATUS_ET5XX_ADDR 0x01
|
||||
/* Detect Define */
|
||||
#define FRAME_READY_MASK 0x01
|
||||
|
||||
#define SHIFT_BYTE_OF_IMAGE 0
|
||||
#define DIVISION_OF_IMAGE 4
|
||||
#define LARGE_SPI_TRANSFER_BUFFER 64
|
||||
#define MAX_NVM_LEN (32 * 2) /* NVM length in bytes (32 * 16 bits internally) */
|
||||
#define NVM_WRITE_LENGTH 4096
|
||||
#define DETECT_ADM 1
|
||||
|
||||
struct egis_ioc_transfer {
|
||||
u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
|
||||
__u32 len;
|
||||
__u32 speed_hz;
|
||||
|
||||
__u16 delay_usecs;
|
||||
__u8 bits_per_word;
|
||||
__u8 cs_change;
|
||||
__u8 opcode;
|
||||
__u8 pad[3];
|
||||
|
||||
};
|
||||
|
||||
#define EGIS_IOC_MAGIC 'k'
|
||||
#define EGIS_MSGSIZE(N) \
|
||||
((((N)*(sizeof(struct egis_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
|
||||
? ((N)*(sizeof(struct egis_ioc_transfer))) : 0)
|
||||
#define EGIS_IOC_MESSAGE(N) _IOW(EGIS_IOC_MAGIC, 0, char[EGIS_MSGSIZE(N)])
|
||||
|
||||
struct etspi_data {
|
||||
dev_t devt;
|
||||
spinlock_t spi_lock;
|
||||
struct spi_device *spi;
|
||||
struct list_head device_entry;
|
||||
|
||||
/* buffer is NULL unless this device is open (users > 0) */
|
||||
struct mutex buf_lock;
|
||||
unsigned int users;
|
||||
u8 *buf;/* tx buffer for sensor register read/write */
|
||||
unsigned int bufsiz; /* MAX size of tx and rx buffer */
|
||||
unsigned int drdyPin; /* DRDY GPIO pin number */
|
||||
unsigned int sleepPin; /* Sleep GPIO pin number */
|
||||
unsigned int ldo_pin; /* Ldo GPIO pin number */
|
||||
unsigned int min_cpufreq_limit;
|
||||
unsigned int spi_cs; /* spi cs pin <temporary gpio setting> */
|
||||
|
||||
unsigned int drdy_irq_flag; /* irq flag */
|
||||
bool ldo_onoff;
|
||||
|
||||
/* For polling interrupt */
|
||||
int int_count;
|
||||
struct timer_list timer;
|
||||
struct work_struct work_debug;
|
||||
struct workqueue_struct *wq_dbg;
|
||||
struct timer_list dbg_timer;
|
||||
int sensortype;
|
||||
u32 spi_value;
|
||||
struct device *fp_device;
|
||||
int reset_count;
|
||||
int interrupt_count;
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
bool enabled_clk;
|
||||
bool isGpio_cfgDone;
|
||||
struct wake_lock fp_spi_lock;
|
||||
#endif
|
||||
struct wake_lock fp_signal_lock;
|
||||
bool tz_mode;
|
||||
int detect_period;
|
||||
int detect_threshold;
|
||||
bool finger_on;
|
||||
const char *chipid;
|
||||
unsigned int orient;
|
||||
struct pinctrl *p;
|
||||
struct pinctrl_state *pins_sleep;
|
||||
struct pinctrl_state *pins_idle;
|
||||
#ifdef NONTZ_BOOST_CONTROL
|
||||
atomic_t is_boosting;
|
||||
atomic_t is_sensor_ok;
|
||||
#endif
|
||||
struct pm_qos_request pm_qos;
|
||||
};
|
||||
|
||||
int etspi_io_burst_read_register(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_burst_read_register_backward(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_burst_write_register(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_burst_write_register_backward(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_read_register(struct etspi_data *etspi,
|
||||
u8 *addr, u8 *buf);
|
||||
int etspi_io_read_registerex(struct etspi_data *etspi,
|
||||
u8 *addr, u8 *buf, u32 len);
|
||||
int etspi_io_write_register(struct etspi_data *etspi,
|
||||
u8 *buf);
|
||||
int etspi_read_register(struct etspi_data *etspi,
|
||||
u8 addr, u8 *buf);
|
||||
int etspi_write_register(struct etspi_data *etspi,
|
||||
u8 addr, u8 buf);
|
||||
int etspi_io_nvm_read(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_nvm_write(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_nvm_writeex(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_nvm_off(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_vdm_read(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_vdm_write(struct etspi_data *etspi,
|
||||
struct egis_ioc_transfer *ioc);
|
||||
int etspi_io_get_frame(struct etspi_data *etspi, u8 *frame, u32 size);
|
||||
#endif
|
||||
47
drivers/fingerprint/fingerprint.h
Executable file
47
drivers/fingerprint/fingerprint.h
Executable file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Samsung Electronics. 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 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.
|
||||
*/
|
||||
|
||||
#ifndef FINGERPRINT_H_
|
||||
#define FINGERPRINT_H_
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include "fingerprint_sysfs.h"
|
||||
|
||||
/* fingerprint debug timer */
|
||||
#define FPSENSOR_DEBUG_TIMER_SEC (10 * HZ)
|
||||
|
||||
enum {
|
||||
DETECT_NORMAL = 0,
|
||||
DETECT_ADM, /* Always on Detect Mode */
|
||||
};
|
||||
|
||||
/* For Sensor Type Check */
|
||||
enum {
|
||||
SENSOR_OOO = -2,
|
||||
SENSOR_UNKNOWN,
|
||||
SENSOR_FAILED,
|
||||
SENSOR_VIPER,
|
||||
SENSOR_RAPTOR,
|
||||
SENSOR_EGIS,
|
||||
SENSOR_VIPER_WOG,
|
||||
SENSOR_NAMSAN,
|
||||
SENSOR_GOODIX,
|
||||
SENSOR_QBT2000,
|
||||
SENSOR_MAXIMUM,
|
||||
};
|
||||
|
||||
#define SENSOR_STATUS_SIZE 10
|
||||
static char sensor_status[SENSOR_STATUS_SIZE][10] = {"ooo", "unknown", "failed",
|
||||
"viper", "raptor", "egis", "viper_wog", "namsan", "goodix", "qbt2000"};
|
||||
|
||||
#endif
|
||||
108
drivers/fingerprint/fingerprint_sysfs.c
Executable file
108
drivers/fingerprint/fingerprint_sysfs.c
Executable file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Samsung Electronics. 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 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* fingerprint sysfs class
|
||||
*/
|
||||
|
||||
#include "fingerprint_sysfs.h"
|
||||
|
||||
struct class *fingerprint_class;
|
||||
EXPORT_SYMBOL_GPL(fingerprint_class);
|
||||
|
||||
/*
|
||||
* Create sysfs interface
|
||||
*/
|
||||
void set_fingerprint_attr(struct device *dev,
|
||||
struct device_attribute *attributes[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; attributes[i] != NULL; i++)
|
||||
if ((device_create_file(dev, attributes[i])) < 0)
|
||||
pr_err("%s: fail device_create_file (dev, attributes[%d])\n", __func__, i);
|
||||
}
|
||||
|
||||
int fingerprint_register(struct device *dev, void *drvdata,
|
||||
struct device_attribute *attributes[], char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!fingerprint_class) {
|
||||
fingerprint_class = class_create(THIS_MODULE, "fingerprint");
|
||||
if (IS_ERR(fingerprint_class))
|
||||
return PTR_ERR(fingerprint_class);
|
||||
}
|
||||
|
||||
dev = device_create(fingerprint_class, NULL, 0, drvdata, "%s", name);
|
||||
|
||||
if (IS_ERR(dev)) {
|
||||
ret = PTR_ERR(dev);
|
||||
pr_err("%s: device_create failed! [%d]\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_fingerprint_attr(dev, attributes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fingerprint_register);
|
||||
|
||||
void fingerprint_unregister(struct device *dev,
|
||||
struct device_attribute *attributes[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; attributes[i] != NULL; i++)
|
||||
device_remove_file(dev, attributes[i]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fingerprint_unregister);
|
||||
|
||||
void destroy_fingerprint_class(void)
|
||||
{
|
||||
if (fingerprint_class) {
|
||||
class_destroy(fingerprint_class);
|
||||
fingerprint_class = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(destroy_fingerprint_class);
|
||||
|
||||
static int __init fingerprint_class_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
fingerprint_class = class_create(THIS_MODULE, "fingerprint");
|
||||
|
||||
if (IS_ERR(fingerprint_class)) {
|
||||
pr_err("%s, create fingerprint_class is failed.\n",
|
||||
__func__);
|
||||
return PTR_ERR(fingerprint_class);
|
||||
}
|
||||
|
||||
fingerprint_class->dev_uevent = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit fingerprint_class_exit(void)
|
||||
{
|
||||
if (fingerprint_class) {
|
||||
class_destroy(fingerprint_class);
|
||||
fingerprint_class = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
subsys_initcall(fingerprint_class_init);
|
||||
module_exit(fingerprint_class_exit);
|
||||
|
||||
MODULE_DESCRIPTION("fingerprint sysfs class");
|
||||
MODULE_LICENSE("GPL");
|
||||
34
drivers/fingerprint/fingerprint_sysfs.h
Executable file
34
drivers/fingerprint/fingerprint_sysfs.h
Executable file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2017, Samsung Electronics Co. Ltd. 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 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FINGERPRINTSYSFS_H_
|
||||
#define FINGERPRINTSYSFS_H_
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
void set_fingerprint_attr(struct device *dev,
|
||||
struct device_attribute *attributes[]);
|
||||
int fingerprint_register(struct device *dev, void *drvdata,
|
||||
struct device_attribute *attributes[], char *name);
|
||||
void fingerprint_unregister(struct device *dev,
|
||||
struct device_attribute *attributes[]);
|
||||
void destroy_fingerprint_class(void);
|
||||
|
||||
#endif
|
||||
1162
drivers/fingerprint/gf_common.c
Normal file
1162
drivers/fingerprint/gf_common.c
Normal file
File diff suppressed because it is too large
Load Diff
227
drivers/fingerprint/gf_common.h
Normal file
227
drivers/fingerprint/gf_common.h
Normal file
@@ -0,0 +1,227 @@
|
||||
#ifndef __GF_SPI_DRIVER_H
|
||||
#define __GF_SPI_DRIVER_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/cdev.h>
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
#include <linux/earlysuspend.h>
|
||||
#else
|
||||
#include <linux/notifier.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
#if defined (CONFIG_ARCH_EXYNOS9) || defined(CONFIG_ARCH_EXYNOS8)\
|
||||
|| defined (CONFIG_ARCH_EXYNOS7)
|
||||
#include <linux/smc.h>
|
||||
#endif
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/pl330.h>
|
||||
#if defined(CONFIG_SECURE_OS_BOOSTER_API)
|
||||
#if defined(CONFIG_SOC_EXYNOS7870) || defined(CONFIG_SOC_EXYNOS7880)\
|
||||
|| defined(CONFIG_SOC_EXYNOS7570) || defined(CONFIG_SOC_EXYNOS7885)
|
||||
#include <soc/samsung/secos_booster.h>
|
||||
#else
|
||||
#include <mach/secos_booster.h>
|
||||
#endif
|
||||
#elif defined(CONFIG_TZDEV_BOOST)
|
||||
#include <../drivers/misc/tzdev/tz_boost.h>
|
||||
#endif
|
||||
|
||||
struct sec_spi_info {
|
||||
int port;
|
||||
unsigned long speed;
|
||||
};
|
||||
#endif
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include "../pinctrl/core.h"
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
/*
|
||||
* This feature is temporary for exynos AP only.
|
||||
* It's for control GPIO config on enabled TZ before enable GPIO protection.
|
||||
* If it's still defined this feature after enable GPIO protection,
|
||||
* it will be happened kernel panic
|
||||
* So it should be un-defined after enable GPIO protection
|
||||
*/
|
||||
#undef DISABLED_GPIO_PROTECTION
|
||||
|
||||
#define GF_IOC_MAGIC 'g'
|
||||
|
||||
#define GF_GW32J_CHIP_ID 0x00220e
|
||||
#define GF_GW32N_CHIP_ID 0x002215
|
||||
#define GF_GW36H_CHIP_ID 0x002504
|
||||
#define GF_GW36C_CHIP_ID 0x002502
|
||||
|
||||
#define MAX_BAUD_RATE 4800000
|
||||
|
||||
enum gf_netlink_cmd {
|
||||
GF_NETLINK_TEST = 0,
|
||||
GF_NETLINK_IRQ = 1,
|
||||
GF_NETLINK_SCREEN_OFF,
|
||||
GF_NETLINK_SCREEN_ON
|
||||
};
|
||||
|
||||
struct gf_ioc_transfer {
|
||||
u8 cmd; /* spi read = 0, spi write = 1 */
|
||||
u8 reserved;
|
||||
u16 addr;
|
||||
u32 len;
|
||||
u8 *buf;
|
||||
};
|
||||
|
||||
struct gf_ioc_transfer_raw {
|
||||
u32 len;
|
||||
u8 *read_buf;
|
||||
u8 *write_buf;
|
||||
uint32_t high_time;
|
||||
uint32_t low_time;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY
|
||||
struct gf_ioc_transfer_32 {
|
||||
u8 cmd; /* spi read = 0, spi write = 1 */
|
||||
u8 reserved;
|
||||
u16 addr;
|
||||
u32 len;
|
||||
u32 buf;
|
||||
};
|
||||
|
||||
struct gf_ioc_transfer_raw_32 {
|
||||
u32 len;
|
||||
u32 read_buf;
|
||||
u32 write_buf;
|
||||
uint32_t high_time;
|
||||
uint32_t low_time;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* define commands */
|
||||
#define GF_IOC_INIT _IOR(GF_IOC_MAGIC, 0, u8)
|
||||
#define GF_IOC_EXIT _IO(GF_IOC_MAGIC, 1)
|
||||
#define GF_IOC_RESET _IO(GF_IOC_MAGIC, 2)
|
||||
#define GF_IOC_ENABLE_IRQ _IO(GF_IOC_MAGIC, 3)
|
||||
#define GF_IOC_DISABLE_IRQ _IO(GF_IOC_MAGIC, 4)
|
||||
#define GF_IOC_ENABLE_SPI_CLK _IOW(GF_IOC_MAGIC, 5, uint32_t)
|
||||
#define GF_IOC_DISABLE_SPI_CLK _IO(GF_IOC_MAGIC, 6)
|
||||
#define GF_IOC_ENABLE_POWER _IO(GF_IOC_MAGIC, 7)
|
||||
#define GF_IOC_DISABLE_POWER _IO(GF_IOC_MAGIC, 8)
|
||||
#define GF_IOC_ENTER_SLEEP_MODE _IO(GF_IOC_MAGIC, 10)
|
||||
#define GF_IOC_GET_FW_INFO _IOR(GF_IOC_MAGIC, 11, u8)
|
||||
#define GF_IOC_REMOVE _IO(GF_IOC_MAGIC, 12)
|
||||
|
||||
/* for SPI REE transfer */
|
||||
#define GF_IOC_TRANSFER_CMD _IOWR(GF_IOC_MAGIC, 15, \
|
||||
struct gf_ioc_transfer)
|
||||
#ifndef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY
|
||||
#define GF_IOC_TRANSFER_RAW_CMD _IOWR(GF_IOC_MAGIC, 16, \
|
||||
struct gf_ioc_transfer_raw)
|
||||
#else
|
||||
#define GF_IOC_TRANSFER_RAW_CMD _IOWR(GF_IOC_MAGIC, 16, \
|
||||
struct gf_ioc_transfer_raw_32)
|
||||
#endif
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
#define GF_IOC_SET_SENSOR_TYPE _IOW(GF_IOC_MAGIC, 18, unsigned int)
|
||||
#endif
|
||||
#define GF_IOC_POWER_CONTROL _IOW(GF_IOC_MAGIC, 19, unsigned int)
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
#define GF_IOC_SPEEDUP _IOW(GF_IOC_MAGIC, 20, unsigned int)
|
||||
#define GF_IOC_SET_LOCKSCREEN _IOW(GF_IOC_MAGIC, 21, unsigned int)
|
||||
#endif
|
||||
#define GF_IOC_GET_ORIENT _IOR(GF_IOC_MAGIC, 22, unsigned int)
|
||||
|
||||
#define GF_IOC_MAXNR 23 /* THIS MACRO IS NOT USED NOW... */
|
||||
|
||||
struct gf_device {
|
||||
dev_t devno;
|
||||
struct cdev cdev;
|
||||
struct device *fp_device;
|
||||
struct class *class;
|
||||
struct spi_device *spi;
|
||||
int device_count;
|
||||
|
||||
spinlock_t spi_lock;
|
||||
struct list_head device_entry;
|
||||
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
||||
u8 *spi_buffer;
|
||||
#endif
|
||||
struct mutex buf_lock;
|
||||
struct mutex release_lock;
|
||||
struct sock *nl_sk;
|
||||
u8 buf_status;
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
struct early_suspend early_suspend;
|
||||
#else
|
||||
struct notifier_block notifier;
|
||||
#endif
|
||||
|
||||
u8 irq_enabled;
|
||||
u8 sig_count;
|
||||
u8 system_status;
|
||||
|
||||
u32 pwr_gpio;
|
||||
u32 reset_gpio;
|
||||
u32 irq_gpio;
|
||||
u32 irq;
|
||||
u8 need_update;
|
||||
/* for netlink use */
|
||||
int pid;
|
||||
|
||||
struct work_struct work_debug;
|
||||
struct workqueue_struct *wq_dbg;
|
||||
struct timer_list dbg_timer;
|
||||
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
bool enabled_clk;
|
||||
#endif
|
||||
unsigned int current_spi_speed;
|
||||
unsigned int orient;
|
||||
unsigned int min_cpufreq_limit;
|
||||
int sensortype;
|
||||
int reset_count;
|
||||
int interrupt_count;
|
||||
bool ldo_onoff;
|
||||
bool tz_mode;
|
||||
const char *chipid;
|
||||
struct wake_lock wake_lock;
|
||||
|
||||
struct pinctrl *p;
|
||||
struct pinctrl_state *pins_sleep;
|
||||
struct pinctrl_state *pins_idle;
|
||||
struct pm_qos_request pm_qos;
|
||||
};
|
||||
|
||||
|
||||
int gfspi_get_gpio_dts_info(struct device *dev, struct gf_device *gf_dev);
|
||||
void gfspi_cleanup_info(struct gf_device *gf_dev);
|
||||
void gfspi_hw_power_enable(struct gf_device *gf_dev, u8 onoff);
|
||||
int gfspi_spi_clk_enable(struct gf_device *gf_dev);
|
||||
int gfspi_spi_clk_disable(struct gf_device *gf_dev);
|
||||
void gfspi_hw_reset(struct gf_device *gf_dev, u8 delay);
|
||||
void gfspi_spi_setup_conf(struct gf_device *gf_dev, u32 speed);
|
||||
int gfspi_pin_control(struct gf_device *gf_dev, bool pin_set);
|
||||
|
||||
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
||||
int gfspi_spi_read_bytes(struct gf_device *gf_dev, u16 addr,
|
||||
u32 data_len, u8 *rx_buf);
|
||||
int gfspi_spi_write_bytes(struct gf_device *gf_dev, u16 addr,
|
||||
u32 data_len, u8 *tx_buf);
|
||||
int gfspi_spi_read_byte(struct gf_device *gf_dev, u16 addr, u8 *value);
|
||||
int gfspi_spi_write_byte(struct gf_device *gf_dev, u16 addr, u8 value);
|
||||
int gfspi_ioctl_transfer_raw_cmd(struct gf_device *gf_dev,
|
||||
unsigned long arg, unsigned int bufsiz);
|
||||
int gfspi_ioctl_spi_init_cfg_cmd(struct gf_device *gf_dev,
|
||||
unsigned long arg);
|
||||
#endif /* !ENABLE_SENSORS_FPRINT_SECURE */
|
||||
|
||||
#endif /* __GF_SPI_DRIVER_H */
|
||||
470
drivers/fingerprint/gf_platform.c
Normal file
470
drivers/fingerprint/gf_platform.c
Normal file
@@ -0,0 +1,470 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include "gf_common.h"
|
||||
|
||||
void gfspi_spi_setup_conf(struct gf_device *gf_dev, u32 speed)
|
||||
{
|
||||
u32 max_speed_hz;
|
||||
|
||||
switch (speed) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 6:
|
||||
case 8:
|
||||
default:
|
||||
max_speed_hz = MAX_BAUD_RATE;
|
||||
break;
|
||||
}
|
||||
|
||||
gf_dev->spi->mode = SPI_MODE_0;
|
||||
gf_dev->spi->max_speed_hz = max_speed_hz;
|
||||
gf_dev->spi->bits_per_word = 8;
|
||||
|
||||
gf_dev->current_spi_speed = max_speed_hz;
|
||||
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
||||
if (spi_setup(gf_dev->spi))
|
||||
pr_err("%s, failed to setup spi conf\n", __func__);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
||||
int gfspi_spi_read_bytes(struct gf_device *gf_dev, u16 addr,
|
||||
u32 data_len, u8 *rx_buf)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer *xfer = NULL;
|
||||
u8 *tmp_buf = NULL;
|
||||
|
||||
xfer = kzalloc(sizeof(*xfer) * 2, GFP_KERNEL);
|
||||
if (xfer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tmp_buf = gf_dev->spi_buffer;
|
||||
|
||||
spi_message_init(&msg);
|
||||
*tmp_buf = 0xF0;
|
||||
*(tmp_buf + 1) = (u8)((addr >> 8) & 0xFF);
|
||||
*(tmp_buf + 2) = (u8)(addr & 0xFF);
|
||||
xfer[0].tx_buf = tmp_buf;
|
||||
xfer[0].len = 3;
|
||||
xfer[0].delay_usecs = 5;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
spi_message_init(&msg);
|
||||
/* memset((tmp_buf + 4), 0x00, data_len + 1); */
|
||||
/* 4 bytes align */
|
||||
*(tmp_buf + 4) = 0xF1;
|
||||
xfer[1].tx_buf = tmp_buf + 4;
|
||||
xfer[1].rx_buf = tmp_buf + 4;
|
||||
xfer[1].len = data_len + 1;
|
||||
xfer[1].delay_usecs = 5;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
memcpy(rx_buf, (tmp_buf + 5), data_len);
|
||||
|
||||
kfree(xfer);
|
||||
if (xfer != NULL)
|
||||
xfer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfspi_spi_write_bytes(struct gf_device *gf_dev, u16 addr,
|
||||
u32 data_len, u8 *tx_buf)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer *xfer = NULL;
|
||||
u8 *tmp_buf = NULL;
|
||||
|
||||
xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
|
||||
if (xfer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tmp_buf = gf_dev->spi_buffer;
|
||||
|
||||
spi_message_init(&msg);
|
||||
*tmp_buf = 0xF0;
|
||||
*(tmp_buf + 1) = (u8)((addr >> 8) & 0xFF);
|
||||
*(tmp_buf + 2) = (u8)(addr & 0xFF);
|
||||
memcpy(tmp_buf + 3, tx_buf, data_len);
|
||||
xfer[0].len = data_len + 3;
|
||||
xfer[0].tx_buf = tmp_buf;
|
||||
xfer[0].delay_usecs = 5;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
kfree(xfer);
|
||||
if (xfer != NULL)
|
||||
xfer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfspi_spi_read_byte(struct gf_device *gf_dev, u16 addr, u8 *value)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer *xfer = NULL;
|
||||
|
||||
xfer = kzalloc(sizeof(*xfer) * 2, GFP_KERNEL);
|
||||
if (xfer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&msg);
|
||||
*gf_dev->spi_buffer = 0xF0;
|
||||
*(gf_dev->spi_buffer + 1) = (u8)((addr >> 8) & 0xFF);
|
||||
*(gf_dev->spi_buffer + 2) = (u8)(addr & 0xFF);
|
||||
|
||||
xfer[0].tx_buf = gf_dev->spi_buffer;
|
||||
xfer[0].len = 3;
|
||||
xfer[0].delay_usecs = 5;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
spi_message_init(&msg);
|
||||
/* 4 bytes align */
|
||||
*(gf_dev->spi_buffer + 4) = 0xF1;
|
||||
xfer[1].tx_buf = gf_dev->spi_buffer + 4;
|
||||
xfer[1].rx_buf = gf_dev->spi_buffer + 4;
|
||||
xfer[1].len = 2;
|
||||
xfer[1].delay_usecs = 5;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
*value = *(gf_dev->spi_buffer + 5);
|
||||
|
||||
kfree(xfer);
|
||||
if (xfer != NULL)
|
||||
xfer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfspi_spi_write_byte(struct gf_device *gf_dev, u16 addr, u8 value)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer *xfer = NULL;
|
||||
|
||||
xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
|
||||
if (xfer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&msg);
|
||||
*gf_dev->spi_buffer = 0xF0;
|
||||
*(gf_dev->spi_buffer + 1) = (u8)((addr >> 8) & 0xFF);
|
||||
*(gf_dev->spi_buffer + 2) = (u8)(addr & 0xFF);
|
||||
*(gf_dev->spi_buffer + 3) = value;
|
||||
|
||||
xfer[0].tx_buf = gf_dev->spi_buffer;
|
||||
xfer[0].len = 3 + 1;
|
||||
xfer[0].delay_usecs = 5;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
kfree(xfer);
|
||||
if (xfer != NULL)
|
||||
xfer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gfspi_spi_transfer_raw(struct gf_device *gf_dev, u8 *tx_buf,
|
||||
u8 *rx_buf, u32 len)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer;
|
||||
|
||||
spi_message_init(&msg);
|
||||
memset(&xfer, 0, sizeof(struct spi_transfer));
|
||||
|
||||
xfer.tx_buf = tx_buf;
|
||||
xfer.rx_buf = rx_buf;
|
||||
xfer.len = len;
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
spi_sync(gf_dev->spi, &msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfspi_ioctl_transfer_raw_cmd(struct gf_device *gf_dev,
|
||||
unsigned long arg, unsigned int bufsiz)
|
||||
{
|
||||
struct gf_ioc_transfer_raw ioc_xraw;
|
||||
int retval = 0;
|
||||
#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY
|
||||
struct gf_ioc_transfer_raw_32 ioc_xraw_32;
|
||||
u64 read_buf_64;
|
||||
u64 write_buf_64;
|
||||
#endif
|
||||
|
||||
do {
|
||||
u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
uint32_t len;
|
||||
|
||||
#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY
|
||||
if (copy_from_user(&ioc_xraw_32, (void __user *)arg,
|
||||
sizeof(struct gf_ioc_transfer_raw_32)))
|
||||
#else
|
||||
if (copy_from_user(&ioc_xraw, (void __user *)arg,
|
||||
sizeof(struct gf_ioc_transfer_raw)))
|
||||
#endif
|
||||
{
|
||||
pr_err("%s: Failed to copy gf_ioc_transfer_raw from user to kernel\n",
|
||||
__func__);
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SENSORS_FINGERPRINT_32BITS_PLATFORM_ONLY
|
||||
read_buf_64 = (u64)ioc_xraw_32.read_buf;
|
||||
write_buf_64 = (u64)ioc_xraw_32.write_buf;
|
||||
ioc_xraw.read_buf = (u8 *)read_buf_64;
|
||||
ioc_xraw.write_buf = (u8 *)write_buf_64;
|
||||
ioc_xraw.high_time = ioc_xraw_32.high_time;
|
||||
ioc_xraw.len = ioc_xraw_32.len;
|
||||
ioc_xraw.low_time = ioc_xraw_32.low_time;
|
||||
#endif
|
||||
|
||||
if ((ioc_xraw.len > bufsiz) || (ioc_xraw.len == 0)) {
|
||||
pr_err("%s: request transfer length larger than maximum buffer\n",
|
||||
__func__);
|
||||
pr_err("%s: buf max %x, buf_len %x\n", __func__, bufsiz, ioc_xraw.len);
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ioc_xraw.read_buf == NULL || ioc_xraw.write_buf == NULL) {
|
||||
pr_err("%s: read buf and write buf can not equal to NULL simultaneously.\n",
|
||||
__func__);
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* change speed and set transfer mode */
|
||||
gfspi_spi_setup_conf(gf_dev, ioc_xraw.high_time);
|
||||
|
||||
len = ioc_xraw.len;
|
||||
|
||||
tx_buf = kzalloc(len, GFP_KERNEL);
|
||||
if (tx_buf == NULL) {
|
||||
pr_err("%s: failed to allocate tx buffer\n",
|
||||
__func__);
|
||||
retval = -EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
rx_buf = kzalloc(len, GFP_KERNEL);
|
||||
if (rx_buf == NULL) {
|
||||
kfree(tx_buf);
|
||||
pr_err("%s: failed to allocate rx buffer\n",
|
||||
__func__);
|
||||
retval = -EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (copy_from_user(tx_buf, (void __user *)ioc_xraw.write_buf,
|
||||
ioc_xraw.len)) {
|
||||
kfree(tx_buf);
|
||||
kfree(rx_buf);
|
||||
pr_err("Failed to copy gf_ioc_transfer from user to kernel\n");
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
gfspi_spi_transfer_raw(gf_dev, tx_buf, rx_buf, len);
|
||||
|
||||
if (copy_to_user((void __user *)ioc_xraw.read_buf,
|
||||
rx_buf, ioc_xraw.len)) {
|
||||
pr_err("Failed to copy gf_ioc_transfer_raw from kernel to user\n");
|
||||
retval = -EFAULT;
|
||||
}
|
||||
|
||||
kfree(tx_buf);
|
||||
kfree(rx_buf);
|
||||
} while (0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int gfspi_ioctl_spi_init_cfg_cmd(struct gf_device *gf_dev, unsigned long arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*GPIO pins reference.*/
|
||||
int gfspi_get_gpio_dts_info(struct device *dev, struct gf_device *gf_dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
int status = 0;
|
||||
|
||||
/*get pwr resource*/
|
||||
gf_dev->pwr_gpio = of_get_named_gpio(np, "goodix,gpio_pwr", 0);
|
||||
if (!gpio_is_valid(gf_dev->pwr_gpio)) {
|
||||
pr_err("%s, PWR GPIO is invalid.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
pr_info("%s, goodix_pwr:%d\n", __func__, gf_dev->pwr_gpio);
|
||||
status = gpio_request(gf_dev->pwr_gpio, "goodix_pwr");
|
||||
if (status < 0) {
|
||||
pr_err("%s, Failed to request PWR GPIO. rc = %d\n",
|
||||
__func__, status);
|
||||
return status;
|
||||
}
|
||||
gpio_direction_output(gf_dev->pwr_gpio, 0);
|
||||
|
||||
/*get reset resource*/
|
||||
gf_dev->reset_gpio = of_get_named_gpio(np, "goodix,gpio_reset", 0);
|
||||
if (!gpio_is_valid(gf_dev->reset_gpio)) {
|
||||
pr_err("%s, RESET GPIO is invalid.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
pr_info("%s, goodix_reset:%d\n",
|
||||
__func__, gf_dev->reset_gpio);
|
||||
status = gpio_request(gf_dev->reset_gpio, "goodix_reset");
|
||||
if (status < 0) {
|
||||
pr_err("%s, Failed to request RESET GPIO. rc = %d\n",
|
||||
__func__, status);
|
||||
return status;
|
||||
}
|
||||
gpio_direction_output(gf_dev->reset_gpio, 0);
|
||||
|
||||
/*get irq resourece*/
|
||||
gf_dev->irq_gpio = of_get_named_gpio(np, "goodix,gpio_irq", 0);
|
||||
if (!gpio_is_valid(gf_dev->irq_gpio)) {
|
||||
pr_err("%s, IRQ GPIO is invalid.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
pr_info("%s, irq_gpio:%d\n", __func__, gf_dev->irq_gpio);
|
||||
status = gpio_request(gf_dev->irq_gpio, "goodix_irq");
|
||||
if (status < 0) {
|
||||
pr_err("%s, Failed to request IRQ GPIO. rc = %d\n",
|
||||
__func__, status);
|
||||
return status;
|
||||
}
|
||||
gpio_direction_input(gf_dev->irq_gpio);
|
||||
|
||||
if (of_property_read_u32(np, "goodix,min_cpufreq_limit",
|
||||
&gf_dev->min_cpufreq_limit))
|
||||
gf_dev->min_cpufreq_limit = 0;
|
||||
|
||||
if (of_property_read_string_index(np, "goodix,chip_id", 0,
|
||||
(const char **)&gf_dev->chipid))
|
||||
gf_dev->chipid = "NULL";
|
||||
pr_info("%s, Chip ID:%s\n", __func__, gf_dev->chipid);
|
||||
|
||||
gf_dev->p = pinctrl_get_select(dev, "default");
|
||||
if (IS_ERR(gf_dev->p)) {
|
||||
status = -EINVAL;
|
||||
pr_err("%s: failed pinctrl_get\n", __func__);
|
||||
goto fail_pinctrl_get;
|
||||
}
|
||||
|
||||
gf_dev->pins_sleep = pinctrl_lookup_state(gf_dev->p, "sleep");
|
||||
if (IS_ERR(gf_dev->pins_sleep)) {
|
||||
pr_err("%s : could not get pins sleep_state (%li)\n",
|
||||
__func__, PTR_ERR(gf_dev->pins_sleep));
|
||||
status = -EINVAL;
|
||||
goto fail_pinctrl_get;
|
||||
}
|
||||
|
||||
gf_dev->pins_idle = pinctrl_lookup_state(gf_dev->p, "idle");
|
||||
if (IS_ERR(gf_dev->pins_idle)) {
|
||||
pr_err("%s : could not get pins idle_state (%li)\n",
|
||||
__func__, PTR_ERR(gf_dev->pins_idle));
|
||||
goto fail_pinctrl_get;
|
||||
}
|
||||
|
||||
gfspi_pin_control(gf_dev, false);
|
||||
|
||||
if (of_property_read_u32(np, "goodix,orient", &gf_dev->orient))
|
||||
gf_dev->orient = 0;
|
||||
pr_info("%s: orient=%d\n", __func__, gf_dev->orient);
|
||||
|
||||
fail_pinctrl_get:
|
||||
pinctrl_put(gf_dev->p);
|
||||
return status;
|
||||
}
|
||||
|
||||
void gfspi_cleanup_info(struct gf_device *gf_dev)
|
||||
{
|
||||
if (gpio_is_valid(gf_dev->irq_gpio)) {
|
||||
gpio_free(gf_dev->irq_gpio);
|
||||
pr_debug("%s, remove irq_gpio.\n", __func__);
|
||||
}
|
||||
if (gpio_is_valid(gf_dev->reset_gpio)) {
|
||||
gpio_free(gf_dev->reset_gpio);
|
||||
pr_debug("%s, remove reset_gpio.\n", __func__);
|
||||
}
|
||||
if (gpio_is_valid(gf_dev->pwr_gpio)) {
|
||||
gpio_free(gf_dev->pwr_gpio);
|
||||
pr_debug("%s, remove pwr_gpio.\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
int gfspi_spi_clk_enable(struct gf_device *gf_dev)
|
||||
{
|
||||
int ret_val = 0;
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
if (gf_dev->enabled_clk) {
|
||||
pr_info("%s already enabled same clock.\n",
|
||||
__func__);
|
||||
return ret_val;
|
||||
}
|
||||
pr_info("%s ENABLE_SPI_CLOCK %ld\n",
|
||||
__func__, (long int)gf_dev->spi->max_speed_hz);
|
||||
wake_lock(&gf_dev->wake_lock);
|
||||
gf_dev->enabled_clk = true;
|
||||
#endif
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int gfspi_spi_clk_disable(struct gf_device *gf_dev)
|
||||
{
|
||||
int ret_val = 0;
|
||||
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
||||
if (gf_dev->enabled_clk) {
|
||||
pr_info("%s DISABLE_SPI_CLOCK\n", __func__);
|
||||
wake_unlock(&gf_dev->wake_lock);
|
||||
gf_dev->enabled_clk = false;
|
||||
pr_info("%s, clk disalbed\n", __func__);
|
||||
}
|
||||
#endif
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int gfspi_pin_control(struct gf_device *gf_dev, bool pin_set)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (pin_set) {
|
||||
if (!IS_ERR(gf_dev->pins_idle)) {
|
||||
status = pinctrl_select_state(gf_dev->p,
|
||||
gf_dev->pins_idle);
|
||||
if (status)
|
||||
pr_err("%s: can't set pin default state\n",
|
||||
__func__);
|
||||
pr_debug("%s idle\n", __func__);
|
||||
}
|
||||
} else {
|
||||
if (!IS_ERR(gf_dev->pins_sleep)) {
|
||||
status = pinctrl_select_state(gf_dev->p,
|
||||
gf_dev->pins_sleep);
|
||||
if (status)
|
||||
pr_err("%s: can't set pin sleep state\n",
|
||||
__func__);
|
||||
pr_debug("%s sleep\n", __func__);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -28,4 +28,9 @@ config FPGA_MGR_ZYNQ_FPGA
|
||||
|
||||
endif # FPGA
|
||||
|
||||
config POGO_FPGA
|
||||
tristate "fpga for pogo keyboard"
|
||||
help
|
||||
FPGA manager driver support for pogo fpga.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -8,3 +8,4 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o
|
||||
# FPGA Manager Drivers
|
||||
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
|
||||
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
|
||||
obj-$(CONFIG_POGO_FPGA) += pogo_fpga_dev.o
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user