2011年1月28日 星期五

Cross Compile Lighttpd + PHP + MySQL for ARM (2)

5.Cross compile libiconv-1.13.1

$ CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-linux --target=arm-linux --prefix=/opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr --cache-file=/dev/null

$ make
$ sudo PATH=$PATH:/opt/arm-2008q1/bin make install

6.Cross compile libxml2-2.7.8

$ CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-linux --target=arm-linux --prefix=/opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr --cache-file=/dev/null

$ make
$ sudo PATH=$PATH:/opt/arm-2008q1/bin make install

7.Cross compile php-5.2.17

$ ac_cv_lib_mysqlclient_mysql_close=yes ac_cv_lib_mysqlclient_mysql_stmt_field_count=yes ac_cv_lib_mysqlclient_mysql_set_server_option=yes ac_cv_lib_mysqlclient_mysql_errno=yes ac_cv_func_iconv=yes CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-linux --target=arm-linux --prefix=/home/gigijoe/BeagleBoard/rootfs/usr/local --disable-all --with-config-file-path=/rootfs/usr/local/etc --with-mysql=/home/gigijoe/BeagleBoard/rootfs/usr/local/mysql --with-mysqli=/home/gigijoe/BeagleBoard/rootfs/usr/local/mysql/bin/mysql_config --with-mysql-sock=/tmp/mysqld.sock --disable-rpath --enable-discard-path --enable-safe-mode --enable-shmop --enable-sysvsem --enable-inline-optimization --enable-fastcgi --enable-force-cgi-redirect --enable-embedded-mysqli --with-libxml-dir=/opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr --cache-file=/dev/null

$ make
$ make install

8.Cross compile lighttpd-1.4.28

$ CFLAGS="-I/home/gigijoe/BeagleBoard/rootfs/usr/local/mysql/include" CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-linux --target=arm-linux --prefix=/home/gigijoe/BeagleBoard/rootfs/usr/local --with-mysql=/home/gigijoe/BeagleBoard/rootfs/usr/local/mysql --without-pcre --without-bzip2 --disable-ipv6 --cache-file=/dev/null

$ make
$ make install

9.ARM Target setup

記的將以上相關安裝到/opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr的libraries複製到ARM target.

指定library搜尋路徑
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/mysql/lib

10.Reference

http://wiki.huihoo.com/index.php?title=FreeBSD_Lighttpd_PHP_MySQL

http://www.cmake.org/Wiki/CMake_Cross_Compiling

http://forge.mysql.com/wiki/Autotools_to_CMake_Transition_Guide

http://forge.mysql.com/wiki/CMake

http://redmine.lighttpd.net/wiki/1/TutorialConfiguration

http://www.memorybbs.com/cgi-bin/topic.cgi?forum=77&topic=92



Cross Compile Lighttpd + PHP + MySQL for ARM (1)

目標是要在ARM platform上跑HTTP Server並且支援PHP & MySQL.
HTTP server選擇lighttpd.整個cross comiple流程大致是先build MySQL,
因為PHP需要連結MySQL,再來是bulid PHP 最後才是lighttpd.
ARM cross compile 是 CodeSourcery 2008q1版本.

首先了解一下cross compile搜尋路徑
$ arm-none-linux-gnueabi-gcc -print-search-dirs
install: /opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/
programs: =/opt/arm-2008q1/bin/../libexec/gcc/arm-none-linux-gnueabi/4.2.3/:/opt/arm-2008q1/bin/../libexec/gcc/:/opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi/4.2.3/:/opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/bin/
libraries: =/opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/:/opt/arm-2008q1/bin/../lib/gcc/:/opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/lib/arm-none-linux-gnueabi/4.2.3/:/opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/lib/:/opt/arm-2008q1/bin/../arm-none-linux-gnueabi/libc/lib/arm-none-linux-gnueabi/4.2.3/:/opt/arm-2008q1/bin/../arm-none-linux-gnueabi/libc/lib/:/opt/arm-2008q1/bin/../arm-none-linux-gnueabi/libc/usr/lib/arm-none-linux-gnueabi/4.2.3/:/opt/arm-2008q1/bin/../arm-none-linux-gnueabi/libc/usr/lib/

由以上libraries搜尋路徑偶選擇 /opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr 作為相關所需libraries的安裝位置

1.Download & cross compile pcre-8.12 & ncurses-5.7

$ cd pcre-8.12/

$ CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-linux --target=arm-linux --prefix=/opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr --cache-file=/dev/null
$ make
$ sudo PATH=$PATH:/opt/arm-2008q1/bin make install

$ cd ../ncurses-5.7

$ CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-linux --target=arm-linux --prefix=/opt/arm-2008q1/arm-none-linux-gnueabi/libc/usr --cache-file=/dev/null

$ make
$ sudo PATH=$PATH:/opt/arm-2008q1/bin make install

在make install 時加上PATH=$PATH:/opt/arm-2008q1/bin是為了在root權限下也能執行cross compiler.

2.Cross compile mysql-5.5.8

MySQL在5.5之後不在使用autoconf系統而採cmake.

建立 toolchain.cmake 內容如下

# this one is important
SET(CMAKE_SYSTEM_NAME Linux)
#this one not so much
SET(CMAKE_SYSTEM_VERSION 1)

# specify the cross compiler
SET(CMAKE_C_COMPILER   /opt/arm-2008q1/bin/arm-none-linux-gnueabi-gcc)
SET(CMAKE_CXX_COMPILER /opt/arm-2008q1/bin/arm-none-linux-gnueabi-g++)
SET(CMAKE_STRIP           /opt/arm-2008q1/bin/arm-none-linux-gnueabi-strip)

# where is the target environment
SET(CMAKE_FIND_ROOT_PATH  /opt/arm-2008q1/arm-none-linux-gnueabi/libc)

# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

接著執行cmake

$ cmake -DCMAKE_TOOLCHAIN_FILE=./toolchain.cmake -DMYSQL_DATADIR=/var/lib/mysql/data -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DEXTRA_CHARSETS=all -DMYSQL_UNIX_ADDR=/tmp/mysqld.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DMYSQL_USER=mysql -DCMAKE_INSTALL_PREFIX=/home/gigijoe/BeagleBoard/rootfs/usr/local/mysql -DWITH_EMBEDDED_SERVER=1 -DCMAKE_C_COMPILER=arm-none-linux-gnueabi-gcc -DCMAKE_CXX_COMPILER=arm-none-linux-gnueabi-g++ -DSTACK_DIRECTION=1

$ make VERBOSE=1

出現錯誤

[  2%] Building C object cmd-line-utils/libedit/CMakeFiles/edit.dir/term.c.o
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit && /opt/arm-2008q1/bin/arm-none-linux-gnueabi-gcc  -DHAVE_CONFIG_H -Wall -O2 -g -DDBUG_OFF -I/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit -I/home/gigijoe/BeagleBoard/mysql-5.5.8/include   -o CMakeFiles/edit.dir/term.c.o   -c /home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:59:20: error: curses.h: No such file or directory
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c: In function 'term_set':
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:928: warning: implicit declaration of function 'tgetent'
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:946: warning: implicit declaration of function 'tgetflag'
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:955: warning: implicit declaration of function 'tgetnum'
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:959: warning: implicit declaration of function 'tgetstr'
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:960: warning: passing argument 3 of 'term_alloc' makes pointer from integer without a cast
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c: In function 'term_tputs':
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:1261: warning: implicit declaration of function 'tputs'
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c: In function 'term_echotc':
/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c:1560: warning: assignment makes pointer from integer without a cast
make[2]: *** [cmd-line-utils/libedit/CMakeFiles/edit.dir/term.c.o] Error 1
make[2]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make[1]: *** [cmd-line-utils/libedit/CMakeFiles/edit.dir/all] Error 2
make[1]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make: *** [all] Error 2

修改 /home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit/term.c

#ifdef HAVE_CURSES_H
-#include
+#include
#endif
#ifdef HAVE_NCURSES_H
-#include
+#include
#endif
/* Don't use Solaris's term.h. */
#if (defined(HAVE_TERM_H) && !defined(__SunOS))
-#include
+#include
#endif

再次嘗試

$ make VERBOSE=1

出現錯誤

[ 28%] Generating ../include/mysqld_error.h, ../sql/share/english/errmsg.sys
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/extra && ./comp_err --charset=/home/gigijoe/BeagleBoard/mysql-5.5.8/sql/share/charsets --out-dir=/home/gigijoe/BeagleBoard/mysql-5.5.8/sql/share/ --header_file=/home/gigijoe/BeagleBoard/mysql-5.5.8/include/mysqld_error.h --name_file=/home/gigijoe/BeagleBoard/mysql-5.5.8/include/mysqld_ername.h --state_file=/home/gigijoe/BeagleBoard/mysql-5.5.8/include/sql_state.h --in_file=/home/gigijoe/BeagleBoard/mysql-5.5.8/sql/share/errmsg-utf8.txt
./comp_err: 1: Syntax error: word unexpected (expecting ")")
make[2]: *** [include/mysqld_error.h] Error 2
make[2]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make[1]: *** [extra/CMakeFiles/GenError.dir/all] Error 2
make[1]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make: *** [all] Error 2

有個comp_err無法執行,先找出它在哪

$ find ./ -name "comp_err" -print
./extra/comp_err

看一下屬性

$ file ./extra/comp_err
./extra/comp_err: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.14, not stripped

嗯,看來需要先在host side compile mysql.

3.Compile mysql-5.5.8 for host

$ cmake -DMYSQL_DATADIR=/var/lib/mysql/data -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DEXTRA_CHARSETS=all -DMYSQL_UNIX_ADDR=/tmp/mysqld.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DMYSQL_USER=mysql -DWITH_EMBEDDED_SERVER=1 -DSTACK_DIRECTION=1

$ make VERBOSE=1

4.Continue cross compile mysql-5.5.8

以host版本comp_err覆蓋

$ cp ../../mysql-5.5.8/extra/comp_err extra/

$ make VERBOSE=1

出現錯誤

[ 75%] Building CXX object client/CMakeFiles/mysql.dir/sql_string.cc.o
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/client && /opt/arm-2008q1/bin/arm-none-linux-gnueabi-g++   -DHAVE_CONFIG_H -DHAVE_DLOPEN -Wall -Wno-unused-parameter -fno-implicit-templates -fno-exceptions -fno-rtti -O2 -g -DDBUG_OFF -I/home/gigijoe/BeagleBoard/mysql-5.5.8/include -I/home/gigijoe/BeagleBoard/mysql-5.5.8/libmysql -I/home/gigijoe/BeagleBoard/mysql-5.5.8/regex -I/home/gigijoe/BeagleBoard/mysql-5.5.8/sql -I/home/gigijoe/BeagleBoard/mysql-5.5.8/strings -I/home/gigijoe/BeagleBoard/mysql-5.5.8/cmd-line-utils/libedit -I/home/gigijoe/BeagleBoard/mysql-5.5.8/client   -o CMakeFiles/mysql.dir/sql_string.cc.o -c /home/gigijoe/BeagleBoard/mysql-5.5.8/client/sql_string.cc
Linking CXX executable mysql
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/client && /usr/bin/cmake -E cmake_link_script CMakeFiles/mysql.dir/link.txt --verbose=1
/opt/arm-2008q1/bin/arm-none-linux-gnueabi-g++    -Wall -Wno-unused-parameter -fno-implicit-templates -fno-exceptions -fno-rtti -O2 -g -DDBUG_OFF   CMakeFiles/mysql.dir/completion_hash.cc.o CMakeFiles/mysql.dir/mysql.cc.o CMakeFiles/mysql.dir/readline.cc.o CMakeFiles/mysql.dir/sql_string.cc.o  -o mysql  -lpthread ../libmysql/libmysqlclient.a ../cmd-line-utils/libedit/libedit.a -lz -lm -lrt -ldl -lpthread -lcurses
/opt/arm-2008q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/bin/ld: cannot find -lcurses
collect2: ld returned 1 exit status
make[2]: *** [client/mysql] Error 1
make[2]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make[1]: *** [client/CMakeFiles/mysql.dir/all] Error 2
make[1]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make: *** [all] Error 2

用以下方式修正

編輯
/home/gigijoe/BeagleBoard/mysql-5.5.8/client/CMakeFiles/mysql.dir/link.txt

/home/gigijoe/BeagleBoard/mysql-5.5.8/libmysqld/examples/CMakeFiles/mysql_embedded.dir/link.txt

將 -lcurses 成 -lncurses

$ make VERBOSE=1

出現錯誤

[ 76%] Generating mysql_fix_privilege_tables_sql.c
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/scripts && /usr/bin/cmake -E chdir /home/gigijoe/BeagleBoard/mysql-5.5.8/scripts /bin/cat mysql_system_tables.sql mysql_system_tables_fix.sql > /home/gigijoe/BeagleBoard/mysql-5.5.8/scripts/mysql_fix_privilege_tables.sql
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/scripts && /home/gigijoe/BeagleBoard/mysql-5.5.8/scripts/comp_sql mysql_fix_privilege_tables mysql_fix_privilege_tables.sql mysql_fix_privilege_tables_sql.c
/home/gigijoe/BeagleBoard/mysql-5.5.8/scripts/comp_sql: 1: Syntax error: word unexpected (expecting ")")
make[2]: *** [scripts/mysql_fix_privilege_tables_sql.c] Error 2
make[2]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make[1]: *** [scripts/CMakeFiles/GenFixPrivs.dir/all] Error 2
make[1]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make: *** [all] Error 2

以host版本comp_sql覆蓋

$ cp ../../mysql-5.5.8/scripts/comp_sql scripts/

$ make VERBOSE=1

出現錯誤

[ 78%] Generating lex_hash.h
cd /home/gigijoe/BeagleBoard/mysql-5.5.8/sql && ./gen_lex_hash > lex_hash.h
./gen_lex_hash: 1: Syntax error: word unexpected (expecting ")")
make[2]: *** [sql/lex_hash.h] Error 2
make[2]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make[1]: *** [sql/CMakeFiles/GenServerSource.dir/all] Error 2
make[1]: Leaving directory `/home/gigijoe/BeagleBoard/mysql-5.5.8'
make: *** [all] Error 2

以host版本gen_lex_hash覆蓋

cp ../../mysql-5.5.8/sql/gen_lex_hash sql/

$ make VERBOSE=1

$ make install




2011年1月24日 星期一

Rowboat Android u-boot env

Texas Instruments X-Loader 1.4.2 (Feb 19 2009 - 12:01:24)
Loading u-boot.bin from nand


U-Boot 2009.11 (Feb 23 2010 - 15:33:48)

OMAP3530-GP ES3.1, CPU-OPP2 L3-165MHz
OMAP3 Beagle board + LPDDR/NAND
I2C:   ready
DRAM:  256 MB
NAND:  256 MiB
In:    serial
Out:   serial
Err:   serial
Board revision C4
Die ID #799e000400000000040373051500e007
Hit any key to stop autoboot:  0
OMAP3 beagleboard.org # printenv
bootcmd=if mmcinit; then if run loadbootscript; then run bootscript; else if run loaduimage; then if run loadramdisk; then run ramboot; else run mmcboot; fii
bootdelay=10
baudrate=115200
loadaddr=0x80200000
rdaddr=0x81600000
console=ttyS2,115200n8
mmcargs=setenv bootargs console=${console} ${optargs} root=/dev/mmcblk0p2 rw rootfstype=ext3 rootwait
ramargs=setenv bootargs console=${console} ${optargs} root=/dev/ram0 rw ramdisk_size=32768 initrd=${rdaddr},32M
jffs2args=setenv bootargs console=${console} ${optargs} root=/dev/mtdblock4 rw rootfstype=jffs2
loadbootscript=fatload mmc 0 ${loadaddr} boot.scr
bootscript=echo Running bootscript from mmc ...; autoscr ${loadaddr}
loaduimage=fatload mmc 0 ${loadaddr} uImage.bin
loadramdisk=fatload mmc 0 ${rdaddr} ramdisk.gz
ramboot=echo Booting from ramdisk.gz ...; run ramargs; bootm ${loadaddr}
mmcboot=echo Booting from mmc ...; run mmcargs; bootm ${loadaddr}
usbtty=cdc_acm
stdout=serial,usbtty
stdin=serial,usbtty
stderr=serial,usbtty
serial=799e000400000000040373051500e007
dieid#=799e000400000000040373051500e007
ubifsargs=setenv bootargs console=${console} ${optargs} mem=88M@0x80000000 mem=128M@0x88000000 root=ubi0:rootfs ubi.mtd=4 rw rootfstype=ubifs
nandboot=echo Booting from nand ...; run ubifsargs; nand read ${loadaddr} 280000 400000; bootm ${loadaddr}
optargs=init=/init omapdss.def_disp=dvi omapfb.mode=dvi:720x480MR-16

Environment size: 1561/131068 bytes
OMAP3 beagleboard.org #
NAND read: device 0 offset 0x280000, size 0x400000
 4194304 bytes read: OK
## Booting kernel from Legacy Image at 80200000 ...
   Image Name:   Linux-2.6.32
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2529392 Bytes =  2.4 MB
   Load Address: 80008000
   Entry Point:  80008000
   Verifying Checksum ... OK
   Loading Kernel Image ... OK
OK

Starting kernel ...

Uncompressing Linux..........................................................................................................................................
Linux version 2.6.32 (gigijoe@gigijoe-laptop) (gcc version 4.4.0 (GCC) ) #4 Wed Jan 5 02:16:23 CST 2011
CPU: ARMv7 Processor [411fc083] revision 3 (ARMv7), cr=10c53c7f
CPU: VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
Machine: OMAP3 Beagle Board
Memory policy: ECC disabled, Data cache writeback
OMAP3430/3530 ES3.1 (l2cache iva sgx neon isp 720m )
SRAM: Mapped pa 0x40200000 to va 0xfe400000 size: 0x100000
Reserving 4194304 bytes SDRAM for VRAM
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 54784
Kernel command line: console=ttyS2,115200n8 init=/init omapdss.def_disp=dvi omapfb.mode=dvi:720x480MR-16 mem=88M@0x80000000 mem=128M@0x88000000 root=ubi0:ros
PID hash table entries: 1024 (order: 0, 4096 bytes)
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
Memory: 88MB 128MB = 216MB total
Memory: 209408KB available (4368K code, 822K data, 172K init, 0K highmem)



2011年1月20日 星期四

BeagleBoard PWM with RC Servo


OMAP3 PWM resource

http://elinux.org/BeagleBoardPWM

http://www.jumpnowtek.com/index.php?option=com_content&view=article&id=56&Itemid=63

RC Servo resource

http://robot.avayanex.com/?tag=rc-servo

http://robot.pixnet.net/blog/post/26779024

RC Servo duty cycle 最短要 20.83 ms, 也就是48Hz 以下.
Pulse width範圍在 1~2.5 ms左右,各廠牌型號Servo略有不同

2.OMAP3 PWM
OMAP3 總共支援 4 個PWM channel, 在BeagleBoard線路有拉出來的有3組,分別是PWM 9, 10, 11
位於Expansion Connector Pin 4,  8, 10. (參考BBSRM Table 20)

3.kernel 需要稍作修改(2.6.29)
將mux設為PWM pin(參考 OMAP35x Technical Reference Manual (Rev.M) 7.4.4.3 p.772)

arch/arm/mach-omap2/mux.c
...

#if 0
/* UART2 */
MUX_CFG_34XX("AA25_34XX_UART2_TX", 0x178,
        OMAP34XX_MUX_MODE0 | OMAP34XX_PIN_OUTPUT)
MUX_CFG_34XX("AD25_34XX_UART2_RX", 0x17A,
        OMAP34XX_MUX_MODE0 | OMAP34XX_PIN_INPUT_PULLUP)
MUX_CFG_34XX("AB25_34XX_UART2_RTS", 0x176,
        OMAP34XX_MUX_MODE0 | OMAP34XX_PIN_OUTPUT)
MUX_CFG_34XX("AB26_34XX_UART2_CTS", 0x174,
        OMAP34XX_MUX_MODE0 | OMAP34XX_PIN_INPUT_PULLUP)
#endif

/* PWM */
/*  OMAP35x Technical Reference Manual (Rev.M) 7.4.4.3 p.772*/
MUX_CFG_34XX("AA25_34XX_UART2_TX", 0x178,
    OMAP34XX_MUX_MODE2 | OMAP34XX_PIN_OUTPUT)
MUX_CFG_34XX("AD25_34XX_UART2_RX", 0x17A,
    OMAP34XX_MUX_MODE2 | OMAP34XX_PIN_OUTPUT)
MUX_CFG_34XX("AB25_34XX_UART2_RTS", 0x176,
    OMAP34XX_MUX_MODE2 | OMAP34XX_PIN_OUTPUT)
MUX_CFG_34XX("AB26_34XX_UART2_CTS", 0x174,
    OMAP34XX_MUX_MODE2 | OMAP34XX_PIN_OUTPUT)

再來要disable CONFIG_OMAP_RESET_CLOCKS這樣PWM才能正常工作.

3.OMAP3 PWM driver

https://github.com/scottellis/omap3-pwm

https://github.com/neo01124/omap3-pwm

以上面兩個PWM driver為基礎作修改可分別控制3組PWM.
Device node
/dev/pwm/9
/dev/pwm/10
/dev/pwm/11

Clock source從文件及source code所了解的應該是可以更改.
我實際測試的結果是

PWM9    13MHz
PWM10  32KHz
PWM11  32KHz

不只是更改GPTi input frequency 這麼單純,必須實際修改omap34xx clock source.
搞了半天還是不能把32KHz 換成 13MHz

Frequency預設是48Hz
13MHz clock source 精確度很好.
32KHz clock source 的 duty cycle 範圍約在 4 ~ 13%, 約只有50個刻度.因此精確度並不高.



4.PWM Driver source

From github : git@github.com:gigijoe/omap3-pwm.git

omap_pwm.h

/*
 Copyright (c) 2010, Scott Ellis
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY Scott Ellis ''AS IS'' AND ANY
 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL Scott Ellis BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

 Register definitions used by the pwm driver.
 Some for the PADCONF pin muxing, the rest for the PWM timer control.
*/

#ifndef PWM_H
#define PWM_H

#define OMAP34XX_PADCONF_START  0x48002030
#define OMAP34XX_PADCONF_SIZE   0x05cc

#define GPT8_MUX_OFFSET        (0x4800217A - OMAP34XX_PADCONF_START)
#define GPT9_MUX_OFFSET        (0x48002174 - OMAP34XX_PADCONF_START)
#define GPT10_MUX_OFFSET    (0x48002176 - OMAP34XX_PADCONF_START)
#define GPT11_MUX_OFFSET    (0x48002178 - OMAP34XX_PADCONF_START)

#define PWM_ENABLE_MUX        0x0002    /* IDIS | PTD | DIS | M2 */

#define CLK_32K_FREQ    32768
#define CLK_13K_FREQ    13312
#define CLK_SYS_FREQ    13000000

#define GPTIMER8        0x4903E000
#define GPTIMER9        0x49040000
#define GPTIMER10         0x48086000
#define GPTIMER11        0x48088000

#define GPT_REGS_PAGE_SIZE      4096

#define PWM8_CTL_BASE        GPTIMER8
#define PWM9_CTL_BASE        GPTIMER9
#define PWM10_CTL_BASE        GPTIMER10
#define PWM11_CTL_BASE        GPTIMER11


/* GPT register offsets */
#define GPT_TIOCP_CFG 0x010
#define GPT_TISTAT    0x014
#define GPT_TISR      0x018
#define GPT_TIER      0x01C
#define GPT_TWER      0x020
#define GPT_TCLR      0x024
#define GPT_TCRR      0x028
#define GPT_TLDR      0x02C
#define GPT_TTGR      0x030
#define GPT_TWPS      0x034
#define GPT_TMAR      0x038
#define GPT_TCAR1     0x03C
#define GPT_TSICR     0x040
#define GPT_TCAR2     0x044
#define GPT_TPIR      0x048
#define GPT_TNIR      0x04C
#define GPT_TCVR      0x050
#define GPT_TOCR      0x054
#define GPT_TOWR      0x058  

/* TCLR bits for PWM */
#define GPT_TCLR_ST         (1 << 0)    /* stop/start */
#define GPT_TCLR_AR         (1 << 1)    /* one shot/auto-reload */
#define GPT_TCLR_PTV_MASK        (7 << 2)    /* prescaler value 2^(PTV + 1) */
#define GPT_TCLR_PRE        (1 << 5)    /* disable/enable prescaler */
#define GPT_TCLR_CE         (1 << 6)    /* disable/enable compare */
#define GPT_TCLR_SCPWM      (1 << 7)    /* PWM value when off */
#define GPT_TCLR_TCM_MASK        (3 << 8)    /* transition capture mode */

#define GPT_TCLR_TRG_MASK     (3 << 10)    /* trigger output mode */
#define GPT_TCLR_TRG_OVFL    (1 << 10)    /* trigger on overflow */
#define GPT_TCLR_TRG_OVFL_MATCH    (2 << 10)    /* trigger on overflow and match */   

#define GPT_TCLR_PT         (1 << 12)    /* pulse/toggle modulation */
#define GPT_TCLR_CAPT_MODE      (1 << 13)    /* capture mode config */
#define GPT_TCLR_GPO_CFG        (1 << 14)    /* pwm or capture mode */

/* Ioctl definitions */

#define PWM_IOC_MAGIC 0x00

#define PWM_SET_DUTYCYCLE _IOW(PWM_IOC_MAGIC , 1, int)
#define PWM_GET_DUTYCYCLE _IOW(PWM_IOC_MAGIC , 2, int)
#define PWM_SET_FREQUENCY _IOW(PWM_IOC_MAGIC , 3, int)
#define PWM_GET_FREQUENCY _IOW(PWM_IOC_MAGIC , 4, int)
#define PWM_ON _IO(PWM_IOC_MAGIC , 5)
#define PWM_OFF _IO(PWM_IOC_MAGIC , 6)
#define PWM_SET_POLARITY _IOW(PWM_IOC_MAGIC , 7, int)
#define PWM_SET_CLK _IOW(PWM_IOC_MAGIC , 8, int)
#define PWM_SET_PRE _IOW(PWM_IOC_MAGIC , 9, int)
#define PWM_PLUS_DUTYCYCLE _IOW(PWM_IOC_MAGIC , 10, int)
#define PWM_MINUS_DUTYCYCLE _IOW(PWM_IOC_MAGIC , 11, int)

#define PWM_IOC_MAXNR 11

#endif /* ifndef PWM_H */





omap_pwm.c

/*
 Copyright (c) 2010, Scott Ellis
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY Scott Ellis ''AS IS'' AND ANY
 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL Scott Ellis BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "omap_pwm.h"

/* default frequency of 1 kHz */
//#define DEFAULT_TLDR    0xFFFFFFE0
/* default frequency of 48 Hz */
//#define DEFAULT_TLDR    0xFFFFFD55

/* default 50% duty cycle */
/* TMAR = (0xFFFFFFFF - ((0xFFFFFFFF - (DEFAULT_TLDR + 1)) / 2)) */
//#define DEFAULT_TMAR    0xFFFFFFEF
//#define DEFAULT_TMAR    0xFFFFFEAB

/* default TCLR is off state */
#define DEFAULT_TCLR (GPT_TCLR_PT | GPT_TCLR_TRG_OVFL_MATCH | GPT_TCLR_CE | GPT_TCLR_AR)

//#define DEFAULT_PWM_FREQUENCY 1024
#define DEFAULT_PWM_FREQUENCY 48

static int defaultFrequency = DEFAULT_PWM_FREQUENCY;
module_param(defaultFrequency, int, S_IRUGO);
MODULE_PARM_DESC(defaultFrequency, "The PWM frequency, power of two, max of 16384");


#define USER_BUFF_SIZE    128

struct gpt {
    u32 timer_num;
    u32 mux_offset;
    u32 gpt_base;
    u32 input_freq;
    u32 old_mux;
    u32 tldr;
    u32 tmar;
    u32 tclr;
    u32 num_freqs;
};

struct pwm_dev {
    int frequency;
    struct semaphore sem;
    struct gpt gpt;
    char *user_buff;
};

static dev_t devt;
static struct cdev cdev;

static struct pwm_dev pwm9_dev;
static struct pwm_dev pwm10_dev;
static struct pwm_dev pwm11_dev;


static int init_mux(struct pwm_dev *pwm_dev)
{
    void __iomem *base;

    base = ioremap(OMAP34XX_PADCONF_START, OMAP34XX_PADCONF_SIZE);
    if (!base) {
        printk(KERN_ALERT "init_mux(): ioremap() failed\n");
        return -1;
    }

    pwm_dev->gpt.old_mux = ioread16(base + pwm_dev->gpt.mux_offset);
    iowrite16(PWM_ENABLE_MUX, base + pwm_dev->gpt.mux_offset);
    iounmap(base);   

    return 0;
}

static int restore_mux(struct pwm_dev *pwm_dev)
{
    void __iomem *base;

    if (pwm_dev->gpt.old_mux) {
        base = ioremap(OMAP34XX_PADCONF_START, OMAP34XX_PADCONF_SIZE);
   
        if (!base) {
            printk(KERN_ALERT "restore_mux(): ioremap() failed\n");
            return -1;
        }

        iowrite16(pwm_dev->gpt.old_mux, base + pwm_dev->gpt.mux_offset);
        iounmap(base);   
    }

    return 0;
}

static int set_pwm_frequency(struct pwm_dev *pwm_dev, int frequency)
{
    void __iomem *base;

    base = ioremap(pwm_dev->gpt.gpt_base, GPT_REGS_PAGE_SIZE);
    if (!base) {
        printk(KERN_ALERT "set_pwm_frequency(): ioremap failed\n");
        return -1;
    }

    if (frequency < 0) {
        frequency = DEFAULT_PWM_FREQUENCY;
    } else {
        /* only powers of two, for simplicity */
        //frequency &= ~0x01;

        if (frequency > (pwm_dev->gpt.input_freq / 2))
            frequency = pwm_dev->gpt.input_freq / 2;
        else if (frequency == 0)
            frequency = DEFAULT_PWM_FREQUENCY;
    }

    pwm_dev->frequency = frequency;

    /* PWM_FREQ = 32768 / ((0xFFFF FFFF - TLDR) + 1) */
    pwm_dev->gpt.tldr = 0xFFFFFFFF - ((pwm_dev->gpt.input_freq / pwm_dev->frequency) - 1);

    /* just for convenience */   
    pwm_dev->gpt.num_freqs = 0xFFFFFFFE - pwm_dev->gpt.tldr;   

    iowrite32(pwm_dev->gpt.tldr, base + GPT_TLDR);

    /* initialize TCRR to TLDR, have to start somewhere */
    iowrite32(pwm_dev->gpt.tldr, base + GPT_TCRR);

    iounmap(base);

    return 0;
}

static int pwm_off(struct pwm_dev *pwm_dev)
{
    void __iomem *base;

    base = ioremap(pwm_dev->gpt.gpt_base, GPT_REGS_PAGE_SIZE);
    if (!base) {
        printk(KERN_ALERT "pwm_off(): ioremap failed\n");
        return -1;
    }

    pwm_dev->gpt.tclr &= ~GPT_TCLR_ST;
    iowrite32(pwm_dev->gpt.tclr, base + GPT_TCLR);
    iounmap(base);

    return 0;
}

static int pwm_on(struct pwm_dev *pwm_dev)
{
    void __iomem *base;

    base = ioremap(pwm_dev->gpt.gpt_base, GPT_REGS_PAGE_SIZE);

    if (!base) {
        printk(KERN_ALERT "pwm_on(): ioremap failed\n");
        return -1;
    }

    /* set the duty cycle */
    iowrite32(pwm_dev->gpt.tmar, base + GPT_TMAR);
   
    /* now turn it on */
    pwm_dev->gpt.tclr = ioread32(base + GPT_TCLR);
    pwm_dev->gpt.tclr |= GPT_TCLR_ST;

    iowrite32(pwm_dev->gpt.tclr, base + GPT_TCLR);
     iounmap(base);

    return 0;
}

static int scpwm(struct pwm_dev *pwm_dev, int sc)
{
    void __iomem *base;

    base = ioremap(pwm_dev->gpt.gpt_base, GPT_REGS_PAGE_SIZE);
    if (!base) {
        printk(KERN_ALERT "pwm_off(): ioremap failed\n");
        return -1;
    }

    if (sc == 1)
        pwm_dev->gpt.tclr |= GPT_TCLR_SCPWM;
    else
        pwm_dev->gpt.tclr &= ~GPT_TCLR_SCPWM;

    iowrite32(pwm_dev->gpt.tclr, base + GPT_TCLR);
    iounmap(base);

    return 0;
}

static int prescale(struct pwm_dev *pwm_dev, int div)
{
    void __iomem *base;
    int i = 0;
    base = ioremap(pwm_dev->gpt.gpt_base, GPT_REGS_PAGE_SIZE);

    if (!base) {
        printk(KERN_ALERT "pwm_off(): ioremap failed\n");
        return -1;
    }

    while (div > 2) {
        i++;
        div /= 2;
    }

    pwm_dev->gpt.tclr |= GPT_TCLR_PRE; //enable prescaler
    pwm_dev->gpt.tclr &= i << 2; //set prescaler ratio
    iowrite32(pwm_dev->gpt.tclr, base + GPT_TCLR);
    iounmap(base);

    return 0;
}

static int set_duty_cycle(struct pwm_dev *pwm_dev, unsigned int duty_cycle)
{
    unsigned int new_tmar;

    pwm_off(pwm_dev);

    if (duty_cycle == 0)
        return 0;
 
    new_tmar = (duty_cycle * pwm_dev->gpt.num_freqs) / 100;

    if (new_tmar < 1)
        new_tmar = 1;
    else if (new_tmar > pwm_dev->gpt.num_freqs)
        new_tmar = pwm_dev->gpt.num_freqs;
       
    pwm_dev->gpt.tmar = pwm_dev->gpt.tldr + new_tmar;
printk("TMAR : 0x%08x\n", pwm_dev->gpt.tmar);   
    return pwm_on(pwm_dev);
}

static ssize_t pwm_read(struct file *filp, char __user *buff, size_t count,
            loff_t *offp)
{
    size_t len;
    unsigned int duty_cycle;
    ssize_t error = 0;

      struct pwm_dev *pwm_dev = (struct pwm_dev *)filp->private_data;
 
    if (!buff)
        return -EFAULT;

    /* tell the user there is no more */
    if (*offp > 0)
        return 0;

    if (down_interruptible(&pwm_dev->sem))
        return -ERESTARTSYS;

    if (pwm_dev->gpt.tclr & GPT_TCLR_ST) {
        duty_cycle = (100 * (pwm_dev->gpt.tmar - pwm_dev->gpt.tldr))
                / pwm_dev->gpt.num_freqs;

        snprintf(pwm_dev->user_buff, USER_BUFF_SIZE,
                "PWM%d Frequency %u Hz Duty Cycle %u%%\n",
                pwm_dev->gpt.timer_num, pwm_dev->frequency, duty_cycle);
    }
    else {
        snprintf(pwm_dev->user_buff, USER_BUFF_SIZE,
                "PWM%d Frequency %u Hz Stopped\n",
                pwm_dev->gpt.timer_num, pwm_dev->frequency);
    }

    len = strlen(pwm_dev->user_buff);
 
    if (len + 1 < count)
        count = len + 1;

    if (copy_to_user(buff, pwm_dev->user_buff, count))  {
        printk(KERN_ALERT "pwm_read(): copy_to_user() failed\n");
        error = -EFAULT;
    }
    else {
        *offp += count;
        error = count;
    }

    up(&pwm_dev->sem);

    return error;   
}

static ssize_t pwm_write(struct file *filp, const char __user *buff,
            size_t count, loff_t *offp)
{
    size_t len;
    unsigned int duty_cycle;
    ssize_t error = 0;
   
      struct pwm_dev *pwm_dev = (struct pwm_dev *)filp->private_data;

    if (down_interruptible(&pwm_dev->sem))
        return -ERESTARTSYS;

    if (!buff || count < 1) {
        printk(KERN_ALERT "pwm_write(): input check failed\n");
        error = -EFAULT;
        goto pwm_write_done;
    }
   
    /* we are only expecting a small integer, ignore anything else */
    if (count > 8)
        len = 8;
    else
        len = count;
       
    memset(pwm_dev->user_buff, 0, 16);

    if (copy_from_user(pwm_dev->user_buff, buff, len)) {
        printk(KERN_ALERT "pwm_write(): copy_from_user() failed\n");
        error = -EFAULT;    
        goto pwm_write_done;
    }

    if(pwm_dev->user_buff[0] == '+')    {
        pwm_off(pwm_dev);
        if(pwm_dev->user_buff[1] != '\0')
            pwm_dev->gpt.tmar += simple_strtoul(&pwm_dev->user_buff[1], NULL, 0);
        pwm_on(pwm_dev);
printk("TMAR : 0x%08x\n", pwm_dev->gpt.tmar);
    } else if(pwm_dev->user_buff[0] == '-')    {
        pwm_off(pwm_dev);
        if(pwm_dev->user_buff[1] != '\0')
            pwm_dev->gpt.tmar -= simple_strtoul(&pwm_dev->user_buff[1], NULL, 0);
        pwm_on(pwm_dev);
printk("TMAR : 0x%08x\n", pwm_dev->gpt.tmar);
    } else    {
        duty_cycle = simple_strtoul(pwm_dev->user_buff, NULL, 0);

        set_duty_cycle(pwm_dev, duty_cycle);

    }
    /* pretend we ate it all */
    *offp += count;

    error = count;
pwm_write_done:

    up(&pwm_dev->sem);
   
    return error;
}

static int pwm_open(struct inode *inode, struct file *filp)
{
    int error = 0;

    struct pwm_dev *pwm_dev = 0;
   
    unsigned int minor = iminor(inode);

    switch(minor)    {
        case 9:        pwm_dev = &pwm9_dev;   
            break;
        case 10:    pwm_dev = &pwm10_dev;   
            break;
        case 11:    pwm_dev = &pwm11_dev;
            break;
    }
     
    if(pwm_dev == 0)
        return -EFAULT;

    filp->private_data = pwm_dev;

    if (down_interruptible(&pwm_dev->sem))
        return -ERESTARTSYS;

    if (pwm_dev->gpt.old_mux == 0) {
        if (init_mux(pwm_dev)) 
            error = -EIO;
        else if (set_pwm_frequency(pwm_dev, defaultFrequency))
            error = -EIO;
    }

    if (!pwm_dev->user_buff) {
        pwm_dev->user_buff = kmalloc(USER_BUFF_SIZE, GFP_KERNEL);
        if (!pwm_dev->user_buff)
            error = -ENOMEM;
    }

    up(&pwm_dev->sem);

    return error;   
}

int pwm_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
    int retval = 0;
      struct pwm_dev *pwm_dev = (struct pwm_dev *)filp->private_data;

    /*
    * extract the type and number bitfields, and don't decode
    * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
    */
    if (_IOC_TYPE(cmd) != PWM_IOC_MAGIC)
        return -ENOTTY;
    if (_IOC_NR(cmd) > PWM_IOC_MAXNR)
        return -ENOTTY;

     switch (cmd) {
        case PWM_ON:
            if (pwm_on(pwm_dev))
                retval = -EIO;
            break;
        case PWM_OFF:
            if (pwm_off(pwm_dev))
                retval = -EIO;
            break;
        case PWM_SET_DUTYCYCLE:
            if (set_duty_cycle(pwm_dev, arg))
                retval = -EIO;
            break;
        case PWM_GET_DUTYCYCLE:
            if (pwm_dev->gpt.tclr & GPT_TCLR_ST) { //PWM is on
                retval = (100 * (pwm_dev->gpt.tmar - pwm_dev->gpt.tldr))
                        / pwm_dev->gpt.num_freqs; //real duty cycle
            } else {
                printk(KERN_ALERT "PWM%d is OFF\n", pwm_dev->gpt.timer_num);
                retval = -EIO;
            }
            break;
        case PWM_SET_FREQUENCY:
            if (set_pwm_frequency(pwm_dev, arg))
                retval = -EIO;
            break;
        case PWM_GET_FREQUENCY:
            retval = pwm_dev->frequency;
            break;
        case PWM_SET_POLARITY:
            if (scpwm(pwm_dev, arg))
                retval = -EIO;
            break;
        case PWM_SET_CLK:
/* Do no change clock source !!! */
/*
            if (pwm_dev->gpt.timer_num == 9) {
                printk(KERN_ALERT "Only 32K clk can be used with GPT9\n");
                retval = -EIO;
            } else {
                if (arg == 1)
                    pwm_dev->gpt.input_freq = CLK_13K_FREQ;
                else
                    pwm_dev->gpt.input_freq = CLK_32K_FREQ;
            }
*/
        break;
        case PWM_SET_PRE:
            if (prescale(pwm_dev, arg))
                retval = -EIO;
            break;
        case PWM_PLUS_DUTYCYCLE:
            pwm_off(pwm_dev);
            pwm_dev->gpt.tmar++;
            pwm_on(pwm_dev);
            break;
        case PWM_MINUS_DUTYCYCLE:
            pwm_off(pwm_dev);
            if(pwm_dev->gpt.tmar > 0)
                pwm_dev->gpt.tmar--;
            pwm_on(pwm_dev);
            break;
        default: /* redundant, as cmd was checked against MAXNR */
            return -ENOTTY;
    }

    return retval;
}

static struct file_operations pwm_fops = {
    .owner = THIS_MODULE,
    .read = pwm_read,
    .write = pwm_write,
    .open = pwm_open,
    .ioctl = pwm_ioctl,
};

static int __init pwm_init_cdev(void)
{
    int error;

    error = alloc_chrdev_region(&devt, 9, 3, "pwm");    /*    minor number start from 9, count 3*/

    if (error < 0) {
        printk(KERN_ALERT "alloc_chrdev_region() failed: %d \n",
            error);
        return -1;
    }

    cdev_init(&cdev, &pwm_fops);
    cdev.owner = THIS_MODULE;
   
    error = cdev_add(&cdev, devt, 3);
    if (error) {
        printk(KERN_ALERT "cdev_add() failed: %d\n", error);
        unregister_chrdev_region(devt, 3);
        return -1;
    }   

    return 0;
}
 
static int __init pwm_init(void)
{
    memset(&pwm9_dev, 0, sizeof(struct pwm_dev));

    /* change these 4 values to use a different PWM */
    pwm9_dev.frequency = defaultFrequency;
    pwm9_dev.gpt.timer_num = 9;
    pwm9_dev.gpt.mux_offset = GPT9_MUX_OFFSET;
    pwm9_dev.gpt.gpt_base = PWM9_CTL_BASE;
    pwm9_dev.gpt.input_freq = CLK_SYS_FREQ;    /* GPT9 can only use 13MHz clock source */

    pwm9_dev.gpt.tldr = 0xFFFFFFFF - (pwm9_dev.gpt.input_freq / pwm9_dev.frequency) + 1;
    pwm9_dev.gpt.tmar = 0xFFFFFFFF - ((0xFFFFFFFF - (pwm9_dev.gpt.tldr + 1)) / 2);
    pwm9_dev.gpt.tclr = DEFAULT_TCLR;

    sema_init(&pwm9_dev.sem, 1);

    memset(&pwm10_dev, 0, sizeof(struct pwm_dev));

    /* change these 4 values to use a different PWM */
    pwm10_dev.frequency = defaultFrequency;
    pwm10_dev.gpt.timer_num = 10;
    pwm10_dev.gpt.mux_offset = GPT10_MUX_OFFSET;
    pwm10_dev.gpt.gpt_base = PWM10_CTL_BASE;
    pwm10_dev.gpt.input_freq = CLK_32K_FREQ;

    pwm10_dev.gpt.tldr = 0xFFFFFFFF - (pwm10_dev.gpt.input_freq / pwm10_dev.frequency) + 1;
    pwm10_dev.gpt.tmar = 0xFFFFFFFF - ((0xFFFFFFFF - (pwm10_dev.gpt.tldr + 1)) / 2);
    pwm10_dev.gpt.tclr = DEFAULT_TCLR;

    sema_init(&pwm10_dev.sem, 1);

    memset(&pwm11_dev, 0, sizeof(struct pwm_dev));

    /* change these 4 values to use a different PWM */
    pwm11_dev.frequency = defaultFrequency;
    pwm11_dev.gpt.timer_num = 11;
    pwm11_dev.gpt.mux_offset = GPT11_MUX_OFFSET;
    pwm11_dev.gpt.gpt_base = PWM11_CTL_BASE;
    pwm11_dev.gpt.input_freq = CLK_32K_FREQ;

    pwm11_dev.gpt.tldr = 0xFFFFFFFF - (pwm11_dev.gpt.input_freq / pwm11_dev.frequency) + 1;
    pwm11_dev.gpt.tmar = 0xFFFFFFFF - ((0xFFFFFFFF - (pwm11_dev.gpt.tldr + 1)) / 2);
    pwm11_dev.gpt.tclr = DEFAULT_TCLR;

    sema_init(&pwm11_dev.sem, 1);

    if (pwm_init_cdev())
        goto init_fail;

    printk(KERN_INFO "OMAP3 PWM: character device initialized (major=%d)\n", MAJOR(devt));

    return 0;

init_fail:
    return -1;
}
module_init(pwm_init);

static void __exit pwm_exit(void)
{
    cdev_del(&cdev);

    pwm_off(&pwm9_dev);
    restore_mux(&pwm9_dev);

    if (pwm9_dev.user_buff)
        kfree(pwm9_dev.user_buff);

    pwm_off(&pwm10_dev);
    restore_mux(&pwm10_dev);

    if (pwm10_dev.user_buff)
        kfree(pwm10_dev.user_buff);

    pwm_off(&pwm11_dev);
    restore_mux(&pwm11_dev);

    if (pwm11_dev.user_buff)
        kfree(pwm11_dev.user_buff);

    unregister_chrdev_region(devt, 3);

}
module_exit(pwm_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Scott Ellis - Jumpnow");
MODULE_AUTHOR("Steve Chang");
MODULE_DESCRIPTION("PWM example for Gumstix Overo");







2011年1月12日 星期三

Embedded Linux software upgrade with initramfs (1)

1.源由限制
由於有限的memory及flash空間,Software upgrade一直是embedded system不太好處理的一塊.
一般的Linux base Wifi router通常只有16MBytes甚至8MBytes memory而flash大小通常是memory的一半.
系統在正常運作的狀況下kernel & applications往往已經用掉的4MBytes以上.如果是功能更多的裝置還有可能使用更多的memory.
在這樣的狀況下upgrade 是有疑慮的,無法保證系統有足夠的memory來進行.

更新正在運作中的系統是危險的行為.
試想更新正在執行的applicatins或libraries會發生什麼事?
甚至是想要複寫整個已經掛載(mount)的mtd block?

2.網路環境
Upgrade一般需要透過網路取得upgrade image因此必須啟動網路功能.
Upgrade也可以在bootloader中執行,但只限於單純的網路環境,例如Static IP \ DHCP.
若是VLAN 或 IEEE802.1X 的網路環境bootloader就無法正確設定網路.
下載upgrade image的動作最好還是在Linux下執行.

3.版本檢查的時機與方式
系統需要自行檢查伺服器上是否有更新的版本.
太頻繁的檢查會造成無謂的網路頻寬損耗,因此適當時間即可.
檢查更新版本若都必須下載整個upgrade image將造成巨大的頻寬損耗.
比較好的方式是下載一個簡短描述upgrade image的檔案用以檢查是否需要更新.

4.Upgrade與initramfs應用

關於initramfs技術細節請參考以下
http://blog.linux.org.tw/~jserv/archives/001954.html

initramfs 的特性

early user space :
在實際的user space applications啟動前作upgrade檢查及更新動作有幾個好處.
一,這時已經能掛載(mount) mtd block取得設定檔進行網路設定,之後可馬上卸載(umount).
二,系統未掛載任何mtd block因此能直接覆寫整塊mtd block.
三,系統還未執行user space applications因此可使用memory是比較多的而且size是可預期的.