本系列所有问题都会指定芯片和系统版本,不同芯片和系统处理可能不一致,只能进行参考。
问题: TTY Serial支持带控制引脚的RS485通讯方式。
// 设备树增加tty支持
&uart4 {
//...
rts-gpios = <&gpio1 18 GPIO_ACTIVE_LOW>; //rs458和rts引脚配合使用,rts-gpio对应控制引脚
rs485-rts-delay = <2 2>; //设置rs485切换延时
linux,rs485-enabled-at-boot-time; //开启rs485标志位
};
//应用层控制
#include <asm-generic/ioctls.h>
//应用层配置rs485模式
int rs485_enable(const int fd, const RS485_ENABLE_t enable)
{
struct serial_rs485 rs485conf;
int res;
//read config
res = ioctl(fd, TIOCGRS485, &rs485conf);
if (res < 0) {
perror("Ioctl error on getting 485 configure:");
close(fd);
return res;
}
if (enable) { // Enable rs485 mode
rs485conf.flags |= SER_RS485_ENABLED;
} else { // Disable rs485 mode
rs485conf.flags &= ~(SER_RS485_ENABLED);
}
rs485conf.delay_rts_before_send = 4;
//write config
res = ioctl(fd, TIOCSRS485, &rs485conf);
if (res < 0) {
perror("Ioctl error on setting 485 configure:");
close(fd);
}
return res;
}
问题: I.MX6ull支持双网口模式.
修改双网口主要是设备树里面支持双网口的实现。
# 设备树支持双网口
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_fec1_reset>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
phy-reset-duration = <26>;
phy-supply = <®_peri_3v3>;
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2
&pinctrl_fec2_reset>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-supply = <®_peri_3v3>;
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
phy-reset-duration = <26>;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-id0022.1560";
reg = <0>;
micrel,led-mode = <1>;
clocks = <&clks IMX6UL_CLK_ENET_REF>;
clock-names = "rmii-ref";
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-id0022.1560";
reg = <1>;
micrel,led-mode = <1>;
clocks = <&clks IMX6UL_CLK_ENET2_REF>;
clock-names = "rmii-ref";
};
};
};
# 系统使能eth1
ifconfig eth1 192.168.2.100 netmask 255.255.255.0
ifconfig eth1 up
问题: I.MX6ull解决网口报错问题
[ 28.685938] Micrel KSZ8081 or KSZ8091 20b4000.ethernet-1:00: attached PHY driver (mii_bus:phy_addr=20b4000.ethernet-1:00, irq=POLL)
[ 30.805784] Micrel KSZ8081 or KSZ8091 20b4000.ethernet-1:00: Master/Slave resolution failed, maybe conflicting manual settings?
[ 30.817336] ------------[ cut here ]------------
[ 30.821962] WARNING: CPU: 0 PID: 24 at drivers/net/phy/phy.c:1013 phy_state_machine+0x9c/0x29c
[ 30.830660] Modules linked in:
[ 30.833735] CPU: 0 PID: 24 Comm: kworker/0:2 Not tainted 6.1.36 #11
[ 30.840013] Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[ 30.846202] Workqueue: events_power_efficient phy_state_machine
[ 30.852162] unwind_backtrace from show_stack+0x10/0x14
[ 30.857413] show_stack from dump_stack_lvl+0x40/0x4c
[ 30.862485] dump_stack_lvl from __warn+0x90/0xbc
[ 30.867213] __warn from warn_slowpath_fmt+0xbc/0x1b8
[ 30.872287] warn_slowpath_fmt from phy_state_machine+0x9c/0x29c
[ 30.878322] phy_state_machine from process_one_work+0x1c4/0x3d0
[ 30.884357] process_one_work from worker_thread+0x50/0x50c
[ 30.889949] worker_thread from kthread+0xec/0x11c
[ 30.894763] kthread from ret_from_fork+0x14/0x2c
[ 30.899491] Exception stack(0xa08e1fb0 to 0xa08e1ff8)
[ 30.904554] 1fa0: 00000000 00000000 00000000 00000000
[ 30.912741] 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 30.920927] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 30.927600] ---[ end trace 0000000000000000 ]---
//....
修改方法:
//位置: drivres/net/phy/phy_device.c中的phy_probe函数
//注释如下代码
//not read 1000M register to avoid phy issue.
#if 0
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
phydev->supported))
phydev->is_gigabit_capable = 1;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->supported))
phydev->is_gigabit_capable = 1;
#endif
问题: 硬件在U-Boot中指定I/O上电默认输出的电平。
上电默认电平可以在U-Boot中修改,路径为board/freescale/mx6ullevk/mx6ullevk.c中(如果移植u-boot,放置在指定位置)
int setup_led(void)
{
//请求LED,设置LED电平
gpio_request(IMX_GPIO_NR(1, 3), "led shows");
gpio_direction_output(IMX_GPIO_NR(1, 3), 0);
return 0;
}
int board_late_init(void)
{
#ifdef CONFIG_CMD_BMODE
add_board_boot_modes(board_boot_modes);
#endif
env_set("tee", "no");
#ifdef CONFIG_IMX_OPTEE
env_set("tee", "yes");
#endif
#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
env_set("board_name", "EVK");
if (is_mx6ull_9x9_evk())
env_set("board_rev", "9X9");
else
env_set("board_rev", "14X14");
if (is_cpu_type(MXC_CPU_MX6ULZ)) {
env_set("board_name", "ULZ-EVK");
env_set("usb_net_cmd", "usb start");
}
#endif
setup_lcd();
setup_led();
#ifdef CONFIG_ENV_IS_IN_MMC
board_late_mmc_env_init();
#endif
set_wdog_reset((struct wdog_regs *)WDOG1_BASE_ADDR);
return 0;
}
问题: 在设备树中查询到某节点,状态为okay,且内核自带驱动,如何确定开启配置方法。
这里以key-gpios为例,节点名称如下。
gpio_keys: gpio_keys@0 {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_keys>;
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
key1@1 {
label = "USER-KEY1";
linux,code = <114>;
gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
gpio-key,wakeup;
};
};
第一步,全局检索compatible对应的字符串,查找到驱动地址,确定为drivers/input/keyboard/gpio-keys.c
第二步,查询同目录下的Makefile文件,确定该文件被包含需要满足的条件,确定为宏CONFIG_KEYBOARD_GPIO
第三步, 在menuconfig菜单中检索CONFIG_KEYBOARD_GPIO,查找路径并置位。可以看到路径如下所示。
Device Driver > Input device support > Generic input layer > Keyboards > GPIO buttons
修改保存后,查看内核根目录下是否有CONFIG_KEYBOARD_GPIO,如果存在则表示设置成功。
问题: pinctrl复用被占用如何处理
pinctrl复用被占用一般发生在设备树添加某个模块时,其gpio和设备树中已有的I/O功能冲突,此时注释掉原pinctrl中引脚即可。
可以看到gpio-keys0中增加的pinctrl与原2190000.mmc冲突(对应usdhc1接口),注释掉相应的pinctrl,并关闭模块即可。
usdhc1: mmc@2190000 {
compatible = "fsl,imx6ul-usdhc", "fsl,imx6sx-usdhc";
//......
};
&usdhc1 {
// pinctrl-names = "default", "state_100mhz", "state_200mhz"; //注释掉对应的pinctrl
// pinctrl-0 = <&pinctrl_usdhc1>;
// pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
// pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
wakeup-source;
vmmc-supply = <®_sd1_vmmc>;
status = "disabled"; //关闭usdhc1模块
};
spi支持不同位数的通讯,如8bit, 16bit, 32bit(T113_i, Linux5.4)。
// ================ spi设备驱动文件kernel_bus_spi.c ===============
// 配置spi的位数, 这里以支持32bit为例
struct spi_device *spi;
//1.初始化spi接口
spi = (struct spi_device *)chip->private_data;
spi->bits_per_word = 32;
spi->mode = SPI_MODE_0;
spi_setup(spi);
// ================ spi总线驱动文件 ===================
// 文件: drivers/spi/spi-sunxi.c
// 函数: static int sunxi_spi_probe(struct platform_device *pdev)
- master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
// 配置硬件寄存器,支持相应的位数,此方法修改后,spi只支持指定位数访问,如果spi挂载多个器件,可能访问其它器件不正常。
// 函数: static int sunxi_spi_xfer_setup(struct spi_device *spi, struct spi_transfer *t)
if (spi->bits_per_word) {
spi_bit_transfer_configure(spi->bits_per_word, base_addr);
}
static void spi_bit_transfer_configure(u8 bit_per_word, void __iomem *base_addr)
{
unsigned int rval = 0xA0;
rval |= bit_per_word<<16;
rval |= bit_per_word<<8;
rval |= 0x11;
writel(rval, base_addr + SPI_BACC_REG);
}
Linux串口接收二进制字符0x11,0x13, 0x0d等特殊字符串丢弃和转换,如何处理
一般linux串口编程中,对c_iflag(termios成员变量)这个变量都没有进行有效的设置。在传送ASCII码时没什么问题,但在传送二进制数据时遇到0x0d、0x11和0x13却会被丢掉,通过配置c_iflag可以解决
// 关闭输入的CR转换为NL - 0x0d
// 关闭起动/停止输出控制流起作用 - 0x11 0x13
term.c_cflag |= CLOCAL | CREAD;
term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
term.c_oflag &= ~OPOST;
term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
linux驱动中引脚如何支持应用中切换引脚复用功能(如支持gpio不同复用功能),可以实现类似正常情况作为通讯引脚,休眠或者挂起模式作为输入引脚降低功耗或者实现其它特殊需求。
驱动支持引脚复用功能,通过pinctrl框架可以实现,包含设备树,驱动中共同实现。
//设备树说明
&iomuxc {
//...
pinctrl_gpio_led: gpio-leds {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x17059 //引脚复用功能1
>;
};
pinctrl_led_improve: led-improve {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x40017059 //引脚复用功能2
>;
};
};
gpiosgrp {
compatible = "simple-bus";
//...
usr_led {
//...
pinctrl-names = "default", "improve"; //复用功能别名,用于驱动中根据名称访问
pinctrl-0 = <&pinctrl_gpio_led>; //对应第一种复用功能
pinctrl-1 = <&pinctrl_led_improve>; //对应第二种复用功能
};
};
//驱动中添加处理
//获取"pinctrl-x"属性
chip->led_pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(chip->led_pinctrl)) {
dev_info(&pdev->dev, "[devm_pinctrl_get]failed\n");
return -EIO;
}
chip->pinctrl_state[0] = pinctrl_lookup_state(chip->led_pinctrl, "default");
if (IS_ERR_OR_NULL(chip->pinctrl_state[0])) {
dev_info(&pdev->dev, "[pinctrl_lookup_state]read default failed!\n");
return -EIO;
}
chip->pinctrl_state[1] = pinctrl_lookup_state(chip->led_pinctrl, "improve");
if (IS_ERR_OR_NULL(chip->pinctrl_state[1])) {
dev_info(&pdev->dev, "[pinctrl_lookup_state]read improve failed:%d!\n", IS_ERR_OR_NULL(chip->pinctrl_state[1]));
return -EIO;
}
//修改引脚的pinctrl配置信息
static ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
static struct led_data *chip;
struct platform_device *pdev;
u32 regval;
chip = container_of(attr, struct led_data, led_attr);
pdev = chip->pdev;
if (0 == memcmp(buf, "0", 1)) {
pinctrl_select_state(chip->led_pinctrl, chip->pinctrl_state[0]);
dev_info(&pdev->dev, "led pinctrl 0!\n");
} else if (0 == memcmp(buf, "1", 1)) {
pinctrl_select_state(chip->led_pinctrl, chip->pinctrl_state[1]);
dev_info(&pdev->dev, "led pinctrl 1!\n");
}
else {
dev_info(&pdev->dev, "led store issue!\n");
}
regval = readl(chip->io_reg);
dev_info(&pdev->dev, "regval:0x%x!\n", regval);
return count;
}
ip命令设置can波特率: bitrate error 100.0 too high
# 波特率设置值和时序不匹配,导致计算错误率过高(可能过低或者过高,根据实际情况修改)
# 具体错误原因参考driver/net/can/dev/bit_timing.c中的
ip link set can0 bitrate 1000000
ifconfig使能can: bit‑timing not yet defined
# 未设置bitrate,设置后修改
ip link set can0 bitrate 1000000
ip配置报错: ip: either “dev” is duplicate, or “type” is garbage
# 使用了busybox命令中的ip,不支持can功能
# 参考iproute2移植解决
直接开始下一节说明: 应用设计说明