該系列文章将分為四個部分:
第一部分,将對SPI子系統整體進行描述,同時給出SPI的相關資料結構,最後描述SPI總線的注冊。基于S3C2440的嵌入式Linux驅動——SPI子系統解讀(一)
第二部分,即本篇文章,該文将對SPI的主要制器(master)驅動進行描述。
第三部分,該文将對SPI裝置驅動,也稱protocol 驅動,進行講解。基于S3C2440的嵌入式Linux驅動——SPI子系統解讀(三)
第四部分,通過SPI裝置驅動留給使用者層的API,我們将從上到下描述資料是如何通過SPI的protocol 驅動,由bitbang中轉,最後由master驅動将資料傳輸出
去。 基于S3C2440的嵌入式Linux驅動——SPI子系統解讀(四)
本文屬于第二部分。
4. 主要制器驅動程式
4.1 定義 platform device
下列資料結構位于arch/arm/plat-s3c24XX/devs.c
/* SPI (0) */
static struct resource s3c_spi0_resource[] = {
[0] = {
.start = S3C24XX_PA_SPI,
.end = S3C24XX_PA_SPI + 0x1f,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI0,
.end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
struct platform_device s3c_device_spi0 = {
.name = "s3c2410-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &s3c_device_spi0_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
platform裝置給出了spi0接口的寄存器位址資源以及IRQ資源。注意其裝置名為s3c2410-spi。
4.2 定義platform driver
下列函數位于deivers/spi/s3c24xx.c。
MODULE_ALIAS("platform:s3c2410-spi");
static struct platform_driver s3c24xx_spi_driver = {
.remove = __exit_p(s3c24xx_spi_remove),
.suspend = s3c24xx_spi_suspend,
.resume = s3c24xx_spi_resume,
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
},
};
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//裝置不可熱插拔,是以使用該函數,而不是platform_driver_register
}
static void __exit s3c24xx_spi_exit(void)
{
platform_driver_unregister(&s3c24xx_spi_driver);
}
module_init(s3c24xx_spi_init);
module_exit(s3c24xx_spi_exit);
調用了platform_driver_probe注冊platform驅動,注冊完成以後将會調用platform的s3c24xx_spi_probe函數。
NOTE:platform驅動的name和platform device的name是相同的。
4.2.1 s3c24xx_spi_probe函數
下列函數位于deivers/spi/s3c24xx.c。
static int __init s3c24xx_spi_probe(struct platform_device *pdev)
{
struct s3c2410_spi_info *pdata;
struct s3c24xx_spi *hw;
struct spi_master *master;
struct resource *res;
int err = 0;
/*配置設定master結構體,其中包括s3c24xx_spi結構的記憶體空間,使用master.dev.driver_data指向它*/
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
/*獲得s3c24xx_spi結構,并清0該結構*/
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master); /*儲存master結構體,同時增加引用計數*/
hw->pdata = pdata = pdev->dev.platform_data; /*擷取s3c2410_spi_info結構體指針*/
hw->dev = &pdev->dev; /*儲存platform裝置的dev*/
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data supplied\n");
err = -ENOENT;
goto err_no_pdata;
}
platform_set_drvdata(pdev, hw); /*讓platform_devuce.dev.driver_data 指向 s3c24xx_spi*/
init_completion(&hw->done); /*初始化completion*/
/* setup the master state. */ /*填充master結構體的兩個字段*/
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = pdata->bus_num;
/* setup the state for the bitbang driver */ /*填充bitbang字段*/
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->bitbang.master->setup = s3c24xx_spi_setup;
dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /*擷取IO資源*/
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
err = -ENOENT;
goto err_no_iores;
}
hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, /*申請IO記憶體*/
pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region\n");
err = -ENXIO;
goto err_no_iores;
}
hw->regs = ioremap(res->start, (res->end - res->start)+1); /*建立映射*/
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
err = -ENXIO;
goto err_no_iomap;
}
hw->irq = platform_get_irq(pdev, 0); /*擷取irq号*/
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_no_irq;
}
err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); /*申請spi中斷,ISR為 s3c24xx_spi_irq*/
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_no_irq;
}
hw->clk = clk_get(&pdev->dev, "spi"); /*擷取spi時鐘*/
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/* setup any gpio we can */
if (!pdata->set_cs) { /*沒有定義配置設定CS管腳的函數*/
if (pdata->pin_cs < 0) { /*pin_cs為cs管腳*/
dev_err(&pdev->dev, "No chipselect pin\n");
goto err_register;
}
err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));/*申請IO位址*/
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
hw->set_cs = s3c24xx_spi_gpiocs; /*給出配置設定cs管腳函數*/
gpio_direction_output(pdata->pin_cs, 1); /*設定該管腳為輸出,貌似多次一舉,在probe已經調用過該函數*/
} else
hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw); /*spi控制器初始化*/
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
err_register:
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_free(pdata->pin_cs);
clk_disable(hw->clk);
clk_put(hw->clk);
err_no_clk:
free_irq(hw->irq, hw);
err_no_irq:
iounmap(hw->regs);
err_no_iomap:
release_resource(hw->ioarea); /*先釋放資源*/
kfree(hw->ioarea); /*再釋放空間*/
err_no_iores:
err_no_pdata:
spi_master_put(hw->master);; /*減少引用計數*/
err_nomem:
return err;
}
/**
* spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus
* @size: how much zeroed driver-private data to allocate; the pointer to this
* memory is in the driver_data field of the returned device,
* accessible with spi_master_get_devdata().
* Context: can sleep
*
* This call is used only by SPI master controller drivers, which are the
* only ones directly touching chip registers. It's how they allocate
* an spi_master structure, prior to calling spi_register_master().
*
* This must be called from context that can sleep. It returns the SPI
* master structure on success, else NULL.
*
* The caller is responsible for assigning the bus number and initializing
* the master's methods before calling spi_register_master(); and (after errors
* adding the device) calling spi_master_put() to prevent a memory leak.
*/
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
master = kzalloc(size + sizeof *master, GFP_KERNEL);
if (!master)
return NULL;
device_initialize(&master->dev);
master->dev.class = &spi_master_class;
master->dev.parent = get_device(dev);
spi_master_set_devdata(master, &master[1]);
return master;
}
EXPORT_SYMBOL_GPL(spi_alloc_master);
該函數首先為spi_master結構體以及s3c24xx_spi結構體配置設定了空間,同時,spi_master.dev.driver_data指向了s3c24xx_spi。
s3c24xx_spi結構如下:
struct s3c24xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *regs;
int irq;
int len;
int count;
void (*set_cs)(struct s3c2410_spi_info *spi,
int cs, int pol);
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
struct clk *clk;
struct resource *ioarea;
struct spi_master *master;
struct spi_device *curdev;
struct device *dev;
struct s3c2410_spi_info *pdata;
};
接着執行了該條語句:
hw->pdata = pdata = pdev->dev.platform_data;
NOTE:在這裡擷取platform_device.dev.platform_data,也就是平台裝置的相關資料,而在4.1小結中的arch/arm/plat-s3c24XX/devs.c檔案中并沒有發現platform_data的身影,是以這正式需要我們移植的地方。
随後初始化了completion,這個東東将用于實作同步I/O,詳見下文。之後,為master定義了setup方法,為bitbang定義了3個方法。
接着擷取了一系列的資源,同時注冊了中斷服務程式。接着調用s3c24xx_spi_initialsetup初始化控制器。我們來看下該函數。
該函數位于下列函數位于deivers/spi/s3c24xx.c。
static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
{
/* for the moment, permanently enable the clock */
clk_enable(hw->clk); /*使能時鐘*/
/* program defaults into the registers */
writeb(0xff, hw->regs + S3C2410_SPPRE); /*設定預分頻系數,baudrate=pclk/2/(prescaler value+1)*/
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);/*使能master out keep*/
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);/*master, interrupt mode*/
if (hw->pdata) {
if (hw->set_cs == s3c24xx_spi_gpiocs) /*set_cs 在probe方法中設定為s3c24xx_spi_gpiocs*/
gpio_direction_output(hw->pdata->pin_cs, 1); /*設定該管腳為輸出,貌似多次一舉,在probe已經調用過該函數*/
*/
if (hw->pdata->gpio_setup)
hw->pdata->gpio_setup(hw->pdata, 1);
}
}
注意,這裡設定了SPI0主要制器工作在master方式,使用中斷模式。
最後調用了spi_bitbang_start函數,該函數非常重要,在下一小節中單獨講解。
4.2.2 spi_bitbang_start函數
下列函數位于drivers/spi/spi_bitbang.c
/**
* spi_bitbang_start - start up a polled/bitbanging SPI master driver
* @bitbang: driver handle
*
* Caller should have zero-initialized all parts of the structure, and then
* provided callbacks for chip selection and I/O loops. If the master has
* a transfer method, its final step should call spi_bitbang_transfer; or,
* that's the default if the transfer routine is not initialized. It should
* also set up the bus number and number of chipselects.
*
* For i/o loops, provide callbacks either per-word (for bitbanging, or for
* hardware that basically exposes a shift register) or per-spi_transfer
* (which takes better advantage of hardware like fifos or DMA engines).
*
* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup,
* spi_bitbang_cleanup and spi_bitbang_setup_transfer to handle those spi
* master methods. Those methods are the defaults if the bitbang->txrx_bufs
* routine isn't initialized.
*
* This routine registers the spi_master, which will process requests in a
* dedicated task, keeping IRQs unblocked most of the time. To stop
* processing those requests, call spi_bitbang_stop().
*/
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work, bitbang_work); /*初始化工作,工作為bitbang_work*/
spin_lock_init(&bitbang->lock); /*初始化自旋鎖*/
INIT_LIST_HEAD(&bitbang->queue); /*初始化連結清單頭,連結清單為雙向循環連結清單*/
if (!bitbang->master->transfer) /*master的transfer方法沒有定義過*/
bitbang->master->transfer = spi_bitbang_transfer; /*使用spi_bitbang_transfe方法*/
if (!bitbang->txrx_bufs) { /*如果bitbang沒有txrx_bufs方法,在probe函數中定義過該方法*/
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
} else if (!bitbang->master->setup) /*setup方法在probe函數中有定義*/
return -EINVAL;
/* this task is the only thing to touch the SPI bits */
bitbang->busy = 0;
bitbang->workqueue = create_singlethread_workqueue( /*建立工作隊列*/
dev_name(bitbang->master->dev.parent));
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
status = spi_register_master(bitbang->master); /*注冊spi控制器*/
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
在這裡,定義了控制器的transfer方法為spi_bitbang_transfer。建立了一個工作隊列和一個工作bitbang_work,同時建立了一個連結清單。這些東東的作用将在後面介紹。
最後,調用了spi_register_master函數,該函數将完成SPI控制器的注冊,其中還牽涉到spi_device的注冊。是以該函數也非常重要。我們來看看這個函數
下列函數位于drivers/spi/spi_bitbang.c
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
* Context: can sleep
*
* SPI master controllers connect to their drivers using some non-SPI bus,
* such as the platform bus. The final stage of probe() in that code
* includes calling spi_register_master() to hook up to this SPI bus glue.
*
* SPI controllers use board specific (often SOC specific) bus numbers,
* and board-specific addressing for SPI devices combines those numbers
* with chip select numbers. Since SPI does not directly support dynamic
* device identification, boards need configuration tables telling which
* chip is at which address.
*
* This must be called from context that can sleep. It returns zero on
* success, else a negative error code (dropping the master's refcount).
* After a successful return, the caller is responsible for calling
* spi_unregister_master().
*/
int spi_register_master(struct spi_master *master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device *dev = master->dev.parent;
int status = -ENODEV;
int dynamic = 0;
if (!dev)
return -ENODEV;
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect == 0)
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
/* FIXME switch to an IDR based scheme, something like
* I2C now uses, so we can't run out of "dynamic" IDs
*/
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev); /*注冊裝置*/
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
/* populate children from any spi device tables */
scan_boardinfo(master);
status = 0;
done:
return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);
該函數中,執行了相關的檢查,然後注冊了master裝置,随後調用了scan_boardinfo。函數如下:
下列函數位于drivers/spi/spi.c
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
mutex_lock(&board_lock);
/*以board_list為連結清單頭,周遊所有的boardinfo結構,連結清單由spi_register_board_info添加*/
list_for_each_entry(bi, &board_list, list) {
struct spi_board_info *chip = bi->board_info;
unsigned n;
/*周遊該boardinfo指向的spi_board_info數組*/
for (n = bi->n_board_info; n > 0; n--, chip++) {
if (chip->bus_num != master->bus_num) /*通過bus_num對spi裝置和master進行比對*/
continue;
/* NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
/*執行到此,表示比對完成,SPI裝置由該SPI接口來控制,開始建立spi_device*/
(void) spi_new_device(master, chip);
}
}
mutex_unlock(&board_lock);
}
NOTE:這個函數通過boardinfo周遊的spi_board_info數組,而spi_board_info是在核心初始化過程中由spi_register_board_info進行注冊的,在
linux/arch/arm/mach-s3c2440/mach-smdk2440.c中并沒有調用過該函數,是以這也是需要移植的地方。
S3C2440共有兩個接口:spi0和spi1。chip->bus_num表示該裝置使用哪個spi接口,而master->bus_num正好表示了目前的接口。
該函數中,周遊spi_board_info,通過bus_num完成SPI裝置和SPI控制器的比對,比對成功則開始建立spi_device裝置,該過程通過調用spi_new_device實作。我們接着看下這個函數。
下列函數位于drivers/spi/spi.c
/**
* spi_new_device - instantiate one new SPI device
* @master: Controller to which device is connected
* @chip: Describes the SPI device
* Context: can sleep
*
* On typical mainboards, this is purely internal; and it's not needed
* after board init creates the hard-wired devices. Some development
* platforms may not be able to use spi_register_board_info though, and
* this is exported so that for example a USB or parport based adapter
* driver could add devices (which it would learn about out-of-band).
*
* Returns the new device, or NULL.
*/
struct spi_device *spi_new_device(struct spi_master *master,
struct spi_board_info *chip)
{
struct spi_device *proxy;
int status;
/* NOTE: caller did any chip->bus_num checks necessary.
*
* Also, unless we change the return value convention to use
* error-or-pointer (not NULL-or-pointer), troubleshootability
* suggests syslogged diagnostics are best here (ugh).
*/
proxy = spi_alloc_device(master); /*配置設定spi_device結構,并初始化一些字段*/
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
/*從spi_board_info擷取SPI從裝置的參數*/
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
proxy->irq = chip->irq;
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
proxy->dev.platform_data = (void *) chip->platform_data;
proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL;
status = spi_add_device(proxy);
if (status < 0) {
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
EXPORT_SYMBOL_GPL(spi_new_device);
首先,建立了spi_device結構,讓後通過闆級資訊spi_board_info将SPI從裝置的相關資訊複制給spi_device結構,進而完成了spi_device結構的定義,最後調用spi_add_device,完成spi_device的注冊。
看下spi_add_device函數,該函數位于drivers/spi/spi.c
/**
* spi_add_device - Add spi_device allocated with spi_alloc_device
* @spi: spi_device to register
*
* Companion function to spi_alloc_device. Devices allocated with
* spi_alloc_device can be added onto the spi bus with this function.
*
* Returns 0 on success; negative errno on failure
*/
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev = spi->master->dev.parent;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= spi->master->num_chipselect) {
dev_err(dev, "cs%d >= max %d\n",
spi->chip_select,
spi->master->num_chipselect);
return -EINVAL;
}
/* Set the bus ID string */
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
/* We need to make sure there's no other device with this
* chipselect **BEFORE** we call setup(), else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))
!= NULL) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
status = -EBUSY;
goto done;
}
/* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
*/
status = spi->master->setup(spi); /*調用setup方法,即s3c24xx_spi_setup函數*/
if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n",
"setup", dev_name(&spi->dev), status);
goto done;
}
/* Device may be bound to an active driver when this returns */
status = device_add(&spi->dev); /*注冊SPI_device*/
if (status < 0)
dev_err(dev, "can't %s %s, status %d\n",
"add", dev_name(&spi->dev), status);
else
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
EXPORT_SYMBOL_GPL(spi_add_device);
在注冊spi_device之前,調用了master的setup方法,該方法又将調用s3c24xx_spi_setupxfer和s3c24xx_spi_chipsel函數。
s3c24xx_spi_setupxfer函數計算預分頻系數并寫入寄存器。
s3c24xx_spi_chipsel函數用于禁止或使能CS信号。當使能CS信号時,要設定控制寄存器。這裡調用是禁止CS信号。
下列代碼位于 deivers/spi/s3c24xx.c。
#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
gpio_set_value(spi->pin_cs, pol);
}
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
unsigned int spcon;
switch (value) {
case BITBANG_CS_INACTIVE: /*CS無效時*/
hw->set_cs(hw->pdata, spi->chip_select, cspol^1);/*即調用s3c24xx_spi_gpiocs使CS無效*/
break;
case BITBANG_CS_ACTIVE: /*CS有效時*/
spcon = readb(hw->regs + S3C2410_SPCON); /*擷取目前SPCON寄存器的值*/
/*開始設定工作模式*/
if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB;
else
spcon &= ~S3C2410_SPCON_CPHA_FMTB;
if (spi->mode & SPI_CPOL)
spcon |= S3C2410_SPCON_CPOL_HIGH;
else
spcon &= ~S3C2410_SPCON_CPOL_HIGH;
/*激活時鐘sck輸出*/
spcon |= S3C2410_SPCON_ENSCK;
/* write new configration */
writeb(spcon, hw->regs + S3C2410_SPCON); /*儲存新的配置*/
hw->set_cs(hw->pdata, spi->chip_select, cspol);/*即調用s3c24xx_spi_gpiocs使CS有效*/
break;
}
}
static int s3c24xx_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int bpw;
unsigned int hz;
unsigned int div;
/*沒有transfer,則使用spi_device進行配置*/
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
if (bpw != 8) {
dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
return -EINVAL;
}
div = clk_get_rate(hw->clk) / hz;
/* is clk = pclk / (2 * (pre+1)), or is it
* clk = (pclk * 2) / ( pre + 1) */
/*計算預分頻系數*/
div /= 2;
if (div > 0)
div -= 1;
if (div > 255) /*隻有8位*/
div = 255;
dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz);
writeb(div, hw->regs + S3C2410_SPPRE); /*設定預分頻系數*/
spin_lock(&hw->bitbang.lock); /*自旋鎖加鎖*/
if (!hw->bitbang.busy) { /*如果不忙*/
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);/*即調用s3c24xx_spi_chipsel使CS無效*/
/* need to ndelay for 0.5 clocktick ? */
}
spin_unlock(&hw->bitbang.lock);
return 0;
}
/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
static int s3c24xx_spi_setup(struct spi_device *spi) /*maser.setup方法*/
{
int ret;
if (!spi->bits_per_word)
spi->bits_per_word = 8; /*沒有設定則使用8位*/
if (spi->mode & ~MODEBITS) { /*檢查mode是否有錯*/
dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
spi->mode & ~MODEBITS);
return -EINVAL;
}
ret = s3c24xx_spi_setupxfer(spi, NULL);
if (ret < 0) {
dev_err(&spi->dev, "setupxfer returned %d\n", ret);
return ret;
}
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
__func__, spi->mode, spi->bits_per_word,
spi->max_speed_hz);
return 0;
}
至此,在probe函數中, 由spi_bitbang_start調用所引起的一系列函數調用都已講解完畢。下面總結下整個調用過程:
可以看到,調用spi_bitbang_start以後,spi_master和spi_device都将被注冊到核心中。
下面來看下platform driver的其他幾個方法。
4.2.3 remove,suspend以及resume方法
static int __exit s3c24xx_spi_remove(struct platform_device *dev)
{
struct s3c24xx_spi *hw = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
spi_unregister_master(hw->master); /*登出spi主要制器*/
clk_disable(hw->clk); /*禁止時鐘*/
clk_put(hw->clk); /*釋放CLK*/
free_irq(hw->irq, hw); /*登出IRQ*/
iounmap(hw->regs); /*解除映射*/
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_free(hw->pdata->pin_cs); /*釋放用于cs的gpio*/
release_resource(hw->ioarea);
kfree(hw->ioarea);
spi_master_put(hw->master); /*減少master引用計數*/
return 0;
}
#ifdef CONFIG_PM /*如果定義了電源管理*/
static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct s3c24xx_spi *hw = platform_get_drvdata(pdev);
if (hw->pdata && hw->pdata->gpio_setup)
hw->pdata->gpio_setup(hw->pdata, 0);
clk_disable(hw->clk);
return 0;
}
static int s3c24xx_spi_resume(struct platform_device *pdev)
{
struct s3c24xx_spi *hw = platform_get_drvdata(pdev);
s3c24xx_spi_initialsetup(hw);
return 0;
}
#else
#define s3c24xx_spi_suspend NULL
#define s3c24xx_spi_resume NULL
#endif
至此,master 驅動的大體結構都已分析完畢,随後第三篇文章将介紹spi裝置驅動。