首页 > 代码库 > linux4.1.36 SPI 时钟找不到 不生成设备 device

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);
}

看一下 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 = http://www.mamicode.com/{
    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 = http://www.mamicode.com/{
    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 时钟是否可以工作使用。

程序还不是很好用,但是可以读到 flash id ,id 好像也不太对。 后期在更新。

  1 #include <linux/err.h>
  2 #include <linux/kernel.h>
  3 #include <linux/module.h>
  4 #include <linux/slab.h>
  5 #include <linux/delay.h>
  6 #include <linux/io.h>
  7 #include <linux/clk.h>
  8  
  9 
 10 static volatile unsigned long  *gpg_con;
 11 static volatile unsigned long *gpg_dat;
 12 
 13 /**
 14  * 引脚定义
 15  * SPIMI GPG5 SPICLK GPG7
 16  * SPIMO GPG6 nSSSPI GPG2
 17  */
 18 
 19 struct spi_regs {
 20     unsigned long spcon;  //0x59000000
 21     unsigned long spsta;  //0x59000004
 22     unsigned long sppin;  //0x59000008
 23     unsigned long sppre;  //0x5900000c
 24     unsigned long sptdat; //0x59000010
 25     unsigned long sprdat; //0x59000014
 26 };
 27 
 28 static volatile struct spi_regs *spi_reg;
 29 static void spi_set_cs(int enable){    
 30     //低电平选中    
 31     if(enable)    
 32     {        
 33         *gpg_dat &= ~(1<<2);    
 34     }    
 35     else    
 36     {        
 37         *gpg_dat |= 1<<2;    
 38     }
 39 }
 40 
 41 static void spi_ready(void)
 42 {
 43     while(! (spi_reg->spsta & 1));
 44 }
 45 
 46 static unsigned char spi_read_byte(void)
 47 {
 48     unsigned char val = 0;
 49     spi_reg->sptdat = 0xff;
 50     spi_ready();
 51     val =  spi_reg->sprdat & 0xff;
 52     return val;
 53 }
 54 
 55 static void spi_init(void)
 56 {
 57     struct clk *clk;
 58     clk = clk_get(NULL, "spi");
 59     if (IS_ERR(clk)) 
 60     {
 61         printk("get spi clock error \n");
 62         return ;
 63     }
 64     clk_prepare_enable(clk);
 65     usleep_range(1000, 1100);
 66     //清0
 67     *gpg_con &= ~(3<<(2*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2));
 68     //设为SPI功能
 69     *gpg_con |= (1<<(2*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2));
 70     *gpg_dat |= 1<<2;
 71     //启用 SCK
 72     //主机模式
 73     spi_reg->spcon = (1<<4) | (1<<3);
 74     spi_reg->sppre = 2;
 75 }
 76 
 77 static void spi_write_byte(unsigned char val)
 78 {
 79     spi_ready();
 80     spi_reg->sptdat = val;
 81 }
 82 
 83 static void w25q_write_addr(unsigned int addr)
 84 {
 85     spi_write_byte((addr >> 16) & 0xff);
 86     spi_write_byte((addr >> 8) & 0xff);
 87     spi_write_byte(addr & 0xff);
 88 }
 89 
 90 static void w25q_init(void)
 91 {
 92     spi_init();
 93 }
 94 
 95 static void w25q_read_id(unsigned char *factory_id, unsigned char *dev_id)
 96 {
 97     *factory_id = 0;
 98     *dev_id     = 0;
 99     spi_set_cs(1);
100     spi_write_byte(0x90);
101     w25q_write_addr(0x0);
102     *factory_id = spi_read_byte();
103     *dev_id = spi_read_byte();
104     spi_set_cs(0);
105 }
106 
107 static int spi_25q_init(void)
108 {
109     unsigned char factory_id=0, dev_id=0;
110 
111     gpg_con = ioremap(0x56000060, 8);
112     gpg_dat  = gpg_con+1;
113     
114     spi_reg = ioremap(0x59000020, sizeof(struct spi_regs));
115     //初始化
116     w25q_init();
117     
118     //读ID
119     w25q_read_id(&factory_id, &dev_id);
120     printk("w25q64 fid: 0x%2x devid: 0x%2x \n", factory_id, dev_id);
121     
122     
123     return 0;
124 }
125 
126 static void spi_25q_exit(void)
127 {
128     iounmap(gpg_con);
129     iounmap(spi_reg);
130 }
131 module_init(spi_25q_init);
132 module_exit(spi_25q_exit);
133 MODULE_LICENSE("GPL");

 技术分享

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