linux4.1.36 解决 SPI 时钟找不到 不生成设备 device

最初的问题是 编译内核添加了 spi 支持,配置了 board 后,加载25q64驱动不执行probe 函数。
然后发现是,spi-s3c24xx.c 中的 probe 没有执行完就退出了 没有生成 spi-master
/drivers/spi/spi-s3c24xx.c
定位在 出错
hw->clk = devm_clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_pdata;
}
对应下面
struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
struct clk *clk;
if (dev) {
clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
return clk;
}
return clk_get_sys(dev_id, con_id);
}
of_是设备树相关的函数。 clk_get_sys 是 调用 clk_get 时 传的 NULL 的情况下调用。
看一下 clk_get_sys(dev_id, con_id) 实现
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
struct clk_lookup *cl;
struct clk *clk = NULL;
mutex_lock(&clocks_mutex);
cl = clk_find(dev_id, con_id);
if (!cl)
goto out;
clk = __clk_create_clk(__clk_get_hw(cl->clk), dev_id, con_id);
if (IS_ERR(clk))
goto out;
if (!__clk_get(clk)) {
__clk_free_clk(clk);
cl = NULL;
goto out;
}
out:
mutex_unlock(&clocks_mutex);
return cl ? clk : ERR_PTR(-ENOENT);
}
查找 clk_find(dev_id, con_id); 函数
static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
{
struct clk_lookup *p, *cl = NULL;
int match, best_found = 0, best_possible = 0;
if (dev_id)
best_possible += 2;
if (con_id)
best_possible += 1;
list_for_each_entry(p, &clocks, node) {
match = 0;
if (p->dev_id) {
if (!dev_id || strcmp(p->dev_id, dev_id))
continue;
match += 2;
}
if (p->con_id) {
if (!con_id || strcmp(p->con_id, con_id))
continue;
match += 1;
}
if (match > best_found) {
cl = p;
if (match != best_possible)
best_found = match;
else
break;
}
}
return cl;
}
检查 clocks 创建过程,有非常多的嵌套,就像是 俄罗期套娃。
/arch/arm/mach-s3c24xx/mach-smdk2440.c
smdk2440_init_time()
/arch/arm/mach-s3c24xx/common.c
s3c2440_init_clocks(12000000)
/drivers/clk/samsung/clk-s3c2410.c
s3c2410_common_clk_init(NULL, 12000000, 1, S3C24XX_VA_CLKPWR);
/* Register common internal clocks. 通用的内部时钟 */
samsung_clk_register_mux(ctx, s3c2410_common_muxes,
ARRAY_SIZE(s3c2410_common_muxes));
samsung_clk_register_div(ctx, s3c2410_common_dividers,
ARRAY_SIZE(s3c2410_common_dividers));
samsung_clk_register_gate(ctx, s3c2410_common_gates,
ARRAY_SIZE(s3c2410_common_gates));
if (current_soc == S3C2440 || current_soc == S3C2442) {
samsung_clk_register_div(ctx, s3c244x_common_dividers,
ARRAY_SIZE(s3c244x_common_dividers));
samsung_clk_register_gate(ctx, s3c244x_common_gates,
ARRAY_SIZE(s3c244x_common_gates));
samsung_clk_register_mux(ctx, s3c244x_common_muxes,
ARRAY_SIZE(s3c244x_common_muxes));
samsung_clk_register_fixed_factor(ctx, s3c244x_common_ffactor,
ARRAY_SIZE(s3c244x_common_ffactor));
}
/* 注册别名
* Register common aliases at the end, as some of the aliased clocks
* are SoC specific.
*/
samsung_clk_register_alias(ctx, s3c2410_common_aliases,
ARRAY_SIZE(s3c2410_common_aliases));
if (current_soc == S3C2440 || current_soc == S3C2442) {
samsung_clk_register_alias(ctx, s3c244x_common_aliases,
ARRAY_SIZE(s3c244x_common_aliases));
}
最终定位到
struct samsung_gate_clock s3c2410_common_gates[] __initdata = {
GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0),
GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0),
GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0),
GATE(PCLK_ADC, "adc", "pclk", CLKCON, 15, 0, 0),
GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 14, 0, 0),
GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 13, CLK_IGNORE_UNUSED, 0),
GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 12, 0, 0),
GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 11, 0, 0),
GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 10, 0, 0),
GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 9, 0, 0),
GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 8, 0, 0),
GATE(HCLK_USBD, "usb-device", "hclk", CLKCON, 7, 0, 0),
GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0),
GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0),
GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0),
};
/* should be added _after_ the soc-specific clocks are created */
struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"),
ALIAS(PCLK_ADC, NULL, "adc"),
ALIAS(PCLK_RTC, NULL, "rtc"),
ALIAS(PCLK_PWM, NULL, "timers"),
ALIAS(HCLK_LCD, NULL, "lcd"),
ALIAS(HCLK_USBD, NULL, "usb-device"),
ALIAS(HCLK_USBH, NULL, "usb-host"),
ALIAS(UCLK, NULL, "usb-bus-host"),
ALIAS(UCLK, NULL, "usb-bus-gadget"),
ALIAS(ARMCLK, NULL, "armclk"),
ALIAS(UCLK, NULL, "uclk"),
ALIAS(HCLK, NULL, "hclk"),
ALIAS(MPLL, NULL, "mpll"),
ALIAS(FCLK, NULL, "fclk"),
ALIAS(PCLK, NULL, "watchdog"),
ALIAS(PCLK_SDI, NULL, "sdi"),
ALIAS(HCLK_NAND, NULL, "nand"),
ALIAS(PCLK_I2S, NULL, "iis"),
ALIAS(PCLK_I2C, NULL, "i2c"),
ALIAS(PCLK_SPI, NULL, "spi"), // 添加一行
};
使用新内核启动
WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:1067 clk_core_enable+0x94/0xa4()
Modules linked in:
不确定到底是何原因引起是 SPI 时钟添加的方式不对,还是spi-s3c24xx.c 中使用的不对。
先改为 i2c 启动看是否还有错误
hw->clk = devm_clk_get(&pdev->dev, "i2c");
这一次启动没有了 WARNING 信息,说明 spi-s3c24xx.c 驱动没有问题。
后来我又一想,可能 是因为内核中有 I2C 的驱动,I2C 初始化了时钟,所以 没有问题。
重新编译 内核去掉了 I2C 后,重新启动,果然还是有 WARNING 说明 spi-s3c24xx.c 有问题。
自己写一个小的驱动来测试下 SPI 时钟是否可以工作使用。clk_get(NULL, "spi"); 已经可以生效了。
程序还不是很好用,但是可以读到 flash id ,id 好像也不太对。 后期在更新。

更新2: 使用spi 平台总线驱动测试
------------------------------------------------------------------------------------------------------------
启动出错
WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:1067 clk_core_enable+0x94/0xa4()
s3c2410-spi s3c2410-spi.1: Failed to get gpio for cs
s3c2410-spi: probe of s3c2410-spi.1 failed with error -16
if (!pdata->set_cs) {
//因为我没有提供 pin_cs 这里判断是小于0 所以没有判断出来
if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n");
err = -EINVAL;
goto err_register;
}
//出错位置
err = devm_gpio_request(&pdev->dev, pdata->pin_cs,
dev_name(&pdev->dev));
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
//设置操作函数
hw->set_cs = s3c24xx_spi_gpiocs;
//设置为输出功能
gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
看来只能自己设置一个选中函数
devs.c
static void s3c2400_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
gpio_set_value(cs, pol);
}
struct s3c2410_spi_info spi_info0 = {
//平台 总线号
.bus_num = 0,
//最大片选数量
.num_cs = 0xff,
.set_cs = s3c2400_spi_gpiocs,
};
struct s3c2410_spi_info spi_info1 = {
//平台 总线号
.bus_num = 1,
//最大片选数量
.num_cs = 0xff,
.set_cs = s3c2400_spi_gpiocs,
};
添加了一个函数后,编译内核,重新启动还是有
WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:1067 clk_core_enable+0x94/0xa4()
出错位置
对比下 i2c
/* initialise the i2c controller */
clk_prepare_enable(i2c->clk);
ret = s3c24xx_i2c_init(i2c);
clk_disable(i2c->clk);
static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
{
/* for the moment, permanently enable the clock */
clk_prepare_enable(i2c->clk); //新添加 可能是要先启用父级时钟?
clk_enable(hw->clk);
}
重编译并启动
s3c2410-spi s3c2410-spi.1: chipselect 194 already in use
s3c2410-spi s3c2410-spi.1: can't create new device for spi_flash
经过查找代码在
spi.c
int spi_add_device(struct spi_device *spi)
{
status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
if (status) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
goto done;
}
}
//在这里,检查 board 的片选是否相同
static int spi_dev_check(struct device *dev, void *data)
{
struct spi_device *spi = to_spi_device(dev);
struct spi_device *new_spi = data;
if (spi->master == new_spi->master &&
spi->chip_select == new_spi->chip_select)
return -EBUSY;
return 0;
}
因为我写的2个board的片选是同一个引脚。(因为我是自己焊接连线,要是买成本板就不会遇到这种事,为了接线比较简单,就用同一个片选)
结果在这里出错。
static struct spi_board_info spi_info_jz2440[] = {
{
.modalias = "spi_tft", /* 对应的spi_driver名字也是"spi_tft" */
.max_speed_hz = 10000000, /* max spi clock (SCK) speed in HZ */
.bus_num = 1, /* jz2440里OLED接在SPI CONTROLLER 1 */
.mode = SPI_MODE_0, /* 相位极性 */
//.chip_select = S3C2410_GPG(2), /* oled_cs, 它的含义由spi_master确定 */
//改为不同的片选 和下面的 spi_flash 不能相同
.chip_select = S3C2410_GPE(14), /* IICSCL, 它的含义由spi_master确定 */
.platform_data = (const void *)S3C2410_GPE(15), /* SDA oled_dc, 它在spi_driver里使用 */
},
{
.modalias = "spi_flash",
.max_speed_hz = 80000000,
.bus_num = 1,
.mode = SPI_MODE_0,
.chip_select = S3C2410_GPG(2),
},
};
static int spi_info_jz2440_init(void)
{
return spi_register_board_info(spi_info_jz2440, ARRAY_SIZE(spi_info_jz2440));
}
使用内核启动以后发现2个 spi 设备。就是刚才注册的 spi_tft 和 spi_flash

加载驱动后出错。

可以看到 spi 平台driver 了。

驱动还是有点问题,后期在更新。(此驱动和上面的测试时钟的不是一样的,是采用 spi 平台总线编写,不是直接操作spi寄存器)
第3次,更新解决问题。
WARNING: CPU: 0 PID: 986 at drivers/base/dd.c:286 driver_probe_device+0x270/0x298()
Modules linked in: spi_flash_mtd(O+)
CPU: 0 PID: 986 Comm: insmod Tainted: G O 4.1.36 #135
Hardware name: SMDK2440
在 git 上找到了补丁
https://github.com/raspberrypi/linux/commit/a97c883a16da7e0691a3be5465926c92a8da4da6
方法是,不过和我的有点不同。
drivers/spi/spi-dw.c
devm_kzalloc 换为 kzalloc
我这里修改
drivers/spi/spi-s3c24xx.c
static int s3c24xx_spi_setup(struct spi_device *spi)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state;
struct s3c24xx_spi *hw = to_hw(spi);
int ret;
/* allocate settings on the first call */
if (!cs) {
/*
cs = devm_kzalloc(&spi->dev,
sizeof(struct s3c24xx_spi_devstate),
GFP_KERNEL);
*/
cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
if (!cs)
return -ENOMEM;
cs->spcon = SPCON_DEFAULT;
cs->hz = -1;
spi->controller_state = cs;
}
}
添加一个清理函数, master 好像也不会调用销毁, 对了,当SPI核心支持做为模块,卸载的时候就会调用下
我在这里加个 printk 看看是不是会调用
static void s3c24xx_spi_cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
kfree(chip);
spi_set_ctldata(spi, NULL);
printk("\n\n\n\n\n\n spi_clanup ok \n\n\n\n\n");
}
static int s3c24xx_spi_probe(struct platform_device *pdev)
{
添加一行
hw->master->cleanup = s3c24xx_spi_cleanup;
}
编译新内核,spi 变成模块,加载后,卸载,打印出来了,清理信息。
重新加载 spi_flash 模块,正常了没有错误。

spi-s3c24xx.c 改后的文件
第4次更新,读不到flash id
读出来的id 都是 0xff , 检查代码,发现没有设置spi 的 gpio 引脚功能。
spi-s3c24xx.c
static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
{
if (hw->pdata) {
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_direction_output(hw->pdata->pin_cs, 1);
if (hw->pdata->gpio_setup)
hw->pdata->gpio_setup(hw->pdata, 1);
}
}
这里需要自己在 平台资源中提供一个 。
改后的 devs.c
static void s3c2400_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
gpio_set_value(cs, pol);
} static void s3c2400_spi_gpiosetup(struct s3c2410_spi_info *spi, int pin)
{
if( == spi->bus_num)
{ }
else
{
/**
SPIMI GPG5 SPICLK GPG7
SPIMO GPG6 nSSSPI GPG2
*/
s3c_gpio_cfgpin(S3C2410_GPG(), S3C2410_GPG5_SPIMISO1);
s3c_gpio_cfgpin(S3C2410_GPG(), S3C2410_GPG6_SPIMOSI1);
s3c_gpio_cfgpin(S3C2410_GPG(), S3C2410_GPG7_SPICLK1);
}
} struct s3c2410_spi_info spi_info0 = {
//平台 总线号
.bus_num = ,
//最大片选数量
.num_cs = 0xff,
.set_cs = s3c2400_spi_gpiocs,
.gpio_setup = s3c2400_spi_gpiosetup,
}; struct s3c2410_spi_info spi_info1 = {
//平台 总线号
.bus_num = ,
//最大片选数量
.num_cs = 0xff,
.set_cs = s3c2400_spi_gpiocs,
.gpio_setup = s3c2400_spi_gpiosetup,
}; static struct resource s3c_spi0_resource[] = {
[] = DEFINE_RES_MEM(S3C24XX_PA_SPI, SZ_32),
[] = DEFINE_RES_IRQ(IRQ_SPI0),
}; struct platform_device s3c_device_spi0 = {
.name = "s3c2410-spi",
.id = ,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(),
.platform_data = &spi_info0,
}
}; static struct resource s3c_spi1_resource[] = {
[] = DEFINE_RES_MEM(S3C24XX_PA_SPI1, SZ_32),
[] = DEFINE_RES_IRQ(IRQ_SPI1),
}; struct platform_device s3c_device_spi1 = {
.name = "s3c2410-spi",
.id = ,
.num_resources = ARRAY_SIZE(s3c_spi1_resource),
.resource = s3c_spi1_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(),
.platform_data = &spi_info1,
}
};
因为我这里没有使用 spi0 pcb 上也没有接线, 这里就不设置了。
到此,spi 总线工作正常。
总结问题有:
1, spi 时钟获取不到
2,分配内存函数使用不当 devm_kzalloc
3,同一个spi 总线上的 片选不能相同
4,需要提供,初始化 spi gpio 功能设置函数
后记:有人说,用新的内核,更简单,随便配置下,就能使用,完全学不到东西。
现在linux 内核正向, 设备树方面转,是大趋势,老内核肯定是学不到。
linux4.1.36 解决 SPI 时钟找不到 不生成设备 device的更多相关文章
- Java_java动态编译整个项目,解决jar包找不到问题
java动态编译整个项目,解决jar包找不到问题原文:http://itzyx.com/index.php/javac/ 动态将java文件编译为class文件解决方案:将temp\sdl\src目录 ...
- s3c2440 移值新内核 linux-4.1.36
arm-linuxgcc version 4.3.2 经过试验,最高可以编译到 linux-4.1.36 ,在高的版本会有错误 ,可能是 GCC 编译器版本较低造成. 解压比较麻烦还要装一个 xz x ...
- 转(解决GLIBC_2.x找不到的编译问题)
Linux/CentOS 升级C基本运行库CLIBC的注意事项(当想解决GLIBC_2.x找不到的编译问题) 分类: 开发环境 Linux2014-09-24 10:32 8933人阅读 评论(5) ...
- Eclipse解决项目中找不到Maven Dependencies
项目中找不到Maven Dependencies 正常的Maven项目应该是这样的 自己的项目中却没有Maven Dependencies 先做第一步 若项目中还没有出现Maven Dependenc ...
- Java 解决Enum.valueOf找不到枚举出现的异常
由于Enum.valueOf匹配不到枚举时会出现异常,这个可以用try...catch来解决,但是这样会导致代码往臃肿的道路上越走越远. 本文与其说是解决Enum.valueOf找不到枚举出现的异常还 ...
- 解决使用Git找不到.ssh文件夹的办法
解决使用Git找不到.ssh文件夹的办法 首先生成git密钥 git config --global user.name "你的名字(最好是中文全名)" git config -- ...
- C#设置进程PATH环境变量值解决某些Win32DLL找不到路径问题
C#.NET通过设置当前进程PATH环境变量值解决某些Win32DLL找不到路径问题 以下函数设置PATH环境变量值(请注意:该环境变量为当前进程的环境变量,非系统环境变量)用于解决在调用某些Win3 ...
- eclipse-java-2018-09-win32-x86_64配置tomcat(内含更新eclipse,如何解决添加时找不到最新tomcat版本)
我下的是eclipse精简版,建议下载企业版,可以省略后面的很多步骤(其中的辛酸...) 这里就是说明下载精简版的eclipse如何配置tomcat的步骤,其实还是更新eclipse的步骤 1.首先点 ...
- 读取web项目properties文件路径 解决tomcat服务器找不到properties路径问题
1.需求:有时候我们产品经理给我们的需求是会不断变化的,例如数量是1000现在变成500,我们不可以去改代码吧,这样很麻烦,所以就可以改配置文件properties(这个数据库链接一样),当然也有js ...
随机推荐
- KUKA机器人常见十大问题及解决方法
1 开机坐标系无效 世界坐标系是以枪头为基点,在这种坐标系中,机器人所有的动作都是按照以枪头为顶点来完成移动,XYZ方向切割枪方向不改变,如果机器人在世界坐标系中移动,枪头也随着改变方向,那就是我们在 ...
- CDC与HDC的区别以及相互转换
CDC是MFC的DC的一个类 HDC是DC的句柄,API中的一个类似指针的数据类型. MFC类的前缀都是C开头的 H开头的大多数是句柄 这是为了助记,是编程读\写代码的好的习惯. CDC中所 ...
- laravel-事件
1.注册事件以及监听器 首先我们需要在 app/Providers/目录下的EventServiceProvider.php中注册事件监听器映射关系,如下: /** * The event liste ...
- js类型比较
比较数据类型做比较的三种方法typeofinstanceofObject.prototype.toString.call() javascript七大类型 javascript的数据类型分为两类:原始 ...
- Python---5Python内置的有序集合-list和tuple
list Python内置的一种数据类型是列表:list,[ ].可以修改的集合. list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: ...
- 2020 倒计时 1 天,Python 工程师找工作更难了?
Python 是最神奇的编程语言. 无意引战,我说的是"神奇",不是"最好",并不想去"撼动" PHP 的地位. ...
- 根据现有的数据库自动生成Django model
Django引入外部数据库还是比较方便的,首先在setting里面设置你要连接的数据库类型和连接名称,地址之类,和创建新项目的时候一致 运行下面代码可以自动生成models模型文件 python ma ...
- 变身六次失去核心的小米Note还能火吗
奥特曼变身有时间限制,因此我们总是希望它多变几次身,从而把小怪兽打得嗷嗷叫.但对于科技产品来说,不断推出"变身版",似乎总有江河日下.大势已去之感.三星形形色色的复仇者联盟S6版, ...
- 将java project打包成jar包,web project 打包成war包的几种演示
将java项目打包成jar 第一种:MyEclipse将java项目打包成jar. 1,右击项目,选择export . 2,点击Java,选择JAR file . 3,在JAR file文本中浏览打包 ...
- The difference between applicationContext.xml in Spring and xxx-servlet.xml in SpringMVC
一直搞不明白两者的区别.如果使用了SpringMVC,事实上,bean的配置完全可以在xxx-servlet.xml中进行配置.为什么需要applicationContext.xml?一定必须? 因为 ...