Files
luban-lite-t3e-pro/bsp/artinchip/hal/rtp/hal_rtp.c
刘可亮 7bbc029dae v1.0.0
2023-08-30 16:21:18 +08:00

686 lines
18 KiB
C

/*
* Copyright (c) 2022-2023, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: matteo <duanmt@artinchip.com>
*/
#include <string.h>
#include "aic_core.h"
#include "aic_hal_clk.h"
#include "hal_rtp.h"
/* Register definition for RTP */
#define RTP_MCR (RTP_BASE + 0x000)
#define RTP_INTR (RTP_BASE + 0x004)
#define RTP_PDEB (RTP_BASE + 0x008)
#define RTP_PCTL (RTP_BASE + 0x00C)
#define RTP_CHCFG (RTP_BASE + 0x010)
#define RTP_MMSC (RTP_BASE + 0x014)
#define RTP_FIL (RTP_BASE + 0x018)
#define RTP_AMSC (RTP_BASE + 0x01C)
#define RTP_FCR (RTP_BASE + 0x020)
#define RTP_DATA (RTP_BASE + 0x024)
#define RTP_DLY (RTP_BASE + 0x028)
#define RTP_VERSION (RTP_BASE + 0xFFC)
#define RTP_MCR_PRES_DET_BYPASS BIT(16)
#define RTP_MCR_RISE_STS BIT(12)
#define RTP_MCR_PRES_DET_EN BIT(8)
#define RTR_MCR_MODE_SHIFT 4
#define RTR_MCR_MODE_MASK GENMASK(7, 4)
#define RTP_MCR_EN BIT(0)
#define RTP_INTR_SCI_FLG BIT(21)
#define RTP_INTR_DOUR_FLG BIT(20)
#define RTP_INTR_FIFO_FLG BIT(19)
#define RTP_INTR_DRDY_FLG BIT(18)
#define RTP_INTR_RISE_DET_FLG BIT(17)
#define RTP_INTR_PRES_DET_FLG BIT(16)
#define RTP_INTR_SCI_IE BIT(5)
#define RTP_INTR_DOUR_INTEN BIT(4)
#define RTP_INTR_FIFO_ERR_IE BIT(3)
#define RTP_INTR_DAT_RDY_IE BIT(2)
#define RTP_INTR_RISE_DET_IE BIT(1)
#define RTP_INTR_PRES_DET_IE BIT(0)
#define RTP_PCTL_PRES_DET_BYPASS BIT(16)
#define RTP_MMSC_VREF_MINUS_SEL_SHIFT 22
#define RTP_MMSC_VREF_PLUS_SEL_SHIFT 20
#define RTP_MMSC_XY_DRV_X_PLUS BIT(19)
#define RTP_MMSC_XY_DRV_Y_PLUS BIT(18)
#define RTP_MMSC_XY_DRV_X_MINUS BIT(17)
#define RTP_MMSC_XY_DRV_Y_MINUS BIT(16)
#define RTP_MMSC_XY_DRV_SHIFT 16
#define RTP_MMSC_SMP_CNT_PER_TRIG_SHIFT 8
#define RTP_MMSC_SMP_CH_SEL_SHIFT 4
#define RTP_MMSC_SMP_TRIG BIT(0)
#define RTP_FIL_Z_REL_RANGE_SHIFT 28
#define RTP_FIL_X_ABS_RANGE_SHIFT 24
#define RTP_FIL_XY_REL_RANGE_SHIFT 20
#define RTP_FIL_XY_ABS_RANGE_SHIFT 16
#define RTP_AMSC_PERIOD_SAMPLE_INT_SHIFT 12
#define RTP_AMSC_PERIOD_SAMPLE_EN BIT(1)
#define RTP_AMSC_SINGLE_SAMPLE_EN BIT(0)
#define RTP_FCR_DAT_CNT_SHIFT 24
#define RTP_FCR_DAT_CNT_MASK GENMASK(28, 24)
#define RTP_FCR_UF_FLAG BIT(18)
#define RTP_FCR_OF_FLAG BIT(17)
#define RTP_FCR_DAT_RDY_THD_SHIFT 8
#define RTP_FCR_DAT_RDY_THD_MASK GENMASK(12, 8)
#define RTP_FCR_UF_IE BIT(2)
#define RTP_FCR_OF_IE BIT(1)
#define RTP_FCR_FLUSH BIT(0)
#define RTP_DATA_CH_NUM_SHIFT 12
#define RTP_DATA_CH_NUM_MASK GENMASK(13, 12)
#define RTP_DATA_DATA_MASK GENMASK(11, 0)
enum aic_rtp_vref_minus_sel {
RTP_VREF_MINUS_2_GND = 0,
RTP_VREF_MINUS_2_X_MINUS,
RTP_VREF_MINUS_2_Y_MINUS
};
enum aic_rtp_vref_plus_sel {
RTP_VREF_PLUS_2_VCC = 0,
RTP_VREF_PLUS_2_X_PLUS,
RTP_VREF_PLUS_2_Y_PLUS
};
enum aic_rtp_relative_range {
RTP_REL_RANGE_DISABLE = 0,
RTP_REL_RANGE_MAX_1_8,
RTP_REL_RANGE_MAX_1_16,
RTP_REL_RANGE_MAX_1_32,
RTP_REL_RANGE_MAX_1_64,
RTP_REL_RANGE_MAX_1_128,
RTP_REL_RANGE_MAX_1_256,
RTP_REL_RANGE_MAX_1_512
};
enum aic_rtp_absolute_range {
RTP_ABS_RANGE_DISABLE = 0,
RTP_ABS_RANGE_MAX_2_9,
RTP_ABS_RANGE_MAX_2_8,
RTP_ABS_RANGE_MAX_2_7,
RTP_ABS_RANGE_MAX_2_6,
RTP_ABS_RANGE_MAX_2_5,
RTP_ABS_RANGE_MAX_2_4,
RTP_ABS_RANGE_MAX_2_3
};
/* n_m - Pick out n samples from m continuous samples,
* and drop (m-n)/2 max and (m-n)/2 min samples.
*/
enum aic_rtp_filter_type {
RTP_FILTER_NONE = 0,
RTP_FILTER_2_4,
RTP_FILTER_4_6,
RTP_FILTER_4_8
};
enum aic_rtp_ch {
RTP_CH_Y_MINUS = 0,
RTP_CH_X_MINUS,
RTP_CH_Y_PLUS,
RTP_CH_X_PLUS,
RTP_CH_Z_A,
RTP_CH_Z_B,
RTP_CH_Z_C,
RTP_CH_Z_D,
RTP_CH_MAX
};
enum aic_rtp_manual_mode_status {
RTP_MMS_X_MINUS = 0,
RTP_MMS_Y_MINUS,
RTP_MMS_Z_A,
RTP_MMS_Z_B,
RTP_MMS_DOWN,
RTP_MMS_IDLE,
RTP_MMS_FINISH
};
void hal_rtp_status_show(struct aic_rtp_dev *rtp)
{
int mcr, version;
mcr = readl(RTP_MCR);
version = readl(RTP_VERSION);
printf("In RTP controller V%d.%02d:\n"
"Mode hw %d/ sw %d, RTP enale %d, Press detect enable %d\n"
"Pressure enable %d, max %d, x-plate %d, y-plate %d\n"
"Sample period: %d ms, Fuzz: %d\n",
version >> 8, version & 0xff,
(u32)((mcr & RTR_MCR_MODE_MASK) >> RTR_MCR_MODE_SHIFT),
rtp->mode, (u32)(mcr & RTP_MCR_EN),
(mcr & RTP_MCR_PRES_DET_EN) ? 1 : 0,
rtp->pressure_det, rtp->max_press,
rtp->x_plate, rtp->y_plate,
rtp->smp_period, rtp->fuzz);
}
static struct aic_rtp_dev *g_rtp_dev_of_user = NULL;
static u32 rtp_ms2itv(u32 pclk_rate, u32 ms)
{
u32 tmp = pclk_rate / 1000;
tmp = (tmp * ms) >> 12;
return tmp;
}
static s32 rtp_is_rise(void)
{
return readl(RTP_MCR) & RTP_MCR_RISE_STS ? 1 : 0;
}
static void rtp_reg_enable(int offset, int bit, int enable)
{
int tmp;
tmp = readl((long)offset);
tmp &= ~bit;
if (enable)
tmp |= bit;
writel(tmp, (long)offset);
}
static void rtp_fifo_flush(void)
{
u32 sta = readl(RTP_FCR);
if (sta & RTP_FCR_UF_FLAG)
pr_err("FIFO is Underflow!%#x\n", sta);
if (sta & RTP_FCR_OF_FLAG)
pr_err("FIFO is Overflow!%#x\n", sta);
writel(sta | RTP_FCR_FLUSH, RTP_FCR);
}
void hal_rtp_enable(struct aic_rtp_dev *rtp, int en)
{
if (!en) {
rtp_fifo_flush();
pr_info("clean fifo\n");
}
rtp_reg_enable(RTP_MCR,
rtp->mode << RTR_MCR_MODE_SHIFT | RTP_MCR_PRES_DET_EN | RTP_MCR_EN,
en);
#if defined(CONFIG_ARTINCHIP_ADCIM_DM)
writel(0, RTP_PDEB);
#else
writel(0x230f230f, RTP_PDEB);
#endif
if (rtp->mode != RTP_MODE_MANUAL) {
writel(0x1f000021, RTP_DLY);
rtp_reg_enable(RTP_MCR, RTP_MCR_PRES_DET_BYPASS, en);
}
rtp->pclk_rate = hal_clk_get_freq(hal_clk_get_parent(CLK_RTP));
g_rtp_dev_of_user = rtp;
}
void hal_rtp_int_enable(struct aic_rtp_dev *rtp, int en)
{
u32 val = RTP_INTR_FIFO_ERR_IE | RTP_INTR_DAT_RDY_IE
| RTP_INTR_RISE_DET_IE | RTP_INTR_SCI_IE;
if (rtp->mode == RTP_MODE_MANUAL)
val |= RTP_INTR_PRES_DET_IE;
rtp_reg_enable(RTP_INTR, val, en);
}
static void rtp_fifo_init(enum aic_rtp_mode mode, u32 smp_period)
{
u32 thd = 0;
switch (mode) {
case RTP_MODE_AUTO1:
if (smp_period)
thd = 8;
else
thd = 2;
break;
case RTP_MODE_AUTO2:
if (smp_period)
thd = 12;
else
thd = 4;
break;
case RTP_MODE_AUTO3:
if (smp_period)
thd = 12;
else
thd = 6;
break;
case RTP_MODE_AUTO4:
default:
thd = 8;
break;
}
thd <<= RTP_FCR_DAT_RDY_THD_SHIFT;
writel(thd | RTP_FCR_UF_IE | RTP_FCR_OF_IE, RTP_FCR);
}
static u32 rtp_press_calc(struct aic_rtp_dev *rtp)
{
struct aic_rtp_dat *dat = &rtp->latest;
u32 pressure = rtp->x_plate * dat->x_minus / AIC_RTP_VAL_RANGE;
if (rtp->y_plate) {
pressure = pressure * (AIC_RTP_VAL_RANGE - dat->z_a) / dat->z_a;
pressure -= rtp->y_plate * (AIC_RTP_VAL_RANGE - dat->y_minus)
/ AIC_RTP_VAL_RANGE;
} else {
pressure = pressure * (dat->z_b - dat->z_a) / dat->z_a;
}
pr_debug("Current pressure: %d\n", pressure);
if (pressure > rtp->max_press) {
pr_debug("Invalid pressure %d\n", pressure);
pressure = AIC_RTP_INVALID_VAL;
}
#if defined(CONFIG_ARTINCHIP_ADCIM_DM)
return (dat->z_a + dat->z_b) / 2;
#else
return pressure;
#endif
}
static void rtp_report_abs(struct aic_rtp_dev *rtp, u16 down)
{
struct aic_rtp_dat *dat = &rtp->latest;
struct aic_rtp_event e = {0};
if (dat->x_minus == AIC_RTP_INVALID_VAL
|| dat->y_minus == AIC_RTP_INVALID_VAL)
return;
if (rtp->pressure_det) {
int pressure = rtp_press_calc(rtp);
if (pressure == AIC_RTP_INVALID_VAL)
return;
e.pressure = pressure;
}
e.x = dat->x_minus;
e.y = dat->y_minus;
e.down = down;
e.timestamp = dat->timestamp;
if (rtp->callback)
rtp->callback();
hal_rtp_ebuf_write(&rtp->ebuf, &e);
}
static void rtp_smp_period(u32 period)
{
u32 val = 0;
if (period) {
val = period << RTP_AMSC_PERIOD_SAMPLE_INT_SHIFT
| RTP_AMSC_PERIOD_SAMPLE_EN;
} else {
val = RTP_AMSC_SINGLE_SAMPLE_EN;
writel(0, RTP_AMSC);
}
writel(val, RTP_AMSC);
}
void hal_rtp_auto_mode(struct aic_rtp_dev *rtp)
{
writel(RTP_FILTER_4_8, RTP_FIL);
rtp_smp_period(rtp_ms2itv(rtp->pclk_rate, rtp->smp_period));
rtp_fifo_init(rtp->mode, rtp->smp_period);
}
/* Data format: XN, YN */
static void rtp_report_abs_auto1(struct aic_rtp_dev *rtp, u16 *ori, u32 cnt)
{
u32 i = 0;
struct aic_rtp_dat *latest = &rtp->latest;
for (i = 0; i < cnt; ) {
latest->x_minus = ori[i];
latest->y_minus = ori[i + 1];
rtp_report_abs(rtp, 1);
i += 2;
pr_debug("X %d, Y %d\n", latest->x_minus, latest->y_minus);
}
}
/* Data format: XN, YN, ZA, ZB */
static void rtp_report_abs_auto2(struct aic_rtp_dev *rtp, u16 *ori, u32 cnt)
{
u32 i = 0;
struct aic_rtp_dat *latest = &rtp->latest;
for (i = 0; i < cnt; ) {
latest->x_minus = ori[i];
latest->y_minus = ori[i + 1];
latest->z_a = ori[i + 2];
latest->z_b = ori[i + 3];
rtp_report_abs(rtp, 1);
i += 4;
pr_debug("X %d, Y %d, ZA %d ZB %d\n", latest->x_minus, latest->y_minus,
latest->z_a, latest->z_b);
}
}
static s32 rtp_distance_is_far(struct aic_rtp_dat *latest)
{
s32 ret = 0;
if (latest->x_minus != latest->x_plus) {
if (latest->x_plus > latest->x_minus)
ret = latest->x_plus > latest->x_minus +
AIC_RTP_SCATTER_THD;
else
ret = latest->x_minus > latest->x_plus +
AIC_RTP_SCATTER_THD;
}
if (ret)
return 1;
if (latest->y_minus != latest->y_plus) {
if (latest->y_plus > latest->y_minus)
ret = latest->y_plus > latest->y_minus +
AIC_RTP_SCATTER_THD;
else
ret = latest->y_minus > latest->y_plus +
AIC_RTP_SCATTER_THD;
}
return ret;
}
/* Data format: XN, XP, YN, YP, ZA, ZB */
static void rtp_report_abs_auto3(struct aic_rtp_dev *rtp, u16 *ori, u32 cnt)
{
u32 i = 0;
struct aic_rtp_dat *latest = &rtp->latest;
for (i = 0; i < cnt; ) {
latest->x_minus = ori[i];
latest->x_plus = ori[i + 1];
latest->y_minus = ori[i + 2];
latest->y_plus = ori[i + 3];
latest->z_a = ori[i + 4];
latest->z_b = ori[i + 5];
pr_debug("X %u-%u, Y %u-%u, ZA %u, ZB %u\n",
latest->x_minus, latest->x_plus,
latest->y_minus, latest->y_plus,
latest->z_a, latest->z_b);
if (!rtp_distance_is_far(latest)) {
latest->x_minus += latest->x_plus;
latest->x_minus >>= 1;
latest->y_minus += latest->y_plus;
latest->y_minus >>= 1;
rtp_report_abs(rtp, 1);
} else {
pr_debug("Distance is so far\n");
}
i += 6;
if (i + 6 > cnt)
break;
}
}
/* Data format: XN, XP, YN, YP, ZA, ZB, ZC, ZD */
static void rtp_report_abs_auto4(struct aic_rtp_dev *rtp, u16 *ori, u32 cnt)
{
u32 i = 0;
struct aic_rtp_dat *latest = &rtp->latest;
for (i = 0; i < cnt; ) {
latest->x_minus = ori[i];
latest->x_plus = ori[i + 1];
latest->y_minus = ori[i + 2];
latest->y_plus = ori[i + 3];
latest->z_a = ori[i + 4];
latest->z_b = ori[i + 5];
latest->z_c = ori[i + 6];
latest->z_d = ori[i + 7];
pr_debug("X %u-%u, Y %u-%u, ZA %u-%u, ZB %u-%u\n",
latest->x_minus, latest->x_plus,
latest->y_minus, latest->y_plus,
latest->z_a, latest->z_c,
latest->z_b, latest->z_d);
#if defined(CONFIG_ARTINCHIP_ADCIM_DM)
rtp_report_abs(rtp, 1);
latest->x_minus = latest->x_plus;
latest->y_minus = latest->y_plus;
latest->z_a = latest->z_c;
latest->z_b = latest->z_d;
rtp_report_abs(rtp, 1);
#else
if (!rtp_distance_is_far(latest)) {
latest->x_minus += latest->x_plus;
latest->x_minus >>= 1;
latest->y_minus += latest->y_plus;
latest->y_minus >>= 1;
rtp_report_abs(rtp, 1);
} else {
pr_debug("Distance is so far\n");
}
#endif
i += 8;
if (i + 8 > cnt)
break;
}
}
static void aic_rtp_read_fifo(struct aic_rtp_dev *rtp, u32 cnt)
{
int i;
u32 tmp;
u16 data[AIC_RTP_FIFO_DEPTH] = {0};
tmp = (readl(RTP_FCR) & RTP_FCR_DAT_CNT_MASK) >> RTP_FCR_DAT_CNT_SHIFT;
if (tmp != cnt) {
if (rtp->mode == RTP_MODE_MANUAL)
pr_err("FIFO did changed %d/%d\n", tmp, cnt);
else
cnt = tmp;
}
for (i = 0; i < cnt; i++) {
if (!(readl(RTP_FCR) & RTP_FCR_DAT_CNT_MASK)) {
pr_err("FIFO is empty %d/%d\n", i, cnt);
return;
}
tmp = readl(RTP_DATA);
data[i] = tmp & RTP_DATA_DATA_MASK;
// pr_debug("%d/%d - Read chan %d, data %d \n", i, 12 + (tmp >> 12), tmp >> 12, data[i]);
}
rtp->latest.timestamp = aic_get_time_ms();
tmp = readl(RTP_FCR) & RTP_FCR_DAT_CNT_MASK;
if (tmp) {
pr_err("FIFO is not empty! %d\n", tmp >> RTP_FCR_DAT_CNT_SHIFT);
rtp_fifo_flush();
}
tmp = readl(RTP_INTR);
if (tmp & (RTP_INTR_DOUR_FLG | RTP_INTR_SCI_FLG))
pr_debug("After read FIFO, INTR %#x, FCR %#x\n", tmp, readl(RTP_FCR));
switch (rtp->mode) {
case RTP_MODE_AUTO1:
rtp_report_abs_auto1(rtp, data, cnt);
break;
case RTP_MODE_AUTO2:
rtp_report_abs_auto2(rtp, data, cnt);
break;
case RTP_MODE_AUTO3:
rtp_report_abs_auto3(rtp, data, cnt);
break;
case RTP_MODE_AUTO4:
rtp_report_abs_auto4(rtp, data, cnt);
break;
default:
return;
}
}
s32 hal_rtp_register_callback(rtp_callback_t callback)
{
struct aic_rtp_dev *rtp = g_rtp_dev_of_user;
if (callback == NULL) {
hal_log_err("Invalid callback function!\n");
return -1;
}
rtp->callback = callback;
return 0;
}
irqreturn_t hal_rtp_isr(int irq, void *arg)
{
// TODO: struct aic_rtp_dev *rtp = (struct aic_rtp_dev *)arg;
struct aic_rtp_dev *rtp = g_rtp_dev_of_user;
u32 intr, fcr;
intr = readl(RTP_INTR);
fcr = readl(RTP_FCR);
writel(fcr, RTP_FCR);
writel(intr, RTP_INTR);
pr_debug("INTS %#x, FCR %#x, Pressed %d\n", intr, fcr, !rtp_is_rise());
if ((intr & RTP_INTR_PRES_DET_FLG) && (intr & RTP_INTR_RISE_DET_FLG)) {
pr_debug("Press&rise happened at the same time!\n");
if (rtp_is_rise())
intr &= ~RTP_INTR_PRES_DET_FLG;
else
intr &= ~RTP_INTR_RISE_DET_FLG;
}
if (intr & RTP_INTR_SCI_FLG) {
pr_err("SCI error, flush the FIFO ...\n");
goto irq_clean_fifo;
}
if (intr & RTP_INTR_DOUR_FLG) {
pr_err("DOUR error, flush the FIFO ...\n");
goto irq_clean_fifo;
}
if (intr & RTP_INTR_FIFO_FLG) {
/* When FIFO is overflow, the FIFO data is valid, so read it */
if (!(fcr & RTP_FCR_OF_FLAG) || !(intr & RTP_INTR_RISE_DET_FLG)) {
pr_err("FIFO error, flush the FIFO ...\n");
goto irq_clean_fifo;
}
}
if (intr & RTP_INTR_RISE_DET_FLG)
rtp_report_abs(rtp, 0);
if (intr & RTP_INTR_DRDY_FLG)
aic_rtp_read_fifo(rtp, (fcr & RTP_FCR_DAT_CNT_MASK)
>> RTP_FCR_DAT_CNT_SHIFT);
goto irq_done;
irq_clean_fifo:
rtp_fifo_flush();
irq_done:
return IRQ_HANDLED;
}
u32 hal_rtp_ebuf_read_space(struct aic_rtp_ebuf *ebuf)
{
u16 rd = ebuf->rd_pos;
u16 wr = ebuf->wr_pos;
if (rd == wr) {
return 0;
} else if (rd < wr) {
return wr - rd;
} else {
return AIC_RTP_EVT_BUF_SIZE - (rd - wr - 1);
}
}
s32 hal_rtp_ebuf_write(struct aic_rtp_ebuf *ebuf, struct aic_rtp_event *e)
{
if (hal_rtp_ebuf_full(ebuf)) {
pr_debug("ebuf is full! Will drop a event\n");
return -1;
}
memcpy(&ebuf->event[ebuf->wr_pos], e, sizeof(struct aic_rtp_event));
ebuf->wr_pos++;
if (ebuf->wr_pos >= AIC_RTP_EVT_BUF_SIZE)
ebuf->wr_pos = 0;
return 0;
}
s32 hal_rtp_ebuf_read(struct aic_rtp_ebuf *ebuf, struct aic_rtp_event *e)
{
if (hal_rtp_ebuf_empty(ebuf)) {
pr_debug("There is no RTP event in ebuf\n");
return -1;
}
memcpy(e, &ebuf->event[ebuf->rd_pos], sizeof(struct aic_rtp_event));
ebuf->rd_pos++;
if (ebuf->rd_pos >= AIC_RTP_EVT_BUF_SIZE)
ebuf->rd_pos = 0;
return 0;
}
s32 hal_rtp_ebuf_sync(struct aic_rtp_ebuf *ebuf)
{
if (ebuf->wr_pos != 0)
ebuf->rd_pos = ebuf->wr_pos - 1;
else
ebuf->rd_pos = AIC_RTP_EVT_BUF_SIZE - 1;
return 0;
}
s32 hal_rtp_clk_init(void)
{
int ret = 0;
ret = hal_clk_enable(CLK_RTP);
if (ret < 0) {
pr_err("RTP clk enable failed!");
return -1;
}
ret = hal_clk_enable_deassertrst(CLK_RTP);
if (ret < 0) {
pr_err("RTP reset deassert failed!");
return -1;
}
return ret;
}