//整個probe主要包含以下幾步,與其它的ARM芯片很相似
//(1)填充三個結構體struct mxc_spi_master,struct spi_master,struct mxc_spi
//(2)申請IO資源,中斷
//(3)SPI寄存器配置
//(4)spi_bitbang_start(即調用spi_register_master)
//(5)spi_new_device
static int mxc_spi_probe(struct platform_device *pdev)
{
//spi私有數據結構體,imx芯片獨有,主要存儲板級配置文件中設置的一些參數
//個人覺得這個結構體有點多餘,還沒弄清楚原因?
struct mxc_spi_master *mxc_platform_info;
//描述spi控制器結構體,spi驅動通用
struct spi_master *master;
struct mxc_spi *master_drv_data = NULL;
//一般的ARM控制器的SPI驅動都會包含以上三個結構體(具體名稱不用)
struct resource *res;
unsigned int spi_ver, wml;
int ret = -ENODEV;
/* Get the platform specific data for this master device */
//爲什麼這裏可以強制轉換struct platform_data爲struct mxc_spi_master?
//因爲platform_data是一個void類型的指針
mxc_platform_info = (struct mxc_spi_master *)pdev->dev.platform_data;
if (!mxc_platform_info) {
dev_err(&pdev->dev, "can't get the platform data for CSPI\n");
return -EINVAL;
}
/* Allocate SPI master controller */
master = spi_alloc_master(&pdev->dev, sizeof(struct mxc_spi));
if (!master) {
dev_err(&pdev->dev, "can't alloc for spi_master\n");
return -ENOMEM;
}
/* Set this device's driver data to master */
//保存master到pdev
platform_set_drvdata(pdev, master);
/* Set this master's data from platform_info */
//主機控制器編號,如果板子上有多個spi總線,靠這個域區分
master->bus_num = pdev->id + 1;
//支持spi設備個數
master->num_chipselect = mxc_platform_info->maxchipselect;
//模式標誌位
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
master->num_chipselect += 1;
#endif
/* Set the master controller driver data for this master */
//spi_master_get_devdata調用dev_set_drvdata獲取設備私有數據
//讀取保存在master->dev中的私有數據
//這個私有數據是在上面的spi_alloc_master中配置的。
master_drv_data = spi_master_get_devdata(master);
//mxc_bitbang是一個spi_bitbang結構體,struct spi_bitbang 是具體的負責數據傳輸的結構體
master_drv_data->mxc_bitbang.master = spi_master_get(master);
//把存儲在mxc_platform_info中的一些參數(板級配置文件中設置)存儲到master_drv_data
if (mxc_platform_info->chipselect_active)
master_drv_data->chipselect_active =
mxc_platform_info->chipselect_active;
if (mxc_platform_info->chipselect_inactive)
master_drv_data->chipselect_inactive =
mxc_platform_info->chipselect_inactive;
/* Identify SPI version */
//根據扳級配置文件中設置的版本
spi_ver = mxc_platform_info->spi_version;
if (spi_ver == 7) {
master_drv_data->spi_ver_def = &spi_ver_0_7;
} else if (spi_ver == 5) {
master_drv_data->spi_ver_def = &spi_ver_0_5;
} else if (spi_ver == 4) {
master_drv_data->spi_ver_def = &spi_ver_0_4;
} else if (spi_ver == 0) {
master_drv_data->spi_ver_def = &spi_ver_0_0;
} else if (spi_ver == 23) {
master_drv_data->spi_ver_def = &spi_ver_2_3;
}
dev_dbg(&pdev->dev, "SPI_REV 0.%d\n", spi_ver);
/* Set the master bitbang data */
//SPI傳輸的各個函數
master_drv_data->mxc_bitbang.chipselect = mxc_spi_chipselect;
master_drv_data->mxc_bitbang.txrx_bufs = mxc_spi_transfer;
//該函數做一些初始化的工作
master_drv_data->mxc_bitbang.master->setup = mxc_spi_setup;
master_drv_data->mxc_bitbang.master->cleanup = mxc_spi_cleanup;
master_drv_data->mxc_bitbang.setup_transfer = mxc_spi_setup_transfer;
/* Initialize the completion object */
//completion是內核中一個輕量級機制,允許一個線程告訴另一個線程工作已完成
init_completion(&master_drv_data->xfer_done);
/* Set the master controller register addresses and irqs */
//獲取板級配置文件中設置的資源
master_drv_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!master_drv_data->res) {
dev_err(&pdev->dev, "can't get platform resource for CSPI%d\n",
master->bus_num);
ret = -ENOMEM;
goto err;
}
//檢測申請的資源是否可用,並把資源標誌爲已用
if (!request_mem_region(master_drv_data->res->start,
master_drv_data->res->end -
master_drv_data->res->start + 1, pdev->name)) {
dev_err(&pdev->dev, "request_mem_region failed for CSPI%d\n",
master->bus_num);
ret = -ENOMEM;
goto err;
}
//映射虛擬內存
master_drv_data->base = ioremap(master_drv_data->res->start,
master_drv_data->res->end - master_drv_data->res->start + 1);
if (!master_drv_data->base) {
dev_err(&pdev->dev, "invalid base address for CSPI%d\n",
master->bus_num);
ret = -EINVAL;
goto err1;
}
//獲取板級配置文件中設置的中斷號
master_drv_data->irq = platform_get_irq(pdev, 0);
if (master_drv_data->irq < 0) {
dev_err(&pdev->dev, "can't get IRQ for CSPI%d\n",
master->bus_num);
ret = -EINVAL;
goto err1;
}
/* Register for SPI Interrupt */
//註冊中斷函數mxc_spi_isr
ret = request_irq(master_drv_data->irq, mxc_spi_isr,
0, "CSPI_IRQ", master_drv_data);
if (ret != 0) {
dev_err(&pdev->dev, "request_irq failed for CSPI%d\n",
master->bus_num);
goto err1;
}
master_drv_data->dev = &pdev->dev;
/* Setup the DMA */
//如果設置了dma,(這個在我的板級配置文件中未配置)
master_drv_data->usedma = 0;
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (res) {
master_drv_data->dma_tx_id = res->start;
master_drv_data->dma_tx_id = res->start;
if (pdev->dev.dma_mask == NULL)
dev_warn(&pdev->dev, "no dma mask\n");
else
master_drv_data->usedma = 1;
}
if (master_drv_data->usedma) {
master_drv_data->dma_tx_ch =
mxc_dma_request(master_drv_data->dma_tx_id, "mxc_spi");
if (master_drv_data->dma_tx_ch < 0) {
dev_info(&pdev->dev, "Can't allocate RX DMA ch\n");
master_drv_data->usedma = 0;
ret = -ENXIO;
goto err_no_txdma;
}
mxc_dma_callback_set(master_drv_data->dma_tx_ch,
mxc_spi_dma_tx_callback,
(void *)master_drv_data);
/* Allocate tmp_buf for tx_buf */
master_drv_data->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL);
if (master_drv_data->tmp_buf == NULL) {
ret = -ENOMEM;
goto err_tmp_buf_alloc;
}
}
/* Setup any GPIO active */
//配置SPI口IO,一般高級ARM芯片有多個SPI口,配置哪個由bus_num決定,bus_num是板級配置文件中設置
gpio_spi_active(master->bus_num - 1);
/* Enable the CSPI Clock, CSPI Module, set as a master */
//i.mx536的SPI控制器的寄存器配置
master_drv_data->ctrl_addr =
master_drv_data->base + master_drv_data->spi_ver_def->ctrl_reg_addr;
master_drv_data->dma_addr =
master_drv_data->base + master_drv_data->spi_ver_def->dma_reg_addr;
master_drv_data->stat_addr =
master_drv_data->base + master_drv_data->spi_ver_def->stat_reg_addr;
master_drv_data->period_addr =
master_drv_data->base +
master_drv_data->spi_ver_def->period_reg_addr;
master_drv_data->test_addr =
master_drv_data->base + master_drv_data->spi_ver_def->test_reg_addr;
master_drv_data->reset_addr =
master_drv_data->base +
master_drv_data->spi_ver_def->reset_reg_addr;
//開啓SPI時鐘
master_drv_data->clk = clk_get(&pdev->dev, "cspi_clk");
clk_enable(master_drv_data->clk);
//獲取時鐘頻率
master_drv_data->spi_ipg_clk = clk_get_rate(master_drv_data->clk);
__raw_writel(master_drv_data->spi_ver_def->reset_start,
master_drv_data->reset_addr);
udelay(1);
__raw_writel((master_drv_data->spi_ver_def->spi_enable +
master_drv_data->spi_ver_def->master_enable),
master_drv_data->base + MXC_CSPICTRL);
__raw_writel(MXC_CSPIPERIOD_32KHZ, master_drv_data->period_addr);
__raw_writel(0, MXC_CSPIINT + master_drv_data->ctrl_addr);
if (master_drv_data->usedma) {
/* Set water mark level to be the half of fifo_size in DMA */
wml = master_drv_data->spi_ver_def->fifo_size / 2;
wml = wml << master_drv_data->spi_ver_def->tx_wml_shift;
__raw_writel((__raw_readl(master_drv_data->dma_addr)
& ~master_drv_data->spi_ver_def->tx_wml_mask)
| wml,
master_drv_data->dma_addr);
}
/* Start the SPI Master Controller driver */
//啓動SPI控制器
//最終調用spi_register_master來註冊spi控制器
ret = spi_bitbang_start(&master_drv_data->mxc_bitbang);
if (ret != 0)
goto err2;
printk(KERN_INFO "CSPI: %s-%d probed\n", pdev->name, pdev->id);
#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
{
int i;
struct spi_board_info *bi = &loopback_info[0];
for (i = 0; i < ARRAY_SIZE(loopback_info); i++, bi++) {
if (bi->bus_num != master->bus_num)
continue;
dev_info(&pdev->dev,
"registering loopback device '%s'\n",
bi->modalias);
spi_new_device(master, bi);
}
}
#endif
clk_disable(master_drv_data->clk);
return ret;
err2:
gpio_spi_inactive(master->bus_num - 1);
clk_disable(master_drv_data->clk);
clk_put(master_drv_data->clk);
if (master_drv_data->usedma)
kfree(master_drv_data->tmp_buf);
err_tmp_buf_alloc:
if (master_drv_data->usedma)
mxc_dma_free(master_drv_data->dma_tx_ch);
err_no_txdma:
free_irq(master_drv_data->irq, master_drv_data);
err1:
//最終調用free函數,釋放內存
iounmap(master_drv_data->base);
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
err:
spi_master_put(master);
kfree(master);
platform_set_drvdata(pdev, NULL);
return ret;
}