2014년 3월 31일 월요일

Device Tree 기반의 UART programming

본 고에서는 open source hardware로 유명한 beaglebone black(BBB)을 이용하여 최신 linux kernel(3.10.x)에서 device driver를 어떻게 작성하는지에 관하여 정리해 보고자 한다.
(앞서 게재한 내용이 너무 길어져 6절의 내용을 별도로 정리하였음^^)

목차는 다음과 같다.

1. BBB 개요
2. BBB용 kernel source download & build 절차 소개
3. Device Tree 소개
4. GPIO driver example - LED driver
5. i2c driver 구현 - Wii nunchuk i2c slave driver
6. UART adapter driver 구현
7. SPI driver 구현
8. USB driver 구현
9. SD/MMC driver 구현
References



6. UART adapter driver 구현
이번 절에서는 device tree에서 uart2 controller를 활성화시키기 위한 pinmux 설정 방법을 소개하고, 이어서 나만의 uart2 controller 디바이스 드라이버를 작성하는 방법을 소개해 보고자 한다. 참고로, 그림 6.1은 이번 실험에서 사용되는 male type의 USB-to-Serial 케이블을 보여준다. 이 케이블을 BBB의 확장 핀 헤더에 연결하여 테스트해 볼 것이다.


그림 6.1 USB-to-Serial Cable(Male type)


[참고 사항]
1) 위의 케이블의 serial 출력 전압은 3.3V(반드시 3.3V를 사용하여야 함. 그렇지 않을 경우, processor가 damage를 입을 수 있음)이며, 아래와 같이 모두 3가닥으로 구성되어 있다(male type).
2) GND : 파란색 선, Rx(input) : 녹색 선, Tx(output): 빨간색 선


참고로, 아래 그림 6.2는 serial console용으로 사용되는 FTDI TTL-232 cable(3.3V)의 경우를 보여주고 있는데, BBB P9 확장 헤더에 인접한 6개의 male 핀에 꽂아 사용한다. 자세한 사항은 BBB SRM 문서를 참조하기 바란다.

그림 6.2 FTDI TTL-232 cable(3.3V)

6.1 UART2 용 Pinmux 설정
먼저 시작에 앞서 AM335x MPU TRM(Technical Reference Manual) 문서의 19장(Universal Asynchronous Receiver/Transmitter - UART)을 살펴 보면, AM335x processor 내에는 최대 6개의 UART 모듈이 존재함을 알 수 있다. 이는 am33xx.dtsi 파일을 통해서도 재차 확인이 가능한데, 아래 내용은 am33xx.dtsi 파일 중, uart1 ~ uart6 노드 부분만을 발췌한 것으로, 모든 노드의 상태가 "disabled"되어 있음을 알 수 있다. 참고로, 보통 SoC 용 dtsi에 있는 각 장치 노드의 status 속성 값은 "disabled"로 지정해 주고, 보드용 dts 파일(혹은 하위 SoC 용 dtsi 파일)에서 해당 속성을 enable시켜 주는 것이 일반적인 절차라 할 수 있다.

        <arch/arm/boot/dts/am33xx.dtsi 파일의 내용>
        uart1: serial@44e09000 {
            compatible = "ti,omap3-uart";
            ti,hwmods = "uart1";
            clock-frequency = <48000000>;
            reg = <0x44e09000 0x2000>;
            interrupts = <72>;
            status = "disabled";
        };
        uart2: serial@48022000 {
            compatible = "ti,omap3-uart";
            ti,hwmods = "uart2";
            clock-frequency = <48000000>;
            reg = <0x48022000 0x2000>;
            interrupts = <73>;
            status = "disabled";
        };
        uart3: serial@48024000 {
            compatible = "ti,omap3-uart";
            ti,hwmods = "uart3";
            clock-frequency = <48000000>;
            reg = <0x48024000 0x2000>;
            interrupts = <74>;
            status = "disabled";
        };

        uart4: serial@481a6000 {
            compatible = "ti,omap3-uart";
            ti,hwmods = "uart4";
            clock-frequency = <48000000>;
            reg = <0x481a6000 0x2000>;
            interrupts = <44>;
            status = "disabled";
        };
        uart5: serial@481a8000 {
            compatible = "ti,omap3-uart";
            ti,hwmods = "uart5";
            clock-frequency = <48000000>;
            reg = <0x481a8000 0x2000>;
            interrupts = <45>;
            status = "disabled";
        };
        uart6: serial@481aa000 {
            compatible = "ti,omap3-uart";
            ti,hwmods = "uart6";
            clock-frequency = <48000000>;
            reg = <0x481aa000 0x2000>;
            interrupts = <46>;
            status = "disabled";
        };

이 중, uart1 노드는 아래 am335x-bone-common.dtsi 파일을 보면 알 수 있듯이, status = "okay"로 설정 변경을 해 주고 있는데, 이 시점에 이르러 비로소 uart1 장치가 활성화(실제 드라이버에서 활성화시키기 이전에 dts에서의 활성화를 의미함)되게 된다. 참고로, uart1의 용도는 독자 여러분이 익히 잘 알고 있는 serial console 장치로 이해하면 된다.

<arch/arm/boot/dts/am335x-bone-common.dtsi 파일의 내용>
  ocp: ocp {
        uart1: serial@44e09000 {
            status = "okay";
        };
        ...
  };

한편, 나머지 uart 2 ~ uart6 의 장치는 AM335x 관련 SoC 및 보드 dts[i] 파일 어디에서도 사용하지 않고 있는데, 이는 다시 말해 자신의 보드에 맞게 이들 내용을 활성화시켜 사용할 수 있음을 뜻한다. 본 고에서는 지금부터 BBB P9 확장 헤더를 이용하여, 이들 중 uart2를 활성화시켜 보도록 하겠다.

결론부터 얘기하면, P9 Pin 21과 22를 UART(Tx, Rx) 포트로 사용할 계획인데, 이를 위해서는 아래와 같이 dts 파일을 수정해 주어야 한다.

<arch/arm/boot/dts/am335x-bone-common.dtsi 파일 수정 사항>
am33xx_pinmux: pinmux@44e10800 {
     ...
     uart2_pins: uart2_pins {
          pinctrl-single,pins = <
              0x154 0x1             /* 표시 1 */
             /* (PIN_OUTPUT_PULLDOWN | MUX_MODE1) - spi0_d0.uart2_tx, MODE 1 */
              0x150 0x31            /* 표시 2*/
             /* (PIN_INPUT_PULLUP | MUX_MODE1) - spi0_sclk.uart2_rx, MODE 1 */
          >;
     };
};
...
&uart2 {
    compatible = "mytest1,serial";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart2_pins>;
};

위의  사항을 자세히 분석해 보면, 먼저, uart2_pins 노드를 am33xx_pinmux 노드 내에 추가하여, 새로운 UART를 사용하기 위한 pinmux 설정을 해주게 되며, 다음으로 uart2 노드를 재정의(override)하여, status를 "okay"로 변경(활성화)해 주고, 앞서 정의한 uart2_pins pinmux 노드를 참조하도록 설정해 주고 있음을 알 수 있다. 한가지 추가로 살펴볼 내용은 compatible 속성 값으로 "mytest1,serial"을 지정해 주었는데, 이는 uart controller driver와 연결되는 정보로 기존의 omap uart controller("ti,omap3-uart") 대신 자신만의 독자적인 uart controller driver를 작성하기 위한 것으로 해석하면 된다(6.2절 참조).
이제부터는 위의 수정 사항 관련하여, 이미 I2C 드라이버 구현 절(5절)에서 어느 정도 설명을 하기는 했으나, 다소 설명이 부족했던 관계로, pinctrl-single,pins 속성에 대해 보다 구체적으로 설명해 보기로 하겠다.

<pinctrl-single,pins 레지스터 주소: 0x154, 0x150>
uart2 노드를 활성화시키기에 앞서, pinmux 관련하여 배경이 될만한 몇가지 사실을 집고 넘어가야 할 것 같다. AM335x MPU TRM(참고 문서 [2]) 문서의 Chpater 9 "Control Module" 절은 주로 Pin Mux를 위한 레지스터 설정에 관한 내용을 다루고 있는데, 이 절을 자세히 살펴 보면, 아래 그림 6.3과 같은 정보를 얻을 수 있다. 그림 6.3은 950h와 954h 레지스터 주소가 각각 "conf_spi0_sclk"와 "conf_spi0_d0" 용으로 사용되고 있음을 보여주는데, 이 내용과 BBB SRM 문서(참고 문서 [1])의 P9 확장 핀 헤더 테이블과 대조(아래 그림 6.5 참조)해 보면, 이는 우리가 사용하고자 하는 Pin 21, 22에 해당하는 부분이라는 것을 어렵사리 알아낼 수 있다. 그렇다면, 이들 레지스터의 주솟값 즉, 0x954, 0x950을 pinctrl-single,pins 속성으로 적어주면 될 것인데, 앞서 수정 사항(<표시 1>, <표시 2> 참조)에서는 왜 다른 값인 0x154와 0x150을 적어 주었을까 ?

그림 6.3 950h, 954h 레지스트 - AM335x MPU TRM Chpater 2 내용 중.

그 이유는 아래 내용을 읽어 보면 알 수가 있다. 아래 am33xx_pinmux 노드를 살펴 보면, 주솟값이 44e10800임을 알 수 있는데, 이는 아래 그림 6.4의 내용(AM335x MPU Technical Reference Manual 참조)에서 알 수 있듯이 Control Module(얘가 AM335x processoer에서 Pin Muxing을 담당함)의 레지스터 주소인 0x44E1_0000에 0x800(offset) 값을 더한 값임을 유출할 수 있다(0x800이 담고 있는 의미에 대해서는 별도로 설명하지 않겠음. TRM 문서를 참조하기 바람). 따라서 pinmux의 시작 주소에 0x800이 더해져 있는 상황이므로, pinctrl-single,pins에 열거하는 pin의 레지스터 주소는 원래 값에서 0x800을 뺀 값 즉, 0x154와 0x150으로 지정해 주어야 하는 것이다.


    <arch/arm/boot/dts/am33xx.dtsi 파일 중 am33xx_pinmux 발췌>
    am33xx_pinmux: pinmux@44e10800 {
        compatible = "pinctrl-single";
        reg = <0x44e10800 0x0238>;
        #address-cells = <1>;
        #size-cells = <0>;
        pinctrl-single,register-width = <32>;
        pinctrl-single,function-mask = <0x7f>;
    };

그림 6.4 AM335x MPU Control Module 레지스터 번지



<pinctrl-single,pins 레지스터 값: 0x1, 0x31>
다음으로 확인해 볼 사항은, pin 레지스터에 저장되는 실제 값에 관한 것이다. BBB P9 확장 헤더 중, Pin 21, Pin 22에 serial cable을 연결하여  UART2 controller로  활성화시킬 예정인데, 이와 관련한 P9 핀 배치를 살펴 보면, 그림 6.5와 같다. 그림 6.5를 자세히 살펴 보면, Pin 21과 22가 UART(uart2_txd, uart2_rxd) 포트로 사용되기 위해서는 Mode1으로 설정해 주어야 한다. 이미 앞서서 언급한 바와 같이 Mode0(default)로 설정할 경우에는 spi0_sclk과 spi0_d0로 사용된다(그림 6.3 참조).


                                                      <Mode0>          <Mode1>      <Mode 2>       <Mode 3> ...
그림 6.5 BBB SRM 문서 중, P9 확장 헤더 핀 맵 중 일부 내용

마지막으로 아래 그림 6.6은은 pinmux에 사용되는 pin 레지스터의 설정 값을 계산하는 방법(32bit register 필드 내용)을 보여주는 것으로, 이 내용을 토대로 아래와 같이 레지스터 값을 쉽게 계산해 낼 수가 있을 것이다.

             PIN_OUTPUT_PULLDOWN | MUX_MODE1 => 0x1
                PIN_INPUT_PULLUP | MUX_MODE1             => 0x31


그림 6.6  Pinmux 레지스터 값 계산 방법


아마도 지금까지 설명한 pinctrl-single,pins의 속성 정보 설정 부분이 TI AM335x processor의 Pinmux 설정에서 가장 어려운 부분이 아닐까 싶다. 실제로 이 방법은 같은 계열의 TI processor에서 유사한 형태로 사용되고 있으므로, 한번 제대로 이해하면 크게 도움이 될 것으로 믿는다(필자의견).



6.2 UART2 controller 드라이버 작성
이제부터는 6.1절에서 추가시킨 uart2 controller 노드에 대한 디바이스 드라이버를 작성하는 방법에 관하여 소개해 보도록 하겠다. 새로운 드라이버를 작성하기에 앞서 기존 드라이버의 소스코드를 분석하는 과정은 어찌보면 매우 당연한 과정이라 할 수 있다. 따라서 이번 절에서는 아래의 순서를 따라, 내용을 전개하고자 한다.

1) drivers/tty/serial/omap-serial.c 파일 분석
2) drivers/tty/serial/mytest1-serial.c 생성

[여기서 잠깐 !] Linux Serial Framework에 관해
리눅스 시리얼 드라이버를 구현하기 위해서는 아랙 그림 6.7과 같이, 단순히 "Serial Driver" 만을 구현하면 되는 것이 아니라, 사용자 영역과의 원할한 통신을 위해 "TTY driver" 및 "Line discipline 계층"을 추가로 마련해 놓고 있다. 이중 TTY driver는 궁극적으로는 character 드라이버 형태이므로, 사용자 영역의 프로그램은 "/dev/ttyS0" 등의 device 파일을 통해서 tty 드라이버(결국 serial 드라이버)에 접근하게 된다.




그림 6.7 리눅스 시리얼 드라이버 구조(1)

그림 6.8은 위의 그림 6.7을 보다 상세히 표현한 것으로, (오른 편에) 호스트 PC에서 자주 사용하는 USB-to-Serial 드라이버의 경우까지도 함께 표현해 주고 있다.

<Target 보드 상황 - 왼편>
1) low-level driver : uart controller 드라이버(BBB의 경우는 omap-serial.c 파일)
2) tty driver
3) line discipline driver

<Ubuntu Desktop 상황 - 오른편>
1) usb-to-serial converting driver(+ tty driver) (FTDI usb-to-serial의 경우는 ftdi_sio.c 파일)
2) usb-serial core & USB core
3) line discipline driver

그림 6.8 리눅스 시리얼 드라이버 구조(2)

각각의 계층에 맞는 코드를 구현하기 위해서는 일정한 규칙(API 및 data structure 활용)을 따라야 하는데, 이와 관련해서는 참고 문헌 [17-18]을 참조하기 바란다.

/////////////////////////////////////////////////////////////////////////////////////////////

1) omap-serial.c 파일 분석 /////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Driver for my test UART controller.
 * Based on drivers/serial/8250.c
 * ...
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/serial_reg.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/serial_core.h>
#include <linux/irq.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/serial-omap.h>
...
struct uart_omap_port {
struct uart_port port;
struct uart_omap_dma uart_dma;
struct device *dev;

unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char fcr;
unsigned char efr;
unsigned char dll;
unsigned char dlh;
unsigned char mdr1;
unsigned char scr;

int use_dma;
/*
* Some bits in registers are cleared on a read, so they must
    * be saved whenever the register is read but the bits will not
    * be immediately processed.
*/
unsigned int lsr_break_flag;
unsigned char msr_saved_flags;
char name[20];
unsigned long port_activity;
int context_loss_cnt;
u32 errata;
u8 wakeups_enabled;

int DTR_gpio;
int DTR_inverted;
int DTR_active;


struct pm_qos_request pm_qos_request;
u32 latency;
u32 calc_latency;
struct work_struct qos_work;
struct pinctrl *pins;
struct serial_rs485 rs485;
};

static void serial_omap_stop_tx(struct uart_port *port)
{

...
}
static void serial_omap_stop_rx(struct uart_port *port)
{

...
}
static void transmit_chars(struct uart_omap_port *up, unsigned int lsr)
{

...
}
static void serial_omap_start_tx(struct uart_port *port)
{

...
}
static irqreturn_t serial_omap_irq(int irq, void *dev_id)
{

...
}

static unsigned int serial_omap_tx_empty(struct uart_port *port)
{
...

}
static unsigned int serial_omap_get_mctrl(struct uart_port *port)

{
...

}

static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
...

}

static int serial_omap_startup(struct uart_port *port)
{
...

}

static void serial_omap_shutdown(struct uart_port *port)
{
...

}

static void
serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
...

}
...

static struct uart_ops serial_omap_pops = {
.tx_empty = serial_omap_tx_empty,
.set_mctrl = serial_omap_set_mctrl,
.get_mctrl = serial_omap_get_mctrl,
.stop_tx = serial_omap_stop_tx,
.start_tx = serial_omap_start_tx,
.throttle = serial_omap_throttle,
.unthrottle = serial_omap_unthrottle,
.stop_rx = serial_omap_stop_rx,
.enable_ms = serial_omap_enable_ms,
.break_ctl = serial_omap_break_ctl,
.startup = serial_omap_startup,
.shutdown = serial_omap_shutdown,
.set_termios = serial_omap_set_termios,
.pm = serial_omap_pm,
.set_wake = serial_omap_set_wake,
.type = serial_omap_type,
.release_port = serial_omap_release_port,
.request_port = serial_omap_request_port,
.config_port = serial_omap_config_port,
.verify_port = serial_omap_verify_port,
.ioctl = serial_omap_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_put_char  = serial_omap_poll_put_char,
.poll_get_char  = serial_omap_poll_get_char,
#endif
};



static struct uart_driver serial_omap_reg = {
  .owner = THIS_MODULE,
.driver_name = "OMAP-SERIAL",
.dev_name = OMAP_SERIAL_NAME,
.nr = OMAP_MAX_HSUART_PORTS,
.cons = OMAP_CONSOLE,
};



static int serial_omap_probe(struct platform_device *pdev)
{
  struct uart_omap_port *up;
    struct resource *mem, *irq;
struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
int ret;

if (pdev->dev.of_node)
omap_up_info = of_get_uart_port_info(&pdev->dev);

mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
return -ENODEV;
}

irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
        dev_err(&pdev->dev, "no irq resource?\n");
return -ENODEV;
}

if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
pdev->dev.driver->name)) {
dev_err(&pdev->dev, "memory region already claimed\n");
return -EBUSY;
}

if (gpio_is_valid(omap_up_info->DTR_gpio) &&
       omap_up_info->DTR_present) {
ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial");
if (ret < 0)
return ret;
ret = gpio_direction_output(omap_up_info->DTR_gpio,
                  omap_up_info->DTR_inverted);
if (ret < 0)
return ret;
}

up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
if (!up)
return -ENOMEM;

if (gpio_is_valid(omap_up_info->DTR_gpio) &&
       omap_up_info->DTR_present) {
up->DTR_gpio = omap_up_info->DTR_gpio;
up->DTR_inverted = omap_up_info->DTR_inverted;
} else
up->DTR_gpio = -EINVAL;
up->DTR_active = 0;

up->dev = &pdev->dev;
up->port.dev = &pdev->dev;
up->port.type = PORT_OMAP;
up->port.iotype = UPIO_MEM;
up->port.irq = irq->start;

up->port.regshift = 2;
up->port.fifosize = 64;
up->port.ops = &serial_omap_pops;

if (pdev->dev.of_node)
up->port.line = of_alias_get_id(pdev->dev.of_node, "serial");
else
up->port.line = pdev->id;

if (up->port.line < 0) {
dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
up->port.line);
ret = -ENODEV;
goto err_port_line;
}

up->pins = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(up->pins)) {
dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n",
                   up->port.line, PTR_ERR(up->pins));
up->pins = NULL;
}

sprintf(up->name, "OMAP UART%d", up->port.line);
up->port.mapbase = mem->start;
up->port.membase = devm_ioremap(&pdev->dev, mem->start,
resource_size(mem));
if (!up->port.membase) {
dev_err(&pdev->dev, "can't ioremap UART\n");
ret = -ENOMEM;
goto err_ioremap;
}

up->port.flags = omap_up_info->flags;
up->port.uartclk = omap_up_info->uartclk;
if (!up->port.uartclk) {
up->port.uartclk = DEFAULT_CLK_SPEED;
dev_warn(&pdev->dev, "No clock speed specified: using default:"
"%d\n", DEFAULT_CLK_SPEED);
}

up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
pm_qos_add_request(&up->pm_qos_request,
PM_QOS_CPU_DMA_LATENCY, up->latency);
serial_omap_uart_wq = create_singlethread_workqueue(up->name);
INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);

platform_set_drvdata(pdev, up);
pm_runtime_enable(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev,
omap_up_info->autosuspend_timeout);

pm_runtime_irq_safe(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);

omap_serial_fill_features_erratas(up);

ui[up->port.line] = up;
serial_omap_add_console_port(up);

ret = uart_add_one_port(&serial_omap_reg, &up->port);
if (ret != 0)
goto err_add_port;

pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
return 0;

err_add_port:
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
err_ioremap:
err_port_line:
dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
pdev->id, __func__, ret);
return ret;
}

static int serial_omap_remove(struct platform_device *dev)
{
  struct uart_omap_port *up = platform_get_drvdata(dev);

    pm_runtime_put_sync(up->dev);
  pm_runtime_disable(up->dev);
  uart_remove_one_port(&serial_omap_reg, &up->port);
  pm_qos_remove_request(&up->pm_qos_request);

  return 0;
}



#if defined(CONFIG_OF)
static const struct of_device_id omap_serial_of_match[] = {

    { .compatible = "ti,omap2-uart" },
    { .compatible = "ti,omap3-uart" },
    { .compatible = "ti,omap4-uart" },


    {},
};
MODULE_DEVICE_TABLE(of, omap_serial_of_match);
#endif

static struct platform_driver serial_omap_driver = {
    .probe          = serial_omap_probe,
    .remove        = serial_omap_remove,
    .driver = {
        .name = DRIVER_NAME,
.pm = &serial_omap_dev_pm_ops,
.of_match_table = of_match_ptr(omap_serial_of_match),
},
};

static int __init serial_omap_init(void)
{
  int ret;

  ret = uart_register_driver(&serial_omap_reg);
if (ret != 0)
        return ret;
    ret = platform_driver_register(&serial_omap_driver);
    if (ret != 0)
        uart_unregister_driver(&serial_omap_reg);
    return ret;
}

static void __exit serial_omap_exit(void)
{
    platform_driver_unregister(&serial_omap_driver);
    uart_unregister_driver(&serial_omap_reg);
}

module_init(serial_omap_init);
module_exit(serial_omap_exit);

MODULE_DESCRIPTION("OMAP High Speed UART driver");


/////////////////////////////////////////////////////////////////////////////////////////////

2)mytest-serial.c 생성
<수정 시 주의 사항>
a) /dev/ttyMine0 파일이 생성하도록 수정
b) compatible = "mytest1,serial" 적용
c) baudrate 115200이 되도록 확인
d) ..

/////////////////////////////////////////////////////////////////////////////////////////////
<TODO>


6.3 실험 결과
6.1과 6.2절에서 작업한 내용을 반영한 후, 시스템을 재부팅하여 아래와 같은 실험을 해 보기로 하자.

1) Target 보드에서는 아래 명령을 수행하여 데이터를 전송하도록 함.
                     $ echo "1111111111111111111" > /dev/ttyMine0

2) Ubuntu Desktop에서는 picocom 명령을 실행하여 데이타를 수신(USB-to-Serial)하도록 함. 주의할 사항은 /dev/ttyUSB0(console 용으로 사용)가 아니라, /dev/ttyUSB1으로 지정해야 한다는 점임.
[참고] picocom은 minicom과 유사한 프로그램으로, 설치가 되어 있지 않을 경우, "sudo apt-get install picocom"으로 설치 가능함.

                 $ sudo picocom -b 115200 /dev/ttyUSB1
                     ...
                     Terminal ready
                    1111111111111111111
                                                         1111111111111111111
                                                                                              1111111111111111111
           

이상으로 간단하게 나마, BBB에서 uart2 controller를 사용할 수 있도록 활성화시키고, 이를 사용하는 드라이버를 작성하는 방법에 관하여 소개하였다.


References

<BBB>
1) BeagleBone Black System Reference Manual Revision B, January 20, 2014, Gerald Coley

2) AM335x ARM® CortexTM-A8 Microprocessors(MPUs) Technical Reference Manual, Texas Instruments

3) Getting Started With BeagleBone, by Matt Richardson, Maker Media

4) http://eewiki.net/display/linuxonarm/BeagleBone+Black - Robert Nelson

<Device Tree & Overlay>
5) Device Tree for dummies.pdf - Thomas Petazzoni, Free Electrons

6) Linux kernel and driver development training Lab Book, Free Electrons

7) Linux Kernel and Driver Development Training, Free Electrons

8) Linux kernel: consolidation in the ARM architecture support, Thomas Petazzoni, Free Electrons,

9) ARM support in the Linux kernel, Thomas Petazzoni, Free Electrons

10) Your new ARM SoC Linux support check-list! Thomas Petazzoni, CLEMENT, Free Electrons,

11) Supporting 200 different expansionboards - The broken promise of device tree, CircuitCo.

12) The Device Tree: Plug and play for Embedded Linux, Eli Billauer

13) Introduction to the BeagleBone Black Device Tree, Created by Justin Cooper

<Pin Control>
14) Pin Control Sybsystem – Building Pins and GPIO from the ground up, Linus Walleij

15) PIN CONTROL OVERVIEW, Linaro Kernel Workgroup, ST-Ericsson, Linus Walleij

<Wii Nunchuk>
16) ZX-NUNCHUK(#8000339) Wii-Nunchuk interface board

<UART>
17) Essential Linux Device Drivers, Sreekrishnan Venkateswaran, PRENTICE HALL
18) Serial drivers, Thomas Petazzoni, Free Electrons


댓글 2개:

  1. 작성자가 댓글을 삭제했습니다.

    답글삭제
  2. 리눅스 uart 드라이버를 직접 짜보려는 나그네입니다만.. mytest-serial.c 파일 공유 안될까요..?(꾸벅) kukhyun1234@naver.com

    답글삭제