色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

linux的uart驅動示例剖析

嵌入式小生 ? 來源:嵌入式小生 ? 2023-11-09 14:27 ? 次閱讀

linux源碼相關文件:

serial-core.c

include/linux/serial_core.h

unsetunset

一、底層串行硬件驅動程序unset

底層串行硬件的驅動程序負責向serial核心驅動程序提供由struct uart_port定義的端口信息和一組由struct uart_ops定義的控制方法,底層驅動程序還負責處理端口的中斷,并提供對控制臺的支持。

unsetunset

二、Console支持unset

serial核心提供了一些助手函數:

uart_get_console()識別正確的端口結構。

uart_parse_options()解析命令行參數

uart_console_write()用于執行逐字符寫入,將換行符轉換為CRLF序列。在驅動程序編寫的時候建議使用此函數,而不是實現新的寫入接口

unsetunset

三、鎖支持

unset

底層硬件驅動程序負責使用port->lock執行必要的鎖定。支持兩把鎖:一個是端口自旋鎖,另一個是overall信號量。從uart核心驅動程序的角度來看,port->lock用于鎖定以下的數據:

port->mctrl
port->icount
port->state->xmit.head(circ_buf->head)
port->state->xmit.tail(circ_buf->tail)



底層驅動程序可以自由地使用該鎖來實現額外的鎖定,port_mutex互斥量用于防止在不適當的時間添加、刪除或重新配置端口。

unsetunset

四、核心數據結構unset



1、struct uart_driver


struct uart_driver結構表示具體UART驅動。該結構定義如下(/include/linux/serial_core.h):

structuart_driver{
structmodule*owner;//驅動模塊的擁有者
constchar*driver_name;//驅動名稱
constchar*dev_name;//設備名稱
intmajor;//主設備號
intminor;//從設備號
intnr;
structconsole*cons;//console

/*
*theseareprivate;thelowleveldrivershouldnot
*touchthese;theyshouldbeinitialisedtoNULL
*/
structuart_state*state;//uart狀態
structtty_driver*tty_driver;//描述ttydriver
};



2、struct uart_port


struct uart_port表示一個具體的port,該結構定義如下(include/linux/serial_core.h):

structuart_port{
spinlock_tlock;/*port鎖*/
unsignedlongiobase;/*輸入/輸出地址*/
unsignedchar__iomem*membase;/*read/write[bwl]*/
unsignedint(*serial_in)(structuart_port*,int);
void(*serial_out)(structuart_port*,int,int);
void(*set_termios)(structuart_port*,
structktermios*new,
structktermios*old);
void(*set_mctrl)(structuart_port*,unsignedint);
int(*startup)(structuart_port*port);
void(*shutdown)(structuart_port*port);
void(*throttle)(structuart_port*port);
void(*unthrottle)(structuart_port*port);
int(*handle_irq)(structuart_port*);
void(*pm)(structuart_port*,unsignedintstate,
unsignedintold);
void(*handle_break)(structuart_port*);
int(*rs485_config)(structuart_port*,
structserial_rs485*rs485);
unsignedintirq;/*irqnumber*/
unsignedlongirqflags;/*irqflags*/
unsignedintuartclk;/*baseuartclock*/
unsignedintfifosize;/*txfifosize*/
unsignedcharx_char;/*xon/xoffchar*/
unsignedcharregshift;/*regoffsetshift*/
unsignedchariotype;/*ioaccessstyle*/
unsignedcharunused1;

unsignedintread_status_mask;/*driverspecific*/
unsignedintignore_status_mask;/*driverspecific*/
structuart_state*state;/*指向父狀態的指針*/
structuart_icounticount;/*通信信息*/

structconsole*cons;/*structconsole,ifany*/
#ifdefined(CONFIG_SERIAL_CORE_CONSOLE)||defined(SUPPORT_SYSRQ)
unsignedlongsysrq;/*sysrqtimeout*/
#endif

/*flagsmustbeupdatedwhileholdingportmutex*/
upf_tflags;

#if__UPF_CHANGE_MASK>ASYNC_FLAGS
#errorChangemasknotequivalenttouserspace-visiblebitdefines
#endif

/*
*Mustholdtermios_rwsem,portmutexandportlocktochange;
*canholdanyonelocktoread.
*/
upstat_tstatus;

inthw_stopped;/*sw-assistedCTSflowstate*/
unsignedintmctrl;/*當前調制解調器CTRL設置*/
unsignedinttimeout;/*character-basedtimeout*/
unsignedinttype;/*port類型*/
conststructuart_ops*ops;
unsignedintcustom_divisor;
unsignedintline;/*port索引號*/
unsignedintminor;
resource_size_tmapbase;/*用于ioremap*/
resource_size_tmapsize;
structdevice*dev;/*父device*/
unsignedcharhub6;/*應該在8250驅動程序中使用*/
unsignedcharsuspended;
unsignedcharirq_wake;
unsignedcharunused[2];
structattribute_group*attr_group;/*port特殊的屬性*/
conststructattribute_group**tty_groups;/*所有的屬性(僅限于serialcore使用)*/
structserial_rs485rs485;
void*private_data;/*通用platformdata指針*/
};

3、struct uart_ops

struct uart_ops用于描述serial核心驅動程序之間的接口,實現如下:

structuart_ops{
unsignedint(*tx_empty)(structuart_port*);
void(*set_mctrl)(structuart_port*,unsignedintmctrl);
unsignedint(*get_mctrl)(structuart_port*);
void(*stop_tx)(structuart_port*);
void(*start_tx)(structuart_port*);
void(*throttle)(structuart_port*);
void(*unthrottle)(structuart_port*);
void(*send_xchar)(structuart_port*,charch);
void(*stop_rx)(structuart_port*);
void(*start_rx)(structuart_port*);
void(*enable_ms)(structuart_port*);
void(*break_ctl)(structuart_port*,intctl);
int(*startup)(structuart_port*);
void(*shutdown)(structuart_port*);
void(*flush_buffer)(structuart_port*);
void(*set_termios)(structuart_port*,structktermios*new,conststructktermios*old);
void(*set_ldisc)(structuart_port*,structktermios*);
void(*pm)(structuart_port*,unsignedintstate,unsignedintoldstate);
constchar*(*type)(structuart_port*);
void(*release_port)(structuart_port*);
int(*request_port)(structuart_port*);
void(*config_port)(structuart_port*,int);
int(*verify_port)(structuart_port*,structserial_struct*);
int(*ioctl)(structuart_port*,unsignedint,unsignedlong);
#ifdefCONFIG_CONSOLE_POLL;
int(*poll_init)(structuart_port*);
void(*poll_put_char)(structuart_port*,unsignedchar);
int(*poll_get_char)(structuart_port*);
#endif;
};



tx_empty:此函數用于測試端口的發送FIFO和移位器是否為空,如果是空的,這個函數應該返回TIOCSER_TEMT,否則返回0。如果端口不支持此操作,則應該返回TIOCSER_TEMT。沒有鎖定。中斷:依賴于調用者,這個調用不能導致睡眠。



set_mctrl:此功能將端口的調制解調器控制線設置為mcctrl所描述的狀態。mctrl支持的參數是:



TIOCM_RTS表示RTS信號,TIOCM_DTR表示DTR信號,TIOCM_OUT1表示OUT1信號,TIOCM_OUT2表示OUT2信號,TIOCM_LOOP表示設置端口為環回模式。如果設置了合適的位,則信號應被驅動激活;如果該位被清除,則信號應被驅動為非激活狀態。



在port->lock獲取的情況下鎖定。禁用本地中斷。該調用不能睡眠。

get_mctrl:返回端口調制解調器控制輸入的當前狀態。輸出的狀態不應該被返回,因為內核會跟蹤它們的狀態。狀態信息應包括:TIOCM_CAR表示DCD的信號狀態。TIOCM_CTS表示CTS的信號狀態,TIOCM_DSR表示DSR信號狀態,TIOCM_RI表示RI信號狀態。



如果設置該位,則將信號驅動為激活狀態,如果端口不支持CTS, DCD或DSR,驅動程序應該表明信號是永久激活的。如果RI不可用,信號不應該顯示為激活狀態。在port->lock獲取的情況下鎖定。禁用本地中斷。調用該函數必須不能睡眠。


stop_tx:停止傳輸字符。這可能是由于CTS線路沒有激活,或者tty層表明由于XOFF字符而停止傳輸。驅動程序應該盡快停止傳輸字符。在port->lock獲取的情況下鎖定,禁用本地中斷,該調用不能睡眠。



start_tx:開始傳輸字符。在port->lock獲取的情況下鎖定,禁用本地中斷,該調用不能睡眠。



throttle:通知串行驅動程序,line規則的輸入緩沖區已接近滿,并且它應該以某種方式發出信號,不應該再向串行端口發送字符,只有在啟用了硬件輔助流控制時才會調用該函數。由tty層通過unthrottle()和termios修改序列化鎖定。


unthrottle:通知串行驅動程序,字符現在可以發送到串行端口,而不必擔心超出line規則的輸入緩沖區。只有在啟用了硬件輔助流控制時才會調用該函數。


send_xchar:發送一個高優先級字符,即使端口停止。這是用來實現XON/XOFF流量控制和tcflow()。如果串行驅動程序沒有實現這個函數,那么tty內核將把字符附加到循環緩沖區,然后調用start_tx()/stop_tx()來清除數據。如果ch == '0' (__DISABLED_CHAR)則不傳輸。不需要鎖定,中斷情況依賴于調用者。


stop_rx:停止接收字符,該端口正在關閉中。在port->lock獲取的情況下鎖定。禁用本地中斷,該調用必須不能睡眠。


start_rx:開始接收字符。在port->lock獲取的情況下鎖定。禁用本地中斷,該調用必須不能睡眠。


enable_ms:啟用modem狀態中斷。這個方法可以被多次調用。在調用shutdown()方法時,應該禁用調制解調器狀態中斷。在``port->lock```獲取的情況下鎖定。禁用本地中斷,該調用必須不能睡眠。


break_ctl:控制中斷信號的傳輸,如果ctl不為零,則應發送斷路信號。當ctl=0進行另一個調用時,信號應該終止。調用者持有tty_port->mutex鎖定。


startup:獲取中斷資源并初始化所有底層驅動程序狀態。啟用接收端口。該函數不應該激活RTS或DTR;這將通過單獨調用set_mctrl()來完成。此方法僅在端口初始化打開時調用。


shutdown:禁用端口,禁用可能生效的中斷條件,并釋放中斷資源。該函數不應該禁用RTS或DTR;這已經通過對set_mctrl()的單獨調用完成。一旦調用完成,驅動程序不能訪問port->state。此方法僅在該端口沒有更多用戶時調用。


flush_buffer:刷新所有寫緩沖區,重置所有DMA狀態,并停止所有正在進行的DMA傳輸。當清除了port->state->xmit循環緩沖區時,將調用該函數。在port->lock獲取的情況下鎖定。禁用本地中斷,該調用必須不能睡眠。


set_termios:更改端口參數,包括字長,奇偶校驗,停止位。更新port->read_status_mask和port->ignore_status_mask,以指示接收的事件類型。


set_ldisc:描述線變更通知。在tty_port->mutex持有的情況下調用鎖定。

pm:在指定端口上執行電源管理相關活動。state指示由enum uart_pm_state定義的狀態,oldstate 指示前一個狀態。該函數不應該用來獲取任何資源。該函數將在端口最初打開并最終關閉時被調用,除非端口也是系統控制臺。即使沒有設置CONFIG_PM,也會發生這種情況。無鎖定的情況下調用。


type:返回一個指向描述指定端口的字符串常量的指針,或者返回NULL,在這種情況下,字符串'unknown'被替換。無鎖定,中斷設置依賴于調用者。


release_port:釋放端口當前正在使用的所有內存和IO區域資源。沒有鎖定,中斷設置依賴于調用者。


request_port:請求端口所需的任何內存和IO區域資源。如果任何一個請求失敗,當這個函數返回時不應該注冊任何資源,并且它應該在失敗時返回-EBUSY。無鎖定,中斷設置依賴于調用者。


config_port:執行port所需的自動配置步驟。type包含所需配置的位掩碼。UART_CONFIG_TYPE表示該端口需要檢測和識別。port->type應該設置為找到的類型,如果沒有檢測到端口,則設置為PORT_UNKNOWN。


UART_CONFIG_IRQ表示中斷信號的自動配置,應該使用標準內核自動探測技術進行探測。在端口有內部硬連線中斷的平臺上(例如,片上系統實現),這是不必要的。無鎖定,中斷設置依賴于調用者。


verify_port:驗證serinfo中包含的新串行端口信息是否適合此端口類型。無鎖定,中斷設置依賴于調用者。


ioctl:執行任何端口特定的ioctl。IOCTL命令必須使用中找到的標準編號系統來定義。無鎖定,中斷設置依賴于調用者。

unsetunset
四、常用API總結unset

//調度寫處理
voiduart_write_wakeup(structuart_port*port)

//更新每個端口幀定時信息
voiduart_update_timeout(structuart_port*port,unsignedintcflag,unsignedintbaud)

//返回特定端口的波特率
unsignedintuart_get_baud_rate(structuart_port*port,structktermios*termios,conststructktermios*old,unsignedintmin,unsignedintmax)

//返回uart的時鐘分頻系數
unsignedintuart_get_divisor(structuart_port*port,unsignedintbaud)

//獲取行狀態寄存器信息
intuart_get_lsr_info(structtty_struct*tty,structuart_state*state,unsignedint__user*value)

//將控制臺(console)消息寫入串口
voiduart_console_write(structuart_port*port,constchar*s,unsignedintcount,void(*putchar)(structuart_port*,unsignedchar))

//獲取控制臺(console)的端口
structuart_port*uart_get_console(structuart_port*ports,intnr,structconsole*co)

//解析earlycon選項參數
intuart_parse_earlycon(char*p,unsignedchar*iotype,resource_size_t*addr,char**options)

//解析串口baud/parity/bits/flow控制
voiduart_parse_options(constchar*options,int*baud,int*parity,int*bits,int*flow)

//設置串口控制臺參數
intuart_set_options(structuart_port*port,structconsole*co,intbaud,intparity,intbits,intflow)


//----------------------------Port/driver注冊和移除----------------------------//

//向uart核心層注冊一個驅動程序
intuart_register_driver(structuart_driver*drv)

//從uart核心層移除驅動程序。
//如果底層驅動程序在uart_add_one_port()中注冊了端口,則必須通過uart_remove_one_port()刪除已經注冊的端口。
voiduart_unregister_driver(structuart_driver*drv)
intuart_add_one_port(structuart_driver*reg,structuart_port*port);
voiduart_remove_one_port(structuart_driver*reg,structuart_port*port);

//判斷兩個端口是否相等。
//此函數可用于確定兩個uart_port結構是否描述相同的端口。
booluart_match_port(conststructuart_port*port1,conststructuart_port*port2)


//電源管理
intuart_suspend_port(structuart_driver*reg,structuart_port*port);
intuart_resume_port(structuart_driver*reg,structuart_port*port);

底層驅動的助手函數

voiduart_handle_dcd_change(structuart_port*uport,boolactive)
voiduart_handle_cts_change(structuart_port*uport,boolactive)
voiduart_insert_char(structuart_port*port,unsignedintstatus,unsignedintoverrun,u8ch,u8flag);
voiduart_xchar_out(structuart_port*uport,intoffset);

booluart_try_toggle_sysrq(structuart_port*port,u8ch)
uart_port_tx_limited(port,ch,count,tx_ready,put_char,tx_done)

//uart端口的發送助手函數
uart_port_tx(port,ch,tx_ready,put_char)

unsetunset
五、uart驅動示例剖析unset


1、原廠設計的uart驅動


有些芯片原廠會針對自家的芯片設計開發出uart驅動,例如nxp的imx6ull,針對該系列的SOC,NXP原廠設計出了一個名為imx.c的驅動,位于/drivers/tty/serial目錄中。該驅動以平臺驅動為框架設計:

staticstructplatform_driverserial_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,
},
};

設備樹匹配表是:

b7638a92-7eae-11ee-939d-92fbcf53809c.png

.probe對應的serial_imx_probe()實現如下:

staticintserial_imx_probe(structplatform_device*pdev)
{
structimx_port*sport;
void__iomem*base;
intret=0;
structresource*res;
inttxirq,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);
elseif(retdev,res);
if(IS_ERR(base))
returnPTR_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;
sport->port.flags=UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function=imx_timeout;
sport->timer.data=(unsignedlong)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,"failedtogetipgclk:%d
",ret);
returnret;
}

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,"failedtogetperclk:%d
",ret);
returnret;
}

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(retdev,"clk_set_rate()failed
");
returnret;
}
}
sport->port.uartclk=clk_get_rate(sport->clk_per);

/*
*AllocatetheIRQ(s)i.MX1hasthreeinterruptswhereaslater
*chipsonlyhaveoneinterrupt.
*/
if(txirq>0){
ret=devm_request_irq(&pdev->dev,rxirq,imx_rxint,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;

ret=devm_request_irq(&pdev->dev,txirq,imx_txint,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
}else{
ret=devm_request_irq(&pdev->dev,rxirq,imx_int,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
}

imx_ports[sport->port.line]=sport;

platform_set_drvdata(pdev,sport);

returnuart_add_one_port(&imx_reg,&sport->port);
}


從上述代碼可知,依然是常規驅動程序設計的思路。在.probe中進行的步驟有:

(1)為描述imx的uart的struct imx_port分配內存。

(2)解析設備樹中信息,獲取resource。

(3)獲取中斷相關配置參數。

(4)初始化struct imx_port中的組成元素。

(5)uart時鐘參數配置和使能。

(6)使用uart_add_one_port()向uart_driver添加uart_port,在這里就是向imx_reg添加sport->port。imx_reg是struct uart_driver的具體實例;sport->port是struct imx_port中關聯的struct uart_port。


2、8250標準uart驅動


本小節中的uart驅動指單純針對一款SOC設計的驅動,該部分驅動一般由芯片原廠提供。除此之外,有些SOC設計公司會基于標準(例如16550A)的uart通信機制設計UART硬件部分。從而軟件驅動上也能使用標準的uart驅動進行通信。例如:8250。linux內核中,8250串口通用驅動的主要文件如下:

drivers/tty/serial/8250/8250_core.c :8250串口驅動核心。

drivers/tty/serial/8250/8250_dw.c :Synopsis DesignWare 8250串口驅動。

drivers/tty/serial/8250/8250_dma.c :8250串口DMA驅動。

drivers/tty/serial/8250/8250_port.c :8250串口端口操作。

drivers/tty/serial/8250/8250_early.c :8250串口early console驅動。


例如rk3568,對于rk3568關于uart的設備,是使用設備樹進行描述:

b76fe71a-7eae-11ee-939d-92fbcf53809c.png


主機側對應的驅動程序則是Synopsis DesignWare 8250串口驅動,由drivers/tty/serial/8250/8250_dw.c文件描述。在該驅動程序中,使用platform驅動方案實現驅動的設計:

b77c755c-7eae-11ee-939d-92fbcf53809c.png


當設備和驅動匹配后,會執行dw8250_probe()函數,該函數實現如下:

staticintdw8250_probe(structplatform_device*pdev)
{
structuart_8250_portuart={},*up=&uart;
structuart_port*p=&up->port;
structdevice*dev=&pdev->dev;
structdw8250_data*data;
structresource*regs;
intirq;
interr;
u32val;

regs=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(!regs)
returndev_err_probe(dev,-EINVAL,"noregistersdefined
");

irq=platform_get_irq_optional(pdev,0);
/*nointerrupt->fallbacktopolling*/
if(irq==-ENXIO)
irq=0;
if(irqlock);
p->mapbase=regs->start;
p->irq=irq;
p->handle_irq=dw8250_handle_irq;
p->pm=dw8250_do_pm;
p->type=PORT_8250;
p->flags=UPF_SHARE_IRQ|UPF_FIXED_PORT;
p->dev=dev;
p->iotype=UPIO_MEM;
p->serial_in=dw8250_serial_in;
p->serial_out=dw8250_serial_out;
p->set_ldisc=dw8250_set_ldisc;
p->set_termios=dw8250_set_termios;

p->membase=devm_ioremap(dev,regs->start,resource_size(regs));
if(!p->membase)
return-ENOMEM;

data=devm_kzalloc(dev,sizeof(*data),GFP_KERNEL);
if(!data)
return-ENOMEM;

data->data.dma.fn=dw8250_fallback_dma_filter;
data->pdata=device_get_match_data(p->dev);
p->private_data=&data->data;

data->uart_16550_compatible=device_property_read_bool(dev,
"snps,uart-16550-compatible");

err=device_property_read_u32(dev,"reg-shift",&val);
if(!err)
p->regshift=val;

err=device_property_read_u32(dev,"reg-io-width",&val);
if(!err&&val==4){
p->iotype=UPIO_MEM32;
p->serial_in=dw8250_serial_in32;
p->serial_out=dw8250_serial_out32;
}

if(device_property_read_bool(dev,"dcd-override")){
/*AlwaysreportDCDasactive*/
data->msr_mask_on|=UART_MSR_DCD;
data->msr_mask_off|=UART_MSR_DDCD;
}

if(device_property_read_bool(dev,"dsr-override")){
/*AlwaysreportDSRasactive*/
data->msr_mask_on|=UART_MSR_DSR;
data->msr_mask_off|=UART_MSR_DDSR;
}

if(device_property_read_bool(dev,"cts-override")){
/*AlwaysreportCTSasactive*/
data->msr_mask_on|=UART_MSR_CTS;
data->msr_mask_off|=UART_MSR_DCTS;
}

if(device_property_read_bool(dev,"ri-override")){
/*AlwaysreportRingindicatorasinactive*/
data->msr_mask_off|=UART_MSR_RI;
data->msr_mask_off|=UART_MSR_TERI;
}

/*Alwaysaskforfixedclockratefromaproperty.*/
device_property_read_u32(dev,"clock-frequency",&p->uartclk);

/*Ifthereisseparatebaudclk,gettheratefromit.*/
data->clk=devm_clk_get_optional(dev,"baudclk");
if(data->clk==NULL)
data->clk=devm_clk_get_optional(dev,NULL);
if(IS_ERR(data->clk))
returnPTR_ERR(data->clk);

INIT_WORK(&data->clk_work,dw8250_clk_work_cb);
data->clk_notifier.notifier_call=dw8250_clk_notifier_cb;

err=clk_prepare_enable(data->clk);
if(err)
returndev_err_probe(dev,err,"couldnotenableoptionalbaudclk
");

err=devm_add_action_or_reset(dev,dw8250_clk_disable_unprepare,data->clk);
if(err)
returnerr;

if(data->clk)
p->uartclk=clk_get_rate(data->clk);

/*Ifnoclockrateisdefined,fail.*/
if(!p->uartclk)
returndev_err_probe(dev,-EINVAL,"clockratenotdefined
");

data->pclk=devm_clk_get_optional(dev,"apb_pclk");
if(IS_ERR(data->pclk))
returnPTR_ERR(data->pclk);

err=clk_prepare_enable(data->pclk);
if(err)
returndev_err_probe(dev,err,"couldnotenableapb_pclk
");

err=devm_add_action_or_reset(dev,dw8250_clk_disable_unprepare,data->pclk);
if(err)
returnerr;

data->rst=devm_reset_control_get_optional_exclusive(dev,NULL);
if(IS_ERR(data->rst))
returnPTR_ERR(data->rst);

reset_control_deassert(data->rst);

err=devm_add_action_or_reset(dev,dw8250_reset_control_assert,data->rst);
if(err)
returnerr;

dw8250_quirks(p,data);

/*IftheBusyFunctionalityisnotimplemented,don'thandleit*/
if(data->uart_16550_compatible)
p->handle_irq=NULL;

if(!data->skip_autocfg)
dw8250_setup_port(p);

/*Ifwehaveavalidfifosize,tryhookingupDMA*/
if(p->fifosize){
data->data.dma.rxconf.src_maxburst=p->fifosize/4;
data->data.dma.txconf.dst_maxburst=p->fifosize/4;
up->dma=&data->data.dma;
}

data->data.line=serial8250_register_8250_port(up);
if(data->data.linedata.line;

/*
*Someplatformsmayprovideareferenceclocksharedbetweenseveral
*devices.Inthiscaseanyclockstatechangemustbeknowntothe
*UARTportatleastpostfactum.
*/
if(data->clk){
err=clk_notifier_register(data->clk,&data->clk_notifier);
if(err)
returndev_err_probe(dev,err,"Failedtosettheclocknotifier
");
queue_work(system_unbound_wq,&data->clk_work);
}

platform_set_drvdata(pdev,data);

pm_runtime_set_active(dev);
pm_runtime_enable(dev);

return0;
}

在上述probe中,主要執行的操作如下:

(1)從platform_device中提取中struct resource。

(2)設置struct uart_port中組成元素的初始化參數值和一些必要的callback。

(3)讀取dev中的參數值。

(4)設置時鐘。

(5)調用serial8250_register_8250_port()注冊8250端口。

unsetunset

六、總結unset

1、本文描述了linux下的uart框架,因uart隸屬于tty,故而芯片原廠一般會將與uart相關的驅動放置于/drivers/tty/serial目錄中。

2、關于linux下的uart驅動,芯片原廠一般都會去實現,而不用再去開發這一層的驅動。但是uart驅動框架還是值得去了解和學習。本文總結了一些常用的API(以具體linux版本為主),也簡要分析了兩款芯片的uart驅動程序。

3、基于linux,作為uart的使用者,只需要通過設備樹傳遞uart相關的參數(假如linux支持設備樹),這時候uart驅動程序會自動加載運行,向用戶空間暴露出設備節點,這時候用戶空間就可以方便的使用uart進行通信了。

4、芯片原廠設計的驅動,往往具有兼容性,支持多款同系列或者同類型的芯片!

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11339

    瀏覽量

    210119
  • 驅動程序
    +關注

    關注

    19

    文章

    848

    瀏覽量

    48161
  • 源碼
    +關注

    關注

    8

    文章

    652

    瀏覽量

    29358
  • 函數
    +關注

    關注

    3

    文章

    4345

    瀏覽量

    62867

原文標題:玩玩linux的uart,爽歪歪!

文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    UART串口通信示例:開啟通信寶藏之門的定位模組LuatOS入門

    今天我們要講解的是UART串口通信示例,關于Air201資產定位模組LuatOS入門,我會從搭建環境、代碼燒錄、示例效果等來細說:
    的頭像 發表于 11-03 19:55 ?1005次閱讀
    <b class='flag-5'>UART</b>串口通信<b class='flag-5'>示例</b>:開啟通信寶藏之門的定位模組LuatOS入門

    RTOS版本用示例提供的UART驅動,編譯失敗提示找不到函數uart_div_modify(),是怎么回事?

    RTOS版本用示例提供的UART驅動,編譯失敗提示找不到函數uart_div_modify(),是怎么回事? 搜索了整個工程也沒有找到這個函數的定義或者聲明!
    發表于 07-22 08:17

    linux內核深度剖析,另附有光盤資料

    linux內核深度剖析,對于想學linux內核的人來說,絕對值得一看,另附有光盤資料。
    發表于 01-15 21:25

    淺析linux UART驅動和tty架構

    關于linux UART驅動和tty架構的理解
    發表于 07-03 09:55

    Linux下的UART驅動框架詳解

    Linux下的UART驅動框架
    發表于 12-22 07:18

    全面剖析嵌入式Linux開發

    嵌入式Linux基礎教程(第2版) 編輯推薦  嵌入式Linux權威著作   Amazon全五星評價  全面剖析嵌入式Linux開發,揭示大量技術內幕基本信息原書名:Embedded
    發表于 11-05 08:35

    AN-1282:在示例應用中剖析ADuCM350電源電流

    AN-1282:在示例應用中剖析ADuCM350電源電流
    發表于 04-21 20:55 ?3次下載
    AN-1282:在<b class='flag-5'>示例</b>應用中<b class='flag-5'>剖析</b>ADuCM350電源電流

    嵌入式Linux UART

    文章目錄前言串口連接串口測試C代碼微信公眾號前言這是前篇:嵌入式Linux i.MX開發板嵌入式Linux NFS嵌入式Linux 交叉編譯工具鏈嵌入式Linux LED GPIO嵌入
    發表于 11-01 16:26 ?8次下載
    嵌入式<b class='flag-5'>Linux</b> <b class='flag-5'>UART</b>

    STM32 UART串口驅動程序

    文章原始地址: http://www.sheeptech.cc/?id=14示例1.通過UART1進行數據發送 UART 1 的初始化/*** @brief UART
    發表于 12-23 20:03 ?0次下載
    STM32 <b class='flag-5'>UART</b>串口<b class='flag-5'>驅動</b>程序

    WK系列SPI擴展UART驅動移植參考

    ,WK2132能實現擴展 2 路 UART。目前這幾款芯片使用的都是相同的 linux 驅動。WK 系列擴展的子通道的 UART 具備如下功能特點:   每個子通道
    發表于 04-18 09:10 ?14次下載

    MPC82G516 MCU的串行UART中斷示例代碼

    MPC82G516 MCU的串行UART中斷示例代碼
    發表于 06-30 17:32 ?0次下載
    MPC82G516 MCU的串行<b class='flag-5'>UART</b>中斷<b class='flag-5'>示例</b>代碼

    MPC82G516 MCU的串行UART示例代碼

    MPC82G516 MCU的串行UART示例代碼
    發表于 06-30 17:24 ?0次下載
    MPC82G516 MCU的串行<b class='flag-5'>UART</b><b class='flag-5'>示例</b>代碼

    Linux UART開發指南

    介紹 Linux 內核中 UART 驅動的接口及使用方法,為 UART 設備的使用者提供參考。
    的頭像 發表于 03-06 10:26 ?1348次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>UART</b>開發指南

    Zynq UltraScale+RFSoC的AXI CDMA Linux用戶空間示例

    本文將為您演示如何創建?AXI CDMA Linux?用戶空間示例應用
    的頭像 發表于 07-07 14:15 ?853次閱讀
    Zynq UltraScale+RFSoC的AXI CDMA <b class='flag-5'>Linux</b>用戶空間<b class='flag-5'>示例</b>

    全志T507 uart驅動使用文檔

    本文檔介紹Linux內核中UART驅動的接口及使用方法為UART設備的使用者提供參考。適用于linux4.9內容資料來源:https://w
    發表于 08-14 15:10 ?0次下載
    主站蜘蛛池模板: 美女张开腿让男生桶动态图| 国产精品九九久久| 亚州AV人片一区二区三区99久 | 欧美亚洲高清国产| 久久天天婷婷五月俺也去| 国产在线一卡二卡| 国产亚洲日韩另类在线观看| 国产精品一区二区人妻无码| 国产盗摄TP摄像头偷窥| 高清国产激情视频在线观看| 成人片免费看| 大香交伊人| 国产av久久免费观看| 国产成人a在一区线观看高清| 大学生高潮无套内谢视频 | 欧美国产日韩久久久| 免费在线观看一区| 暖暖视频在线高清播放| 欧美午夜精品A片一区二区HD| 女女破视频在线观看| 欧美含羞草免费观看全部完| 皮皮在线精品亚洲| 日本一区精品久久久久影院| 日韩 亚洲 欧美 中文 高清| 色噜噜狠狠色综合中文字幕| 天天干夜夜叭| 亚洲AV无码国产精品午夜久久 | 国产成人a在一区线观看高清| 国产精品久久久久久免费字体| 国产互换后人妻的疯狂VIDEO| 国产精品日本无码久久一老A| 国产人妻久久久精品麻豆| 国精产品一区二区三区有限公司| 国语精彩对白2021| 久久久久琪琪精品色| 免费播放美女一级毛片| 琪琪see色原网站在线观看| 肉肉描写很细致的黄文| 驯服有夫之妇HD中字日本| 夜夜躁婷婷AV蜜桃视频| 中文字幕无码A片久久|