【最后更新20200424】
【因有朋友咨询相关问题,在这里简要说明下】
【正确的RS485驱动操作说明:】
【1. 原厂的驱动没问题,不要去修改驱动文件,只需要做两件事情】
【1.1 修改设备树文件,下文有说明 】【注意,修改设备树仅说明使能了RS485的功能,但未激活或启用】
【1.2 必须激活RS485模式,然后控制引脚才能起动作,激活如下:】
【以上,更新说明完毕】
前言:RS485驱动有两种模式,一种是硬件的(也就是通过TXD信号控制485芯片的使能),这种模式简单实用,但是也有弊端,例如驱动能力不够强。另外一种是软件的方式,发送数据前,通过软件控制GPIO使能外部485芯片的使能引脚,发送完毕后,再去禁用外部RS485芯片的发送使能引脚,进入接收状态。
理论上,RS485发送数据的时候,是收不到数据的,问题是我的电路为什么发送数据的时候也能收到自己发送的数据?这才是问题的根源,解决了再汇总一下。
那么在目前的情况下,如果RS485收到自己发送出去的字符,怎么解决呢?
1. 首先,驱动改成RS485模式。IMXUL的驱动中是支持RS485模式的,通过修改设备树文件即可实现。
附文件树源码
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/dts-v1/;
#include
#include "imx6ull.dtsi"
/ {
model = "Freescale i.MX6 ULL 14x14 EVK Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
chosen {
stdout-path = &uart1;
};
memory {
reg = <0x80000000 0x20000000>;
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x14000000>;
linux,cma-default;
};
};
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
status = "okay";
};
pxp_v4l2 {
compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
status = "okay";
};
regulators {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
reg_can_3v3: regulator@0 {
compatible = "regulator-fixed";
reg = <0>;
regulator-name = "can-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
reg_usb_ltemodule: regulator@1 {
compatible = "regulator-fixed";
regulator-name = "ltemodule-pwr";
regulator-min-microvolt = <3800000>;
regulator-max-microvolt = <3800000>;
gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-boot-on;
};
reg_gpio_wifi: regulator@2 {
compatible = "regulator-fixed";
regulator-name = "wifi-pwr";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
regulator-boot-on;
};
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_leds>;
led0: cpu {
label = "cpu";
gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
default-state = "on";
linux,default-trigger = "heartbeat";
};
};
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_key>;
user {
label = "User Button";
gpios = <&gpio5 0 GPIO_ACTIVE_HIGH>;
gpio-key,wakeup;
linux,code = ;
};
};
};
&gpmi{
status = "disabled";
};
&cpu0 {
arm-supply = <®_arm>;
soc-supply = <®_soc>;
};
&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <786432000>;
};
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
phy-reset-duration = <26>;
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
//phy-reset-gpios = <&gpio5 6 GPIO_ACTIVE_LOW>;
//phy-reset-duration = <26>;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
smsc,disable-energy-detect;
reg = <0>;
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
smsc,disable-energy-detect;
reg = <1>;
};
};
};
&flexcan1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan1>;
xceiver-supply = <®_can_3v3>;
status = "disabled";
};
&flexcan2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan2>;
xceiver-supply = <®_can_3v3>;
status = "disabled";
};
&gpc {
fsl,cpu_pupscr_sw2iso = <0x1>;
fsl,cpu_pupscr_sw = <0x0>;
fsl,cpu_pdnscr_iso2sw = <0x1>;
fsl,cpu_pdnscr_iso = <0x1>;
fsl,ldo-bypass = <0>; /* DCDC, ldo-enable */
};
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "disabled";
};
&i2c2 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x17059 /* USB OTG1 ID */
MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x1b0b0 /*UART6 MODE*/
MX6UL_PAD_JTAG_TCK__GPIO1_IO14 0x1b0b0 /*UART5 MODE*/
MX6UL_PAD_JTAG_TDI__GPIO1_IO13 0x1b0b0 /*UART4 MODE*/
MX6UL_PAD_JTAG_TRST_B__GPIO1_IO15 0x1b0b0 /*UART3 MODE*/
MX6UL_PAD_JTAG_TMS__GPIO1_IO11 0x1b0b0 /*UART2 MODE*/
MX6UL_PAD_JTAG_TDO__GPIO1_IO12 0x000090B1 /*UART1 MODE*/
MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x1b0b0 /*WDI*/
MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x1b0b0 /*KEY*/
MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x1b0b0 /*RUN*/
MX6UL_PAD_CSI_DATA03__GPIO4_IO24 0x0a020 /*BEEP*/
>;
};
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
>;
};
pinctrl_enet2: enet2grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0
MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
>;
};
pinctrl_flexcan1: flexcan1grp{
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX 0x1b020
MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX 0x1b020
>;
};
pinctrl_flexcan2: flexcan2grp{
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX 0x1b020
MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX 0x1b020
>;
};
pinctrl_i2c1: i2c1grp {
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
>;
};
pinctrl_i2c2: i2c2grp {
fsl,pins = <
MX6UL_PAD_CSI_HSYNC__I2C2_SCL 0x4001b8b0
MX6UL_PAD_CSI_VSYNC__I2C2_SDA 0x4001b8b0
>;
};
pinctrl_lcdif_dat: lcdifdatgrp {
fsl,pins = <
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79
MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79
MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79
MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79
MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79
MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79
MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79
MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79
MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79
MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79
MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79
MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79
MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79
MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79
MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79
MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79
>;
};
pinctrl_lcdif_dat_16bits: lcdifdatgrp_16bits {
fsl,pins = <
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79
MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79
MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79
MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79
MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79
MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79
MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79
MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79
>;
};
pinctrl_lcdif_ctrl: lcdifctrlgrp {
fsl,pins = <
MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79
MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79
MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79
MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79
>;
};
pinctrl_pwm1: pwm1grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0
>;
};
pinctrl_qspi: qspigrp {
fsl,pins = <
MX6UL_PAD_NAND_WP_B__QSPI_A_SCLK 0x70a1
MX6UL_PAD_NAND_READY_B__QSPI_A_DATA00 0x70a1
MX6UL_PAD_NAND_CE0_B__QSPI_A_DATA01 0x70a1
MX6UL_PAD_NAND_CE1_B__QSPI_A_DATA02 0x70a1
MX6UL_PAD_NAND_CLE__QSPI_A_DATA03 0x70a1
MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B 0x70a1
>;
};
/*
pinctrl_sai2: sai2grp {
fsl,pins = <
MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088
MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088
MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088
MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x11088
MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088
>;
};
*/
/*
pinctrl_tsc: tscgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0
MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0
>;
};
*/
pinctrl_uart1: uart1grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x1b0b1
>;
};
pinctrl_uart2: uart2grp {
fsl,pins = <
MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1
MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1
MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b1
>;
};
pinctrl_uart3: uart3grp {
fsl,pins = <
MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0x1b0b1
MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0x1b0b1
MX6UL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x1b0b1
>;
};
pinctrl_uart4: uart4grp {
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__UART4_DCE_TX 0x1b0b1
MX6UL_PAD_UART4_RX_DATA__UART4_DCE_RX 0x1b0b1
MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x1b0b1
>;
};
pinctrl_uart5: uart5grp {
fsl,pins = <
MX6UL_PAD_UART5_TX_DATA__UART5_DCE_TX 0x1b0b1
MX6UL_PAD_UART5_RX_DATA__UART5_DCE_RX 0x1b0b1
MX6UL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x1b0b1
>;
};
pinctrl_uart6: uart6grp {
fsl,pins = <
MX6UL_PAD_CSI_MCLK__UART6_DCE_TX 0x1b0b1
MX6UL_PAD_CSI_PIXCLK__UART6_DCE_RX 0x1b0b1
MX6UL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x1b0b1
>;
};
pinctrl_usdhc1: usdhc1grp {
fsl,pins = <
MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059
MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10071
MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059
MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059
MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059
MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059
>;
};
pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
fsl,pins = <
MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9
MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9
MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9
MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9
MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9
MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9
>;
};
pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
fsl,pins = <
MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9
MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9
MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9
MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9
MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9
MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9
>;
};
pinctrl_usdhc2: usdhc2grp {
fsl,pins = <
MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x10069
MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059
MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059
MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059
MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059
MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059
>;
};
pinctrl_usdhc2_8bit: usdhc2grp_8bit {
fsl,pins = <
MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x10069
MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059
MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059
MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059
MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059
MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059
MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x17059
MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x17059
MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x17059
MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x17059
>;
};
pinctrl_usdhc2_8bit_100mhz: usdhc2grp_8bit_100mhz {
fsl,pins = <
MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x100b9
MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x170b9
MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170b9
MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170b9
MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170b9
MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170b9
MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170b9
MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170b9
MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170b9
MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170b9
>;
};
pinctrl_usdhc2_8bit_200mhz: usdhc2grp_8bit_200mhz {
fsl,pins = <
MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x100f9
MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x170f9
MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170f9
MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170f9
MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170f9
MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170f9
MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170f9
MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170f9
MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170f9
MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170f9
>;
};
pinctrl_lcdif_reset: lcdifresetgrp {
fsl,pins = <
MX6UL_PAD_LCD_RESET__GPIO3_IO04 0x1b0b0
>;
};
edt_ft5x06_pins: ft5x06 {
fsl,pins = <
MX6UL_PAD_GPIO1_IO05__GPIO1_IO05 0x1b0b0 /* TP INT */
>;
};
};
};
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
imx6ul-evk {
pinctrl_hog_2: hoggrp-2 {
fsl,pins = <
/*MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x1b0b0*/ /* enet1 reset */
/*MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x1b0b0*/ /* LTE module WakeOut */
/*MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x1b0b0*/ /* enet2 reset */
/*MX6ULL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x1b8b0*/ /* LTE module power */
/*MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x1b0b0*/ /* Camera RST */
/*MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x1b0b0*/ /* Camera PWRDN */
/*MX6ULL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b0*/ /* TP Reset */
>;
};
pinctrl_spi4: spi4grp {
fsl,pins = <
MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000
>;
};
pinctrl_leds: ledgrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x1b0b0
>;
};
pinctrl_gpio_key: keygrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x1b0b0
>;
};
};
};
&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat_16bits
&pinctrl_lcdif_ctrl
&pinctrl_lcdif_reset>;
display = <&display0>;
status = "okay";
display0: display {
bits-per-pixel = <16>;
bus-width = <16>;
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <9200000>;
hactive = <480>;
vactive = <272>;
hfront-porch = <8>;
hback-porch = <4>;
hsync-len = <41>;
vback-porch = <2>;
vfront-porch = <4>;
vsync-len = <10>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
/*
timing0: timing0 {
clock-frequency = <33000000>;
hactive = <800>;
vactive = <480>;
hfront-porch = <210>;
hback-porch = <46>;
hsync-len = <1>;
vback-porch = <22>;
vfront-porch = <23>;
vsync-len = <20>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
*/
};
};
};
&pwm1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm1>;
status = "disabled";
};
&pxp {
status = "disabled";
};
&qspi {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_qspi>;
status = "disabled";
ddrsmp=<0>;
flash0: n25q256a@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "micron,n25q256a";
spi-max-frequency = <29000000>;
spi-nor,ddr-quad-read-dummy = <6>;
reg = <0>;
};
};
/*
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2>;
assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
<&clks IMX6UL_CLK_SAI2>;
assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <12288000>;
status = "disable";
};
*/
/*
&tsc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc>;
xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
measure-delay-time = <0xfffff>;
pre-charge-time = <0xffff>;
status = "okay";
};
*/
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
fsl,rs485-gpio-txen = <&gpio5 1 GPIO_ACTIVE_HIGH>;
linux,rs485-enable-at-boot-time;
status = "okay";
};
&uart2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart2>;
fsl,rs485-gpio-txen = <&gpio5 2 GPIO_ACTIVE_HIGH>;
linux,rs485-enable-at-boot-time;
status = "okay";
};
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3>;
fsl,rs485-gpio-txen = <&gpio5 3 GPIO_ACTIVE_HIGH>;
linux,rs485-enable-at-boot-time;
status = "okay";
};
&uart4 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart4>;
fsl,rs485-gpio-txen = <&gpio5 4 GPIO_ACTIVE_HIGH>;
linux,rs485-enable-at-boot-time;
status = "okay";
};
&uart5 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart5>;
fsl,rs485-gpio-txen = <&gpio5 5 GPIO_ACTIVE_HIGH>;
linux,rs485-enable-at-boot-time;
status = "okay";
};
&uart6 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart6>;
fsl,rs485-gpio-txen = <&gpio5 6 GPIO_ACTIVE_HIGH>;
linux,rs485-enable-at-boot-time;
status = "okay";
};
&usbotg1 {
dr_mode = "otg";
srp-disable;
hnp-disable;
adp-disable;
status = "okay";
};
&usbotg2 {
dr_mode = "host";
disable-over-current;
status = "okay";
};
&usbphy1 {
tx-d-cal = <0x5>;
};
&usbphy2 {
tx-d-cal = <0x5>;
};
&usdhc1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc1>;
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
enable-sdio-wakeup;
bus-width = <4>;
status = "disable";
};
&usdhc2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc2>;
non-removable;
status = "disabled";
};
&wdog1 {
status = "okay";
};
2. 但是由于电路的问题,导致在发送数据的同时,也收到了自己发送出去的数据。这个可以通过应用层的软件的避免这个问题,把数据丢掉即可。但是不是每个应用应用软件都能配合。硬件也已经定型,剩下的就只能修改驱动了。
3. 思路:发送数据的时候,禁止接收即可。
串口驱动文件:
driver/tty/serial/imx.c
第1步,使能 SER_RS485_RTS_AFTER_SEND
第2步,发送前,先把接收停止
第3步,发送结束的时候,允许接收。
但是测试的时候发现,在某种特定情况下,可能导致串口异常,引起启动的死机,所以做了如下优化:
事实证明,以上的方法存在问题,系统在某种情况下还是出现死机的情况,我也不知道是什么问题,如有高人看到以上,还请解答一下。
最后,突然想到,实际上不需要那么复杂,只需要在接收中断那里判断一下,如果正在发送过程中,直接将接收的数据丢弃即可。问题解决!
附imx.c源码
/*
* Driver for Motorola/Freescale IMX serial ports
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
* Author: Sascha Hauer
* Copyright (C) 2004 Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
#define URTX0 0x40 /* Transmitter Register */
#define UCR1 0x80 /* Control Register 1 */
#define UCR2 0x84 /* Control Register 2 */
#define UCR3 0x88 /* Control Register 3 */
#define UCR4 0x8c /* Control Register 4 */
#define UFCR 0x90 /* FIFO Control Register */
#define USR1 0x94 /* Status Register 1 */
#define USR2 0x98 /* Status Register 2 */
#define UESC 0x9c /* Escape Character Register */
#define UTIM 0xa0 /* Escape Timer Register */
#define UBIR 0xa4 /* BRM Incremental Register */
#define UBMR 0xa8 /* BRM Modulator Register */
#define UBRC 0xac /* Baud Rate Count Register */
#define IMX21_ONEMS 0xb0 /* One Millisecond register */
#define IMX1_UTS 0xd0 /* UART Test Register on i.mx1 */
#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
/* UART Control Register Bit Fields.*/
#define URXD_DUMMY_READ (1<<16)
#define URXD_CHARRDY (1<<15)
#define URXD_ERR (1<<14)
#define URXD_OVRRUN (1<<13)
#define URXD_FRMERR (1<<12)
#define URXD_BRK (1<<11)
#define URXD_PRERR (1<<10)
#define URXD_RX_DATA (0xFF<<0)
#define UCR1_ADEN (1<<15) /* Auto detect interrupt */
#define UCR1_ADBR (1<<14) /* Auto detect baud rate */
#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */
#define UCR1_DOZE (1<<1) /* Doze */
#define UCR1_UARTEN (1<<0) /* UART enabled */
#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
#define UCR2_IRTS (1<<14) /* Ignore RTS pin */
#define UCR2_CTSC (1<<13) /* CTS pin control */
#define UCR2_CTS (1<<12) /* Clear to send */
#define UCR2_ESCEN (1<<11) /* Escape enable */
#define UCR2_PREN (1<<8) /* Parity enable */
#define UCR2_PROE (1<<7) /* Parity odd/even */
#define UCR2_STPB (1<<6) /* Stop */
#define UCR2_WS (1<<5) /* Word size */
#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */
#define UCR2_ATEN (1<<3) /* Aging Timer Enable */
#define UCR2_TXEN (1<<2) /* Transmitter enabled */
#define UCR2_RXEN (1<<1) /* Receiver enabled */
#define UCR2_SRST (1<<0) /* SW reset */
#define UCR3_DTREN (1<<13) /* DTR interrupt enable */
#define UCR3_PARERREN (1<<12) /* Parity enable */
#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */
#define UCR3_DSR (1<<10) /* Data set ready */
#define UCR3_DCD (1<<9) /* Data carrier detect */
#define UCR3_RI (1<<8) /* Ring indicator */
#define UCR3_ADNIMP (1<<7) /* Autobaud Detection Not Improved */
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
#define IMX21_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select */
#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
#define UCR3_BPEN (1<<0) /* Preset registers enable */
#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */
#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */
#define UCR4_INVR (1<<9) /* Inverted infrared reception */
#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
#define UCR4_WKEN (1<<7) /* Wake interrupt enable */
#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */
#define UCR4_IRSC (1<<5) /* IR special case */
#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */
#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
#define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */
#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
#define USR1_RTSS (1<<14) /* RTS pin status */
#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
#define USR1_RTSD (1<<12) /* RTS delta */
#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */
#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */
#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */
#define USR1_AGTIM (1<<8) /* Ageing timer interrfupt flag */
#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */
#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */
#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */
#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */
#define USR2_ADET (1<<15) /* Auto baud rate detect complete */
#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */
#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */
#define USR2_IDLE (1<<12) /* Idle condition */
#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */
#define USR2_WAKE (1<<7) /* Wake */
#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */
#define USR2_TXDC (1<<3) /* Transmitter complete */
#define USR2_BRCD (1<<2) /* Break condition */
#define USR2_ORE (1<<1) /* Overrun error */
#define USR2_RDR (1<<0) /* Recv data ready */
#define UTS_FRCPERR (1<<13) /* Force parity error */
#define UTS_LOOP (1<<12) /* Loop tx and rx */
#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */
#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */
#define UTS_TXFULL (1<<4) /* TxFIFO full */
#define UTS_RXFULL (1<<3) /* RxFIFO full */
#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
/*
* This determines how often we check the modem status signals
* for any change. They generally aren't connected to an IRQ
* so we have to poll them. We also check immediately before
* filling the TX fifo incase CTS has been dropped.
*/
#define MCTRL_TIMEOUT (250*HZ/1000)
#define DRIVER_NAME "IMX-uart"
#define UART_NR 8
#define IMX_RXBD_NUM 20
#define IMX_MODULE_MAX_CLK_RATE 80000000
/* i.MX21 type uart runs on all i.mx except i.MX1 and i.MX6q */
enum imx_uart_type {
IMX1_UART,
IMX21_UART,
IMX6Q_UART,
};
/* device type dependent stuff */
struct imx_uart_data {
unsigned uts_reg;
enum imx_uart_type devtype;
};
struct imx_dma_bufinfo {
bool filled;
unsigned int rx_bytes;
};
struct imx_dma_rxbuf {
unsigned int periods;
unsigned int period_len;
unsigned int buf_len;
void *buf;
dma_addr_t dmaaddr;
unsigned int cur_idx;
unsigned int last_completed_idx;
dma_cookie_t cookie;
struct imx_dma_bufinfo buf_info[IMX_RXBD_NUM];
};
struct imx_port {
struct uart_port port;
struct timer_list timer;
unsigned int old_status;
unsigned int have_rtscts:1;
unsigned int dte_mode:1;
unsigned int irda_inv_rx:1;
unsigned int irda_inv_tx:1;
unsigned short trcv_delay; /* transceiver delay */
struct clk *clk_ipg;
struct clk *clk_per;
int txen_gpio;
const struct imx_uart_data *devdata;
/* DMA fields */
unsigned int dma_is_inited:1;
unsigned int dma_is_enabled:1;
unsigned int dma_is_rxing:1;
unsigned int dma_is_txing:1;
struct dma_chan *dma_chan_rx, *dma_chan_tx;
struct scatterlist tx_sgl[2];
struct imx_dma_rxbuf rx_buf;
unsigned int tx_bytes;
unsigned int dma_tx_nents;
struct delayed_work tsk_dma_tx;
wait_queue_head_t dma_wait;
unsigned int saved_reg[10];
#define DMA_TX_IS_WORKING 1
unsigned long flags;
};
struct imx_port_ucrs {
unsigned int ucr1;
unsigned int ucr2;
unsigned int ucr3;
};
static struct imx_uart_data imx_uart_devdata[] = {
[IMX1_UART] = {
.uts_reg = IMX1_UTS,
.devtype = IMX1_UART,
},
[IMX21_UART] = {
.uts_reg = IMX21_UTS,
.devtype = IMX21_UART,
},
[IMX6Q_UART] = {
.uts_reg = IMX21_UTS,
.devtype = IMX6Q_UART,
},
};
static struct platform_device_id imx_uart_devtype[] = {
{
.name = "imx1-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],
}, {
.name = "imx21-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
}, {
.name = "imx6q-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
static const struct of_device_id imx_uart_dt_ids[] = {
{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
static inline unsigned uts_reg(struct imx_port *sport)
{
return sport->devdata->uts_reg;
}
static inline int is_imx1_uart(struct imx_port *sport)
{
return sport->devdata->devtype == IMX1_UART;
}
static inline int is_imx21_uart(struct imx_port *sport)
{
return sport->devdata->devtype == IMX21_UART;
}
static inline int is_imx6q_uart(struct imx_port *sport)
{
return sport->devdata->devtype == IMX6Q_UART;
}
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
#if defined(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_port_ucrs_save(struct uart_port *port,
struct imx_port_ucrs *ucr)
{
/* save control registers */
ucr->ucr1 = readl(port->membase + UCR1);
ucr->ucr2 = readl(port->membase + UCR2);
ucr->ucr3 = readl(port->membase + UCR3);
}
static void imx_port_ucrs_restore(struct uart_port *port,
struct imx_port_ucrs *ucr)
{
/* restore control registers */
writel(ucr->ucr1, port->membase + UCR1);
writel(ucr->ucr2, port->membase + UCR2);
writel(ucr->ucr3, port->membase + UCR3);
}
#endif
/*
* Handle any change of modem status signal since we were last called.
*/
static void imx_mctrl_check(struct imx_port *sport)
{
unsigned int status, changed;
status = sport->port.ops->get_mctrl(&sport->port);
changed = status ^ sport->old_status;
if (changed == 0)
return;
sport->old_status = status;
if (changed & TIOCM_RI)
sport->port.icount.rng++;
if (changed & TIOCM_DSR)
sport->port.icount.dsr++;
if (changed & TIOCM_CAR)
uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
if (changed & TIOCM_CTS)
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
}
/*
* This is our per-port timeout handler, for checking the
* modem status signals.
*/
static void imx_timeout(unsigned long data)
{
struct imx_port *sport = (struct imx_port *)data;
unsigned long flags;
if (sport->port.state) {
spin_lock_irqsave(&sport->port.lock, flags);
imx_mctrl_check(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
}
}
/*
* interrupts disabled on entry
*/
static void imx_stop_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
/*
* We are maybe in the SMP context, so if the DMA TX thread is running
* on other cpu, we have to wait for it to finish.
*/
if (sport->dma_is_enabled && sport->dma_is_txing)
return;
temp = readl(port->membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);
/* in rs485 mode disable transmitter if shifter is empty */
if (port->rs485.flags & SER_RS485_ENABLED &&
readl(port->membase + USR2) & USR2_TXDC) {
if (sport->txen_gpio != -1) {
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND){
gpio_set_value(sport->txen_gpio, 0);
}
} else {
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
temp &= ~UCR2_CTS;
else
temp |= UCR2_CTS;
writel(temp, port->membase + UCR2);
}
/* disable shifter empty irq */
temp = readl(port->membase + UCR4);
temp &= ~UCR4_TCEN;
writel(temp, port->membase + UCR4);
}
}
/*
* interrupts disabled on entry
*/
static void imx_stop_rx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
if (sport->dma_is_enabled && sport->dma_is_rxing) {
if (sport->port.suspended) {
dmaengine_terminate_all(sport->dma_chan_rx);
sport->dma_is_rxing = 0;
} else {
return;
}
}
/* disable the Receiver Ready and overrun Interrrupt */
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1);
temp = readl(sport->port.membase + UCR4);
writel(temp & ~UCR4_OREN, sport->port.membase + UCR4);
temp = readl(sport->port.membase + UCR2);
writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
}
/*
* Set the modem control timer to fire immediately.
*/
static void imx_enable_ms(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
mod_timer(&sport->timer, jiffies);
}
static inline void imx_transmit_buffer(struct imx_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long temp;
if (sport->port.x_char) {
/* Send next char */
writel(sport->port.x_char, sport->port.membase + URTX0);
sport->port.icount.tx++;
sport->port.x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
imx_stop_tx(&sport->port);
return;
}
if (sport->dma_is_enabled) {
/*
* We've just sent a X-char Ensure the TX DMA is enabled
* and the TX IRQ is disabled.
**/
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TXMPTYEN;
if (sport->dma_is_txing) {
temp |= UCR1_TDMAEN;
writel(temp, sport->port.membase + UCR1);
} else {
writel(temp, sport->port.membase + UCR1);
schedule_delayed_work(&sport->tsk_dma_tx, 0);
}
}
while (!uart_circ_empty(xmit) &&
!(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
* out the port here */
writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);
if (uart_circ_empty(xmit))
imx_stop_tx(&sport->port);
}
static void dma_tx_callback(void *data)
{
struct imx_port *sport = data;
struct scatterlist *sgl = &sport->tx_sgl[0];
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
sport->dma_is_txing = 0;
/* update the stat */
spin_lock_irqsave(&sport->port.lock, flags);
xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx += sport->tx_bytes;
spin_unlock_irqrestore(&sport->port.lock, flags);
dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
clear_bit(DMA_TX_IS_WORKING, &sport->flags);
smp_mb__after_atomic();
uart_write_wakeup(&sport->port);
schedule_delayed_work(&sport->tsk_dma_tx, msecs_to_jiffies(1));
if (waitqueue_active(&sport->dma_wait)) {
wake_up(&sport->dma_wait);
dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
return;
}
}
static void dma_tx_work(struct work_struct *w)
{
struct delayed_work *delay_work = to_delayed_work(w);
struct imx_port *sport = container_of(delay_work, struct imx_port, tsk_dma_tx);
struct circ_buf *xmit = &sport->port.state->xmit;
struct scatterlist *sgl = sport->tx_sgl;
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = sport->dma_chan_tx;
struct device *dev = sport->port.dev;
unsigned long flags;
int ret;
if (test_and_set_bit(DMA_TX_IS_WORKING, &sport->flags))
return;
spin_lock_irqsave(&sport->port.lock, flags);
sport->tx_bytes = uart_circ_chars_pending(xmit);
if (sport->tx_bytes > 0) {
if (xmit->tail > xmit->head && xmit->head > 0) {
sport->dma_tx_nents = 2;
sg_init_table(sgl, 2);
sg_set_buf(sgl, xmit->buf + xmit->tail,
UART_XMIT_SIZE - xmit->tail);
sg_set_buf(sgl + 1, xmit->buf, xmit->head);
} else {
sport->dma_tx_nents = 1;
sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
}
spin_unlock_irqrestore(&sport->port.lock, flags);
ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
if (ret == 0) {
dev_err(dev, "DMA mapping error for TX.\n");
goto err_out;
}
desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
if (!desc) {
dev_err(dev, "We cannot prepare for the TX slave dma!\n");
goto err_out;
}
desc->callback = dma_tx_callback;
desc->callback_param = sport;
dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
uart_circ_chars_pending(xmit));
/* fire it */
sport->dma_is_txing = 1;
dmaengine_submit(desc);
dma_async_issue_pending(chan);
return;
}
spin_unlock_irqrestore(&sport->port.lock, flags);
err_out:
clear_bit(DMA_TX_IS_WORKING, &sport->flags);
smp_mb__after_atomic();
}
/*
* interrupts disabled on entry
*/
static void imx_start_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
if (port->rs485.flags & SER_RS485_ENABLED) {
/* enable transmitter */
if (sport->txen_gpio != -1) {
if (port->rs485.flags & SER_RS485_RTS_ON_SEND){
gpio_set_value(sport->txen_gpio, 1);
}
} else {
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
temp &= ~UCR2_CTS;
else
temp |= UCR2_CTS;
writel(temp, port->membase + UCR2);
}
/* enable shifter empty irq */
temp = readl(port->membase + UCR4);
temp |= UCR4_TCEN;
writel(temp, port->membase + UCR4);
}
if (!sport->dma_is_enabled) {
temp = readl(sport->port.membase + UCR1);
writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
}
if (sport->dma_is_enabled) {
if (sport->port.x_char) {
/* We have X-char to send, so enable TX IRQ and
* disable TX DMA to let TX interrupt to send X-char */
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TDMAEN;
temp |= UCR1_TXMPTYEN;
writel(temp, sport->port.membase + UCR1);
return;
}
schedule_delayed_work(&sport->tsk_dma_tx, 0);
return;
}
}
static irqreturn_t imx_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
writel(USR1_RTSD, sport->port.membase + USR1);
val = readl(sport->port.membase + USR1) & USR1_RTSS;
uart_handle_cts_change(&sport->port, !!val);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
}
static irqreturn_t imx_txint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
imx_transmit_buffer(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
}
static irqreturn_t imx_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int rx, flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
unsigned long flags, temp;
spin_lock_irqsave(&sport->port.lock, flags);
while (readl(sport->port.membase + USR2) & USR2_RDR) {
//huangdadao
if(sport->port.rs485.flags & SER_RS485_ENABLED){
if (sport->txen_gpio != -1) {
if(gpio_get_value(sport->txen_gpio) == 1) continue;
}
}
//end
flg = TTY_NORMAL;
sport->port.icount.rx++;
rx = readl(sport->port.membase + URXD0);
temp = readl(sport->port.membase + USR2);
if (temp & USR2_BRCD) {
writel(USR2_BRCD, sport->port.membase + USR2);
if (uart_handle_break(&sport->port))
continue;
}
if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
continue;
if (unlikely(rx & URXD_ERR)) {
if (rx & URXD_BRK)
sport->port.icount.brk++;
else if (rx & URXD_PRERR)
sport->port.icount.parity++;
else if (rx & URXD_FRMERR)
sport->port.icount.frame++;
if (rx & URXD_OVRRUN)
sport->port.icount.overrun++;
if (rx & sport->port.ignore_status_mask) {
if (++ignored > 100)
goto out;
continue;
}
rx &= (sport->port.read_status_mask | 0xFF);
if (rx & URXD_BRK)
flg = TTY_BREAK;
else if (rx & URXD_PRERR)
flg = TTY_PARITY;
else if (rx & URXD_FRMERR)
flg = TTY_FRAME;
if (rx & URXD_OVRRUN)
flg = TTY_OVERRUN;
#ifdef SUPPORT_SYSRQ
sport->port.sysrq = 0;
#endif
}
if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
goto out;
tty_insert_flip_char(port, rx, flg);
}
out:
spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port);
return IRQ_HANDLED;
}
static irqreturn_t imx_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int sts;
unsigned int sts2;
sts = readl(sport->port.membase + USR1);
sts2 = readl(sport->port.membase + USR2);
if ((sts & USR1_RRDY || sts & USR1_AGTIM) &&
!sport->dma_is_enabled) {
if (sts & USR1_AGTIM)
writel(USR1_AGTIM, sport->port.membase + USR1);
imx_rxint(irq, dev_id);
}
if ((sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
(sts2 & USR2_TXDC &&
readl(sport->port.membase + UCR4) & UCR4_TCEN))
imx_txint(irq, dev_id);
if (sts & USR1_RTSD)
imx_rtsint(irq, dev_id);
if (sts & USR1_AWAKE)
writel(USR1_AWAKE, sport->port.membase + USR1);
if (sts2 & USR2_ORE) {
dev_err(sport->port.dev, "Rx FIFO overrun\n");
sport->port.icount.overrun++;
writel(USR2_ORE, sport->port.membase + USR2);
}
return IRQ_HANDLED;
}
/*
* Return TIOCSER_TEMT when transmitter is not busy.
*/
static unsigned int imx_tx_empty(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned int ret;
ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
/* If the TX DMA is working, return 0. */
if (sport->dma_is_enabled && sport->dma_is_txing)
ret = 0;
return ret;
}
/*
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
*/
static unsigned int imx_get_mctrl(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
if (readl(sport->port.membase + USR1) & USR1_RTSS)
tmp |= TIOCM_CTS;
if (readl(sport->port.membase + UCR2) & UCR2_CTS)
tmp |= TIOCM_RTS;
if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP)
tmp |= TIOCM_LOOP;
return tmp;
}
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_CTS | UCR2_CTSC);
if (mctrl & TIOCM_RTS)
temp |= UCR2_CTS | UCR2_CTSC;
writel(temp, sport->port.membase + UCR2);
}
temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
if (mctrl & TIOCM_LOOP)
temp |= UTS_LOOP;
writel(temp, sport->port.membase + uts_reg(sport));
}
/*
* Interrupts always disabled.
*/
static void imx_break_ctl(struct uart_port *port, int break_state)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags, temp;
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
if (break_state != 0)
temp |= UCR1_SNDBRK;
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
#define TXTL 2 /* reset default */
#define RXTL 1 /* For console port */
#define RXTL_UART 16 /* For uart */
static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
{
unsigned int val;
unsigned int rx_fifo_trig;
if (uart_console(&sport->port))
rx_fifo_trig = RXTL;
else
rx_fifo_trig = RXTL_UART;
/* set receiver / transmitter trigger level */
val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
val |= TXTL << UFCR_TXTL_SHF | rx_fifo_trig;
writel(val, sport->port.membase + UFCR);
return 0;
}
#define RX_BUF_SIZE (PAGE_SIZE)
static void dma_rx_push_data(struct imx_port *sport, struct tty_struct *tty,
unsigned int start, unsigned int end)
{
unsigned int i;
struct tty_port *port = &sport->port.state->port;
for (i = start; i < end; i++) {
if (sport->rx_buf.buf_info[i].filled) {
tty_insert_flip_string(port, sport->rx_buf.buf + (i
* RX_BUF_SIZE), sport->rx_buf.buf_info[i].rx_bytes);
tty_flip_buffer_push(port);
sport->rx_buf.buf_info[i].filled = false;
sport->rx_buf.last_completed_idx++;
sport->rx_buf.last_completed_idx %= IMX_RXBD_NUM;
sport->port.icount.rx += sport->rx_buf.buf_info[i].rx_bytes;
}
}
}
static void dma_rx_work(struct imx_port *sport)
{
struct tty_struct *tty = sport->port.state->port.tty;
unsigned int cur_idx = sport->rx_buf.cur_idx;
if (sport->rx_buf.last_completed_idx < cur_idx) {
dma_rx_push_data(sport, tty, sport->rx_buf.last_completed_idx + 1, cur_idx);
} else if (sport->rx_buf.last_completed_idx == (IMX_RXBD_NUM - 1)) {
dma_rx_push_data(sport, tty, 0, cur_idx);
} else {
dma_rx_push_data(sport, tty, sport->rx_buf.last_completed_idx + 1,
IMX_RXBD_NUM);
dma_rx_push_data(sport, tty, 0, cur_idx);
}
}
static void imx_rx_dma_done(struct imx_port *sport)
{
sport->dma_is_rxing = 0;
/* Is the shutdown waiting for us? */
if (waitqueue_active(&sport->dma_wait))
wake_up(&sport->dma_wait);
}
/*
* There are three kinds of RX DMA interrupts(such as in the MX6Q):
* [1] the RX DMA buffer is full.
* [2] the Aging timer expires(wait for 8 bytes long)
* [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
*
* The [2] is trigger when a character was been sitting in the FIFO
* meanwhile [3] can wait for 32 bytes long when the RX line is
* on IDLE state and RxFIFO is empty.
*/
static void dma_rx_callback(void *data)
{
struct imx_port *sport = data;
struct dma_chan *chan = sport->dma_chan_rx;
struct tty_struct *tty = sport->port.state->port.tty;
struct dma_tx_state state;
enum dma_status status;
unsigned int count;
/* If we have finish the reading. we will not accept any more data. */
if (tty->closing) {
imx_rx_dma_done(sport);
return;
}
status = dmaengine_tx_status(chan, sport->rx_buf.cookie, &state);
count = RX_BUF_SIZE - state.residue;
sport->rx_buf.buf_info[sport->rx_buf.cur_idx].filled = true;
sport->rx_buf.buf_info[sport->rx_buf.cur_idx].rx_bytes = count;
sport->rx_buf.cur_idx++;
sport->rx_buf.cur_idx %= IMX_RXBD_NUM;
dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
if (sport->rx_buf.cur_idx == sport->rx_buf.last_completed_idx)
dev_err(sport->port.dev, "overwrite!\n");
if (count)
dma_rx_work(sport);
}
static int start_rx_dma(struct imx_port *sport)
{
struct dma_chan *chan = sport->dma_chan_rx;
struct dma_async_tx_descriptor *desc;
sport->rx_buf.periods = IMX_RXBD_NUM;
sport->rx_buf.period_len = RX_BUF_SIZE;
sport->rx_buf.buf_len = IMX_RXBD_NUM * RX_BUF_SIZE;
sport->rx_buf.cur_idx = 0;
sport->rx_buf.last_completed_idx = -1;
desc = dmaengine_prep_dma_cyclic(chan, sport->rx_buf.dmaaddr,
sport->rx_buf.buf_len, sport->rx_buf.period_len,
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (!desc) {
dev_err(sport->port.dev, "Prepare for the RX slave dma failed!\n");
return -EINVAL;
}
desc->callback = dma_rx_callback;
desc->callback_param = sport;
dev_dbg(sport->port.dev, "RX: prepare for the DMA.\n");
sport->rx_buf.cookie = dmaengine_submit(desc);
dma_async_issue_pending(chan);
sport->dma_is_rxing = 1;
return 0;
}
static void imx_uart_dma_exit(struct imx_port *sport)
{
if (sport->dma_chan_rx) {
dma_release_channel(sport->dma_chan_rx);
sport->dma_chan_rx = NULL;
if (sport->rx_buf.buf) {
dma_free_coherent(NULL, IMX_RXBD_NUM * RX_BUF_SIZE,
(void *)sport->rx_buf.buf,
sport->rx_buf.dmaaddr);
sport->rx_buf.buf = NULL;
}
}
if (sport->dma_chan_tx) {
dma_release_channel(sport->dma_chan_tx);
sport->dma_chan_tx = NULL;
}
sport->dma_is_inited = 0;
}
static int imx_uart_dma_init(struct imx_port *sport)
{
struct dma_slave_config slave_config = {};
struct device *dev = sport->port.dev;
int ret, i;
/* Prepare for RX : */
sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");
if (!sport->dma_chan_rx) {
dev_dbg(dev, "cannot get the DMA channel.\n");
ret = -EINVAL;
goto err;
}
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = sport->port.mapbase + URXD0;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.src_maxburst = RXTL_UART;
ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
if (ret) {
dev_err(dev, "error in RX dma configuration.\n");
goto err;
}
sport->rx_buf.buf = dma_alloc_coherent(NULL, IMX_RXBD_NUM * RX_BUF_SIZE,
&sport->rx_buf.dmaaddr, GFP_KERNEL);
if (!sport->rx_buf.buf) {
dev_err(dev, "cannot alloc DMA buffer.\n");
ret = -ENOMEM;
goto err;
}
for (i = 0; i < IMX_RXBD_NUM; i++) {
sport->rx_buf.buf_info[i].rx_bytes = 0;
sport->rx_buf.buf_info[i].filled = false;
}
/* Prepare for TX : */
sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
if (!sport->dma_chan_tx) {
dev_err(dev, "cannot get the TX DMA channel!\n");
ret = -EINVAL;
goto err;
}
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = sport->port.mapbase + URTX0;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.dst_maxburst = TXTL;
ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
if (ret) {
dev_err(dev, "error in TX dma configuration.");
goto err;
}
sport->dma_is_inited = 1;
return 0;
err:
imx_uart_dma_exit(sport);
return ret;
}
static void imx_enable_dma(struct imx_port *sport)
{
unsigned long temp;
init_waitqueue_head(&sport->dma_wait);
sport->flags = 0;
/* set UCR1 */
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
/* wait for 32 idle frames for IDDMA interrupt */
UCR1_ICD_REG(3);
writel(temp, sport->port.membase + UCR1);
/* set UCR4 */
temp = readl(sport->port.membase + UCR4);
temp |= UCR4_IDDMAEN;
writel(temp, sport->port.membase + UCR4);
sport->dma_is_enabled = 1;
}
static void imx_disable_dma(struct imx_port *sport)
{
unsigned long temp;
/* clear UCR1 */
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
writel(temp, sport->port.membase + UCR1);
/* clear UCR2 */
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_CTSC | UCR2_CTS);
writel(temp, sport->port.membase + UCR2);
/* clear UCR4 */
temp = readl(sport->port.membase + UCR4);
temp &= ~UCR4_IDDMAEN;
writel(temp, sport->port.membase + UCR4);
sport->dma_is_enabled = 0;
}
/* half the RX buffer size */
#define CTSTL 16
static int imx_startup(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
int retval, i;
unsigned long flags, temp;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
return retval;
retval = clk_prepare_enable(sport->clk_ipg);
if (retval) {
clk_disable_unprepare(sport->clk_per);
return retval;
}
imx_setup_ufcr(sport, 0);
/* disable the DREN bit (Data Ready interrupt enable) before
* requesting IRQs
*/
temp = readl(sport->port.membase + UCR4);
/* set the trigger level for CTS */
temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
temp |= CTSTL << UCR4_CTSTL_SHF;
writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
/* Reset fifo's and state machines */
i = 100;
temp = readl(sport->port.membase + UCR2);
temp &= ~UCR2_SRST;
writel(temp, sport->port.membase + UCR2);
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/* Can we enable the DMA support? */
if (is_imx6q_uart(sport) && !uart_console(port)
&& !sport->dma_is_inited)
imx_uart_dma_init(sport);
if (sport->dma_is_inited)
INIT_DELAYED_WORK(&sport->tsk_dma_tx, dma_tx_work);
spin_lock_irqsave(&sport->port.lock, flags);
/*
* Finally, clear and enable interrupts
*/
writel(USR1_RTSD, sport->port.membase + USR1);
writel(USR2_ORE, sport->port.membase + USR2);
temp = readl(sport->port.membase + UCR1);
if (!sport->dma_is_inited)
temp |= UCR1_RRDYEN;
if (sport->have_rtscts)
temp |= UCR1_RTSDEN;
temp |= UCR1_UARTEN;
writel(temp, sport->port.membase + UCR1);
temp = readl(sport->port.membase + UCR4);
temp |= UCR4_OREN;
writel(temp, sport->port.membase + UCR4);
temp = readl(sport->port.membase + UCR2);
temp |= (UCR2_RXEN | UCR2_TXEN);
if (!sport->have_rtscts)
temp |= UCR2_IRTS;
writel(temp, sport->port.membase + UCR2);
if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
writel(temp, sport->port.membase + UCR3);
}
/*
* Enable modem status interrupts
*/
imx_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
static void imx_shutdown(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
unsigned long flags;
if (sport->dma_is_enabled) {
int ret;
/* We have to wait for the DMA to finish. */
ret = wait_event_interruptible_timeout(sport->dma_wait,
!sport->dma_is_rxing && !sport->dma_is_txing,
msecs_to_jiffies(1));
if (ret <= 0) {
sport->dma_is_rxing = 0;
sport->dma_is_txing = 0;
dmaengine_terminate_all(sport->dma_chan_tx);
dmaengine_terminate_all(sport->dma_chan_rx);
}
cancel_delayed_work_sync(&sport->tsk_dma_tx);
spin_lock_irqsave(&sport->port.lock, flags);
imx_stop_tx(port);
imx_stop_rx(port);
imx_disable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
imx_uart_dma_exit(sport);
}
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
writel(temp, sport->port.membase + UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
/*
* Stop our timer.
*/
del_timer_sync(&sport->timer);
/*
* Disable all interrupts, port and break condition.
*/
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
clk_disable_unprepare(sport->clk_per);
clk_disable_unprepare(sport->clk_ipg);
}
static void imx_flush_buffer(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
struct scatterlist *sgl = &sport->tx_sgl[0];
unsigned long temp;
int i = 100, ubir, ubmr, uts;
if (!sport->dma_chan_tx)
return;
sport->tx_bytes = 0;
dmaengine_terminate_all(sport->dma_chan_tx);
if (sport->dma_is_txing) {
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TDMAEN;
writel(temp, sport->port.membase + UCR1);
sport->dma_is_txing = false;
}
/*
* According to the Reference Manual description of the UART SRST bit:
* "Reset the transmit and receive state machines,
* all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD
* and UTS[6-3]". As we don't need to restore the old values from
* USR1, USR2, URXD, UTXD, only save/restore the other four registers
*/
ubir = readl(sport->port.membase + UBIR);
ubmr = readl(sport->port.membase + UBMR);
uts = readl(sport->port.membase + IMX21_UTS);
temp = readl(sport->port.membase + UCR2);
temp &= ~UCR2_SRST;
writel(temp, sport->port.membase + UCR2);
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/* Restore the registers */
writel(ubir, sport->port.membase + UBIR);
writel(ubmr, sport->port.membase + UBMR);
writel(uts, sport->port.membase + IMX21_UTS);
}
static void
imx_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int div, ufcr;
unsigned long num, denom;
uint64_t tdiv64;
/*
* We only support CS7 and CS8.
*/
while ((termios->c_cflag & CSIZE) != CS7 &&
(termios->c_cflag & CSIZE) != CS8) {
termios->c_cflag &= ~CSIZE;
termios->c_cflag |= old_csize;
old_csize = CS8;
}
if ((termios->c_cflag & CSIZE) == CS8)
ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
else
ucr2 = UCR2_SRST | UCR2_IRTS;
if (termios->c_cflag & CRTSCTS) {
if (sport->have_rtscts) {
ucr2 &= ~UCR2_IRTS;
if ((port->rs485.flags & SER_RS485_ENABLED) &&
(sport->txen_gpio == -1)) {
/*
* RTS is mandatory for rs485 operation, so keep
* it under manual control and keep transmitter
* disabled.
*/
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
ucr2 |= UCR2_CTS;
} else {
ucr2 |= UCR2_CTSC;
}
} else {
termios->c_cflag &= ~CRTSCTS;
}
} else if (port->rs485.flags & SER_RS485_ENABLED &&
sport->txen_gpio == -1)
/* disable transmitter */
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
ucr2 |= UCR2_CTS;
if (termios->c_cflag & CSTOPB)
ucr2 |= UCR2_STPB;
if (termios->c_cflag & PARENB) {
ucr2 |= UCR2_PREN;
if (termios->c_cflag & PARODD)
ucr2 |= UCR2_PROE;
}
del_timer_sync(&sport->timer);
/*
* Ask the core to calculate the divisor for us.
*/
baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
quot = uart_get_divisor(port, baud);
spin_lock_irqsave(&sport->port.lock, flags);
sport->port.read_status_mask = 0;
if (termios->c_iflag & INPCK)
sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
if (termios->c_iflag & (BRKINT | PARMRK))
sport->port.read_status_mask |= URXD_BRK;
/*
* Characters to ignore
*/
sport->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
sport->port.ignore_status_mask |= URXD_PRERR | URXD_FRMERR;
if (termios->c_iflag & IGNBRK) {
sport->port.ignore_status_mask |= URXD_BRK;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
sport->port.ignore_status_mask |= URXD_OVRRUN;
}
if ((termios->c_cflag & CREAD) == 0)
sport->port.ignore_status_mask |= URXD_DUMMY_READ;
/*
* Update the per-port timeout.
*/
uart_update_timeout(port, termios->c_cflag, baud);
/*
* disable interrupts and drain transmitter
*/
old_ucr1 = readl(sport->port.membase + UCR1);
writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
sport->port.membase + UCR1);
while (!(readl(sport->port.membase + USR2) & USR2_TXDC))
barrier();
/* then, disable everything */
old_txrxen = readl(sport->port.membase + UCR2);
writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN),
sport->port.membase + UCR2);
old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
/* custom-baudrate handling */
div = sport->port.uartclk / (baud * 16);
if (baud == 38400 && quot != div)
baud = sport->port.uartclk / (quot * 16);
div = sport->port.uartclk / (baud * 16);
if (div > 7)
div = 7;
if (!div)
div = 1;
rational_best_approximation(16 * div * baud, sport->port.uartclk,
1 << 16, 1 << 16, &num, &denom);
tdiv64 = sport->port.uartclk;
tdiv64 *= num;
do_div(tdiv64, denom * 16 * div);
tty_termios_encode_baud_rate(termios,
(speed_t)tdiv64, (speed_t)tdiv64);
num -= 1;
denom -= 1;
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
if (sport->dte_mode)
ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
writel(num, sport->port.membase + UBIR);
writel(denom, sport->port.membase + UBMR);
if (!is_imx1_uart(sport))
writel(sport->port.uartclk / div / 1000,
sport->port.membase + IMX21_ONEMS);
writel(old_ucr1, sport->port.membase + UCR1);
/* set the parity, stop bits and data size */
writel(ucr2 | old_txrxen, sport->port.membase + UCR2);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_enable_ms(&sport->port);
if (sport->dma_is_inited && !sport->dma_is_enabled) {
imx_enable_dma(sport);
start_rx_dma(sport);
}
if (!sport->dma_is_enabled) {
ucr2 = readl(sport->port.membase + UCR2);
writel(ucr2 | UCR2_ATEN, sport->port.membase + UCR2);
}
spin_unlock_irqrestore(&sport->port.lock, flags);
}
static const char *imx_type(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
return sport->port.type == PORT_IMX ? "IMX" : NULL;
}
/*
* Configure/autoconfigure the port.
*/
static void imx_config_port(struct uart_port *port, int flags)
{
struct imx_port *sport = (struct imx_port *)port;
if (flags & UART_CONFIG_TYPE)
sport->port.type = PORT_IMX;
}
/*
* Verify the new serial_struct (for TIOCSSERIAL).
* The only change we allow are to the flags and type, and
* even then only between PORT_IMX and PORT_UNKNOWN
*/
static int
imx_verify_port(struct uart_port *port, struct serial_struct *ser)
{
struct imx_port *sport = (struct imx_port *)port;
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX)
ret = -EINVAL;
if (sport->port.irq != ser->irq)
ret = -EINVAL;
if (ser->io_type != UPIO_MEM)
ret = -EINVAL;
if (sport->port.uartclk / 16 != ser->baud_base)
ret = -EINVAL;
if (sport->port.mapbase != (unsigned long)ser->iomem_base)
ret = -EINVAL;
if (sport->port.iobase != ser->port)
ret = -EINVAL;
if (ser->hub6 != 0)
ret = -EINVAL;
return ret;
}
#if defined(CONFIG_CONSOLE_POLL)
static int imx_poll_init(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
unsigned long temp;
int retval;
retval = clk_prepare_enable(sport->clk_ipg);
if (retval)
return retval;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
clk_disable_unprepare(sport->clk_ipg);
imx_setup_ufcr(sport, 0);
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1);
if (is_imx1_uart(sport))
temp |= IMX1_UCR1_UARTCLKEN;
temp |= UCR1_UARTEN | UCR1_RRDYEN;
temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN);
writel(temp, sport->port.membase + UCR1);
temp = readl(sport->port.membase + UCR2);
temp |= UCR2_RXEN;
writel(temp, sport->port.membase + UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
static int imx_poll_get_char(struct uart_port *port)
{
if (!(readl_relaxed(port->membase + USR2) & USR2_RDR))
return NO_POLL_CHAR;
return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
}
static void imx_poll_put_char(struct uart_port *port, unsigned char c)
{
unsigned int status;
/* drain */
do {
status = readl_relaxed(port->membase + USR1);
} while (~status & USR1_TRDY);
/* write */
writel_relaxed(c, port->membase + URTX0);
/* flush */
do {
status = readl_relaxed(port->membase + USR2);
} while (~status & USR2_TXDC);
}
#endif
static int imx_rs485_config(struct uart_port *port,
struct serial_rs485 *rs485conf)
{
struct imx_port *sport = (struct imx_port *)port;
/* unimplemented */
rs485conf->delay_rts_before_send = 0;
rs485conf->delay_rts_after_send = 0;
rs485conf->flags |= SER_RS485_RX_DURING_TX;
/* RTS is required to control the transmitter */
if (sport->txen_gpio == -1 && !sport->have_rtscts)
rs485conf->flags &= ~SER_RS485_ENABLED;
if (rs485conf->flags & SER_RS485_ENABLED) {
if (sport->txen_gpio != -1) {
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
gpio_set_value(sport->txen_gpio, 1);
else
gpio_set_value(sport->txen_gpio, 0);
} else {
unsigned long temp;
/* disable transmitter */
temp = readl(sport->port.membase + UCR2);
temp &= ~UCR2_CTSC;
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
temp &= ~UCR2_CTS;
else
temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
}
}
port->rs485 = *rs485conf;
return 0;
}
static struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
.get_mctrl = imx_get_mctrl,
.stop_tx = imx_stop_tx,
.start_tx = imx_start_tx,
.stop_rx = imx_stop_rx,
.enable_ms = imx_enable_ms,
.break_ctl = imx_break_ctl,
.startup = imx_startup,
.shutdown = imx_shutdown,
.flush_buffer = imx_flush_buffer,
.set_termios = imx_set_termios,
.type = imx_type,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
.poll_init = imx_poll_init,
.poll_get_char = imx_poll_get_char,
.poll_put_char = imx_poll_put_char,
#endif
};
static struct imx_port *imx_ports[UART_NR];
#ifdef CONFIG_SERIAL_IMX_CONSOLE
static void imx_console_putchar(struct uart_port *port, int ch)
{
struct imx_port *sport = (struct imx_port *)port;
while (readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)
barrier();
writel(ch, sport->port.membase + URTX0);
}
/*
* Interrupts are disabled on entering
*/
static void
imx_console_write(struct console *co, const char *s, unsigned int count)
{
struct imx_port *sport = imx_ports[co->index];
struct imx_port_ucrs old_ucr;
unsigned int ucr1;
unsigned long flags = 0;
int locked = 1;
int retval;
retval = clk_enable(sport->clk_per);
if (retval)
return;
retval = clk_enable(sport->clk_ipg);
if (retval) {
clk_disable(sport->clk_per);
return;
}
if (sport->port.sysrq)
locked = 0;
else if (oops_in_progress)
locked = spin_trylock_irqsave(&sport->port.lock, flags);
else
spin_lock_irqsave(&sport->port.lock, flags);
/*
* First, save UCR1/2/3 and then disable interrupts
*/
imx_port_ucrs_save(&sport->port, &old_ucr);
ucr1 = old_ucr.ucr1;
if (is_imx1_uart(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
writel(ucr1, sport->port.membase + UCR1);
writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
uart_console_write(&sport->port, s, count, imx_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore UCR1/2/3
*/
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
imx_port_ucrs_restore(&sport->port, &old_ucr);
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
clk_disable(sport->clk_ipg);
clk_disable(sport->clk_per);
}
/*
* If the port was already initialised (eg, by a boot loader),
* try to determine the current setup.
*/
static void __init
imx_console_get_options(struct imx_port *sport, int *baud,
int *parity, int *bits)
{
if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
/* ok, the port was enabled */
unsigned int ucr2, ubir, ubmr, uartclk;
unsigned int baud_raw;
unsigned int ucfr_rfdiv;
ucr2 = readl(sport->port.membase + UCR2);
*parity = 'n';
if (ucr2 & UCR2_PREN) {
if (ucr2 & UCR2_PROE)
*parity = 'o';
else
*parity = 'e';
}
if (ucr2 & UCR2_WS)
*bits = 8;
else
*bits = 7;
ubir = readl(sport->port.membase + UBIR) & 0xffff;
ubmr = readl(sport->port.membase + UBMR) & 0xffff;
ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7;
if (ucfr_rfdiv == 6)
ucfr_rfdiv = 7;
else
ucfr_rfdiv = 6 - ucfr_rfdiv;
uartclk = clk_get_rate(sport->clk_per);
uartclk /= ucfr_rfdiv;
{ /*
* The next code provides exact computation of
* baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1))
* without need of float support or long long division,
* which would be required to prevent 32bit arithmetic overflow
*/
unsigned int mul = ubir + 1;
unsigned int div = 16 * (ubmr + 1);
unsigned int rem = uartclk % div;
baud_raw = (uartclk / div) * mul;
baud_raw += (rem * mul + div / 2) / div;
*baud = (baud_raw + 50) / 100 * 100;
}
if (*baud != baud_raw)
pr_info("Console IMX rounded baud rate from %d to %d\n",
baud_raw, *baud);
}
}
static int __init
imx_console_setup(struct console *co, char *options)
{
struct imx_port *sport;
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
int retval;
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
co->index = 0;
sport = imx_ports[co->index];
if (sport == NULL)
return -ENODEV;
/* For setting the registers, we only need to enable the ipg clock. */
retval = clk_prepare_enable(sport->clk_ipg);
if (retval)
goto error_console;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
imx_console_get_options(sport, &baud, &parity, &bits);
imx_setup_ufcr(sport, 0);
retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
clk_disable(sport->clk_ipg);
if (retval) {
clk_unprepare(sport->clk_ipg);
goto error_console;
}
retval = clk_prepare(sport->clk_per);
if (retval)
clk_disable_unprepare(sport->clk_ipg);
error_console:
return retval;
}
static struct uart_driver imx_reg;
static struct console imx_console = {
.name = DEV_NAME,
.write = imx_console_write,
.device = uart_console_device,
.setup = imx_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &imx_reg,
};
#define IMX_CONSOLE &imx_console
#else
#define IMX_CONSOLE NULL
#endif
static struct uart_driver imx_reg = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = DEV_NAME,
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
.nr = ARRAY_SIZE(imx_ports),
.cons = IMX_CONSOLE,
};
static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
{
struct imx_port *sport = platform_get_drvdata(dev);
unsigned int val;
/* enable wakeup from i.MX UART */
val = readl(sport->port.membase + UCR3);
val |= UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
uart_suspend_port(&imx_reg, &sport->port);
disable_irq(sport->port.irq);
/* Save necessary regs */
clk_prepare_enable(sport->clk_ipg);
sport->saved_reg[0] = readl(sport->port.membase + UCR1);
sport->saved_reg[1] = readl(sport->port.membase + UCR2);
sport->saved_reg[2] = readl(sport->port.membase + UCR3);
sport->saved_reg[3] = readl(sport->port.membase + UCR4);
sport->saved_reg[4] = readl(sport->port.membase + UFCR);
sport->saved_reg[5] = readl(sport->port.membase + UESC);
sport->saved_reg[6] = readl(sport->port.membase + UTIM);
sport->saved_reg[7] = readl(sport->port.membase + UBIR);
sport->saved_reg[8] = readl(sport->port.membase + UBMR);
sport->saved_reg[9] = readl(sport->port.membase + IMX21_UTS);
clk_disable_unprepare(sport->clk_ipg);
pinctrl_pm_select_sleep_state(&dev->dev);
return 0;
}
static int serial_imx_resume(struct platform_device *dev)
{
struct imx_port *sport = platform_get_drvdata(dev);
unsigned int val;
pinctrl_pm_select_default_state(&dev->dev);
clk_prepare_enable(sport->clk_ipg);
writel(sport->saved_reg[4], sport->port.membase + UFCR);
writel(sport->saved_reg[5], sport->port.membase + UESC);
writel(sport->saved_reg[6], sport->port.membase + UTIM);
writel(sport->saved_reg[7], sport->port.membase + UBIR);
writel(sport->saved_reg[8], sport->port.membase + UBMR);
writel(sport->saved_reg[9], sport->port.membase + IMX21_UTS);
writel(sport->saved_reg[0], sport->port.membase + UCR1);
writel(sport->saved_reg[1] | 0x1, sport->port.membase + UCR2);
writel(sport->saved_reg[2], sport->port.membase + UCR3);
writel(sport->saved_reg[3], sport->port.membase + UCR4);
/* disable wakeup from i.MX UART */
val = readl(sport->port.membase + UCR3);
val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
val = readl(sport->port.membase + USR1);
if (val & USR1_AWAKE)
writel(USR1_AWAKE, sport->port.membase + USR1);
clk_disable_unprepare(sport->clk_ipg);
uart_resume_port(&imx_reg, &sport->port);
enable_irq(sport->port.irq);
return 0;
}
#ifdef CONFIG_OF
/*
* This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it
* could successfully get all information from dt or a negative errno.
*/
static int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(imx_uart_dt_ids, &pdev->dev);
int ret;
if (!np)
/* no device tree device */
return 1;
ret = of_alias_get_id(np, "serial");
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
sport->port.line = ret;
if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
sport->have_rtscts = 1;
if (of_get_property(np, "fsl,dte-mode", NULL))
sport->dte_mode = 1;
if (of_get_property(np, "fsl,rs485-gpio-txen", NULL))
sport->txen_gpio = of_get_named_gpio(np, "fsl,rs485-gpio-txen", 0);
else
sport->txen_gpio = -1;
if (gpio_is_valid(sport->txen_gpio))
devm_gpio_request_one(&pdev->dev, sport->txen_gpio, GPIOF_OUT_INIT_LOW, "rs485-txen");
else
sport->txen_gpio = -1;
sport->devdata = of_id->data;
return 0;
}
#else
static inline int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
return 1;
}
#endif
static void serial_imx_probe_pdata(struct imx_port *sport,
struct platform_device *pdev)
{
struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev);
sport->port.line = pdev->id;
sport->devdata = (struct imx_uart_data *) pdev->id_entry->driver_data;
if (!pdata)
return;
if (pdata->flags & IMXUART_HAVE_RTSCTS)
sport->have_rtscts = 1;
}
static int serial_imx_probe(struct platform_device *pdev)
{
struct imx_port *sport;
void __iomem *base;
int ret = 0;
struct resource *res;
int txirq, rxirq, rtsirq;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
return -ENOMEM;
ret = serial_imx_probe_dt(sport, pdev);
if (ret > 0)
serial_imx_probe_pdata(sport, pdev);
else if (ret < 0)
return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
rxirq = platform_get_irq(pdev, 0);
txirq = platform_get_irq(pdev, 1);
rtsirq = platform_get_irq(pdev, 2);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
sport->port.membase = base;
sport->port.type = PORT_IMX,
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.ops = &imx_pops;
sport->port.rs485_config = imx_rs485_config;
sport->port.rs485.flags =
SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | SER_RS485_RTS_AFTER_SEND;
sport->port.flags = UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);
dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
return ret;
}
sport->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(sport->clk_per)) {
ret = PTR_ERR(sport->clk_per);
dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
return ret;
}
sport->port.uartclk = clk_get_rate(sport->clk_per);
if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {
ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);
if (ret < 0) {
dev_err(&pdev->dev, "clk_set_rate() failed\n");
return ret;
}
}
sport->port.uartclk = clk_get_rate(sport->clk_per);
/*
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
* chips only have one interrupt.
*/
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
dev_name(&pdev->dev), sport);
if (ret)
return ret;
ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
dev_name(&pdev->dev), sport);
if (ret)
return ret;
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
dev_name(&pdev->dev), sport);
if (ret)
return ret;
}
imx_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
return uart_add_one_port(&imx_reg, &sport->port);
}
static int serial_imx_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
return uart_remove_one_port(&imx_reg, &sport->port);
}
static struct platform_driver serial_imx_driver = {
.probe = serial_imx_probe,
.remove = serial_imx_remove,
.suspend = serial_imx_suspend,
.resume = serial_imx_resume,
.id_table = imx_uart_devtype,
.driver = {
.name = "imx-uart",
.of_match_table = imx_uart_dt_ids,
},
};
static int __init imx_serial_init(void)
{
int ret = uart_register_driver(&imx_reg);
if (ret)
return ret;
ret = platform_driver_register(&serial_imx_driver);
if (ret != 0)
uart_unregister_driver(&imx_reg);
return ret;
}
static void __exit imx_serial_exit(void)
{
platform_driver_unregister(&serial_imx_driver);
uart_unregister_driver(&imx_reg);
}
module_init(imx_serial_init);
module_exit(imx_serial_exit);
MODULE_AUTHOR("Sascha Hauer");
MODULE_DESCRIPTION("IMX generic serial port driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:imx-uart");