mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-21 03:33:59 +08:00
x86, mrst: remove Moorestown specific serial drivers
Intel Moorestown platform support was removed few years ago. This is a follow up which removes Moorestown specific code for the serial devices. It includes mrst_max3110 and earlyprintk bits. This was used on SFI (Medfield, Clovertrail) based platforms as well, though new ones use normal serial interface for the console service. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: David Cohen <david.a.cohen@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
934084a9d2
commit
874e52086f
@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
|
|||||||
#define SFI_MTMR_MAX_NUM 8
|
#define SFI_MTMR_MAX_NUM 8
|
||||||
#define SFI_MRTC_MAX 8
|
#define SFI_MRTC_MAX 8
|
||||||
|
|
||||||
extern struct console early_mrst_console;
|
|
||||||
extern void mrst_early_console_init(void);
|
|
||||||
|
|
||||||
extern struct console early_hsu_console;
|
extern struct console early_hsu_console;
|
||||||
extern void hsu_early_console_init(const char *);
|
extern void hsu_early_console_init(const char *);
|
||||||
|
|
||||||
|
@ -226,11 +226,6 @@ static int __init setup_early_printk(char *buf)
|
|||||||
early_console_register(&xenboot_console, keep);
|
early_console_register(&xenboot_console, keep);
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_EARLY_PRINTK_INTEL_MID
|
#ifdef CONFIG_EARLY_PRINTK_INTEL_MID
|
||||||
if (!strncmp(buf, "mrst", 4)) {
|
|
||||||
mrst_early_console_init();
|
|
||||||
early_console_register(&early_mrst_console, keep);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strncmp(buf, "hsu", 3)) {
|
if (!strncmp(buf, "hsu", 3)) {
|
||||||
hsu_early_console_init(buf + 3);
|
hsu_early_console_init(buf + 3);
|
||||||
early_console_register(&early_hsu_console, keep);
|
early_console_register(&early_hsu_console, keep);
|
||||||
|
@ -16,8 +16,6 @@ obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o
|
|||||||
obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o
|
obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o
|
||||||
obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
|
obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
|
||||||
obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o
|
obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o
|
||||||
# SPI Devices
|
|
||||||
obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o
|
|
||||||
# MISC Devices
|
# MISC Devices
|
||||||
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
||||||
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
|
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* platform_max3111.c: max3111 platform data initilization file
|
|
||||||
*
|
|
||||||
* (C) Copyright 2013 Intel Corporation
|
|
||||||
* Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; version 2
|
|
||||||
* of the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/spi/spi.h>
|
|
||||||
#include <asm/intel-mid.h>
|
|
||||||
|
|
||||||
static void __init *max3111_platform_data(void *info)
|
|
||||||
{
|
|
||||||
struct spi_board_info *spi_info = info;
|
|
||||||
int intr = get_gpio_by_name("max3111_int");
|
|
||||||
|
|
||||||
spi_info->mode = SPI_MODE_0;
|
|
||||||
if (intr == -1)
|
|
||||||
return NULL;
|
|
||||||
spi_info->irq = intr + INTEL_MID_IRQ_OFFSET;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct devs_id max3111_dev_id __initconst = {
|
|
||||||
.name = "spi_max3111",
|
|
||||||
.type = SFI_DEV_TYPE_SPI,
|
|
||||||
.get_platform_data = &max3111_platform_data,
|
|
||||||
};
|
|
||||||
|
|
||||||
sfi_device(max3111_dev_id);
|
|
@ -10,15 +10,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file implements two early consoles named mrst and hsu.
|
* This file implements early console named hsu.
|
||||||
* mrst is based on Maxim3110 spi-uart device, it exists in both
|
* hsu is based on a High Speed UART device which only exists in the Medfield
|
||||||
* Moorestown and Medfield platforms, while hsu is based on a High
|
* platform
|
||||||
* Speed UART device which only exists in the Medfield platform
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/serial_reg.h>
|
#include <linux/serial_reg.h>
|
||||||
#include <linux/serial_mfd.h>
|
#include <linux/serial_mfd.h>
|
||||||
#include <linux/kmsg_dump.h>
|
|
||||||
#include <linux/console.h>
|
#include <linux/console.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@ -28,216 +26,6 @@
|
|||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
#include <asm/intel-mid.h>
|
#include <asm/intel-mid.h>
|
||||||
|
|
||||||
#define MRST_SPI_TIMEOUT 0x200000
|
|
||||||
#define MRST_REGBASE_SPI0 0xff128000
|
|
||||||
#define MRST_REGBASE_SPI1 0xff128400
|
|
||||||
#define MRST_CLK_SPI0_REG 0xff11d86c
|
|
||||||
|
|
||||||
/* Bit fields in CTRLR0 */
|
|
||||||
#define SPI_DFS_OFFSET 0
|
|
||||||
|
|
||||||
#define SPI_FRF_OFFSET 4
|
|
||||||
#define SPI_FRF_SPI 0x0
|
|
||||||
#define SPI_FRF_SSP 0x1
|
|
||||||
#define SPI_FRF_MICROWIRE 0x2
|
|
||||||
#define SPI_FRF_RESV 0x3
|
|
||||||
|
|
||||||
#define SPI_MODE_OFFSET 6
|
|
||||||
#define SPI_SCPH_OFFSET 6
|
|
||||||
#define SPI_SCOL_OFFSET 7
|
|
||||||
#define SPI_TMOD_OFFSET 8
|
|
||||||
#define SPI_TMOD_TR 0x0 /* xmit & recv */
|
|
||||||
#define SPI_TMOD_TO 0x1 /* xmit only */
|
|
||||||
#define SPI_TMOD_RO 0x2 /* recv only */
|
|
||||||
#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
|
|
||||||
|
|
||||||
#define SPI_SLVOE_OFFSET 10
|
|
||||||
#define SPI_SRL_OFFSET 11
|
|
||||||
#define SPI_CFS_OFFSET 12
|
|
||||||
|
|
||||||
/* Bit fields in SR, 7 bits */
|
|
||||||
#define SR_MASK 0x7f /* cover 7 bits */
|
|
||||||
#define SR_BUSY (1 << 0)
|
|
||||||
#define SR_TF_NOT_FULL (1 << 1)
|
|
||||||
#define SR_TF_EMPT (1 << 2)
|
|
||||||
#define SR_RF_NOT_EMPT (1 << 3)
|
|
||||||
#define SR_RF_FULL (1 << 4)
|
|
||||||
#define SR_TX_ERR (1 << 5)
|
|
||||||
#define SR_DCOL (1 << 6)
|
|
||||||
|
|
||||||
struct dw_spi_reg {
|
|
||||||
u32 ctrl0;
|
|
||||||
u32 ctrl1;
|
|
||||||
u32 ssienr;
|
|
||||||
u32 mwcr;
|
|
||||||
u32 ser;
|
|
||||||
u32 baudr;
|
|
||||||
u32 txfltr;
|
|
||||||
u32 rxfltr;
|
|
||||||
u32 txflr;
|
|
||||||
u32 rxflr;
|
|
||||||
u32 sr;
|
|
||||||
u32 imr;
|
|
||||||
u32 isr;
|
|
||||||
u32 risr;
|
|
||||||
u32 txoicr;
|
|
||||||
u32 rxoicr;
|
|
||||||
u32 rxuicr;
|
|
||||||
u32 msticr;
|
|
||||||
u32 icr;
|
|
||||||
u32 dmacr;
|
|
||||||
u32 dmatdlr;
|
|
||||||
u32 dmardlr;
|
|
||||||
u32 idr;
|
|
||||||
u32 version;
|
|
||||||
|
|
||||||
/* Currently operates as 32 bits, though only the low 16 bits matter */
|
|
||||||
u32 dr;
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
#define dw_readl(dw, name) __raw_readl(&(dw)->name)
|
|
||||||
#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name)
|
|
||||||
|
|
||||||
/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */
|
|
||||||
static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0;
|
|
||||||
|
|
||||||
static u32 *pclk_spi0;
|
|
||||||
/* Always contains an accessible address, start with 0 */
|
|
||||||
static struct dw_spi_reg *pspi;
|
|
||||||
|
|
||||||
static struct kmsg_dumper dw_dumper;
|
|
||||||
static int dumper_registered;
|
|
||||||
|
|
||||||
static void dw_kmsg_dump(struct kmsg_dumper *dumper,
|
|
||||||
enum kmsg_dump_reason reason)
|
|
||||||
{
|
|
||||||
static char line[1024];
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/* When run to this, we'd better re-init the HW */
|
|
||||||
mrst_early_console_init();
|
|
||||||
|
|
||||||
while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
|
|
||||||
early_mrst_console.write(&early_mrst_console, line, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the ratio rate to 115200, 8n1, IRQ disabled */
|
|
||||||
static void max3110_write_config(void)
|
|
||||||
{
|
|
||||||
u16 config;
|
|
||||||
|
|
||||||
config = 0xc001;
|
|
||||||
dw_writel(pspi, dr, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Translate char to a eligible word and send to max3110 */
|
|
||||||
static void max3110_write_data(char c)
|
|
||||||
{
|
|
||||||
u16 data;
|
|
||||||
|
|
||||||
data = 0x8000 | c;
|
|
||||||
dw_writel(pspi, dr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mrst_early_console_init(void)
|
|
||||||
{
|
|
||||||
u32 ctrlr0 = 0;
|
|
||||||
u32 spi0_cdiv;
|
|
||||||
u32 freq; /* Freqency info only need be searched once */
|
|
||||||
|
|
||||||
/* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */
|
|
||||||
pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
|
|
||||||
MRST_CLK_SPI0_REG);
|
|
||||||
spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9;
|
|
||||||
freq = 100000000 / (spi0_cdiv + 1);
|
|
||||||
|
|
||||||
if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL)
|
|
||||||
mrst_spi_paddr = MRST_REGBASE_SPI1;
|
|
||||||
|
|
||||||
pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
|
|
||||||
mrst_spi_paddr);
|
|
||||||
|
|
||||||
/* Disable SPI controller */
|
|
||||||
dw_writel(pspi, ssienr, 0);
|
|
||||||
|
|
||||||
/* Set control param, 8 bits, transmit only mode */
|
|
||||||
ctrlr0 = dw_readl(pspi, ctrl0);
|
|
||||||
|
|
||||||
ctrlr0 &= 0xfcc0;
|
|
||||||
ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET)
|
|
||||||
| (SPI_TMOD_TO << SPI_TMOD_OFFSET);
|
|
||||||
dw_writel(pspi, ctrl0, ctrlr0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Change the spi0 clk to comply with 115200 bps, use 100000 to
|
|
||||||
* calculate the clk dividor to make the clock a little slower
|
|
||||||
* than real baud rate.
|
|
||||||
*/
|
|
||||||
dw_writel(pspi, baudr, freq/100000);
|
|
||||||
|
|
||||||
/* Disable all INT for early phase */
|
|
||||||
dw_writel(pspi, imr, 0x0);
|
|
||||||
|
|
||||||
/* Set the cs to spi-uart */
|
|
||||||
dw_writel(pspi, ser, 0x2);
|
|
||||||
|
|
||||||
/* Enable the HW, the last step for HW init */
|
|
||||||
dw_writel(pspi, ssienr, 0x1);
|
|
||||||
|
|
||||||
/* Set the default configuration */
|
|
||||||
max3110_write_config();
|
|
||||||
|
|
||||||
/* Register the kmsg dumper */
|
|
||||||
if (!dumper_registered) {
|
|
||||||
dw_dumper.dump = dw_kmsg_dump;
|
|
||||||
kmsg_dump_register(&dw_dumper);
|
|
||||||
dumper_registered = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Slave select should be called in the read/write function */
|
|
||||||
static void early_mrst_spi_putc(char c)
|
|
||||||
{
|
|
||||||
unsigned int timeout;
|
|
||||||
u32 sr;
|
|
||||||
|
|
||||||
timeout = MRST_SPI_TIMEOUT;
|
|
||||||
/* Early putc needs to make sure the TX FIFO is not full */
|
|
||||||
while (--timeout) {
|
|
||||||
sr = dw_readl(pspi, sr);
|
|
||||||
if (!(sr & SR_TF_NOT_FULL))
|
|
||||||
cpu_relax();
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!timeout)
|
|
||||||
pr_warn("MRST earlycon: timed out\n");
|
|
||||||
else
|
|
||||||
max3110_write_data(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Early SPI only uses polling mode */
|
|
||||||
static void early_mrst_spi_write(struct console *con, const char *str,
|
|
||||||
unsigned n)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < n && *str; i++) {
|
|
||||||
if (*str == '\n')
|
|
||||||
early_mrst_spi_putc('\r');
|
|
||||||
early_mrst_spi_putc(*str);
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct console early_mrst_console = {
|
|
||||||
.name = "earlymrst",
|
|
||||||
.write = early_mrst_spi_write,
|
|
||||||
.flags = CON_PRINTBUFFER,
|
|
||||||
.index = -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Following is the early console based on Medfield HSU (High
|
* Following is the early console based on Medfield HSU (High
|
||||||
* Speed UART) device.
|
* Speed UART) device.
|
||||||
@ -259,7 +47,7 @@ void hsu_early_console_init(const char *s)
|
|||||||
port = clamp_val(port, 0, 2);
|
port = clamp_val(port, 0, 2);
|
||||||
|
|
||||||
paddr = HSU_PORT_BASE + port * 0x80;
|
paddr = HSU_PORT_BASE + port * 0x80;
|
||||||
phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
|
phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
|
||||||
|
|
||||||
/* Disable FIFO */
|
/* Disable FIFO */
|
||||||
writeb(0x0, phsu + UART_FCR);
|
writeb(0x0, phsu + UART_FCR);
|
||||||
|
@ -482,16 +482,6 @@ config SERIAL_SA1100_CONSOLE
|
|||||||
your boot loader (lilo or loadlin) about how to pass options to the
|
your boot loader (lilo or loadlin) about how to pass options to the
|
||||||
kernel at boot time.)
|
kernel at boot time.)
|
||||||
|
|
||||||
config SERIAL_MRST_MAX3110
|
|
||||||
tristate "SPI UART driver for Max3110"
|
|
||||||
depends on SPI_DW_PCI
|
|
||||||
select SERIAL_CORE
|
|
||||||
select SERIAL_CORE_CONSOLE
|
|
||||||
help
|
|
||||||
This is the UART protocol driver for the MAX3110 device on
|
|
||||||
the Intel Moorestown platform. On other systems use the max3100
|
|
||||||
driver.
|
|
||||||
|
|
||||||
config SERIAL_MFD_HSU
|
config SERIAL_MFD_HSU
|
||||||
tristate "Medfield High Speed UART support"
|
tristate "Medfield High Speed UART support"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
@ -77,7 +77,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
|
|||||||
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
|
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
|
||||||
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
|
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
|
||||||
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
|
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
|
||||||
obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
|
|
||||||
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
|
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
|
||||||
obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
|
obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
|
||||||
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
|
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
|
||||||
|
@ -1,909 +0,0 @@
|
|||||||
/*
|
|
||||||
* mrst_max3110.c - spi uart protocol driver for Maxim 3110
|
|
||||||
*
|
|
||||||
* Copyright (c) 2008-2010, Intel Corporation.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note:
|
|
||||||
* 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
|
|
||||||
* 1 word. If SPI master controller doesn't support sclk frequency change,
|
|
||||||
* then the char need be sent out one by one with some delay
|
|
||||||
*
|
|
||||||
* 2. Currently only RX available interrupt is used, no need for waiting TXE
|
|
||||||
* interrupt for a low speed UART device
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#ifdef CONFIG_MAGIC_SYSRQ
|
|
||||||
#define SUPPORT_SYSRQ
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/console.h>
|
|
||||||
#include <linux/tty.h>
|
|
||||||
#include <linux/tty_flip.h>
|
|
||||||
#include <linux/serial_core.h>
|
|
||||||
#include <linux/serial_reg.h>
|
|
||||||
|
|
||||||
#include <linux/kthread.h>
|
|
||||||
#include <linux/spi/spi.h>
|
|
||||||
#include <linux/pm.h>
|
|
||||||
|
|
||||||
#include "mrst_max3110.h"
|
|
||||||
|
|
||||||
#define UART_TX_NEEDED 1
|
|
||||||
#define CON_TX_NEEDED 2
|
|
||||||
#define BIT_IRQ_PENDING 3
|
|
||||||
|
|
||||||
struct uart_max3110 {
|
|
||||||
struct uart_port port;
|
|
||||||
struct spi_device *spi;
|
|
||||||
char name[SPI_NAME_SIZE];
|
|
||||||
|
|
||||||
wait_queue_head_t wq;
|
|
||||||
struct task_struct *main_thread;
|
|
||||||
struct task_struct *read_thread;
|
|
||||||
struct mutex thread_mutex;
|
|
||||||
struct mutex io_mutex;
|
|
||||||
|
|
||||||
u32 baud;
|
|
||||||
u16 cur_conf;
|
|
||||||
u8 clock;
|
|
||||||
u8 parity, word_7bits;
|
|
||||||
u16 irq;
|
|
||||||
|
|
||||||
unsigned long uart_flags;
|
|
||||||
|
|
||||||
/* console related */
|
|
||||||
struct circ_buf con_xmit;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* global data structure, may need be removed */
|
|
||||||
static struct uart_max3110 *pmax;
|
|
||||||
|
|
||||||
static int receive_chars(struct uart_max3110 *max,
|
|
||||||
unsigned short *str, int len);
|
|
||||||
static int max3110_read_multi(struct uart_max3110 *max);
|
|
||||||
static void max3110_con_receive(struct uart_max3110 *max);
|
|
||||||
|
|
||||||
static int max3110_write_then_read(struct uart_max3110 *max,
|
|
||||||
const void *txbuf, void *rxbuf, unsigned len, int always_fast)
|
|
||||||
{
|
|
||||||
struct spi_device *spi = max->spi;
|
|
||||||
struct spi_message message;
|
|
||||||
struct spi_transfer x;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mutex_lock(&max->io_mutex);
|
|
||||||
spi_message_init(&message);
|
|
||||||
memset(&x, 0, sizeof x);
|
|
||||||
x.len = len;
|
|
||||||
x.tx_buf = txbuf;
|
|
||||||
x.rx_buf = rxbuf;
|
|
||||||
spi_message_add_tail(&x, &message);
|
|
||||||
|
|
||||||
if (always_fast)
|
|
||||||
x.speed_hz = spi->max_speed_hz;
|
|
||||||
else if (max->baud)
|
|
||||||
x.speed_hz = max->baud;
|
|
||||||
|
|
||||||
/* Do the i/o */
|
|
||||||
ret = spi_sync(spi, &message);
|
|
||||||
mutex_unlock(&max->io_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write a 16b word to the device */
|
|
||||||
static int max3110_out(struct uart_max3110 *max, const u16 out)
|
|
||||||
{
|
|
||||||
void *buf;
|
|
||||||
u16 *obuf, *ibuf;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
buf = kzalloc(8, GFP_KERNEL | GFP_DMA);
|
|
||||||
if (!buf)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
obuf = buf;
|
|
||||||
ibuf = buf + 4;
|
|
||||||
*obuf = out;
|
|
||||||
ret = max3110_write_then_read(max, obuf, ibuf, 2, 1);
|
|
||||||
if (ret) {
|
|
||||||
pr_warn("%s: get err msg %d when sending 0x%x\n",
|
|
||||||
__func__, ret, out);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
receive_chars(max, ibuf, 1);
|
|
||||||
|
|
||||||
exit:
|
|
||||||
kfree(buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is usually used to read data from SPIC RX FIFO, which doesn't
|
|
||||||
* need any delay like flushing character out.
|
|
||||||
*
|
|
||||||
* Return how many valide bytes are read back
|
|
||||||
*/
|
|
||||||
static int max3110_read_multi(struct uart_max3110 *max)
|
|
||||||
{
|
|
||||||
void *buf;
|
|
||||||
u16 *obuf, *ibuf;
|
|
||||||
int ret, blen;
|
|
||||||
|
|
||||||
blen = M3110_RX_FIFO_DEPTH * sizeof(u16);
|
|
||||||
buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA);
|
|
||||||
if (!buf)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* tx/rx always have the same length */
|
|
||||||
obuf = buf;
|
|
||||||
ibuf = buf + blen;
|
|
||||||
|
|
||||||
if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) {
|
|
||||||
kfree(buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = receive_chars(max, ibuf, M3110_RX_FIFO_DEPTH);
|
|
||||||
|
|
||||||
kfree(buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_con_putchar(struct uart_port *port, int ch)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max =
|
|
||||||
container_of(port, struct uart_max3110, port);
|
|
||||||
struct circ_buf *xmit = &max->con_xmit;
|
|
||||||
|
|
||||||
if (uart_circ_chars_free(xmit)) {
|
|
||||||
xmit->buf[xmit->head] = (char)ch;
|
|
||||||
xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Print a string to the serial port trying not to disturb
|
|
||||||
* any possible real use of the port...
|
|
||||||
*
|
|
||||||
* The console_lock must be held when we get here.
|
|
||||||
*/
|
|
||||||
static void serial_m3110_con_write(struct console *co,
|
|
||||||
const char *s, unsigned int count)
|
|
||||||
{
|
|
||||||
if (!pmax)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
|
|
||||||
|
|
||||||
if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags))
|
|
||||||
wake_up(&pmax->wq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init
|
|
||||||
serial_m3110_con_setup(struct console *co, char *options)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max = pmax;
|
|
||||||
int baud = 115200;
|
|
||||||
int bits = 8;
|
|
||||||
int parity = 'n';
|
|
||||||
int flow = 'n';
|
|
||||||
|
|
||||||
pr_info("setting up console\n");
|
|
||||||
|
|
||||||
if (co->index == -1)
|
|
||||||
co->index = 0;
|
|
||||||
|
|
||||||
if (!max) {
|
|
||||||
pr_err("pmax is NULL, return\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options)
|
|
||||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
|
||||||
|
|
||||||
return uart_set_options(&max->port, co, baud, parity, bits, flow);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct tty_driver *serial_m3110_con_device(struct console *co,
|
|
||||||
int *index)
|
|
||||||
{
|
|
||||||
struct uart_driver *p = co->data;
|
|
||||||
*index = co->index;
|
|
||||||
return p->tty_driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct uart_driver serial_m3110_reg;
|
|
||||||
static struct console serial_m3110_console = {
|
|
||||||
.name = "ttyS",
|
|
||||||
.write = serial_m3110_con_write,
|
|
||||||
.device = serial_m3110_con_device,
|
|
||||||
.setup = serial_m3110_con_setup,
|
|
||||||
.flags = CON_PRINTBUFFER,
|
|
||||||
.index = -1,
|
|
||||||
.data = &serial_m3110_reg,
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned int serial_m3110_tx_empty(struct uart_port *port)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_stop_tx(struct uart_port *port)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* stop_rx will be called in spin_lock env */
|
|
||||||
static void serial_m3110_stop_rx(struct uart_port *port)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define WORDS_PER_XFER 128
|
|
||||||
static void send_circ_buf(struct uart_max3110 *max,
|
|
||||||
struct circ_buf *xmit)
|
|
||||||
{
|
|
||||||
void *buf;
|
|
||||||
u16 *obuf, *ibuf;
|
|
||||||
int i, len, blen, dma_size, left, ret = 0;
|
|
||||||
|
|
||||||
|
|
||||||
dma_size = WORDS_PER_XFER * sizeof(u16) * 2;
|
|
||||||
buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA);
|
|
||||||
if (!buf)
|
|
||||||
return;
|
|
||||||
obuf = buf;
|
|
||||||
ibuf = buf + dma_size/2;
|
|
||||||
|
|
||||||
while (!uart_circ_empty(xmit)) {
|
|
||||||
left = uart_circ_chars_pending(xmit);
|
|
||||||
while (left) {
|
|
||||||
len = min(left, WORDS_PER_XFER);
|
|
||||||
blen = len * sizeof(u16);
|
|
||||||
memset(ibuf, 0, blen);
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
|
|
||||||
xmit->tail = (xmit->tail + 1) &
|
|
||||||
(UART_XMIT_SIZE - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fail to send msg to console is not very critical */
|
|
||||||
|
|
||||||
ret = max3110_write_then_read(max, obuf, ibuf, blen, 0);
|
|
||||||
if (ret)
|
|
||||||
pr_warn("%s: get err msg %d\n", __func__, ret);
|
|
||||||
|
|
||||||
receive_chars(max, ibuf, len);
|
|
||||||
|
|
||||||
max->port.icount.tx += len;
|
|
||||||
left -= len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void transmit_char(struct uart_max3110 *max)
|
|
||||||
{
|
|
||||||
struct uart_port *port = &max->port;
|
|
||||||
struct circ_buf *xmit = &port->state->xmit;
|
|
||||||
|
|
||||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
|
|
||||||
return;
|
|
||||||
|
|
||||||
send_circ_buf(max, xmit);
|
|
||||||
|
|
||||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
|
||||||
uart_write_wakeup(port);
|
|
||||||
|
|
||||||
if (uart_circ_empty(xmit))
|
|
||||||
serial_m3110_stop_tx(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This will be called by uart_write() and tty_write, can't
|
|
||||||
* go to sleep
|
|
||||||
*/
|
|
||||||
static void serial_m3110_start_tx(struct uart_port *port)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max =
|
|
||||||
container_of(port, struct uart_max3110, port);
|
|
||||||
|
|
||||||
if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
|
|
||||||
wake_up(&max->wq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
|
|
||||||
{
|
|
||||||
struct uart_port *port = &max->port;
|
|
||||||
struct tty_port *tport;
|
|
||||||
char buf[M3110_RX_FIFO_DEPTH];
|
|
||||||
int r, w, usable;
|
|
||||||
|
|
||||||
/* If uart is not opened, just return */
|
|
||||||
if (!port->state)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tport = &port->state->port;
|
|
||||||
|
|
||||||
for (r = 0, w = 0; r < len; r++) {
|
|
||||||
if (str[r] & MAX3110_BREAK &&
|
|
||||||
uart_handle_break(port))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (str[r] & MAX3110_READ_DATA_AVAILABLE) {
|
|
||||||
if (uart_handle_sysrq_char(port, str[r] & 0xff))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
buf[w++] = str[r] & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!w)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (r = 0; w; r += usable, w -= usable) {
|
|
||||||
usable = tty_buffer_request_room(tport, w);
|
|
||||||
if (usable) {
|
|
||||||
tty_insert_flip_string(tport, buf + r, usable);
|
|
||||||
port->icount.rx += usable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tty_flip_buffer_push(tport);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine will be used in read_thread or RX IRQ handling,
|
|
||||||
* it will first do one round buffer read(8 words), if there is some
|
|
||||||
* valid RX data, will try to read 5 more rounds till all data
|
|
||||||
* is read out.
|
|
||||||
*
|
|
||||||
* Use stack space as data buffer to save some system load, and chose
|
|
||||||
* 504 Btyes as a threadhold to do a bulk push to upper tty layer when
|
|
||||||
* receiving bulk data, a much bigger buffer may cause stack overflow
|
|
||||||
*/
|
|
||||||
static void max3110_con_receive(struct uart_max3110 *max)
|
|
||||||
{
|
|
||||||
int loop = 1, num;
|
|
||||||
|
|
||||||
do {
|
|
||||||
num = max3110_read_multi(max);
|
|
||||||
|
|
||||||
if (num) {
|
|
||||||
loop = 5;
|
|
||||||
}
|
|
||||||
} while (--loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int max3110_main_thread(void *_max)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max = _max;
|
|
||||||
wait_queue_head_t *wq = &max->wq;
|
|
||||||
int ret = 0;
|
|
||||||
struct circ_buf *xmit = &max->con_xmit;
|
|
||||||
|
|
||||||
pr_info("start main thread\n");
|
|
||||||
|
|
||||||
do {
|
|
||||||
wait_event_interruptible(*wq,
|
|
||||||
max->uart_flags || kthread_should_stop());
|
|
||||||
|
|
||||||
mutex_lock(&max->thread_mutex);
|
|
||||||
|
|
||||||
if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
|
|
||||||
max3110_con_receive(max);
|
|
||||||
|
|
||||||
/* first handle console output */
|
|
||||||
if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
|
|
||||||
send_circ_buf(max, xmit);
|
|
||||||
|
|
||||||
/* handle uart output */
|
|
||||||
if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
|
|
||||||
transmit_char(max);
|
|
||||||
|
|
||||||
mutex_unlock(&max->thread_mutex);
|
|
||||||
|
|
||||||
} while (!kthread_should_stop());
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max = dev_id;
|
|
||||||
|
|
||||||
/* max3110's irq is a falling edge, not level triggered,
|
|
||||||
* so no need to disable the irq */
|
|
||||||
|
|
||||||
if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
|
|
||||||
wake_up(&max->wq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if don't use RX IRQ, then need a thread to polling read */
|
|
||||||
static int max3110_read_thread(void *_max)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max = _max;
|
|
||||||
|
|
||||||
pr_info("start read thread\n");
|
|
||||||
do {
|
|
||||||
/*
|
|
||||||
* If can't acquire the mutex, it means the main thread
|
|
||||||
* is running which will also perform the rx job
|
|
||||||
*/
|
|
||||||
if (mutex_trylock(&max->thread_mutex)) {
|
|
||||||
max3110_con_receive(max);
|
|
||||||
mutex_unlock(&max->thread_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
schedule_timeout(HZ / 20);
|
|
||||||
} while (!kthread_should_stop());
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int serial_m3110_startup(struct uart_port *port)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max =
|
|
||||||
container_of(port, struct uart_max3110, port);
|
|
||||||
u16 config = 0;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (port->line != 0) {
|
|
||||||
pr_err("uart port startup failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable all IRQ and config it to 115200, 8n1 */
|
|
||||||
config = WC_TAG | WC_FIFO_ENABLE
|
|
||||||
| WC_1_STOPBITS
|
|
||||||
| WC_8BIT_WORD
|
|
||||||
| WC_BAUD_DR2;
|
|
||||||
|
|
||||||
/* as we use thread to handle tx/rx, need set low latency */
|
|
||||||
port->state->port.low_latency = 1;
|
|
||||||
|
|
||||||
if (max->irq) {
|
|
||||||
/* Enable RX IRQ only */
|
|
||||||
config |= WC_RXA_IRQ_ENABLE;
|
|
||||||
} else {
|
|
||||||
/* If IRQ is disabled, start a read thread for input data */
|
|
||||||
max->read_thread =
|
|
||||||
kthread_run(max3110_read_thread, max, "max3110_read");
|
|
||||||
if (IS_ERR(max->read_thread)) {
|
|
||||||
ret = PTR_ERR(max->read_thread);
|
|
||||||
max->read_thread = NULL;
|
|
||||||
pr_err("Can't create read thread!\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = max3110_out(max, config);
|
|
||||||
if (ret) {
|
|
||||||
if (max->read_thread)
|
|
||||||
kthread_stop(max->read_thread);
|
|
||||||
max->read_thread = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
max->cur_conf = config;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_shutdown(struct uart_port *port)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max =
|
|
||||||
container_of(port, struct uart_max3110, port);
|
|
||||||
u16 config;
|
|
||||||
|
|
||||||
if (max->read_thread) {
|
|
||||||
kthread_stop(max->read_thread);
|
|
||||||
max->read_thread = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable interrupts from this port */
|
|
||||||
config = WC_TAG | WC_SW_SHDI;
|
|
||||||
max3110_out(max, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_release_port(struct uart_port *port)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int serial_m3110_request_port(struct uart_port *port)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_config_port(struct uart_port *port, int flags)
|
|
||||||
{
|
|
||||||
port->type = PORT_MAX3100;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
|
|
||||||
{
|
|
||||||
/* we don't want the core code to modify any port params */
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char *serial_m3110_type(struct uart_port *port)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max =
|
|
||||||
container_of(port, struct uart_max3110, port);
|
|
||||||
return max->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
|
|
||||||
struct ktermios *old)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max =
|
|
||||||
container_of(port, struct uart_max3110, port);
|
|
||||||
unsigned char cval;
|
|
||||||
unsigned int baud, parity = 0;
|
|
||||||
int clk_div = -1;
|
|
||||||
u16 new_conf = max->cur_conf;
|
|
||||||
|
|
||||||
switch (termios->c_cflag & CSIZE) {
|
|
||||||
case CS7:
|
|
||||||
cval = UART_LCR_WLEN7;
|
|
||||||
new_conf |= WC_7BIT_WORD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* We only support CS7 & CS8 */
|
|
||||||
termios->c_cflag &= ~CSIZE;
|
|
||||||
termios->c_cflag |= CS8;
|
|
||||||
case CS8:
|
|
||||||
cval = UART_LCR_WLEN8;
|
|
||||||
new_conf |= WC_8BIT_WORD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
baud = uart_get_baud_rate(port, termios, old, 0, 230400);
|
|
||||||
|
|
||||||
/* First calc the div for 1.8MHZ clock case */
|
|
||||||
switch (baud) {
|
|
||||||
case 300:
|
|
||||||
clk_div = WC_BAUD_DR384;
|
|
||||||
break;
|
|
||||||
case 600:
|
|
||||||
clk_div = WC_BAUD_DR192;
|
|
||||||
break;
|
|
||||||
case 1200:
|
|
||||||
clk_div = WC_BAUD_DR96;
|
|
||||||
break;
|
|
||||||
case 2400:
|
|
||||||
clk_div = WC_BAUD_DR48;
|
|
||||||
break;
|
|
||||||
case 4800:
|
|
||||||
clk_div = WC_BAUD_DR24;
|
|
||||||
break;
|
|
||||||
case 9600:
|
|
||||||
clk_div = WC_BAUD_DR12;
|
|
||||||
break;
|
|
||||||
case 19200:
|
|
||||||
clk_div = WC_BAUD_DR6;
|
|
||||||
break;
|
|
||||||
case 38400:
|
|
||||||
clk_div = WC_BAUD_DR3;
|
|
||||||
break;
|
|
||||||
case 57600:
|
|
||||||
clk_div = WC_BAUD_DR2;
|
|
||||||
break;
|
|
||||||
case 115200:
|
|
||||||
clk_div = WC_BAUD_DR1;
|
|
||||||
break;
|
|
||||||
case 230400:
|
|
||||||
if (max->clock & MAX3110_HIGH_CLK)
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Pick the previous baud rate */
|
|
||||||
baud = max->baud;
|
|
||||||
clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
|
|
||||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max->clock & MAX3110_HIGH_CLK) {
|
|
||||||
clk_div += 1;
|
|
||||||
/* High clk version max3110 doesn't support B300 */
|
|
||||||
if (baud == 300) {
|
|
||||||
baud = 600;
|
|
||||||
clk_div = WC_BAUD_DR384;
|
|
||||||
}
|
|
||||||
if (baud == 230400)
|
|
||||||
clk_div = WC_BAUD_DR1;
|
|
||||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
|
|
||||||
|
|
||||||
if (unlikely(termios->c_cflag & CMSPAR))
|
|
||||||
termios->c_cflag &= ~CMSPAR;
|
|
||||||
|
|
||||||
if (termios->c_cflag & CSTOPB)
|
|
||||||
new_conf |= WC_2_STOPBITS;
|
|
||||||
else
|
|
||||||
new_conf &= ~WC_2_STOPBITS;
|
|
||||||
|
|
||||||
if (termios->c_cflag & PARENB) {
|
|
||||||
new_conf |= WC_PARITY_ENABLE;
|
|
||||||
parity |= UART_LCR_PARITY;
|
|
||||||
} else
|
|
||||||
new_conf &= ~WC_PARITY_ENABLE;
|
|
||||||
|
|
||||||
if (!(termios->c_cflag & PARODD))
|
|
||||||
parity |= UART_LCR_EPAR;
|
|
||||||
max->parity = parity;
|
|
||||||
|
|
||||||
uart_update_timeout(port, termios->c_cflag, baud);
|
|
||||||
|
|
||||||
new_conf |= WC_TAG;
|
|
||||||
if (new_conf != max->cur_conf) {
|
|
||||||
if (!max3110_out(max, new_conf)) {
|
|
||||||
max->cur_conf = new_conf;
|
|
||||||
max->baud = baud;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't handle hw handshaking */
|
|
||||||
static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
|
|
||||||
{
|
|
||||||
return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serial_m3110_pm(struct uart_port *port, unsigned int state,
|
|
||||||
unsigned int oldstate)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct uart_ops serial_m3110_ops = {
|
|
||||||
.tx_empty = serial_m3110_tx_empty,
|
|
||||||
.set_mctrl = serial_m3110_set_mctrl,
|
|
||||||
.get_mctrl = serial_m3110_get_mctrl,
|
|
||||||
.stop_tx = serial_m3110_stop_tx,
|
|
||||||
.start_tx = serial_m3110_start_tx,
|
|
||||||
.stop_rx = serial_m3110_stop_rx,
|
|
||||||
.break_ctl = serial_m3110_break_ctl,
|
|
||||||
.startup = serial_m3110_startup,
|
|
||||||
.shutdown = serial_m3110_shutdown,
|
|
||||||
.set_termios = serial_m3110_set_termios,
|
|
||||||
.pm = serial_m3110_pm,
|
|
||||||
.type = serial_m3110_type,
|
|
||||||
.release_port = serial_m3110_release_port,
|
|
||||||
.request_port = serial_m3110_request_port,
|
|
||||||
.config_port = serial_m3110_config_port,
|
|
||||||
.verify_port = serial_m3110_verify_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct uart_driver serial_m3110_reg = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.driver_name = "MRST serial",
|
|
||||||
.dev_name = "ttyS",
|
|
||||||
.major = TTY_MAJOR,
|
|
||||||
.minor = 64,
|
|
||||||
.nr = 1,
|
|
||||||
.cons = &serial_m3110_console,
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int serial_m3110_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
struct spi_device *spi = to_spi_device(dev);
|
|
||||||
struct uart_max3110 *max = spi_get_drvdata(spi);
|
|
||||||
|
|
||||||
if (max->irq > 0)
|
|
||||||
disable_irq(max->irq);
|
|
||||||
uart_suspend_port(&serial_m3110_reg, &max->port);
|
|
||||||
max3110_out(max, max->cur_conf | WC_SW_SHDI);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int serial_m3110_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct spi_device *spi = to_spi_device(dev);
|
|
||||||
struct uart_max3110 *max = spi_get_drvdata(spi);
|
|
||||||
|
|
||||||
max3110_out(max, max->cur_conf);
|
|
||||||
uart_resume_port(&serial_m3110_reg, &max->port);
|
|
||||||
if (max->irq > 0)
|
|
||||||
enable_irq(max->irq);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend,
|
|
||||||
serial_m3110_resume);
|
|
||||||
#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops)
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define SERIAL_M3110_PM_OPS NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int serial_m3110_probe(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max;
|
|
||||||
void *buffer;
|
|
||||||
u16 res;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
max = kzalloc(sizeof(*max), GFP_KERNEL);
|
|
||||||
if (!max)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* Set spi info */
|
|
||||||
spi->bits_per_word = 16;
|
|
||||||
max->clock = MAX3110_HIGH_CLK;
|
|
||||||
|
|
||||||
spi_setup(spi);
|
|
||||||
|
|
||||||
max->port.type = PORT_MAX3100;
|
|
||||||
max->port.fifosize = 2; /* Only have 16b buffer */
|
|
||||||
max->port.ops = &serial_m3110_ops;
|
|
||||||
max->port.line = 0;
|
|
||||||
max->port.dev = &spi->dev;
|
|
||||||
max->port.uartclk = 115200;
|
|
||||||
|
|
||||||
max->spi = spi;
|
|
||||||
strcpy(max->name, spi->modalias);
|
|
||||||
max->irq = (u16)spi->irq;
|
|
||||||
|
|
||||||
mutex_init(&max->thread_mutex);
|
|
||||||
mutex_init(&max->io_mutex);
|
|
||||||
|
|
||||||
max->word_7bits = 0;
|
|
||||||
max->parity = 0;
|
|
||||||
max->baud = 0;
|
|
||||||
|
|
||||||
max->cur_conf = 0;
|
|
||||||
max->uart_flags = 0;
|
|
||||||
|
|
||||||
/* Check if reading configuration register returns something sane */
|
|
||||||
|
|
||||||
res = RC_TAG;
|
|
||||||
ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
|
|
||||||
if (ret < 0 || res == 0 || res == 0xffff) {
|
|
||||||
dev_dbg(&spi->dev, "MAX3111 deemed not present (conf reg %04x)",
|
|
||||||
res);
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto err_get_page;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = (void *)__get_free_page(GFP_KERNEL);
|
|
||||||
if (!buffer) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_get_page;
|
|
||||||
}
|
|
||||||
max->con_xmit.buf = buffer;
|
|
||||||
max->con_xmit.head = 0;
|
|
||||||
max->con_xmit.tail = 0;
|
|
||||||
|
|
||||||
init_waitqueue_head(&max->wq);
|
|
||||||
|
|
||||||
max->main_thread = kthread_run(max3110_main_thread,
|
|
||||||
max, "max3110_main");
|
|
||||||
if (IS_ERR(max->main_thread)) {
|
|
||||||
ret = PTR_ERR(max->main_thread);
|
|
||||||
goto err_kthread;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max->irq) {
|
|
||||||
ret = request_irq(max->irq, serial_m3110_irq,
|
|
||||||
IRQ_TYPE_EDGE_FALLING, "max3110", max);
|
|
||||||
if (ret) {
|
|
||||||
max->irq = 0;
|
|
||||||
dev_warn(&spi->dev,
|
|
||||||
"unable to allocate IRQ, will use polling method\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_set_drvdata(spi, max);
|
|
||||||
pmax = max;
|
|
||||||
|
|
||||||
/* Give membase a psudo value to pass serial_core's check */
|
|
||||||
max->port.membase = (unsigned char __iomem *)0xff110000;
|
|
||||||
uart_add_one_port(&serial_m3110_reg, &max->port);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_kthread:
|
|
||||||
free_page((unsigned long)buffer);
|
|
||||||
err_get_page:
|
|
||||||
kfree(max);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int serial_m3110_remove(struct spi_device *dev)
|
|
||||||
{
|
|
||||||
struct uart_max3110 *max = spi_get_drvdata(dev);
|
|
||||||
|
|
||||||
if (!max)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
uart_remove_one_port(&serial_m3110_reg, &max->port);
|
|
||||||
|
|
||||||
free_page((unsigned long)max->con_xmit.buf);
|
|
||||||
|
|
||||||
if (max->irq)
|
|
||||||
free_irq(max->irq, max);
|
|
||||||
|
|
||||||
if (max->main_thread)
|
|
||||||
kthread_stop(max->main_thread);
|
|
||||||
|
|
||||||
kfree(max);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct spi_driver uart_max3110_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "spi_max3111",
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = SERIAL_M3110_PM_OPS,
|
|
||||||
},
|
|
||||||
.probe = serial_m3110_probe,
|
|
||||||
.remove = serial_m3110_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init serial_m3110_init(void)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ret = uart_register_driver(&serial_m3110_reg);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = spi_register_driver(&uart_max3110_driver);
|
|
||||||
if (ret)
|
|
||||||
uart_unregister_driver(&serial_m3110_reg);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit serial_m3110_exit(void)
|
|
||||||
{
|
|
||||||
spi_unregister_driver(&uart_max3110_driver);
|
|
||||||
uart_unregister_driver(&serial_m3110_reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(serial_m3110_init);
|
|
||||||
module_exit(serial_m3110_exit);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
MODULE_ALIAS("spi:max3110-uart");
|
|
@ -1,61 +0,0 @@
|
|||||||
#ifndef _MRST_MAX3110_H
|
|
||||||
#define _MRST_MAX3110_H
|
|
||||||
|
|
||||||
#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */
|
|
||||||
#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */
|
|
||||||
|
|
||||||
/* status bits for all 4 MAX3110 operate modes */
|
|
||||||
#define MAX3110_READ_DATA_AVAILABLE (1 << 15)
|
|
||||||
#define MAX3110_WRITE_BUF_EMPTY (1 << 14)
|
|
||||||
#define MAX3110_BREAK (1 << 10)
|
|
||||||
|
|
||||||
#define WC_TAG (3 << 14)
|
|
||||||
#define RC_TAG (1 << 14)
|
|
||||||
#define WD_TAG (2 << 14)
|
|
||||||
#define RD_TAG (0 << 14)
|
|
||||||
|
|
||||||
/* bits def for write configuration */
|
|
||||||
#define WC_FIFO_ENABLE_MASK (1 << 13)
|
|
||||||
#define WC_FIFO_ENABLE (0 << 13)
|
|
||||||
|
|
||||||
#define WC_SW_SHDI (1 << 12)
|
|
||||||
|
|
||||||
#define WC_IRQ_MASK (0xF << 8)
|
|
||||||
#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */
|
|
||||||
#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX available irq */
|
|
||||||
#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
|
|
||||||
#define WC_REC_ACT_IRQ_ENABLE (1 << 8)
|
|
||||||
|
|
||||||
#define WC_IRDA_ENABLE (1 << 7)
|
|
||||||
|
|
||||||
#define WC_STOPBITS_MASK (1 << 6)
|
|
||||||
#define WC_2_STOPBITS (1 << 6)
|
|
||||||
#define WC_1_STOPBITS (0 << 6)
|
|
||||||
|
|
||||||
#define WC_PARITY_ENABLE_MASK (1 << 5)
|
|
||||||
#define WC_PARITY_ENABLE (1 << 5)
|
|
||||||
|
|
||||||
#define WC_WORDLEN_MASK (1 << 4)
|
|
||||||
#define WC_7BIT_WORD (1 << 4)
|
|
||||||
#define WC_8BIT_WORD (0 << 4)
|
|
||||||
|
|
||||||
#define WC_BAUD_DIV_MASK (0xF)
|
|
||||||
#define WC_BAUD_DR1 (0x0)
|
|
||||||
#define WC_BAUD_DR2 (0x1)
|
|
||||||
#define WC_BAUD_DR4 (0x2)
|
|
||||||
#define WC_BAUD_DR8 (0x3)
|
|
||||||
#define WC_BAUD_DR16 (0x4)
|
|
||||||
#define WC_BAUD_DR32 (0x5)
|
|
||||||
#define WC_BAUD_DR64 (0x6)
|
|
||||||
#define WC_BAUD_DR128 (0x7)
|
|
||||||
#define WC_BAUD_DR3 (0x8)
|
|
||||||
#define WC_BAUD_DR6 (0x9)
|
|
||||||
#define WC_BAUD_DR12 (0xA)
|
|
||||||
#define WC_BAUD_DR24 (0xB)
|
|
||||||
#define WC_BAUD_DR48 (0xC)
|
|
||||||
#define WC_BAUD_DR96 (0xD)
|
|
||||||
#define WC_BAUD_DR192 (0xE)
|
|
||||||
#define WC_BAUD_DR384 (0xF)
|
|
||||||
|
|
||||||
#define M3110_RX_FIFO_DEPTH 8
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user