linux 2.6.36+s3c6410 SPI子係統接口討論 --添加spi設備節點
下圖是安裝成功後,sys目錄下的主要結構,由於目錄非常複雜僅僅列出了主要的結構
sys目錄下spi子係統結構
接下來將從各struct開始進行分析,struct是構成內核對象的基礎,函數是動態的構建和執行的工具。所以梳理脈絡就從結構開始。
linux下的設備模型包括幾個主要的概念
sysfs (dev是用戶空間接口,根據sysfs下的class目錄由mdev負責建立)
bus總線,linux下的設備都是建立在總線上的,platform總線是一個虛擬的總線,所有的的片上設備基本上都接在這個虛擬總線上
device是設備
device_driver是設備驅動
class是類別,從功能角度對設備進行分類
注意,在sys/bus目錄下有platform目錄和spi目錄
這兩個目錄下麵的設備分別代表什麼呢?
platform下的設備有s3c64xx-spi0和s3c64xx-spi1分別對應了s3c6410上的spi0和spi1接口
一、先說說platform
platform.txt是需要閱讀的參考文檔
platform子係統是linux對不同的架構下設備的抽象歸納,即所有片上的設備都會放在這個子目錄下
我們先看一下platform相關的struct
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
platform_device從字麵理解就是平台設備,注意它內含了一個名為dev的device結構,這有點像C++的類繼承的關係,linux內核大量利用了這種類似繼承的結構實現了C語言下的麵向對象編程。
後麵談到的SPI的設備繼承了platform_device的結構。 另外,還包含了一個resource 結構的指針,注意和dev的區別。也就是說資源是需要另外定義然後,將對象指針賦予這裏的結構指針。資源包含IOMEM和IRQ。
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
platform_driver結構就不多說了,主要是用來說明platform驅動的結構。
下麵看看對象實例s3c64xx-spi.0的定義
static struct resource s3c64xx_spi0_resource[] = {
[0] = {
.start = S3C64XX_PA_SPI0,
.end = S3C64XX_PA_SPI0 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_SPI0_TX,
.end = DMACH_SPI0_TX,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_SPI0_RX,
.end = DMACH_SPI0_RX,
.flags = IORESOURCE_DMA,
},
[3] = {
.start = IRQ_SPI0,
.end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
},
};
static struct s3c64xx_spi_info s3c64xx_spi0_pdata = {
.cfg_gpio = s3c64xx_spi_cfg_gpio,
.fifo_lvl_mask = 0x7f,
.rx_lvl_offset = 13,
};
static u64 spi_dmamask = DMA_BIT_MASK(32);
struct platform_device s3c64xx_device_spi0 = {
.name = "s3c64xx-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c64xx_spi0_resource),
.resource = s3c64xx_spi0_resource,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s3c64xx_spi0_pdata,
},
};
從上麵的定義可以看出,s3c64xx_device_spi0是s3c64xx-spi.0的定義,係統根據這個變量實現了s3c64xx-spi.0,而它的類型是platform_device,也就是說這是一個平台設備。s3c64xx-spi.1就不說了同理。
下麵是驅動s3c64xx_spi_driver 的定義,係統根據這個定義實現了平台驅動s3c64xx-spi
static struct platform_driver s3c64xx_spi_driver = {
.driver = {
.name = "s3c64xx-spi",
.owner = THIS_MODULE,
},
.remove = s3c64xx_spi_remove,
.suspend = s3c64xx_spi_suspend,
.resume = s3c64xx_spi_resume,
};
注意驅動的定義中含有很多的函數指針,這些函數是用來控製設備工作的標準動作。具體功能這裏就不解釋了,各位看源碼。
各位看到這裏可能要問了那s3c64xx-spi.0下的spi0.0是在哪裏實現的?它的類結構是什麼?
首先,我們得明白s3c64xx-spi.0代表了s3c6410下的spi0接口,那麼spi0.0代表什麼呢?注意spi0.0有兩個數字,第一個0代表spi0接口,第2個0呢?它代表這spi0總線上的片外設備,這意味著一個spi0總線可以接多個片外設備,通過cs進行片選。屬於分時複用,那麼spi0.0的定義在什麼地方呢?首先,它和開發板的定義高度相關,也就是說它不會存在於內核代碼,也不會存在於芯片廠商三星公司提供的s3c64xx係列代碼中,那麼它應該在什麼地方呢?答案是它應該在開發板的初始化代碼中,即在友善之臂的mach_mini6410.c中,很遺憾友善之臂暫時沒有支持spi,spi的相關初始化代碼需要我們自行定義,如何定義這裏暫且不說,先說說這個設備的類型定義在哪裏?
下麵就是片外設備的定義,s3c64xx_spi_csinfo是片外設備需要的片選信號的定義,這個片選信號的定義是必須的,很多網上的帖子並沒有介紹到,這也是我之前很鬱悶的地方一直無法成功的加載設備。
/**
* struct s3c64xx_spi_csinfo - ChipSelect description
* @fb_delay: Slave specific feedback delay.
* Refer to FB_CLK_SEL register definition in SPI chapter.
* @line: Custom 'identity' of the CS line.
* @set_level: CS line control.
*
* This is per SPI-Slave Chipselect information.
* Allocate and initialize one in machine init code and make the
* spi_board_info.controller_data point to it.
*/
struct s3c64xx_spi_csinfo {
u8 fb_delay;
unsigned line;
void (*set_level)(unsigned line_id, int lvl);
};
/*
* INTERFACE between board init code and SPI infrastructure.
*
* No SPI driver ever sees these SPI device table segments, but
* it's how the SPI core (or adapters that get hotplugged) grows
* the driver model tree.
*
* As a rule, SPI devices can't be probed. Instead, board init code
* provides a table listing the devices which are present, with enough
* information to bind and set up the device's driver. There's basic
* support for nonstatic configurations too; enough to handle adding
* parport adapters, or microcontrollers acting as USB-to-SPI bridges.
*/
/**
* struct spi_board_info - board-specific template for a SPI device
* @modalias: Initializes spi_device.modalias; identifies the driver.
* @platform_data: Initializes spi_device.platform_data; the particular
* data stored there is driver-specific.
* @controller_data: Initializes spi_device.controller_data; some
* controllers need hints about hardware setup, e.g. for DMA.
* @irq: Initializes spi_device.irq; depends on how the board is wired.
* @max_speed_hz: Initializes spi_device.max_speed_hz; based on limits
* from the chip datasheet and board-specific signal quality issues.
* @bus_num: Identifies which spi_master parents the spi_device; unused
* by spi_new_device(), and otherwise depends on board wiring.
* @chip_select: Initializes spi_device.chip_select; depends on how
* the board is wired.
* @mode: Initializes spi_device.mode; based on the chip datasheet, board
* wiring (some devices support both 3WIRE and standard modes), and
* possibly presence of an inverter in the chipselect path.
*
* When adding new SPI devices to the device tree, these structures serve
* as a partial device template. They hold information which can't always
* be determined by drivers. Information that probe() can establish (such
* as the default transfer wordsize) is not included here.
*
* These structures are used in two places. Their primary role is to
* be stored in tables of board-specific device descriptors, which are
* declared early in board initialization and then used (much later) to
* populate a controller's device tree after the that controller's driver
* initializes. A secondary (and atypical) role is as a parameter to
* spi_new_device() call, which happens after those controller drivers
* are active in some dynamic board configuration models.
*/
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE];
const void *platform_data;
void *controller_data;
int irq;
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz;
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u8 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*/
};
上麵的介紹已經將平台下的各設備及驅動的定義講清楚了
二、接下來介紹SPI目錄下的各對象的定義。
spi-summary.txt是必須閱讀的參考文檔
從spi目錄可以看出這是與平台無關的,也就是linux對spi接口進行抽象,形成的用戶層相關代碼,而platform下麵的代碼是具體進行工作的代碼,它們與平台相關,而spi代碼是與用戶空間接口相關的代碼它必須與平台無關,即無論平台如何更換,對用戶空間的接口都是一致的,這就是linux設備驅動架構的精髓。
我們注意到spi目錄下隻有一個spidev的驅動,其他都是指向platform下的設備鏈接,這裏先不說整個spi子係統的運作機製,先搞清楚spi子係統的靜態結構
spidev是由spidev_spi_driver 定義的,它的結構類型是spi_driver
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
/* NOTE: suspend/resume methods are not necessary here.
* We don't do anything except pass the requests to/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handles the rest.
*/
};
spi_driver 的定義如下:
/**
* struct spi_driver - Host side "protocol" driver
* @id_table: List of SPI devices supported by this driver
* @probe: Binds this driver to the spi device. Drivers can verify
* that the device is actually present, and may need to configure
* characteristics (such as bits_per_word) which weren't needed for
* the initial configuration done during system setup.
* @remove: Unbinds this driver from the spi device
* @shutdown: Standard shutdown callback used during system state
* transitions such as powerdown/halt and kexec
* @suspend: Standard suspend callback used during system state transitions
* @resume: Standard resume callback used during system state transitions
* @driver: SPI device drivers should initialize the name and owner
* field of this structure.
*
* This represents the kind of device driver that uses SPI messages to
* interact with the hardware at the other end of a SPI link. It's called
* a "protocol" driver because it works through messages rather than talking
* directly to SPI hardware (which is what the underlying SPI controller
* driver does to pass those messages). These protocols are defined in the
* specification for the device(s) supported by the driver.
*
* As a rule, those device protocols represent the lowest level interface
* supported by a driver, and it will support upper level interfaces too.
* Examples of such upper levels include frameworks like MTD, networking,
* MMC, RTC, filesystem character device nodes, and hardware monitoring.
*/
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};
static inline struct spi_driver *to_spi_driver(struct device_driver *drv)
{
return drv ? container_of(drv, struct spi_driver, driver) : NULL;
}
extern int spi_register_driver(struct spi_driver *sdrv);
/**
* spi_unregister_driver - reverse effect of spi_register_driver
* @sdrv: the driver to unregister
* Context: can sleep
*/
static inline void spi_unregister_driver(struct spi_driver *sdrv)
{
if (sdrv)
driver_unregister(&sdrv->driver);
}
三、下麵介紹class目錄下的對象定義結構
spi_master的定義如下
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
spidev的類定義如下:
/*-------------------------------------------------------------------------*/
/* The main reason to have this class is to make mdev/udev create the
* /dev/spidevB.C character device nodes exposing our userspace API.
* It also simplifies memory management.
*/
static struct class *spidev_class;
文章寫到這裏,spi子係統主要的設備、驅動、類定義的結構已經介紹清楚了,即靜態的結構大家已經明白了。
下麵我們開始介紹SPI子係統的初始化過程,這也是我和大夥都很頭痛的地方,SPI子係統是如何初始化的?
四、SPI子係統的初始化過程
先說一下和SPI子係統初始化相關的主要代碼
spi.c是spi子係統初始化的核心代碼,由內核負責初始化
spidev.c是spi用戶接口初始化的代碼,編譯的時候需要選擇該模塊
spi_s3c64xx.c是平台驅動的初始化代碼,編譯時需要選擇spi s3c64xx模塊
mach-mini6410.c是開發板初始化的代碼
上述核心設備、驅動、類的初始化過程就是在上述代碼中實現的,它們分別負責那些對象的初始化過程,次序是什麼,需要各位思考一下,我也沒有完全搞清楚。
先說說spi.c中的初始化代碼
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
從這段代碼,我們可以看出,係統注冊及初始化了總線spi和類spi_master
總線spi的定義代碼前麵沒有列出,這裏補充如下:
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.suspend = spi_suspend,
.resume = spi_resume,
};
這個初始化過程是內核在初始化過程中,調用spi_init(void)函數執行的,由宏postcore_initcall(spi_init);加入到啟動代碼。
platform總線的初始化代碼由platform.c中的函數platform_bus_init執行
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
下麵是用戶空間接口的初始化代碼,從代碼可以看出,初始化了字符設備spi
注冊&初始化類spidev
注冊&初始化驅動spidev
static int __init spidev_init(void)
{
int status;
/* Claim our 256 reserved device numbers. Then register a class
* that will key udev/mdev to add/remove /dev nodes. Last, register
* the driver which manages those device numbers.
*/
BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev");
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver);
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
module_init(spidev_init);
下麵平台驅動的初始化過程
static int __init s3c64xx_spi_init(void)
{
return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
int __init_or_module platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *))
{
int retval, code;
/* make sure driver won't have bind/unbind attributes */
drv->driver.suppress_bind_attrs = true;
/* temporary section violation during probe() */
drv->probe = probe;
retval = code = platform_driver_register(drv);
/*
* Fixup that section violation, being paranoid about code scanning
* the list of drivers in order to probe new devices. Check to see
* if the probe was successful, and make sure any forced probes of
* new devices fail.
*/
spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
drv->probe = NULL;
if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
retval = -ENODEV;
drv->driver.probe = platform_drv_probe_fail;
spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
if (code != retval)
platform_driver_unregister(drv);
return retval;
}
上麵這段代碼注冊了平台SPI驅動s3c64xx-spi
看到這裏,我們可以發現還有什麼活沒有幹?
s3c64xx-spi.0和s3c64xx-spi.1設備沒有建立,它們下麵的spi0.0和spi1.0設備也沒有建立。
spidev類下麵的spidev0.0和spidev1.0也沒有建立
spi_master類下麵的spi0和spi1也沒有建立
這些都是在什麼地方建立的?分析一下這些都與具體的開發板有關係,也就是說這部分代碼必須由開發板的初始化代碼來實現,這裏必須由mach-mini6410.c負責實現。
mach-mini6410.c的初始化函數是
static void __init mini6410_machine_init(void)
也就是說上述工作必須在這個函數中實現。
這裏到了本文的高潮部分了,也是很多兄弟姐妹們迫切希望直接拷貝然後加入到自己的代碼中去的地方。但是請注意,我也會犯錯,這裏的代碼我也許說的並不對,各位看官如果發現了錯誤請指出及批判。
我們先分析s3c64xx-spi.0和s3c64xx-spi.1是在什麼地方定義的,因為它與平台架構相關,所以我們到arch/arm/mach-s3c64xx目錄中找到了dev-spi.c,這個代碼中定義了s3c64xx_device_spi0和s3c64xx_device_spi1也就是設備s3c64xx-spi.0和s3c64xx-spi.1的實現代碼,所以dev-spi.c必須加入到編譯中,需要修改相關的編譯配置文件。
然後,我們在分析mini6410的初始化代碼,注意代碼中的這個函數
platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));
可以看出該函數是批量初始化了一批設備,所以將
&s3c64xx_device_spi0,
&s3c64xx_device_spi1,
加入到mini6410_devices結構中,就可以將設備s3c64xx-spi.0和s3c64xx-spi.1進行初始化,
但是事情沒有做完,s3c64xx_device_spi0和s3c64xx_device_spi1結構中的片選數量和時鍾並未定義,需要根據開發板的具體情況進行定義,
函數原型 void __init s3c64xx_spi_set_info(int cntrlr, int src_clk_nr, int num_cs)
這裏具體的執行代碼如下:
s3c64xx_spi_set_info(0,0,1);
s3c64xx_spi_set_info(1,0,1);
到這裏各位已經可以在平台目錄中看見s3c64xx_device_spi0和s3c64xx_device_spi1,在類spi_master目錄下看見spi0和spi1了
下麵介紹如何增加spi0.0和spi1.0設備?
各位請仔細分析在spi.h中的這段代碼
/*---------------------------------------------------------------------------*/
/*
* INTERFACE between board init code and SPI infrastructure.
*
* No SPI driver ever sees these SPI device table segments, but
* it's how the SPI core (or adapters that get hotplugged) grows
* the driver model tree.
*
* As a rule, SPI devices can't be probed. Instead, board init code
* provides a table listing the devices which are present, with enough
* information to bind and set up the device's driver. There's basic
* support for nonstatic configurations too; enough to handle adding
* parport adapters, or microcontrollers acting as USB-to-SPI bridges.
*/
/**
* struct spi_board_info - board-specific template for a SPI device
* @modalias: Initializes spi_device.modalias; identifies the driver.
* @platform_data: Initializes spi_device.platform_data; the particular
* data stored there is driver-specific.
* @controller_data: Initializes spi_device.controller_data; some
* controllers need hints about hardware setup, e.g. for DMA.
* @irq: Initializes spi_device.irq; depends on how the board is wired.
* @max_speed_hz: Initializes spi_device.max_speed_hz; based on limits
* from the chip datasheet and board-specific signal quality issues.
* @bus_num: Identifies which spi_master parents the spi_device; unused
* by spi_new_device(), and otherwise depends on board wiring.
* @chip_select: Initializes spi_device.chip_select; depends on how
* the board is wired.
* @mode: Initializes spi_device.mode; based on the chip datasheet, board
* wiring (some devices support both 3WIRE and standard modes), and
* possibly presence of an inverter in the chipselect path.
*
* When adding new SPI devices to the device tree, these structures serve
* as a partial device template. They hold information which can't always
* be determined by drivers. Information that probe() can establish (such
* as the default transfer wordsize) is not included here.
*
* These structures are used in two places. Their primary role is to
* be stored in tables of board-specific device descriptors, which are
* declared early in board initialization and then used (much later) to
* populate a controller's device tree after the that controller's driver
* initializes. A secondary (and atypical) role is as a parameter to
* spi_new_device() call, which happens after those controller drivers
* are active in some dynamic board configuration models.
*/
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE];
const void *platform_data;
void *controller_data;
int irq;
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz;
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u8 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*/
};
這段代碼中,spi_board_info 是spi總線上從設備的定義由於和硬件的具體種類有非常大的關係,所以需要針對具體的設備類型進行定義,這裏先給一個例子
先在mach-mini6410.c中增加如下代碼
//zhuyong add start
static void cs_set_level(unsigned line_id, int lvl) {
gpio_direction_output(line_id, lvl);
};
static struct s3c64xx_spi_csinfo s3c64xx_spi0_csinfo = {
.fb_delay=100,
.line=S3C64XX_GPC(3),
.set_level=cs_set_level,
};
static struct spi_board_info s3c6410_spi0_board[] = {
[0] = {
.modalias = "spidev",
.bus_num= 0,
.chip_select= 0, //必須小於s3c6410_spi0_platdata.num_cs
.irq =IRQ_SPI0,
.max_speed_hz= 500*1000,
.mode=SPI_MODE_0,
.controller_data=&s3c64xx_spi0_csinfo,
},
};
static struct s3c64xx_spi_csinfo s3c64xx_spi1_csinfo = {
.fb_delay=100,
.line=S3C64XX_GPC(7),
.set_level=cs_set_level,
};
static struct spi_board_info s3c6410_spi1_board[] = {
[0] = {
.modalias = "spidev",
.bus_num= 1,//代表使用芯片的第二個spi模塊
.chip_select= 0, //必須小於s3c6410_spi1_platdata.num_cs
.irq = IRQ_SPI1,
.max_speed_hz = 500*1000,
.mode=SPI_MODE_0,
.controller_data=&s3c64xx_spi1_csinfo,
},
};
//zhuyong add end
為什麼要定義s3c64xx_spi0_csinfo?這是spi從設備必須的片選信號的定義,片選信號中有一個關鍵的函數set_level需要各位自己進行定義,這個函數由係統在打開設備時調用,即設置該SPI從設備的片選信號,一開始由於沒有設置該設備的片選信號,導致spi0.0和spi1.0一直加載不成功。
需要注意是,這裏僅僅隻是給出了一個例子,s3c64xx_spi0_info中還有很多具體的設置需要在該結構中進行定義。
最後在函數 mini6410_machine_init中,增加如下代碼
spi_register_board_info(s3c6410_spi0_board, ARRAY_SIZE(s3c6410_spi0_board));
spi_register_board_info(s3c6410_spi1_board, ARRAY_SIZE(s3c6410_spi1_board));
這段代碼必須加在
s3c64xx_spi_set_info(0,0,1);
s3c64xx_spi_set_info(1,0,1);
之後,至於為什麼請各位自己思考,到這裏所有的spi設備都已經初始化結束,在目錄中已經可以看見本文開頭的文件結構了。
下麵就有兩個議題了,
spi用戶空間接口如何使用?
spi子係統是如何運行的?
最後更新:2017-04-03 07:57:07