kernel_samsung_a34x-permissive/drivers/misc/mediatek/uart/uart.c
2024-04-28 15:49:01 +02:00

3095 lines
84 KiB
C
Executable file

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
/******************************************************************************
* Dependency
*****************************************************************************
*/
#if defined(CONFIG_MTK_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ /*used in serial_core.h */
#endif
/*---------------------------------------------------------------------------*/
#include <generated/autoconf.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/timer.h>
#include <linux/sched/clock.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/irq.h>
/* #include <asm/scatterlist.h> */
#include <mt-plat/dma.h>
#include <mt-plat/mtk_printk_ctrl.h>
/* #include <mach/mt_clkmgr.h> */
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/syscore_ops.h>
#include "include/mtk_uart.h"
#include "include/mtk_uart_intf.h"
#include <linux/version.h>
#include <linux/suspend.h>
#include <linux/of.h>
#include <linux/ratelimit.h>
#include "include/mtk_uart_internal.h"
/*#define TTY_FLIP_ARG(a) ((a)->port)*/
spinlock_t mtk_console_lock;
spinlock_t mtk_uart_bt_lock;
struct mtk_uart *console_port;
struct mtk_uart *bt_port;
/*---------------------------------------------------------------------------*/
#define HW_FLOW_CTRL_PORT(uart) (uart->setting->hw_flow)
/*---------------------------------------------------------------------------*/
#if defined(ENABLE_VFIFO)
/*---------------------------------------------------------------------------*/
static DEFINE_SPINLOCK(mtk_uart_vfifo_port_lock);
/*---------------------------------------------------------------------------*/
#define VFIFO_INIT_RX(c, i, n) \
{.ch = (c), .size = (n), .trig = VFF_RX_THRE(n), \
.type = UART_RX_VFIFO, \
.port = NULL, .addr = NULL, \
.entry = ATOMIC_INIT(0), .reg_cb = ATOMIC_INIT(0), \
.iolock = __SPIN_LOCK_UNLOCKED(mtk_uart_vfifo_port[i].lock)}
/*---------------------------------------------------------------------------*/
#define VFIFO_INIT_TX(c, i, n) \
{.ch = (c), .size = (n), .trig = VFF_TX_THRE(n), \
.type = UART_TX_VFIFO, \
.port = NULL, \
.addr = NULL, .entry = ATOMIC_INIT(0), .reg_cb = ATOMIC_INIT(0), \
.iolock = __SPIN_LOCK_UNLOCKED(mtk_uart_vfifo_port[i].lock)}
/*---------------------------------------------------------------------------*/
static struct mtk_uart_vfifo mtk_uart_vfifo_port[] = {
VFIFO_INIT_TX(P_DMA_UART1_TX, 0, C_UART1_VFF_TX_SIZE),
VFIFO_INIT_RX(P_DMA_UART1_RX, 1, C_UART1_VFF_RX_SIZE),
VFIFO_INIT_TX(P_DMA_UART2_TX, 2, C_UART2_VFF_TX_SIZE),
VFIFO_INIT_RX(P_DMA_UART2_RX, 3, C_UART2_VFF_RX_SIZE),
VFIFO_INIT_TX(P_DMA_UART3_TX, 4, C_UART3_VFF_TX_SIZE),
VFIFO_INIT_RX(P_DMA_UART3_RX, 5, C_UART3_VFF_RX_SIZE),
#if 0 /*MT6589 only 6 DMA channel for UART */
VFIFO_INIT_TX(P_DMA_UART4_TX, 6, C_UART4_VFF_TX_SIZE),
VFIFO_INIT_RX(P_DMA_UART4_RX, 7, C_UART4_VFF_RX_SIZE),
#endif
};
/*---------------------------------------------------------------------------*/
#endif /*ENABLE_VFIFO */
/*---------------------------------------------------------------------------*/
/* uart control blocks */
struct mtk_uart mtk_uarts[UART_NR];
static unsigned int uart_freeze_enable[UART_NR] = { 0 };
struct uart_history_data {
int index;
int offset;
u64 last_update;
unsigned char *buffer;
};
#define UART_HISTORY_DATA_SIZE 4096
static struct uart_history_data tx_history;
static struct uart_history_data rx_history;
/*---------------------------------------------------------------------------*/
static int mtk_uart_init_ports(void);
static void mtk_uart_stop_tx(struct uart_port *port);
/******************************************************************************
* SYSFS support
*****************************************************************************
*/
#if defined(ENABLE_SYSFS)
/*---------------------------------------------------------------------------*/
const struct sysfs_ops mtk_uart_sysfs_ops = {
.show = mtk_uart_attr_show,
.store = mtk_uart_attr_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, char *page);
ssize_t (*store)(struct kobject *kobj, const char *page, size_t size);
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry debug_entry = {
{.name = "debug", .mode = 0644},
mtk_uart_debug_show,
mtk_uart_debug_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry sysrq_entry = {
{.name = "sysrq", .mode = 0644},
mtk_uart_sysrq_show,
mtk_uart_sysrq_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry vffsz_entry = {
{.name = "vffsz", .mode = 0644},
mtk_uart_vffsz_show,
mtk_uart_vffsz_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry conse_entry = {
{.name = "conse", .mode = 0644},
mtk_uart_conse_show,
mtk_uart_conse_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry vff_en_entry = {
{.name = "vff_en", .mode = 0644},
mtk_uart_vff_en_show,
mtk_uart_vff_en_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry lsr_status_entry = {
{.name = "lsr_status", .mode = 0644},
mtk_uart_lsr_status_show,
mtk_uart_lsr_status_store,
};
/*---------------------------------------------------------------------------*/
struct mtuart_entry history_entry = {
{.name = "history", .mode = 0644},
mtk_uart_history_show,
mtk_uart_history_store,
};
/*---------------------------------------------------------------------------*/
struct attribute *mtk_uart_attributes[] = {
&conse_entry.attr, /*console setting */
#if defined(ENABLE_DEBUG)
&debug_entry.attr,
&sysrq_entry.attr,
&vffsz_entry.attr,
&vff_en_entry.attr,
&lsr_status_entry.attr,
&history_entry.attr,
#endif
NULL,
};
/*---------------------------------------------------------------------------*/
struct kobj_type mtk_uart_ktype = {
.sysfs_ops = &mtk_uart_sysfs_ops,
.default_attrs = mtk_uart_attributes,
};
/*---------------------------------------------------------------------------*/
struct mtuart_sysobj {
struct kobject kobj;
atomic_t sysrq;
atomic_t vffLen[UART_NR * UART_VFIFO_NUM];
atomic_t console_enable;
} mtk_uart_sysobj = {
.console_enable = ATOMIC_INIT(1),};
/*---------------------------------------------------------------------------*/
int mtk_uart_sysfs(void)
{
struct mtuart_sysobj *obj = &mtk_uart_sysobj;
#if defined(ENABLE_VFIFO)
int idx;
#endif
memset(&obj->kobj, 0x00, sizeof(obj->kobj));
#if defined(CONFIG_MAGIC_SYSRQ)
atomic_set(&obj->sysrq, 1);
#else
atomic_set(&obj->sysrq, 0);
#endif
#if defined(ENABLE_VFIFO)
for (idx = 0; idx < ARRAY_SIZE(obj->vffLen)
&& idx < ARRAY_SIZE(mtk_uart_vfifo_port); idx++)
atomic_set(&obj->vffLen[idx], mtk_uart_vfifo_port[idx].size);
#endif
atomic_set(&obj->console_enable, 1);
obj->kobj.parent = kernel_kobj;
if (kobject_init_and_add(&obj->kobj, &mtk_uart_ktype, NULL, "mtuart")) {
kobject_put(&obj->kobj);
return -ENOMEM;
}
kobject_uevent(&obj->kobj, KOBJ_ADD);
return 0;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_attr_show(struct kobject *kobj,
struct attribute *attr, char *buffer)
{
struct mtuart_entry *entry = container_of(attr,
struct mtuart_entry, attr);
return entry->show(kobj, buffer);
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_attr_store(struct kobject *kobj,
struct attribute *attr, const char *buffer, size_t size)
{
struct mtuart_entry *entry = container_of(attr,
struct mtuart_entry, attr);
return entry->store(kobj, buffer, size);
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_debug_show(struct kobject *kobj, char *buffer)
{
int remain = PAGE_SIZE;
int len;
char *ptr = buffer;
int idx;
unsigned int evt_mask;
for (idx = 0; idx < UART_NR; idx++) {
evt_mask = (unsigned int)get_uart_evt_mask(idx);
len = scnprintf(ptr, remain, "0x%2x\n", evt_mask);
ptr += len;
remain -= len;
}
return PAGE_SIZE - remain;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_debug_store(struct kobject *kobj,
const char *buffer, size_t size)
{
#if (UART_NR < 5)
int a, b, c, d;
int res = sscanf(buffer, "0x%x 0x%x 0x%x 0x%x", &a, &b, &c, &d);
if (res != 4) {
MSG_ERR("%s: expect 4 numbers\n", __func__);
} else {
set_uart_evt_mask(0, a);
set_uart_evt_mask(1, b);
set_uart_evt_mask(2, c);
set_uart_evt_mask(3, d);
}
#else
int a, b, c, d, e;
int res = sscanf(buffer, "0x%x 0x%x 0x%x 0x%x 0x%x",
&a, &b, &c, &d, &e);
if (res != 5) {
MSG_ERR("%s: expect 5 numbers\n", __func__);
} else {
set_uart_evt_mask(0, a);
set_uart_evt_mask(1, b);
set_uart_evt_mask(2, c);
set_uart_evt_mask(3, d);
set_uart_evt_mask(4, e);
}
#endif
return size;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_sysrq_show(struct kobject *kobj, char *buffer)
{
struct mtuart_sysobj *obj = container_of(kobj,
struct mtuart_sysobj, kobj);
return scnprintf(buffer, PAGE_SIZE, "%d\n", atomic_read(&obj->sysrq));
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_sysrq_store(struct kobject *kobj,
const char *buffer, size_t size)
{
struct mtuart_sysobj *obj = container_of(kobj,
struct mtuart_sysobj, kobj);
int a;
int res = sscanf(buffer, "%d\n", &a);
if (res != 1)
MSG_ERR("%s: expect 1 number\n", __func__);
else
atomic_set(&obj->sysrq, a);
return size;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_vffsz_show(struct kobject *kobj, char *buffer)
{
ssize_t len = 0;
#if defined(ENABLE_VFIFO)
struct mtuart_sysobj *obj = container_of(kobj,
struct mtuart_sysobj, kobj);
int idx;
for (idx = 0; idx < ARRAY_SIZE(obj->vffLen); idx++)
len += scnprintf(buffer + len, PAGE_SIZE - len,
"[%02d] %4d\n", idx,
atomic_read(&obj->vffLen[idx]));
#endif
return len;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_vffsz_store(struct kobject *kobj,
const char *buffer, size_t size)
{
#if defined(ENABLE_VFIFO)
struct mtuart_sysobj *obj = container_of(kobj,
struct mtuart_sysobj, kobj);
int idx, sz;
if (sscanf(buffer, "%d %d", &idx, &sz) != 2)
MSG_ERR("%s: expect 2 variables\n", __func__);
else if (idx >= ARRAY_SIZE(obj->vffLen) || (sz % 8 != 0))
MSG_ERR("%s: invalid args %d, %d\n", __func__, idx, sz);
else
atomic_set(&obj->vffLen[idx], sz);
#endif
return size;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_conse_show(struct kobject *kobj, char *buffer)
{
struct mtuart_sysobj *obj = container_of(kobj,
struct mtuart_sysobj, kobj);
return scnprintf(buffer, PAGE_SIZE, "%d\n",
atomic_read(&obj->console_enable));
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_conse_store(struct kobject *kobj,
const char *buffer, size_t size)
{
struct mtuart_sysobj *obj = container_of(kobj,
struct mtuart_sysobj, kobj);
int enable;
if (kstrtoint(buffer, 10, &enable))
MSG_ERR("%s: expect 1 variables\n", __func__);
else
atomic_set(&obj->console_enable, enable);
return size;
}
/*---------------------------------------------------------------------------*/
#endif /*ENABLE_SYSFS */
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_vff_en_show(struct kobject *kobj, char *buffer)
{
int remain = PAGE_SIZE;
int len;
char *ptr = buffer;
int idx;
struct mtk_uart_setting *uart_setting;
for (idx = 0; idx < UART_NR; idx++) {
uart_setting = get_uart_default_settings(idx);
len = scnprintf(ptr,
remain,
"tx%d_m:%2x rx%d_m:%2x\n",
idx, (unsigned int)uart_setting->tx_mode,
idx, (unsigned int)uart_setting->rx_mode);
ptr += len;
remain -= len;
}
return PAGE_SIZE - remain;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_vff_en_store(struct kobject *kobj,
const char *buffer, size_t size)
{
#if (UART_NR < 5)
int u1_tx, u1_rx, u2_tx, u2_rx, u3_tx, u3_rx, u4_tx, u4_rx;
struct mtk_uart_setting *uart_setting;
int res = sscanf(buffer, "%x %x %x %x %x %x %x %x",
&u1_tx, &u1_rx, &u2_tx, &u2_rx,
&u3_tx, &u3_rx, &u4_tx, &u4_rx);
if (res != 8) {
MSG_ERR("%s: expect 8 numbers\n", __func__);
} else {
uart_setting = get_uart_default_settings(0);
uart_setting->tx_mode = u1_tx;
uart_setting->rx_mode = u1_rx;
uart_setting = get_uart_default_settings(1);
uart_setting->tx_mode = u2_tx;
uart_setting->rx_mode = u2_rx;
uart_setting = get_uart_default_settings(2);
uart_setting->tx_mode = u3_tx;
uart_setting->rx_mode = u3_rx;
uart_setting = get_uart_default_settings(3);
uart_setting->tx_mode = u4_tx;
uart_setting->rx_mode = u4_rx;
}
return size;
#else
int u1_tx, u1_rx, u2_tx, u2_rx, u3_tx;
int u3_rx, u4_tx, u4_rx, u5_tx, u5_rx;
struct mtk_uart_setting *uart_setting;
int res = sscanf(buffer, "%x %x %x %x %x %x %x %x %x %x",
&u1_tx, &u1_rx, &u2_tx, &u2_rx, &u3_tx,
&u3_rx, &u4_tx, &u4_rx, &u5_tx, &u5_rx);
if (res != 8) {
MSG_ERR("%s: expect 8 numbers\n", __func__);
} else {
uart_setting = get_uart_default_settings(0);
uart_setting->tx_mode = u1_tx;
uart_setting->rx_mode = u1_rx;
uart_setting = get_uart_default_settings(1);
uart_setting->tx_mode = u2_tx;
uart_setting->rx_mode = u2_rx;
uart_setting = get_uart_default_settings(2);
uart_setting->tx_mode = u3_tx;
uart_setting->rx_mode = u3_rx;
uart_setting = get_uart_default_settings(3);
uart_setting->tx_mode = u4_tx;
uart_setting->rx_mode = u4_rx;
uart_setting = get_uart_default_settings(4);
uart_setting->tx_mode = u5_tx;
uart_setting->rx_mode = u5_rx;
}
return size;
#endif
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_lsr_status_show(struct kobject *kobj, char *buffer)
{
int remain = PAGE_SIZE;
int len;
char *ptr = buffer;
int idx;
unsigned long lsr_status;
for (idx = 0; idx < UART_NR; idx++) {
lsr_status = get_uart_lsr_status(idx);
len = scnprintf(ptr, remain, "%04x ", (unsigned int)lsr_status);
ptr += len;
remain -= len;
set_uart_lsr_status(idx, 0);
}
len = scnprintf(ptr, remain, "\n");
ptr += len;
remain -= len;
return PAGE_SIZE - remain;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_lsr_status_store(struct kobject *kobj,
const char *buffer, size_t size)
{
#if (UART_NR < 5)
int u1_lsr, u2_lsr, u3_lsr, u4_lsr;
int res = sscanf(buffer, "%x %x %x %x",
&u1_lsr, &u2_lsr, &u3_lsr, &u4_lsr);
if (res != 4) {
MSG_ERR("%s: expect 4 numbers\n", __func__);
} else {
set_uart_lsr_status(0, u1_lsr);
set_uart_lsr_status(1, u2_lsr);
set_uart_lsr_status(2, u3_lsr);
set_uart_lsr_status(3, u4_lsr);
}
return size;
#else
int u1_lsr, u2_lsr, u3_lsr, u4_lsr, u5_lsr;
int res = sscanf(buffer, "%x %x %x %x %x",
&u1_lsr, &u2_lsr, &u3_lsr, &u4_lsr, &u5_lsr);
if (res != 5) {
MSG_ERR("%s: expect 5 numbers\n", __func__);
} else {
set_uart_lsr_status(0, u1_lsr);
set_uart_lsr_status(1, u2_lsr);
set_uart_lsr_status(2, u3_lsr);
set_uart_lsr_status(3, u4_lsr);
set_uart_lsr_status(4, u4_lsr);
}
return size;
#endif
}
/*---------------------------------------------------------------------------*/
static void uart_mem_dump(int nport, void *start_addr, int len)
{
unsigned int *curr_p = (unsigned int *)start_addr;
unsigned char *curr_ch_p;
int _16_fix_num = len / 16;
int tail_num = len % 16;
char buf[16];
int i, j;
if (curr_p == NULL) {
pr_err("[UART%d-DUMP]NULL point to dump!\n", nport);
return;
}
if (len == 0) {
pr_err("[UART%d-DUMP]Not need to dump\n", nport);
return;
}
pr_debug("[UART%d-DUMP]Base: %p\n", nport, start_addr);
/* Fix section */
for (i = 0; i < _16_fix_num; i++) {
pr_debug("[UART%d-DUMP]%03X: %08X %08X %08X %08X\n",
nport, i * 16, *curr_p, *(curr_p + 1),
*(curr_p + 2), *(curr_p + 3));
curr_p += 4;
}
/* Tail section */
if (tail_num > 0) {
curr_ch_p = (unsigned char *)curr_p;
for (j = 0; j < tail_num; j++) {
buf[j] = *curr_ch_p;
curr_ch_p++;
}
for (; j < 16; j++)
buf[j] = 0;
curr_p = (unsigned int *)buf;
pr_debug("[UART%d-DUMP]%03X: %08X %08X %08X %08X\n",
nport, i * 16, *curr_p, *(curr_p + 1),
*(curr_p + 2), *(curr_p + 3));
}
}
void mtk_uart_dump_history(void)
{
u64 ts_nsec;
unsigned long rem_nsec;
ts_nsec = tx_history.last_update;
rem_nsec = do_div(ts_nsec, 1000000000);
pr_debug("UART Tx port %d: %d/%d @[%5lu.%06lu]\n",
tx_history.index, tx_history.offset, UART_HISTORY_DATA_SIZE,
(unsigned long)ts_nsec, rem_nsec / 1000);
uart_mem_dump(tx_history.index, tx_history.buffer,
UART_HISTORY_DATA_SIZE);
ts_nsec = rx_history.last_update;
rem_nsec = do_div(ts_nsec, 1000000000);
pr_debug("UART Rx port %d: %d/%d @[%5lu.%06lu]\n",
rx_history.index, rx_history.offset, UART_HISTORY_DATA_SIZE,
(unsigned long)ts_nsec, rem_nsec / 1000);
uart_mem_dump(rx_history.index, rx_history.buffer,
UART_HISTORY_DATA_SIZE);
}
void update_history_byte(char is_tx, int nport, unsigned char byte)
{
struct uart_history_data *x_history;
x_history = is_tx ? &tx_history : &rx_history;
if (nport == x_history->index) {
*(x_history->buffer + x_history->offset) = byte;
x_history->offset++;
if (x_history->offset == UART_HISTORY_DATA_SIZE)
x_history->offset = 0;
}
}
void update_history_time(char is_tx, int nport)
{
struct uart_history_data *x_history;
x_history = is_tx ? &tx_history : &rx_history;
if (nport == x_history->index)
x_history->last_update = local_clock();
}
void update_history_bulk(char is_tx, int nport, unsigned char *chars, int count)
{
int room;
struct uart_history_data *x_history;
x_history = is_tx ? &tx_history : &rx_history;
room = UART_HISTORY_DATA_SIZE - x_history->offset;
if (nport == x_history->index) {
if (count <= room) {
memcpy(x_history->buffer + x_history->offset,
chars, count);
x_history->offset += count;
if (x_history->offset == UART_HISTORY_DATA_SIZE)
x_history->offset = 0;
} else {
memcpy(x_history->buffer + x_history->offset,
chars, room);
memcpy(x_history->buffer, chars, count - room);
x_history->offset = count - room;
if (x_history->offset == UART_HISTORY_DATA_SIZE)
x_history->offset = 0;
}
}
x_history->last_update = local_clock();
}
ssize_t mtk_uart_history_show(struct kobject *kobj, char *buffer)
{
int remain = PAGE_SIZE;
int len;
char *ptr = buffer;
len = scnprintf(ptr, remain, "tx(%d):%d; rx(%d):%d\n",
tx_history.index, tx_history.offset,
rx_history.index, rx_history.offset);
ptr += len;
remain -= len;
mtk_uart_dump_history();
return PAGE_SIZE - remain;
}
/*---------------------------------------------------------------------------*/
ssize_t mtk_uart_history_store(struct kobject *kobj,
const char *buffer, size_t size)
{
int tx_index, rx_index;
int res = sscanf(buffer, "%d %d", &tx_index, &rx_index);
if (res != 2)
return 0;
tx_history.index = tx_index;
rx_history.index = rx_index;
tx_history.offset = 0;
tx_history.offset = 0;
memset(tx_history.buffer, 0, UART_HISTORY_DATA_SIZE);
memset(rx_history.buffer, 0, UART_HISTORY_DATA_SIZE);
return size;
}
/* ================================ FIQ ================== */
#if (defined(CONFIG_FIQ_DEBUGGER_CONSOLE) && defined(CONFIG_FIQ_DEBUGGER))
#define DEFAULT_FIQ_UART_PORT (3)
int fiq_console_port = DEFAULT_FIQ_UART_PORT;
/* struct uart_port *p_mtk_uart_port =
* &(mtk_uarts[DEFAULT_FIQ_UART_PORT].port);
*/
/* EXPORT_SYMBOL(p_mtk_uart_port); */
struct mtk_uart *mt_console_uart = &(mtk_uarts[DEFAULT_FIQ_UART_PORT]);
#endif
/* ================================================ */
/* --------------------------------------------- */
/* UART Log port switch feature */
static int find_string(char str[], const char *fingerprint, int *offset)
{
char *curr = str;
int i = 0;
int str_len;
int fingerprint_len;
if ((str == NULL) || (fingerprint == NULL))
return 0;
str_len = strlen(str);
fingerprint_len = strlen(fingerprint);
if (str_len < fingerprint_len)
return 0;
for (i = 0; i <= (str_len - fingerprint_len); i++) {
if (strncmp(curr, fingerprint, fingerprint_len) == 0) {
if (offset != NULL)
*offset = i;
return 1;
}
curr++;
}
return 0;
}
static int find_fingerprint(char str[], int *offset)
{
/**
* This function limitation:
* If the ttyMT number large than 9, this function will work abnormal.
* For example, ttyMT12 will be recoginzed as ttyMT1
*/
static const char * const fingerprint[] = {
"console=/dev/null",
"console=ttyMT0",
"console=ttyMT1",
"console=ttyMT2",
"console=ttyMT3",
#if (UART_NR > 4)
"console=ttyMT4",
#endif
};
int i;
for (i = 0; i < sizeof(fingerprint) / sizeof(char *); i++) {
if (find_string(str, fingerprint[i], offset) != 0)
return i; /* Find it. */
}
return -1; /* Not find */
}
static int modify_fingerprint(char str[], int offset, char new_val)
{
if (str == NULL)
return 0;
/* 14 = strlen("console=ttyMTx"), we modify x to 1~3 */
str[offset + 14 - 1] = new_val;
return 1;
}
void adjust_kernel_cmd_line_setting_for_console(char *u_boot_cmd_line,
char *kernel_cmd_line)
{
int offset = 0;
int kernel_console_port_setting = -1;
int u_boot_console_port_setting = -1;
/* Check u-boot command line setting */
u_boot_console_port_setting = find_fingerprint(u_boot_cmd_line, 0);
if (-1 == u_boot_console_port_setting) {
/* printf("U-boot does not have console setting, return\n"); */
return;
}
/* U-boot has console setting, check kernel console setting */
kernel_console_port_setting = find_fingerprint(kernel_cmd_line,
&offset);
if (-1 == kernel_console_port_setting) {
/* printf("Kernel does not have console setting, return\n"); */
goto _Exit;
}
/**
* Both U-boot and Kernel has console setting.
* 1. If the settings are same, return directly;
* 2. If kernel console setting is null, then use kernel setting
* 3. If u-boot console setting is null, then use kernel setting
* 4. If kernel console setting is not null, then use u-boot setting
*/
if (u_boot_console_port_setting == kernel_console_port_setting) {
/* printf("Same console setting, return\n"); */
goto _Exit;
}
if (kernel_console_port_setting == 0) {
/*
* printf("Kernel console setting is null,
* use kernel setting, return\n");
*/
goto _Exit;
}
if (u_boot_console_port_setting == 0) {
/*
* printf("U-boot console setting is null,
* use kernel setting, return\n");
*/
goto _Exit;
}
/*
* Enter here, it means both kernel and u-boot console setting
* are not null, using u-boot setting
*/
switch (u_boot_console_port_setting) {
case 1: /* Using ttyMT0 */
modify_fingerprint(kernel_cmd_line, offset, '0');
break;
case 2: /* Using ttyMT1 */
modify_fingerprint(kernel_cmd_line, offset, '1');
break;
case 3: /* Using ttyMT2 */
modify_fingerprint(kernel_cmd_line, offset, '2');
break;
case 4: /* Using ttyMT3 */
modify_fingerprint(kernel_cmd_line, offset, '3');
break;
case 5: /* Using ttyMT4 */
modify_fingerprint(kernel_cmd_line, offset, '4');
break;
default:
/* Do nothing */
break;
}
_Exit:
kernel_console_port_setting = 0;
#if (defined(CONFIG_FIQ_DEBUGGER_CONSOLE) && defined(CONFIG_FIQ_DEBUGGER))
kernel_console_port_setting = find_fingerprint(kernel_cmd_line,
&offset);
if (-1 == kernel_console_port_setting) {
/* printf("Kernel does not have console setting, return\n"); */
return;
}
if (kernel_console_port_setting > 0) {
fiq_console_port = (kernel_console_port_setting - 1);
mt_console_uart = &(mtk_uarts[fiq_console_port]);
}
#endif
}
/* ================================================= */
/* -------------------------------- PDN ---------------- */
unsigned int mtk_uart_pdn_enable(char *port, int enable)
{
int str_len;
int port_num;
if (port == NULL)
return -1;
str_len = strlen(port);
if (str_len != 6) {
MSG_ERR("Length mismatch! len=%d\n", str_len);
return -1;
}
if (find_string(port, "ttyMT", 0) == 0) {
MSG_ERR("Format mismatch! str=%s\n", port);
return -1;
}
port_num = port[str_len - 1] - '0';
if (port_num >= UART_NR) {
MSG_ERR("wrong port:%d\n", port_num);
return -1;
}
bt_port = &mtk_uarts[port_num];
if (enable)
mtk_uart_enable_dpidle(bt_port);
else
mtk_uart_disable_dpidle(bt_port);
return 0;
}
unsigned int mtk_uart_freeze_enable(char *port, int enable)
{
int str_len;
int port_num;
if (port == NULL)
return -1;
str_len = strlen(port);
if (str_len != 6) {
MSG_ERR("Length mismatch! len=%d\n", str_len);
return -1;
}
if (find_string(port, "ttyMT", 0) == 0) {
MSG_ERR("Format mismatch! str=%s\n", port);
return -1;
}
port_num = port[str_len - 1] - '0';
if (port_num >= UART_NR) {
MSG_ERR("wrong port:%d\n", port_num);
return -1;
}
if (enable)
uart_freeze_enable[port_num] = 1;
else
uart_freeze_enable[port_num] = 0;
return 0;
}
EXPORT_SYMBOL(mtk_uart_freeze_enable);
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_MTK_SERIAL_CONSOLE
/*---------------------------------------------------------------------------*/
static void mtk_uart_console_write(struct console *co,
const char *s, unsigned int count)
{
/* Notice:
* (1) The function is called by printk, hence, spin lock can not be used
* (2) don't care vfifo setting
*/
#define CONSOLE_RETRY (5000)
int i;
struct mtk_uart *uart;
u32 cnt = 0;
unsigned long flags;
if (co->index >= UART_NR || !(co->flags & CON_ENABLED)
|| !atomic_read(&mtk_uart_sysobj.console_enable))
return;
uart = &mtk_uarts[co->index];
for (i = 0; i < (int)count; i++) {
cnt = 0;
while (!mtk_uart_write_allow(uart)) {
barrier();
if (cnt++ >= CONSOLE_RETRY) {
uart->timeout_count++;
return;
}
}
spin_lock_irqsave(&mtk_console_lock, flags);
mtk_uart_write_byte(uart, s[i]);
spin_unlock_irqrestore(&mtk_console_lock, flags);
if (s[i] == '\n') {
cnt = 0;
while (!mtk_uart_write_allow(uart)) {
barrier();
if (cnt++ >= CONSOLE_RETRY) {
uart->timeout_count++;
return;
}
}
spin_lock_irqsave(&mtk_console_lock, flags);
mtk_uart_write_byte(uart, '\r');
spin_unlock_irqrestore(&mtk_console_lock, flags);
}
}
}
/*---------------------------------------------------------------------------*/
static int __init mtk_uart_console_setup(struct console *co, char *options)
{
struct mtk_uart *uart;
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
int ret;
pr_debug("[UART]mtk console setup : co->index %d options:%s\n",
co->index, options);
if (co->index >= UART_NR)
co->index = 0;
uart = &mtk_uarts[co->index];
port = (struct uart_port *)uart;
console_port = uart;
mtk_uart_console_setting_switch(uart);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
ret = uart_set_options(port, co, baud, parity, bits, flow);
pr_debug("[UART]mtk console setup: uart_set_option port(%d) baud(%d) parity(%c) bits(%d) flow(%c) - ret(%d)\n"
, co->index, baud, parity, bits, flow, ret);
pr_debug("[UART]mtk setting: (%d, %d, %d, %lu, %lu)\n",
uart->tx_mode, uart->rx_mode, uart->dma_mode,
uart->tx_trig, uart->rx_trig);
/* mtk_uart_power_up(uart); */
return ret;
}
/*---------------------------------------------------------------------------*/
static struct uart_driver mtk_uart_drv;
static struct console mtk_uart_console = {
.name = "ttyMT",
#if !defined(CONFIG_MTK_SERIAL_MODEM_TEST)
/*don't configure UART4 as console */
.write = mtk_uart_console_write,
.setup = mtk_uart_console_setup,
#endif
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &mtk_uart_drv,
};
/*---------------------------------------------------------------------------*/
static int __init mtk_uart_console_init(void)
{
int err = mtk_uart_init_ports();
if (!err)
register_console(&mtk_uart_console);
return err;
}
/*---------------------------------------------------------------------------*/
console_initcall(mtk_uart_console_init);
/*---------------------------------------------------------------------------*/
static int __init mtk_late_console_init(void)
{
if (!(mtk_uart_console.flags & CON_ENABLED))
register_console(&mtk_uart_console);
return 0;
}
/*---------------------------------------------------------------------------*/
late_initcall(mtk_late_console_init);
/*---------------------------------------------------------------------------*/
#endif /*CONFIG_MTK_SERIAL_CONSOLE */
/******************************************************************************
* Virtual FIFO implementation
*****************************************************************************
*/
#if defined(ENABLE_VFIFO)
/*---------------------------------------------------------------------------*/
static int mtk_uart_vfifo_del_dbgbuf(struct mtk_uart_vfifo *vfifo)
{
#if defined(ENABLE_VFIFO_DEBUG)
int idx;
for (idx = 0; idx < ARRAY_SIZE(vfifo->dbg); idx++) {
if (vfifo->dbg[idx].dat != 0)
kfree(vfifo->dbg[idx].dat);
vfifo->dbg[idx].dat = NULL;
vfifo->dbg[idx].idx = 0;
vfifo->dbg[idx].len = 0;
}
#endif
return 0;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_vfifo_new_dbgbuf(struct mtk_uart_vfifo *vfifo)
{
#if defined(ENABLE_VFIFO_DEBUG)
int idx;
for (idx = 0; idx < ARRAY_SIZE(vfifo->dbg); idx++) {
if (vfifo->dbg[idx].dat != 0)
kfree(vfifo->dbg[idx].dat);
vfifo->dbg[idx].idx = 0;
vfifo->dbg[idx].len = vfifo->size;
vfifo->dbg[idx].dat = kzalloc(vfifo->dbg[idx].len, GFP_ATOMIC);
if (!vfifo->dbg[idx].dat) {
mtk_uart_vfifo_del_dbgbuf(vfifo);
return -ENOMEM;
}
}
#endif
return 0;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_vfifo_create(struct mtk_uart *uart)
{ /*NOTE: please save the phyiscal address in vff->dmahd */
struct mtk_uart_vfifo *vfifo;
int idx, err = 0;
MSG_FUNC_ENTRY();
if (!uart->setting->vff) {
MSG_RAW("[UART%2d] not support VFF, Cancel alloc\n",
uart->nport);
return err;
}
MSG_RAW("[UART%2d] create\n", uart->nport);
for (idx = uart->nport * 2; idx < uart->nport * 2 + 2; idx++) {
vfifo = &mtk_uart_vfifo_port[idx];
MSG_RAW("[UART%2d] idx=%2d\n", uart->nport, idx);
if (vfifo->size) {
vfifo->addr = dma_alloc_coherent(uart->port.dev,
vfifo->size, &vfifo->dmahd, GFP_DMA);
/*
* MSG_RAW("Address: virt = 0x%p, phys = 0x%llx\n",
* vfifo->addr, vfifo->dmahd);
*/
} else {
vfifo->addr = NULL;
}
if (vfifo->size && !vfifo->addr) {
err = -ENOMEM;
break;
}
err = mtk_uart_vfifo_new_dbgbuf(vfifo);
if (err)
break;
MSG_RAW("[%2d] %p (%04d) ;\n", idx, vfifo->addr, vfifo->size);
}
MSG_RAW("\n");
return err;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_vfifo_delete(struct mtk_uart *uart)
{
struct mtk_uart_vfifo *vfifo;
int idx;
MSG_FUNC_ENTRY();
if (!uart->setting->vff) {
MSG_RAW("[UART%2d] not support VFF, Cancel free\n",
uart->nport);
return 0;
}
MSG_RAW("[UART%2d] delete", uart->nport);
for (idx = uart->nport * 2; idx < uart->nport * 2 + 2; idx++) {
vfifo = &mtk_uart_vfifo_port[idx];
if (vfifo->addr)
dma_free_coherent(uart->port.dev, vfifo->size,
vfifo->addr, vfifo->dmahd);
mtk_uart_vfifo_del_dbgbuf(vfifo);
MSG_RAW("[%2d] %p (%04d) ;", idx, vfifo->addr, vfifo->size);
vfifo->addr = NULL;
}
MSG_RAW("\n");
return 0;
}
/*---------------------------------------------------------------------------*/
int mtk_uart_vfifo_prepare(struct mtk_uart *uart)
{
struct mtuart_sysobj *obj = &mtk_uart_sysobj;
struct mtk_uart_vfifo *tport, *rport;
int tx = uart->nport << 1;
int rx = tx + 1;
MSG_FUNC_ENTRY();
if (uart->nport >= UART_NR) {
MSG_ERR("wrong port:%d\n", uart->nport);
return -EINVAL;
} else if (uart->setting->vff == FALSE) {
MSG_ERR("Port :%d not support vfifo\n", uart->nport);
return -EINVAL;
}
tport = &mtk_uart_vfifo_port[tx];
rport = &mtk_uart_vfifo_port[rx];
if ((atomic_read(&obj->vffLen[tx]) == tport->size)
&& (atomic_read(&obj->vffLen[rx]) == rport->size))
return 0;
MSG_RAW("re-alloc +\n");
mtk_uart_vfifo_delete(uart);
tport->size = atomic_read(&obj->vffLen[tx]);
tport->trig = VFF_TX_THRE(tport->size);
rport->size = atomic_read(&obj->vffLen[rx]);
rport->trig = VFF_RX_THRE(rport->size);
mtk_uart_vfifo_create(uart);
MSG_RAW("re-alloc -\n");
return 0;
}
/*---------------------------------------------------------------------------*/
static struct mtk_uart_vfifo *mtk_uart_vfifo_alloc(struct mtk_uart *uart,
int type)
{
struct mtk_uart_vfifo *vfifo = NULL;
unsigned long flags;
spin_lock_irqsave(&mtk_uart_vfifo_port_lock, flags);
MSG(INFO, "(%d, %d)", uart->nport, type);
if ((uart->nport >= (ARRAY_SIZE(mtk_uart_vfifo_port) / 2))
|| (type >= UART_VFIFO_NUM))
vfifo = NULL;
else
vfifo = &mtk_uart_vfifo_port[2 * uart->nport + type];
if (vfifo && vfifo->addr == NULL)
vfifo = NULL;
if (vfifo)
MSG(INFO, "alloc vfifo-%d[%d](%p)\n",
uart->nport, vfifo->size, vfifo->addr);
spin_unlock_irqrestore(&mtk_uart_vfifo_port_lock, flags);
return vfifo;
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_vfifo_free(struct mtk_uart *uart,
struct mtk_uart_vfifo *vfifo)
{
unsigned long flags;
if (vfifo) {
spin_lock_irqsave(&mtk_uart_vfifo_port_lock, flags);
vfifo->dma = NULL;
vfifo->cur = NULL;
vfifo->dbgidx = 0;
spin_unlock_irqrestore(&mtk_uart_vfifo_port_lock, flags);
}
}
/*---------------------------------------------------------------------------*/
static unsigned int mtk_uart_vfifo_write_allow(struct mtk_uart *uart)
{
return !mtk_uart_vfifo_is_full(uart->tx_vfifo);
}
/*---------------------------------------------------------------------------*/
static unsigned int mtk_uart_vfifo_read_allow(struct mtk_uart *uart)
{
return !mtk_uart_vfifo_is_empty(uart->rx_vfifo);
}
/*---------------------------------------------------------------------------*/
static inline unsigned short mtk_uart_vfifo_get_trig(struct mtk_uart *uart,
struct mtk_uart_vfifo *vfifo)
{
return vfifo->trig;
}
/*---------------------------------------------------------------------------*/
#define get_mtk_uart(ptr, type, member) \
(type *)((char *)ptr - offsetof(type, member))
/*---------------------------------------------------------------------------*/
#ifdef ENABE_HRTIMER_FLUSH
static enum hrtimer_restart mtk_uart_tx_vfifo_timeout(struct hrtimer *hrt)
{
struct mtk_uart_vfifo *vfifo = container_of(hrt,
struct mtk_uart_vfifo, flush);
struct mtk_uart_dma *dma = (struct mtk_uart_dma *)vfifo->dma;
struct mtk_uart *uart = dma->uart;
#if defined(ENABLE_VFIFO_DEBUG)
ktime_t cur = ktime_get();
struct timespec a = ktime_to_timespec(cur);
MSG(MSC, "flush timeout [%ld %ld]\n", a.tv_sec, a.tv_nsec);
#endif
mtk_uart_tx_vfifo_flush(uart, 1);
return HRTIMER_NORESTART;
}
#endif
/*---------------------------------------------------------------------------*/
static void mtk_uart_dma_vfifo_callback(void *data)
{
struct mtk_uart_dma *dma = (struct mtk_uart_dma *)data;
struct mtk_uart *uart = dma->uart;
MSG(DMA, "%s VFIFO CB: %4d/%4d\n",
dma->dir == DMA_TO_DEVICE ? "TX" : "RX",
mtk_uart_vfifo_get_counts(dma->vfifo), dma->vfifo->size);
if (dma->dir == DMA_FROM_DEVICE) {
/*the data must be read before return from callback, otherwise, the interrupt
* will be triggered again and again
*/
mtk_uart_dma_vfifo_rx_tasklet((unsigned long)uart);
/* return; [ALPS00031975] */
}
tasklet_schedule(&dma->tasklet);
}
/*---------------------------------------------------------------------------*/
/* static __tcmfunc irqreturn_t mtk_vfifo_irq_handler(int irq, void *dev_id) */
static irqreturn_t mtk_vfifo_irq_handler(int irq, void *dev_id)
{
struct mtk_uart_vfifo *vfifo;
vfifo = (struct mtk_uart_vfifo *)dev_id;
if (!vfifo) {
pr_err("%s: vfifo is NULL\n", __func__);
return IRQ_NONE;
}
if (!vfifo->dma) {
pr_err("%s: dma is NULL\n", __func__);
return IRQ_NONE;
}
/* Call call back function */
mtk_uart_dma_vfifo_callback(vfifo->dma);
/* Clear interrupt flag */
if (vfifo->type == UART_RX_VFIFO)
mtk_uart_vfifo_clear_rx_intr(vfifo);
else
mtk_uart_vfifo_clear_tx_intr(vfifo);
return IRQ_HANDLED;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_dma_alloc(struct mtk_uart *uart,
struct mtk_uart_dma *dma, int mode, struct mtk_uart_vfifo *vfifo)
{
int ret = 0;
MSG_FUNC_ENTRY();
if (mode == UART_NON_DMA)
return -1;
switch (mode) {
case UART_TX_VFIFO_DMA:
if (!vfifo) {
MSG(ERR, "fail due to NULL tx_vfifo\n");
ret = -1;
break;
}
vfifo->dma = dma;
dma->dir = DMA_TO_DEVICE;
dma->mode = mode;
dma->vfifo = vfifo;
dma->uart = uart;
init_completion(&dma->done);
tasklet_init(&dma->tasklet, mtk_uart_dma_vfifo_tx_tasklet,
(unsigned long)uart);
if (!atomic_read(&vfifo->reg_cb)) {
/* disable interrupts */
/* FIXME */
mtk_uart_vfifo_disable_tx_intr(uart);
ret = request_irq(vfifo->irq_id,
(irq_handler_t) mtk_vfifo_irq_handler,
IRQF_LEVEL_TRIGGER_POLARITY, DRV_NAME, vfifo);
if (ret)
return ret;
atomic_set(&vfifo->reg_cb, 1);
}
atomic_set(&dma->free, 1);
break;
case UART_RX_VFIFO_DMA:
if (!vfifo) {
MSG(ERR, "fail due to NULL rx_vfifo\n");
ret = -1;
break;
}
vfifo->dma = dma;
dma->dir = DMA_FROM_DEVICE;
dma->mode = mode;
dma->vfifo = vfifo;
dma->uart = uart;
init_completion(&dma->done);
tasklet_init(&dma->tasklet, mtk_uart_dma_vfifo_rx_tasklet,
(unsigned long)uart);
if (!atomic_read(&vfifo->reg_cb)) {
/* disable interrupts */
mtk_uart_vfifo_disable_rx_intr(uart);
ret = request_irq(vfifo->irq_id,
(irq_handler_t) mtk_vfifo_irq_handler,
IRQF_LEVEL_TRIGGER_POLARITY, DRV_NAME, vfifo);
if (ret)
return ret;
atomic_set(&vfifo->reg_cb, 1);
}
atomic_set(&dma->free, 1);
break;
}
return ret;
}
/*---------------------------------------------------------------------------*/
void mtk_uart_dma_stop(struct mtk_uart *uart, struct mtk_uart_dma *dma)
{
MSG_FUNC_ENTRY();
if (!dma)
return;
mtk_uart_stop_dma(dma);
atomic_set(&dma->free, 1);
complete(&dma->done);
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_dma_free(struct mtk_uart *uart, struct mtk_uart_dma *dma)
{
unsigned long flags;
MSG_FUNC_ENTRY();
if (!dma)
return;
if (dma->mode == UART_NON_DMA)
return;
if ((dma->mode == UART_RX_VFIFO_DMA || dma->mode == UART_TX_VFIFO_DMA)
&& (!dma->vfifo))
return;
if (dma->vfifo && !mtk_uart_vfifo_is_empty(dma->vfifo)) {
tasklet_schedule(&dma->tasklet);
MSG(DMA, "wait for %s vfifo dma completed!!!\n",
dma->dir == DMA_TO_DEVICE ? "TX" : "RX");
wait_for_completion(&dma->done);
}
spin_lock_irqsave(&uart->port.lock, flags);
mtk_uart_stop_dma(dma);
if (dma->mode == UART_TX_VFIFO_DMA) {
if (dma->vfifo && timer_pending(&dma->vfifo->timer))
del_timer_sync(&dma->vfifo->timer);
#ifdef ENABE_HRTIMER_FLUSH
if (dma->vfifo && hrtimer_active(&dma->vfifo->flush))
hrtimer_cancel(&dma->vfifo->flush);
#endif
}
/* [ALPS00030487] tasklet_kill function may schedule,
* so release spin lock first,
* after release, set spin lock again.
*/
/* [ALPS00030487] Add this */
spin_unlock_irqrestore(&uart->port.lock, flags);
tasklet_kill(&dma->tasklet);
/* [ALPS00030487] Add this */
spin_lock_irqsave(&uart->port.lock, flags);
mtk_uart_reset_dma(dma);
mtk_uart_vfifo_disable(uart, dma->vfifo);
mtk_uart_vfifo_free(uart, dma->vfifo);
MSG(INFO, "free %s dma completed!!!\n",
dma->dir == DMA_TO_DEVICE ? "TX" : "RX");
memset(dma, 0, sizeof(struct mtk_uart_dma));
spin_unlock_irqrestore(&uart->port.lock, flags);
}
#endif /*defined(ENABLE_VFIFO) */
/*---------------------------------------------------------------------------*/
static void mtk_uart_set_baud(struct mtk_uart *uart, int baudrate)
{
if (uart->port.flags & ASYNC_SPD_CUST) {
/**
* [ALPS00137126] Begin
* Because the origin design of custom baudrate in linux is for low speed case,
* we add some
* modify to support high speed case.
* NOTE: If the highest bit of "custom_divisor" is ONE,
* we will use custom_divisor store baudrate
* directly. That means(we suppose unsigned int is 32 bits):
* custom_divisor[31] == 1, then custom_divisor[30..0] == custom baud rate
* custom_divisor[31] == 0, then custom_divisor[30..0] == sysclk/16/baudrate
*/
if (uart->port.custom_divisor & (1 << 31)) {
baudrate = uart->port.custom_divisor & (~(1 << 31));
/* Baud rate should not more than sysclk/4 */
if (baudrate > (uart->sysclk >> 2))
baudrate = 9600;
} else {
/*the baud_base gotten in user space eqauls to sysclk/16.
* hence, we need to restore the difference when calculating custom baudrate
*/
if (!uart->custom_baud) {
baudrate = uart->sysclk / 16;
baudrate = baudrate / uart->port.custom_divisor;
} else {
baudrate = uart->custom_baud;
}
/* [ALPS00137126] End */
}
MSG(CFG, "CUSTOM, baudrate = %d, divisor = %d\n",
baudrate, uart->port.custom_divisor);
}
if (uart->auto_baud)
mtk_uart_set_auto_baud(uart);
mtk_uart_baud_setting(uart, baudrate);
uart->baudrate = baudrate;
}
/*---------------------------------------------------------------------------*/
static inline bool mtk_uart_enable_sysrq(struct mtk_uart *uart)
{
return uart->setting->sysrq;
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_rx_chars(struct mtk_uart *uart)
{
struct uart_port *port = &uart->port;
/* struct tty_struct *tty = uart->port.state->port.tty;*/
int max_count = UART_FIFO_SIZE;
unsigned int data_byte, status;
unsigned int flag;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
/* MSG_FUNC_ENTRY(); */
while (max_count-- > 0) {
/* check status */
if (!mtk_uart_data_ready(uart))
break;
#if 0
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
if (tty->low_latency) {
/*
* If this failed then we will throw away the
* bytes but must do so to clear interrupts
*/
tty_flip_buffer_push(tty);
}
}
#endif
/* read the byte */
data_byte = uart->read_byte(uart);
port->icount.rx++;
flag = TTY_NORMAL;
update_history_byte(0, uart->nport, data_byte);
status = mtk_uart_filter_line_status(uart);
/* error handling routine */
if (status & UART_LSR_BI) {
MSG(INFO, "Break interrupt!!\n");
port->icount.brk++;
if (uart_handle_break(port))
continue;
flag = TTY_BREAK;
} else if (status & UART_LSR_PE) {
MSG(INFO, "Parity Error!!\n");
port->icount.parity++;
flag = TTY_PARITY;
} else if (status & UART_LSR_FE) {
MSG(INFO, "Frame Error!!\n");
port->icount.frame++;
flag = TTY_FRAME;
} else if (status & UART_LSR_OE) {
MSG(INFO, "Overrun!!\n");
port->icount.overrun++;
flag = TTY_OVERRUN;
}
#ifdef CONFIG_MAGIC_SYSRQ
if (mtk_uart_enable_sysrq(uart)) {
if (uart_handle_sysrq_char(port, data_byte))
continue;
/* FIXME. Infinity, 20081002, 'BREAK' char to enable sysrq handler { */
#if defined(CONFIG_MAGIC_SYSRQ) && defined(CONFIG_SERIAL_CORE_CONSLE)
if (data_byte == 0)
uart->port.sysrq = 1;
#endif
/* FIXME. Infinity, 20081002, 'BREAK' char to enable sysrq handler } */
}
#endif
if (!tty_insert_flip_char(&(port->state->port),
data_byte, flag))
MSG(ERR, "tty_insert_flip_char: no space");
}
tty_flip_buffer_push(&(port->state->port));
update_history_time(0, uart->nport);
spin_unlock_irqrestore(&port->lock, flags);
MSG(FUC, "%s (%2d)\n", __func__, UART_FIFO_SIZE - max_count - 1);
/*
*#if defined(CONFIG_MTK_HDMI_SUPPORT)
*#ifdef MHL_UART_SHARE_PIN
* if ((UART_FIFO_SIZE - max_count - 1) > 0)
* hdmi_force_on(UART_FIFO_SIZE - max_count - 1);
*#endif
*8#endif
*/
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_tx_chars(struct mtk_uart *uart)
{
/* Notice:
* The function is called by uart_start, which is protected by spin lock,
* Hence, no spin-lock is required in the functions
*/
struct uart_port *port = &uart->port;
struct circ_buf *xmit = &port->state->xmit;
int count;
/* deal with x_char first */
if (unlikely(port->x_char)) {
MSG(INFO, "detect x_char!!\n");
uart->write_byte(uart, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
/* stop tx if circular buffer is empty or this port is stopped */
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
struct tty_struct *tty = port->state->port.tty;
if (!uart_circ_empty(xmit))
MSG(ERR, "\t\tstopped: empty: %d %d %d\n",
uart_circ_empty(xmit), tty->stopped,
tty->hw_stopped);
mtk_uart_stop_tx(port);
return;
}
count = port->fifosize - 1;
do {
if (uart_circ_empty(xmit))
break;
if ((count == 1) || (!uart->write_allow(uart))) {
/* to avoid the interrupt is not enable. */
mtk_uart_enable_intrs(uart, UART_IER_ETBEI);
break;
}
uart->write_byte(uart, xmit->buf[xmit->tail]);
update_history_byte(1, uart->nport, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
} while (--count > 0);
MSG(INFO, "TX %d chars\n", port->fifosize - 1 - count);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
mtk_uart_stop_tx(port);
update_history_time(1, uart->nport);
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_rx_handler(struct mtk_uart *uart, int intrs)
{
if (uart->rx_mode == UART_NON_DMA) {
mtk_uart_rx_chars(uart);
} else if (uart->rx_mode == UART_RX_VFIFO_DMA) {
#if defined(ENABLE_VFIFO)
mtk_uart_rx_pre_handler(uart, intrs);
mtk_uart_dma_vfifo_rx_tasklet((unsigned long)uart);
#endif
}
}
/*---------------------------------------------------------------------------*/
void mtk_uart_tx_handler(struct mtk_uart *uart)
{
struct uart_port *port = &uart->port;
unsigned long flags;
if (uart->tx_mode == UART_NON_DMA) {
spin_lock_irqsave(&port->lock, flags);
mtk_uart_tx_chars(uart);
spin_unlock_irqrestore(&port->lock, flags);
} else if (uart->tx_mode == UART_TX_VFIFO_DMA) {
tasklet_schedule(&uart->dma_tx.tasklet);
}
}
/*---------------------------------------------------------------------------*/
#ifdef ENABLE_DEBUG
/*---------------------------------------------------------------------------*/
static const char * const fifo[] = { "No FIFO", "Unstable FIFO",
"Unknown", "FIFO Enabled"
};
static const char * const interrupt[] = { "Modem Status Chg", "Tx Buffer Empty",
"Rx Data Received", "BI, FE, PE, or OE",
"0x04", "0x05", "Rx Data Timeout", "0x07",
"SW Flow Control", "0x09", "0x10", "0x11", "0x12",
"0x13", "0x14", "0x15", "HW Flow Control"
};
/*---------------------------------------------------------------------------*/
#endif
/*---------------------------------------------------------------------------*/
/* static __tcmfunc irqreturn_t mtk_uart_irq(int irq, void *dev_id) */
static irqreturn_t mtk_uart_irq(int irq, void *dev_id)
{
unsigned int intrs, timeout = 0;
struct mtk_uart *uart = (struct mtk_uart *)dev_id;
#ifndef CONFIG_FIQ_DEBUGGER
#ifdef CONFIG_MTK_ENG_BUILD
#ifdef CONFIG_MTK_PRINTK_UART_CONSOLE
unsigned long base;
base = uart->base;
if ((uart == console_port) && (UART_READ32(UART_LSR) & 0x01))
mt_enable_uart();
#endif
#endif
#endif
intrs = mtk_uart_get_interrupt(uart);
#ifdef ENABLE_DEBUG
{
struct uart_iir_reg *iir = (struct uart_iir_reg *) &intrs;
if (iir->NINT)
MSG(INT, "No interrupt (%s)\n", fifo[iir->FIFOE]);
else if (iir->ID < ARRAY_SIZE(interrupt))
MSG(INT, "%02x %s (%s)\n",
iir->ID, interrupt[iir->ID], fifo[iir->FIFOE]);
else
MSG(INT, "%2x\n", iir->ID);
}
#endif
intrs &= UART_IIR_INT_MASK;
if (intrs == UART_IIR_NO_INT_PENDING)
return IRQ_HANDLED;
/* pr_debug("[UART%d] intrs:0x%x\n", uart->nport, intrs); */
if (intrs == UART_IIR_CTI)
timeout = 1;
else if (intrs == UART_IIR_THRE)
mtk_uart_tx_handler(uart);
else if (intrs == UART_IIR_MS)
mtk_uart_get_modem_status(uart);
mtk_uart_intr_last_check(uart, intrs);
mtk_uart_rx_handler(uart, intrs);
return IRQ_HANDLED;
}
/*---------------------------------------------------------------------------*/
/* test whether the transmitter fifo and shifter for the port is empty. */
static unsigned int mtk_uart_tx_empty(struct uart_port *port)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG_FUNC_ENTRY();
#if defined(ENABLE_VFIFO)
if (uart->tx_mode == UART_TX_VFIFO_DMA)
return mtk_uart_vfifo_is_empty(uart->dma_tx.vfifo) ?
TIOCSER_TEMT : 0;
#endif
return uart->write_allow(uart) ? TIOCSER_TEMT : 0;
}
/*---------------------------------------------------------------------------*/
/* FIXME */
/* stop transmitting characters
* Note: this function is call with interrupt disabled
*/
static void mtk_uart_stop_tx(struct uart_port *port)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG_FUNC_ENTRY();
#if defined(ENABLE_VFIFO)
if (uart->tx_mode == UART_TX_VFIFO_DMA) {
/*1. UART_IER_ETBEI can't be disabled or zero data appears in TX */
/*2. TX_INT_EN.INTEN will be reset automatically by HW */
} else
#endif
/* disable tx interrupt */
mtk_uart_disable_intrs(uart, UART_IER_ETBEI);
uart->tx_stop = 1;
}
/*---------------------------------------------------------------------------*/
/* FIXME */
/* start transmitting characters.
* Note: this function is call with interrupt disabled
*/
static void mtk_uart_start_tx(struct uart_port *port)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
struct circ_buf *xmit = &port->state->xmit;
unsigned long size;
size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
if (!size)
return;
uart->tx_stop = 0;
#ifdef ENABLE_RAW_DATA_DUMP
reset_tx_raw_data(uart);
#endif
#if defined(ENABLE_VFIFO)
if (uart->tx_mode == UART_TX_VFIFO_DMA) {
if (UART_DEBUG_EVT(DBG_EVT_BUF))
pr_debug("[UART%d] %s\n",
uart->nport, __func__);
if (!uart->write_allow(uart))
mtk_uart_vfifo_enable_tx_intr(uart);
else
mtk_uart_dma_vfifo_tx_tasklet((unsigned long)uart);
} else {
#else
{
#endif
if (uart->write_allow(uart))
mtk_uart_tx_chars(uart);
}
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_send_xchar(struct uart_port *port, char ch)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
unsigned long flags;
MSG_FUNC_ENTRY();
if (uart->tx_stop)
return;
spin_lock_irqsave(&port->lock, flags);
while (!mtk_uart_write_allow(uart))
;
mtk_uart_write_byte(uart, (unsigned char)ch);
port->icount.tx++;
spin_unlock_irqrestore(&port->lock, flags);
}
/*---------------------------------------------------------------------------*/
/* enable the modem status interrupts */
static void mtk_uart_enable_ms(struct uart_port *port)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG_FUNC_ENTRY();
uart->ms_enable = 1;
}
/*---------------------------------------------------------------------------*/
/* grab any interrupt resources and initialize any low level driver state */
static int mtk_uart_startup(struct uart_port *port)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
int ret;
long mask = UART_IER_HW_NORMALINTS;
MSG_FUNC_ENTRY();
/*the uart port is power up in power_mgnt */
/* Reset default flag when the uart starts up, or the previous setting,
* Such as custom baudrate will be still applied even it is ever closed
*/
/* uart->port.flags = UPF_BOOT_AUTOCONF; */
/* uart->port.custom_divisor = 1; */
/* Check whether is ATE_Factory mode */
#ifdef ATE_FACTORY_ENABLE
mtk_uart_is_ate_factory_mode(uart);
#endif /*ATE_FACTORY_ENABLE */
uart->fctl_mode = UART_FC_NONE;
/* disable interrupts */
mtk_uart_disable_intrs(uart, UART_IER_ALL_INTS);
/* allocate irq line */
/* ret = request_irq(port->irq, mtk_uart_irq, 0, DRV_NAME, uart); */
/* [ALPS00142658] Fix incompatible pointer type waning */
ret = request_irq(port->irq, (irq_handler_t) mtk_uart_irq,
uart->setting->irq_flags, DRV_NAME, uart);
if (ret)
return ret;
#if defined(ENABLE_VFIFO)
#if defined(ENABLE_VFIFO_DEBUG)
mtk_uart_vfifo_prepare(uart);
uart->dma_mode = uart->setting->dma_mode;
uart->tx_mode = uart->setting->tx_mode;
uart->rx_mode = uart->setting->rx_mode;
uart->tx_trig = uart->setting->tx_trig;
uart->rx_trig = uart->setting->rx_trig;
#endif
/* allocate vfifo */
if (uart->rx_mode == UART_RX_VFIFO_DMA) {
uart->rx_vfifo = mtk_uart_vfifo_alloc(uart, UART_RX_VFIFO);
ret = mtk_uart_dma_alloc(uart, &uart->dma_rx,
uart->rx_mode, uart->rx_vfifo);
if (!uart->rx_vfifo || ret) {
uart->rx_mode = UART_NON_DMA;
MSG(ERR, "RX DMA alloc fail [%d]\n", ret);
}
}
if (uart->tx_mode == UART_TX_VFIFO_DMA) {
uart->tx_vfifo = mtk_uart_vfifo_alloc(uart, UART_TX_VFIFO);
ret = mtk_uart_dma_alloc(uart, &uart->dma_tx,
uart->tx_mode, uart->tx_vfifo);
if (!uart->tx_vfifo || ret) {
uart->tx_mode = UART_NON_DMA;
MSG(ERR, "TX DMA alloc fail [%d]\n", ret);
}
}
/* start vfifo dma */
if (uart->tx_mode == UART_TX_VFIFO_DMA) {
uart->write_allow = mtk_uart_vfifo_write_allow;
uart->write_byte = mtk_uart_vfifo_write_byte;
mtk_uart_vfifo_enable(uart, uart->tx_vfifo);
mtk_uart_dma_setup(uart, &uart->dma_tx);
if (mtk_uart_dma_start(uart, &uart->dma_tx))
MSG(ERR, "mtk_uart_dma_start fails\n");
#ifdef ENABE_HRTIMER_FLUSH
hrtimer_init(&uart->tx_vfifo->flush,
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
uart->tx_vfifo->flush.function = mtk_uart_tx_vfifo_timeout;
#endif
} else if (uart->tx_mode == UART_NON_DMA) {
uart->write_allow = mtk_uart_write_allow;
uart->write_byte = mtk_uart_write_byte;
}
if (uart->rx_mode == UART_RX_VFIFO_DMA) {
uart->read_allow = mtk_uart_vfifo_read_allow;
uart->read_byte = mtk_uart_vfifo_read_byte;
mtk_uart_vfifo_enable(uart, uart->rx_vfifo);
mtk_uart_dma_setup(uart, &uart->dma_rx);
if (mtk_uart_dma_start(uart, &uart->dma_rx))
MSG(ERR, "mtk_uart_dma_start fails\n");
} else if (uart->rx_mode == UART_NON_DMA) {
uart->read_allow = mtk_uart_read_allow;
uart->read_byte = mtk_uart_read_byte;
}
#endif
if ((uart->tx_mode == UART_TX_VFIFO_DMA)
|| (uart->rx_mode == UART_RX_VFIFO_DMA)) {
if (!bt_port || (bt_port && (uart != bt_port)))
mtk_uart_disable_dpidle(uart);
} else if ((uart->tx_mode == UART_NON_DMA)
&& (uart->rx_mode == UART_NON_DMA)) {
mtk_uart_enable_dpidle(uart);
}
uart->tx_stop = 0;
uart->rx_stop = 0;
/* After applying UART as Level-Triggered IRQ, the function must be called or
* the interrupt will be incorrect activated.
*/
mtk_uart_fifo_set_trig(uart, uart->tx_trig, uart->rx_trig);
mtk_uart_enable_sleep(uart);
/* enable interrupts */
mtk_uart_enable_intrs(uart, mask);
return 0;
}
/*---------------------------------------------------------------------------*/
/* disable the port, disable any break condition that may be in effect, and
* free any interrupt resources
*/
static void mtk_uart_shutdown(struct uart_port *port)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG_FUNC_ENTRY();
/* disable interrupts */
mtk_uart_disable_intrs(uart, UART_IER_ALL_INTS);
#if defined(ENABLE_VFIFO)
/* free dma channels and vfifo */
mtk_uart_dma_free(uart, &uart->dma_tx);
mtk_uart_dma_free(uart, &uart->dma_rx);
#endif
mdelay(1);
mtk_uart_fifo_flush(uart);
/* release irq line */
free_irq(port->irq, port);
mtk_uart_enable_dpidle(uart);
/* the uart port will be powered off in power_mgnt */
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_flush_buffer(struct uart_port *port)
{
#if defined(ENABLE_DEBUG)
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG_FUNC_ENTRY();
#endif
/* mtk_uart_fifo_flush(uart); */
}
/*---------------------------------------------------------------------------*/
/*
* For stability test
*/
void mtk_uart_update_sysclk(void)
{
int i;
unsigned long flags;
struct mtk_uart *uart;
struct uart_port *port;
unsigned int baud;
for (i = 0; i < UART_NR; i++) {
uart = &mtk_uarts[i];
port = &uart->port;
baud = uart->baudrate;
port->uartclk = UART_SYSCLK;/* mt6575_get_bus_freq()*1000/4; */
uart->sysclk = UART_SYSCLK;/* mt6575_get_bus_freq()*1000/4; */
if (baud == 0)
continue;/* The istance is not initialized yet. */
spin_lock_irqsave(&port->lock, flags);
mtk_uart_set_baud(uart, baud);
spin_unlock_irqrestore(&port->lock, flags);
}
}
EXPORT_SYMBOL(mtk_uart_update_sysclk);
/*---------------------------------------------------------------------------*/
/* change the port parameters, including word length, parity, stop bits.
* update read_status_mask and ignore_status_mask to indicate the types of
* events we are interrested in receiving
*/
static void mtk_uart_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
unsigned long flags;
unsigned int baud;
int datalen, mode;
int parity = 0;
int stopbit = 1;
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
MSG_FUNC_ENTRY();
/* datalen : default 8bits */
switch (termios->c_cflag & CSIZE) {
case CS5:
datalen = 5;
break;
case CS6:
datalen = 6;
break;
case CS7:
datalen = 7;
break;
case CS8:
default:
datalen = 8;
break;
}
/* stopbit : default 1 */
if (termios->c_cflag & CSTOPB)
stopbit = 2;
/* parity : default none */
if (termios->c_cflag & PARENB) {
if (termios->c_cflag & PARODD)
parity = 1; /* odd */
else
parity = 2; /* even */
}
spin_lock_irqsave(&port->lock, flags);
/* read status mask */
port->read_status_mask = 0;
if (termios->c_iflag & INPCK) {
/* frame error, parity error */
port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
}
if (termios->c_iflag & (BRKINT | PARMRK)) {
/* break error */
port->read_status_mask |= UART_LSR_BI;
}
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR) {
/* ignore parity and framing errors */
port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE;
}
if (termios->c_iflag & IGNBRK) {
/* ignore break errors. */
port->ignore_status_mask |= UART_LSR_BI;
if (termios->c_iflag & IGNPAR) {
/* ignore overrun errors */
port->ignore_status_mask |= UART_LSR_OE;
}
}
/* ignore all characters if CREAD is not set */
if ((termios->c_cflag & CREAD) == 0)
uart->ignore_rx = 1;
/* update per port timeout */
/*when dividor is 1, baudrate = clock */
baud = uart_get_baud_rate(port, termios, old, 0, uart->sysclk);
if (baud == 0) {
spin_unlock_irqrestore(&port->lock, flags);
pr_info("UART error baud\n");
return;
}
uart_update_timeout(port, termios->c_cflag, baud);
mtk_uart_config(uart, datalen, stopbit, parity);
mtk_uart_set_baud(uart, baud);
/* setup fifo trigger level */
mtk_uart_fifo_set_trig(uart, uart->tx_trig, uart->rx_trig);
/* setup hw flow control: only port 0 ~1 support hw rts/cts */
MSG(CFG, "c_lflag:%X, c_iflag:%X, c_oflag:%X, c_cflag:%X\n",
termios->c_lflag, termios->c_iflag,
termios->c_oflag, termios->c_cflag);
if (HW_FLOW_CTRL_PORT(uart) && (termios->c_cflag & CRTSCTS)
&& (!(termios->c_iflag & 0x80000000))) {
if (__ratelimit(&ratelimit))
pr_debug("Hardware Flow Control\n");
mode = UART_FC_HW;
} else if (termios->c_iflag & 0x80000000) {
if (__ratelimit(&ratelimit))
pr_debug("MTK Software Flow Control\n");
mode = UART_FC_SW;
} else if (termios->c_iflag & (IXON | IXOFF | IXANY)) {
if (__ratelimit(&ratelimit))
pr_debug("Linux default SW Flow Control\n");
mode = UART_FC_NONE;
} else {
if (__ratelimit(&ratelimit))
pr_debug("No Flow Control\n");
mode = UART_FC_NONE;
}
mtk_uart_set_flow_ctrl(uart, mode);
/* determine if port should enable modem status interrupt */
if (UART_ENABLE_MS(port, termios->c_cflag))
uart->ms_enable = 1;
else
uart->ms_enable = 0;
if (console_port && (uart == console_port) && uart->port.cons)
uart->port.cons->cflag = termios->c_cflag;
spin_unlock_irqrestore(&port->lock, flags);
}
/*---------------------------------------------------------------------------*/
/* perform any power management related activities on the port */
static void mtk_uart_power_mgnt(struct uart_port *port,
unsigned int state, unsigned int oldstate)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG(FUC, "%s(%d->%d)\n", __func__, oldstate, state);
switch (state) {
case 0:
mtk_uart_power_up(uart);
break;
case 3:
mtk_uart_power_down(uart);
break;
default:
MSG(ERR, "unknown pm: %d\n", state);
}
}
/*---------------------------------------------------------------------------*/
/* return a pointer to a string constant describing the port */
static const char *mtk_uart_type(struct uart_port *port)
{
return "MTK UART";
}
/*---------------------------------------------------------------------------*/
/* release any memory and io region resources currently in used by the port */
static void mtk_uart_release_port(struct uart_port *port)
{
}
/*---------------------------------------------------------------------------*/
/* request any memory and io region resources required by the port */
static int mtk_uart_request_port(struct uart_port *port)
{
return 0;
}
/*---------------------------------------------------------------------------*/
/* perform any autoconfiguration steps required by the port.
* it's expected to claim the resources and map the port.
*/
static void mtk_uart_config_port(struct uart_port *port, int flags)
{
struct mtk_uart *uart = (struct mtk_uart *)port;
if (flags & UART_CONFIG_TYPE) {
if (mtk_uart_request_port(port))
MSG(ERR, "mtk_uart_request_port fail\n");
port->type = PORT_MTK;
}
}
/*---------------------------------------------------------------------------*/
/* verify if the new serial information contained within 'ser' is suitable */
static int mtk_uart_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
/*[ALPS00142658] Fix unused variable waring */
#if (defined(ENABLE_DEBUG) || defined(SERIAL_STRUCT_EXT))
struct mtk_uart *uart = (struct mtk_uart *)port;
#endif
int ret = 0;
MSG(FUC, "%s: %8x, %d, %d\n", __func__,
ser->flags, ser->custom_divisor, uart->custom_baud);
if (ser->type != PORT_UNKNOWN && ser->type != PORT_MTK)
ret = -EINVAL;
if (ser->irq != port->irq)
ret = -EINVAL;
if (ser->baud_base < 110)
ret = -EINVAL;
#if defined(SERIAL_STRUCT_EXT)
/*EXtension: the custom baudrate is stored in reserved field */
uart->custom_baud = ser->reserved[0];
#endif
return ret;
}
/*---------------------------------------------------------------------------*/
/* perform any port specific IOCTLs */
static int mtk_uart_ioctl(struct uart_port *port,
unsigned int cmd, unsigned long arg)
{
#if defined(ENABLE_DEBUG)
struct mtk_uart *uart = (struct mtk_uart *)port;
MSG(FUC, "IOCTL: %8X\n", cmd);
#endif
return -ENOIOCTLCMD;
}
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_CONSOLE_POLL
/*---------------------------------------------------------------------------*/
static int mtk_uart_get_poll_char(struct uart_port *port)
{ /* don't care vfifo setting */
struct mtk_uart *uart = (struct mtk_uart *)port;
/* [ALPS00033048] For Linux 2.6.35 kgdb chagne, using while loop may block kgdb,
* return NO_POLL_CHAR directly if no data to read
*/
#if 0
while (!(uart->read_status(uart) & UART_LSR_DR))
cpu_relax();
#else
if (!mtk_uart_data_ready(uart))
return NO_POLL_CHAR;
#endif
/* End of [ALPS00033048] */
return mtk_uart_read_byte(uart);
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_put_poll_char(struct uart_port *port, unsigned char c)
{ /* don't care vfifo setting */
struct mtk_uart *uart = (struct mtk_uart *)port;
while (!mtk_uart_write_allow(uart))
barrier();
mtk_uart_write_byte(uart, c);
}
/*---------------------------------------------------------------------------*/
#endif
/*---------------------------------------------------------------------------*/
static const struct uart_ops mtk_uart_ops = {
.tx_empty = mtk_uart_tx_empty,
.set_mctrl = mtk_uart_set_mctrl,
.get_mctrl = mtk_uart_get_mctrl,
.stop_tx = mtk_uart_stop_tx,
.start_tx = mtk_uart_start_tx,
.stop_rx = mtk_uart_stop_rx,
.send_xchar = mtk_uart_send_xchar,
.enable_ms = mtk_uart_enable_ms,
.break_ctl = mtk_uart_break_ctl,
.startup = mtk_uart_startup,
.shutdown = mtk_uart_shutdown,
.flush_buffer = mtk_uart_flush_buffer,
.set_termios = mtk_uart_set_termios,
.pm = mtk_uart_power_mgnt,
.type = mtk_uart_type,
.release_port = mtk_uart_release_port,
.request_port = mtk_uart_request_port,
.config_port = mtk_uart_config_port,
.verify_port = mtk_uart_verify_port,
.ioctl = mtk_uart_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = mtk_uart_get_poll_char,
.poll_put_char = mtk_uart_put_poll_char,
#endif
};
/*---------------------------------------------------------------------------*/
static struct uart_driver mtk_uart_drv = {
.owner = THIS_MODULE,
.driver_name = DRV_NAME,
.dev_name = "ttyMT",
.major = UART_MAJOR,
.minor = UART_MINOR,
.nr = UART_NR,
#if defined(CONFIG_MTK_SERIAL_CONSOLE) && !defined(CONFIG_MTK_SERIAL_MODEM_TEST)
.cons = &mtk_uart_console,
#endif
};
/*---------------------------------------------------------------------------*/
static int mtk_uart_probe(struct platform_device *pdev)
{
struct mtk_uart *uart;
int err;
#if !defined(CONFIG_FPGA_EARLY_PORTING)
static const char * const clk_uart_name[] = {
"uart0-main",
"uart1-main",
"uart2-main",
"uart3-main",
"uart4-main",
};
struct mtk_uart_setting *uart_setting = NULL;
#if !defined(CONFIG_MTK_LEGACY)
/* for GPIO pinctrl */
struct pinctrl *ppinctrl = NULL;
#endif
#endif /* !defined(CONFIG_FPGA_EARLY_PORTING) */
if (pdev->dev.of_node) {
struct device_node *node = pdev->dev.of_node;
err = of_property_read_u32(node, "cell-index", &pdev->id);
if (err)
pr_err("[DTS] get uart platform_device id fail!!\n");
}
if (pdev->id >= UART_NR) {
pr_err("DTS cell ID %d > UART nuber %d\n", pdev->id, UART_NR);
return -ENODEV;
}
uart = &mtk_uarts[pdev->id];
MSG_FUNC_ENTRY();
/* For clock setting */
#if !defined(CONFIG_FPGA_EARLY_PORTING)
uart_setting = get_uart_default_settings(pdev->id);
uart_setting->clk_uart_main = devm_clk_get(&pdev->dev,
clk_uart_name[pdev->id]);
if (IS_ERR(uart_setting->clk_uart_main)) {
pr_err("[UART%d][CCF]cannot get %s clock. ptr_err:%ld\n",
pdev->id, clk_uart_name[pdev->id],
PTR_ERR(uart_setting->clk_uart_main));
return PTR_ERR(uart_setting->clk_uart_main);
}
err = clk_prepare(uart_setting->clk_uart_main);
if (err) {
pr_err("[UART%d] cannot prepare main clk ctrl\n", pdev->id);
//return err;
goto CLK_ERR;
}
pr_debug("[UART%d][CCF]clk_uart_main:%p\n",
pdev->id, uart_setting->clk_uart_main);
if (pdev->id == 0) {
struct clk *clk_uart0_dma = devm_clk_get(&pdev->dev,
"uart-apdma");
if (IS_ERR(clk_uart0_dma)) {
pr_err("[UART][CCF]cannot get clk_uart0_dma clock. ptr_err:%ld\n",
PTR_ERR(clk_uart0_dma));
//return PTR_ERR(clk_uart0_dma);
goto CLK_PREPARE_ERR;
}
err = clk_prepare(clk_uart0_dma);
if (err) {
pr_err("[UART%d] cannot prepare dma clk ctrl\n",
pdev->id);
//return err;
goto CLK_PREPARE_ERR;
}
set_uart_dma_clk(pdev->id, clk_uart0_dma);
pr_debug("[UART][CCF]clk_uart0_dma:%p\n", clk_uart0_dma);
}
#else
pr_debug("[UART][CCF]%s CONFIG_FPGA_EARLY_PORTING is defined!\n",
__func__);
#endif
/* For GPIO setting */
#if !defined(CONFIG_MTK_LEGACY) && !defined(CONFIG_FPGA_EARLY_PORTING)
ppinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(ppinctrl)) {
err = PTR_ERR(ppinctrl);
pr_err("[UART%d][PinC]cannot find pinctrl. ptr_err:%ld\n",
pdev->id, PTR_ERR(ppinctrl));
goto CLK_PREPARE_ERR;
}
set_uart_pinctrl(pdev->id, ppinctrl);
pr_debug("[UART%d][PinC]set idx:%d, ppinctrl:%p\n",
pdev->id, pdev->id, ppinctrl);
#else /* !defined(CONFIG_MTK_LEGACY) && !defined(CONFIG_FPGA_EARLY_PORTING) */
pr_debug("[UART][PinC]%s CONFIG_MTK_LEGACY or CONFIG_FPGA_EARLY_PORTING is defined!\n",
__func__);
#endif /* !defined(CONFIG_MTK_LEGACY) && !defined(CONFIG_FPGA_EARLY_PORTING) */
if (uart->setting->support_33bits) {
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(33);
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(33))) {
dev_err(&pdev->dev, "dma_set_mask return error.\n");
err = -EINVAL;
goto PINCTRL_ERR;
}
} else {
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
}
uart->port.dev = &pdev->dev;
err = uart_add_one_port(&mtk_uart_drv, &uart->port);
if (!err)
platform_set_drvdata(pdev, uart);
else
goto PINCTRL_ERR;
#if defined(ENABLE_VFIFO)
err = mtk_uart_vfifo_create(uart);
if (err) {
mtk_uart_vfifo_delete(uart);
pr_err("create vff buffer fail:%d\n", err);
goto VFIFO_ERR;
}
#endif
return err;
#if defined(ENABLE_VFIFO)
VFIFO_ERR:
uart_remove_one_port(&mtk_uart_drv, &uart->port);
#endif
PINCTRL_ERR:
set_uart_pinctrl(pdev->id, NULL);
#if !defined(CONFIG_FPGA_EARLY_PORTING)
CLK_PREPARE_ERR:
clk_unprepare(uart_setting->clk_uart_main);
CLK_ERR:
uart_setting->clk_uart_main = NULL;
#endif
return err;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_remove(struct platform_device *pdev)
{
struct mtk_uart *uart = platform_get_drvdata(pdev);
int err;
platform_set_drvdata(pdev, NULL);
if (!uart)
return -EINVAL;
err = uart_remove_one_port(&mtk_uart_drv, &uart->port);
#if defined(ENABLE_VFIFO)
mtk_uart_vfifo_delete(uart);
#endif
return err;
}
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/*---------------------------------------------------------------------------*/
static int mtk_uart_syscore_suspend(void)
{
int ret = 0;
unsigned long flags;
if (bt_port) {
struct mtk_uart *uart = bt_port;
spin_lock_irqsave(&mtk_uart_bt_lock, flags);
ret = uart_suspend_port(&mtk_uart_drv, &uart->port);
/* To keeping uart idle state */
/* tx pin: idle->high power down->low */
mtk_uart_switch_tx_to_gpio(uart);
spin_unlock_irqrestore(&mtk_uart_bt_lock, flags);
pr_debug("[UART%d] BT Suspend(%d)!\n", uart->nport, ret);
}
return ret;
}
/*---------------------------------------------------------------------------*/
static void mtk_uart_syscore_resume(void)
{
int ret = 0;
unsigned long flags;
if (bt_port) {
struct mtk_uart *uart = bt_port;
spin_lock_irqsave(&mtk_uart_bt_lock, flags);
mtk_uart_switch_to_tx(uart);
ret = uart_resume_port(&mtk_uart_drv, &uart->port);
spin_unlock_irqrestore(&mtk_uart_bt_lock, flags);
disable_irq(uart->port.irq);
pr_debug("[UART%d] BT Resume(%d)!\n", uart->nport, ret);
}
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_suspend(struct platform_device *pdev, pm_message_t state)
{
int ret = 0;
struct mtk_uart *uart = platform_get_drvdata(pdev);
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
if (!uart)
return -1;
/* For console_suspend_enabled=0 */
mtk_uart_save(uart);
if ((uart->nport < UART_NR) && (uart != bt_port)) {
ret = uart_suspend_port(&mtk_uart_drv, &uart->port);
if (__ratelimit(&ratelimit))
pr_debug("[UART%d] Suspend(%d)!\n", uart->nport, ret);
mtk_uart_switch_rx_to_gpio(uart);
}
return ret;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_resume(struct platform_device *pdev)
{
int ret = 0;
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
struct mtk_uart *uart = platform_get_drvdata(pdev);
if (uart && (uart->nport < UART_NR) && (uart != bt_port)) {
mtk_uart_switch_to_rx(uart);
ret = uart_resume_port(&mtk_uart_drv, &uart->port);
if (__ratelimit(&ratelimit))
pr_debug("[UART%d] Resume(%d)!\n", uart->nport, ret);
}
return ret;
}
/*---------------------------------------------------------------------------*/
static int mtk_uart_pm_suspend(struct device *device)
{
struct platform_device *pdev;
/*pr_debug("calling %s()\n", __func__);*/
pdev = to_platform_device(device);
WARN_ON(pdev == NULL);
return mtk_uart_suspend(pdev, PMSG_SUSPEND);
}
static int mtk_uart_pm_resume(struct device *device)
{
struct platform_device *pdev;
/*pr_debug("calling %s()\n", __func__);*/
pdev = to_platform_device(device);
WARN_ON(pdev == NULL);
return mtk_uart_resume(pdev);
}
static int mtk_uart_pm_freeze(struct device *device)
{
struct platform_device *pdev;
struct mtk_uart *uart = NULL;
int ret = 0;
int port_idx = 0;
pdev = to_platform_device(device);
WARN_ON(pdev == NULL);
uart = platform_get_drvdata(pdev);
port_idx = uart->nport;
if (uart_freeze_enable[port_idx]) {
ret = uart_suspend_port(&mtk_uart_drv, &uart->port);
pr_warn("[%s] here.\n", __func__);
/* To keeping uart idle state */
/* tx pin: idle->high power down->low */
mtk_uart_switch_tx_to_gpio(uart);
pr_warn("[%s] done.\n", __func__);
}
return 0; /* mtk_uart_suspend(pdev, PMSG_SUSPEND); */
}
static int mtk_uart_pm_restore(struct device *device)
{
struct platform_device *pdev;
struct mtk_uart *uart;
int ret = 0;
int port_idx = 0;
pdev = to_platform_device(device);
WARN_ON(pdev == NULL);
uart = platform_get_drvdata(pdev);
port_idx = uart->nport;
if (uart_freeze_enable[port_idx]) {
mtk_uart_switch_to_tx(uart);
ret = uart_resume_port(&mtk_uart_drv, &uart->port);
pr_warn("[%s] uart (%p) base: 0x%lx, nport %d, dma mode: %d\n",
__func__,
uart, uart->base, uart->nport, uart->dma_mode);
}
return mtk_uart_resume(pdev);
}
static int mtk_uart_pm_restore_noirq(struct device *device)
{
struct mtk_uart *uart;
uart = dev_get_drvdata(device);
if (!uart || !uart->setting) {
pr_warn("[%s] uart or uart->setting is null!!\n", __func__);
return 0;
}
mtk_uart_fifo_set_trig(uart, uart->tx_trig, uart->rx_trig);
irq_set_irq_type(uart->setting->irq_num, uart->setting->irq_flags);
if (uart->tx_vfifo && uart->tx_mode == UART_TX_VFIFO_DMA)
irq_set_irq_type(uart->tx_vfifo->irq_id,
IRQF_LEVEL_TRIGGER_POLARITY);
if (uart->rx_vfifo && uart->rx_mode == UART_RX_VFIFO_DMA)
irq_set_irq_type(uart->rx_vfifo->irq_id,
IRQF_LEVEL_TRIGGER_POLARITY);
return 0;
}
/*---------------------------------------------------------------------------*/
#else /*CONFIG_PM */
/*---------------------------------------------------------------------------*/
#define mtk_uart_pm_suspend NULL
#define mtk_uart_pm_resume NULL
#define mtk_uart_pm_freeze NULL
#define mtk_uart_pm_restore NULL
#define mtk_uart_pm_restore_noirq NULL
/*---------------------------------------------------------------------------*/
#endif /*CONFIG_PM */
/*---------------------------------------------------------------------------*/
const struct dev_pm_ops mtk_uart_pm_ops = {
.suspend = mtk_uart_pm_suspend,
.resume = mtk_uart_pm_resume,
.freeze = mtk_uart_pm_freeze,
.thaw = mtk_uart_pm_restore,
.poweroff = mtk_uart_pm_suspend,
.restore = mtk_uart_pm_restore,
.restore_noirq = mtk_uart_pm_restore_noirq,
};
/*---------------------------------------------------------------------------*/
static int mtk_uart_init_ports(void)
{
int i;
#if defined(ENABLE_VFIFO)
int idx;
struct mtk_uart_vfifo *vfifo;
#endif
void __iomem *apdma_uart0_base = 0;
struct mtk_uart *uart;
unsigned long base;
spin_lock_init(&mtk_console_lock);
apdma_uart0_base = get_apdma_uart0_base();
for (i = 0; i < UART_NR; i++) {
set_uart_default_settings(i);
uart = &mtk_uarts[i];
uart->setting = get_uart_default_settings(i);
#if defined(ENABLE_VFIFO)
if (uart->setting->vff) {
if (i * 2 < ARRAY_SIZE(mtk_uart_vfifo_port)) {
for (idx = i * 2; idx < i * 2 + 2; idx++) {
vfifo = &mtk_uart_vfifo_port[idx];
vfifo->base = (apdma_uart0_base +
0x0080 * idx);
vfifo->irq_id = get_uart_vfifo_irq_id(
idx);
}
#ifdef CONFIG_MACH_MT8167
if (i == 2) {
for (idx = i * 2; idx < i * 2 + 2;
idx++) {
vfifo =
&mtk_uart_vfifo_port[idx];
vfifo->base =
(apdma_uart0_base +
0x0080 * idx + 0x300);
vfifo->irq_id =
get_uart_vfifo_irq_id(
idx);
}
}
#endif
}
}
#endif
base = uart->setting->uart_base;
uart->port.iotype = UPIO_MEM;
/* for ioremap */
uart->port.mapbase = uart->setting->uart_phys_base;
uart->port.membase = (unsigned char __iomem *)base;
uart->port.irq = uart->setting->irq_num;
uart->port.fifosize = UART_FIFO_SIZE;
uart->port.ops = &mtk_uart_ops;
uart->port.flags = UPF_BOOT_AUTOCONF;
uart->port.line = i;
uart->port.uartclk = UART_SYSCLK;
/* pll_for_uart = mt6575_get_bus_freq(); */
/* uart->port.uartclk = mt6575_get_bus_freq()*1000/4; */
spin_lock_init(&uart->port.lock);
uart->base = base;
uart->auto_baud = CFG_UART_AUTOBAUD;
uart->nport = i;
uart->sysclk = UART_SYSCLK; /* FIXME */
/* uart->sysclk = mt6575_get_bus_freq()*1000/4; */
uart->dma_mode = uart->setting->dma_mode;
uart->tx_mode = uart->setting->tx_mode;
uart->rx_mode = uart->setting->rx_mode;
uart->tx_trig = uart->setting->tx_trig;
uart->rx_trig = uart->setting->rx_trig;
uart->write_allow = mtk_uart_write_allow;
uart->read_allow = mtk_uart_read_allow;
uart->write_byte = mtk_uart_write_byte;
uart->read_byte = mtk_uart_read_byte;
uart->read_status = mtk_uart_read_status;
uart->poweron_count = 0;
uart->timeout_count = 0;
uart->baudrate = 0;
uart->custom_baud = 0;
uart->registers.dll = 1;
uart->registers.dlh = 0;
uart->registers.ier = 0;
uart->registers.lcr = 0;
uart->registers.mcr = 0;
uart->registers.fcr = 0;
uart->registers.lsr = 0x60;
uart->registers.efr = 0;
uart->registers.highspeed = 0;
uart->registers.sample_count = 0;
uart->registers.sample_point = 0xff;
uart->registers.fracdiv_l = 0;
uart->registers.fracdiv_m = 0;
uart->registers.escape_en = 0;
uart->registers.guard = 0;
uart->registers.rx_sel = 0;
uart->evt_mask = (unsigned int)get_uart_evt_mask(i);
#if defined(CONFIG_MTK_SERIAL_MODEM_TEST)
if (get_modem_uart(i)) {
/* u32 dat = UART_READ32(HW_MISC); // mtk does NOT has this register */
mtk_uart_power_up(uart); /* power up */
/* reg_sync_writel(dat | mask[i], HW_MISC); */
continue;
}
#else
/* mtk_uart_power_up(uart); */
mtk_uart_disable_intrs(uart, UART_IER_ALL_INTS);
irq_set_irq_type(uart->setting->irq_num,
uart->setting->irq_flags);
mtk_uart_fifo_init(uart);
mtk_uart_set_mode(uart, uart->dma_mode);
/* mtk_uart_power_down(uart); */
#endif
}
#if defined(CONFIG_MTK_SERIAL_MODEM_TEST)
/*NOTICE: for enabling modem test, UART4 needs to be disabled.
* Howerver, if CONFIG_MTK_SERIAL_CONSOLE
* is defined, resume will fail. Since the root cause is not clear,
* only disable the console-related
* function.
*/
/*printk("HW_MISC: 0x%08X\n", UART_READ32(HW_MISC)); */
/* mtk does NOT has this register */
#endif
return 0;
}
/*---------------------------------------------------------------------------*/
static const struct of_device_id apuart_of_ids[] = {
{.compatible = "mediatek,AP_UART0",},
{.compatible = "mediatek,AP_UART1",},
#ifndef CONFIG_FPGA_EARLY_PORTING
{.compatible = "mediatek,AP_UART2",},
{.compatible = "mediatek,AP_UART3",},
#if (UART_NR > 4)
{.compatible = "mediatek,AP_UART4",},
#endif
#endif
{.compatible = "mediatek,mt6735-uart",},
{.compatible = "mediatek,mt6755-uart",},
{.compatible = "mediatek,mt8173-uart",},
{.compatible = "mediatek,mt6797-uart",},
{.compatible = "mediatek,mt8163-uart",},
{.compatible = "mediatek,mt8167-uart",},
{.compatible = "mediatek,mtk-uart",},
{.compatible = "mediatek,mt6759-uart",},
{.compatible = "mediatek,mt6758-uart",},
{}
};
static struct platform_driver mtk_uart_dev_drv = {
.probe = mtk_uart_probe,
.remove = mtk_uart_remove,
#ifdef CONFIG_PM
.suspend = mtk_uart_suspend,
.resume = mtk_uart_resume,
#endif
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = apuart_of_ids,
#ifdef CONFIG_PM
.pm = &mtk_uart_pm_ops,
#endif
}
};
#ifdef CONFIG_PM
static struct syscore_ops mtk_uart_syscore_ops = {
.suspend = mtk_uart_syscore_suspend,
.resume = mtk_uart_syscore_resume,
};
static int __init mtk_uart_init_ops(void)
{
register_syscore_ops(&mtk_uart_syscore_ops);
return 0;
}
#endif
/*---------------------------------------------------------------------------*/
static int __init mtk_uart_init(void)
{
int ret = 0;
tx_history.buffer = kzalloc(UART_HISTORY_DATA_SIZE, GFP_KERNEL);
rx_history.buffer = kzalloc(UART_HISTORY_DATA_SIZE, GFP_KERNEL);
tx_history.index = -1;
rx_history.index = -1;
if (!tx_history.buffer || !rx_history.buffer)
return -ENOMEM;
#ifndef CONFIG_MTK_SERIAL_CONSOLE
mtk_uart_init_ports();
#endif
#if defined(ENABLE_SYSFS)
mtk_uart_sysfs();
#endif
ret = uart_register_driver(&mtk_uart_drv);
if (ret)
return ret;
ret = platform_driver_register(&mtk_uart_dev_drv);
if (ret) {
uart_unregister_driver(&mtk_uart_drv);
return ret;
}
#ifdef CONFIG_PM
mtk_uart_init_ops();
#endif
#ifdef ENABLE_RAW_DATA_DUMP
mtk_uart_init_debug_spinlock();
#endif
spin_lock_init(&mtk_uart_bt_lock);
return ret;
}
/*---------------------------------------------------------------------------*/
static void __exit mtk_uart_exit(void)
{
platform_driver_unregister(&mtk_uart_dev_drv);
uart_unregister_driver(&mtk_uart_drv);
}
/*---------------------------------------------------------------------------*/
#ifdef ENABLE_UART_SLEEP
int request_uart_to_sleep(void)
{
u32 val1;
int i = 0;
int uart_idx = 0;
struct mtk_uart *uart;
unsigned long base;
for (uart_idx = 0; uart_idx < UART_NR; uart_idx++) {
/*
*#if defined(CONFIG_MTK_HDMI_SUPPORT)
*#ifdef MHL_UART_SHARE_PIN
* {
* for K2 uart2 and mhl share pin,
* if mhl is in low power mode,
* uart rx is not working, so bypass it.
* if ((is_hdmi_active() == 0) && (uart_idx == 1))
* continue;
* }
*#endif
*#endif
*/
uart = &mtk_uarts[uart_idx];
base = uart->base;
if (uart->poweron_count > 0) {
/* request UART to sleep */
val1 = UART_READ32(UART_SLEEP_REQ);
reg_sync_writel(val1 | UART_CLK_OFF_REQ,
UART_SLEEP_REQ);
/* wait for UART to ACK */
while (!(UART_READ32(UART_SLEEP_ACK)
& UART_CLK_OFF_ACK)) {
if (i++ >= WAIT_UART_ACK_TIMES) {
reg_sync_writel(val1, UART_SLEEP_REQ);
pr_err_ratelimited("[UART]CANNOT GET UART[%d] SLEEP ACK\n",
uart_idx);
/* dump_uart_reg(); */
return -EBUSY;
}
udelay(10);
}
} else {
/* printk("[UART%d] clock is off\n", uart->nport); */
}
}
return 0;
}
EXPORT_SYMBOL(request_uart_to_sleep);
int request_uart_to_wakeup(void)
{
/* printk(KERN_ERR "Request UART sleep\n"); */
u32 val1;
int i = 0;
int uart_idx = 0;
struct mtk_uart *uart;
unsigned long base;
for (uart_idx = 0; uart_idx < UART_NR; uart_idx++) {
uart = &mtk_uarts[uart_idx];
base = uart->base;
if (uart->poweron_count > 0) {
/* wakeup uart */
val1 = UART_READ32(UART_SLEEP_REQ);
reg_sync_writel(val1 & (~UART_CLK_OFF_REQ),
UART_SLEEP_REQ);
/* wait for UART to ACK */
while ((UART_READ32(UART_SLEEP_ACK)
& UART_CLK_OFF_ACK)) {
if (i++ >= WAIT_UART_ACK_TIMES) {
reg_sync_writel(val1, UART_SLEEP_REQ);
pr_err("[UART]CANNOT GET UART[%d] WAKE ACK\n",
uart_idx);
/* dump_uart_reg(); */
return -EBUSY;
}
udelay(10);
}
} else {
/* printk("[UART%d] clock is off\n", uart->nport); */
}
}
return 0;
}
EXPORT_SYMBOL(request_uart_to_wakeup);
#endif
#ifdef CONFIG_CONSOLE_LOCK_DURATION_DETECT
char uart_write_statbuf[256];
char *mtk8250_uart_dump(void)
{
u32 high_speed = 0, dll = 0, dlh = 0, line = 0;
u32 lcr = 0, count = 0, point = 0, guide = 0;
struct mtk_uart *uart;
unsigned long base;
for (line = 0; line < UART_NR; line++) {
uart = &mtk_uarts[line];
base = uart->base;
if (uart != console_port)
continue;
lcr = UART_READ32(UART_LCR);
high_speed = UART_READ32(UART_HIGHSPEED);
count = UART_READ32(UART_SAMPLE_COUNT);
point = UART_READ32(UART_SAMPLE_POINT);
reg_sync_writel(0x01, (base+0x9c));//enable new map
dll = UART_READ32(base+0x90);
dlh = UART_READ32(base+0x94);
reg_sync_writel(0x00, (base+0x9c));
guide = UART_READ32(UART_GUARD);
}
snprintf(uart_write_statbuf,
sizeof(uart_write_statbuf) - 1,
"high_speed = 0x%x, dll = 0x%x, dlh = 0x%x, lcr = 0x%x, count = 0x%x, point = 0x%x, guide = 0x%x",
high_speed, dll, dlh, lcr,
count, point, guide);
return uart_write_statbuf;
}
#endif
/*---------------------------------------------------------------------------*/
module_init(mtk_uart_init);
module_exit(mtk_uart_exit);
/*---------------------------------------------------------------------------*/
MODULE_AUTHOR("MTK");
MODULE_DESCRIPTION("MTK Serial Port Driver");
MODULE_LICENSE("GPL");