Configure the camera interface clock in tiny4412

Source: Internet
Author: User

Tiny4412 is a new development board with less information and I am a little white. Therefore, there is no concept of clock configuration for camera. No way, it can only be viewed from the source code.


Clock driver Overview

The tiny4412 clock configuration source code is in/ARCH/ARM/Plat-Samsung/clock. c,/ARCH/ARM/Plat-Samsung/clock-clksrc.c and/ARCH/ARM/Mach-exynos/clock-exynos4.c under.

Clock. the C file mainly implements some function pointers for the Linux clock interface. The declarations and unified calls of these pointers are in include/Linux/CLK. H and Drivers/CLK/clkdev. c, in fact, the system will call the method of registering the clock through the common methods implemented in these two files. For example, calling the clk_set_rate method actually calls the CLK-> OPS-> set_rate method. This is the idea of driver hierarchy Design for Linux systems and is common in many drivers.

Clock-clksrc.c file is relatively small, mainly realize the method of clock Registration

The clock-exynos4.c file declares all the clock sources and configuration registers that can be used in the CPU, and corresponds to the chip datasheet (but does not use ioremap, it seems to be given an offset for Io space ing, I don't know how the offset is calculated)

The main struct definition and top-level function declaration of the clock on the Samsung platform are under/ARCH/ARM/Plat-Samsung/include/plat/clock. h.


These are the source files.


First of all to analyze how the clock is registered, Samsung's clock declaration and registration code in the clock-exynos4.c, the main focus of the last function: exynos4_register_clocks, this function completed the registration operation. There are two types of CLK struct declared by Samsung. One is a pure CLK struct and the other is a clksrc struct. The simple CLK struct can be seen from the source code. It is mainly used to control clock enabling (corresponding to clk_gate_xxx and other registers). It only implements the Enable method and does not implement set_rate, get_rate, and other methods, they are simple switches, and the call method is clk_enable (XXXX );

The other type is the clksrc_clk struct, which not only declares the clock name and enable method (corresponding to the control of clk_mask_xxx register), but also provides the clock source register (corresponding to clk_src_xxx) and the frequency-division register (corresponding to clk_div_xxx). It also provides the upper-layer clock associated with each clock. To enable a clock, you must enable all the clocks associated with the upper layer. Therefore, it uses an array to list all the possible upper-layer clocks, you can select its upper-layer clock by reading the register configuration before registration. This is also the reason why the clock register cannot be directly configured. If only the chip clock register is configured, the dependency between the upper-layer clock in the CLK struct remains unchanged, therefore, the final clock configuration is still incorrect!


After learning about this, you can check the clock registration. Registration is nothing more than adding all CLK struct information to the end of a linked list, so that the system saves all the CLK configuration backup pointers, in this way, you can query the linked list to obtain these CLK pointers as needed.

Registration is also divided into two types, one is a simple CLK struct registration, the registration function is

int s3c24xx_register_clock(struct clk *clk){        if (clk->enable == NULL)                clk->enable = clk_null_enable;        /* fill up the clk_lookup structure and register it*/        clk->lookup.dev_id = clk->devname;        clk->lookup.con_id = clk->name;        clk->lookup.clk = clk;        clkdev_add(&clk->lookup);        return 0;}

Add the name to CLK-> lookup and add the pointer to the end of the linked list. Note that all CLK struct clocks in tiny4412 do not have devname and only have the corresponding name. devname = NULL is input during the call.


The other is clksrc_clk registration, which is troublesome,

void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size){        int ret;        for (; size > 0; size--, clksrc++) {                if (!clksrc->reg_div.reg && !clksrc->reg_src.reg)                        printk(KERN_ERR "%s: clock %s has no registers set\n",                               __func__, clksrc->clk.name);                /* fill in the default functions */                if (!clksrc->clk.ops) {                        if (!clksrc->reg_div.reg)                                clksrc->clk.ops = &clksrc_ops_nodiv;                        else if (!clksrc->reg_src.reg)                                clksrc->clk.ops = &clksrc_ops_nosrc;                        else                                clksrc->clk.ops = &clksrc_ops;                }                   /* setup the clocksource, but do not announce it                 * as it may be re-set by the setup routines                 * called after the rest of the clocks have been                 * registered                 */                s3c_set_clksrc(clksrc, false);                ret = s3c24xx_register_clock(&clksrc->clk);                if (ret < 0) {                        printk(KERN_ERR "%s: failed to register %s (%d)\n",                               __func__, clksrc->clk.name, ret);                }           }   }

The input parameter is a clksrc_clk array, which is registered in sequence. During each registration, you must specify the clock operation function pointer, that is, CLK. ops struct, all clksrc_clk in tiny4412 do not specify CLK at first. ops parameters and pointers must be passed in during registration. Therefore, the final operation function pointers of all clksrc_clk struct are the same clksrc_ops. Next let's take a look at what clksrc_ops has done:

static struct clk_ops clksrc_ops = {        .set_parent     = s3c_setparent_clksrc,        .get_rate       = s3c_getrate_clksrc,        .set_rate       = s3c_setrate_clksrc,        .round_rate     = s3c_roundrate_clksrc,};

Four clock operations are provided. corresponding to the upper-layer functions, the set_rate function pointer is used as an example:

static int s3c_setrate_clksrc(struct clk *clk, unsigned long rate){        struct clksrc_clk *sclk = to_clksrc(clk);        void __iomem *reg = sclk->reg_div.reg;        unsigned int div;        u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size);        u32 val;        rate = clk_round_rate(clk, rate);        div = clk_get_rate(clk->parent) / rate;        if (div > (1 << sclk->reg_div.size))                return -EINVAL;        val = __raw_readl(reg);        val &= ~mask;        val |= (div - 1) << sclk->reg_div.shift;        __raw_writel(val, reg);        return 0;}

Calculate the frequency division coefficient of the clock in the Code and write it into the frequency division register again. This is a common method and the final implementation of Linux top-level calls.

Note that the clksrc_ops assignment operation is only available when the clksrc_clk struct is registered. Therefore, only the functions called at the top level of Linux can obtain and operate CLK in clksrc_clk, this fully demonstrates that the clock declared by the common CLK struct is only a switch function and can only be called using the clk_enable () method.

Based on the value in the clock source register (clk_src_xxx. For detailed procedures, see the source code.

The last step is the normal registration clock.

Now the clock registration is complete.


Linux upper-layer calls:

The CLK. h file provides the standard function names for Linux upper-layer calls:

struct clk *clk_get(struct device *dev, const char *id);int clk_enable(struct clk *clk);void clk_disable(struct clk *clk);unsigned long clk_get_rate(struct clk *clk);void clk_put(struct clk *clk);long clk_round_rate(struct clk *clk, unsigned long rate);int clk_set_rate(struct clk *clk, unsigned long rate);int clk_set_parent(struct clk *clk, struct clk *parent);struct clk *clk_get_parent(struct clk *clk);struct clk *clk_get_sys(const char *dev_id, const char *con_id);int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,struct device *dev);


The specific implementation varies with different platforms. The implementation of these functions on the tiny4412 platform is also described in clk_set_rate in/ARCH/ARM/Plat-Samsung/clock. C.

int clk_set_rate(struct clk *clk, unsigned long rate){        int ret;        if (IS_ERR(clk))                return -EINVAL;        /* We do not default just do a clk->rate = rate as         * the clock may have been made this way by choice.         */        WARN_ON(clk->ops == NULL);        WARN_ON(clk->ops && clk->ops->set_rate == NULL);        if (clk->ops == NULL || clk->ops->set_rate == NULL)                return -EINVAL;        spin_lock(&clocks_lock);        ret = (clk->ops->set_rate)(clk, rate);        spin_unlock(&clocks_lock);        return ret;}


It simply calls the OPS-> set_rate method in the passed CLK object, that is, the above-mentioned initi_setrate_clksrc function.

The entire clock register operation process is like this. Key points:

1. After the clock is registered, the ops pointer is bound to a series of methods.

2. These methods are actually called when you call a Linux top-level function.

How can we obtain the CLK pointer passed in as a parameter?

Clk_get provides a way to get a CLK pointer through the clock name and device name, passed in according to the name and devname used for clock registration in the clock-exynos4.c file as the parameter, returns the corresponding CLK x pointer. This function is actually convenient for the entire clock linked list. By comparing the name and devname, return the first completely matched clock pointer. This part is relatively simple. See the source code of clk_get.

The source code for configuring the camera clock in tiny4412 is as follows: first, configure the gpio

        GPJ0CON_MAP = ioremap(GPJ0CON,4);        GPJ1CON_MAP = ioremap(GPJ1CON,4);        GPJ0PUD_MAP = ioremap(GPJ0PUD,4);        GPJ1PUD_MAP = ioremap(GPJ1PUD,4);        /*Set GPJ as Camera interface*///      printk("%s():GPJ0CON_MAP = 0x%x\n",__func__,readl(GPJ0CON_MAP));//      printk("%s():GPJ1CON_MAP = 0x%x\n",__func__,readl(GPJ1CON_MAP));        writel(0x22222222,GPJ0CON_MAP);        writel(0x00022222,GPJ1CON_MAP);

        struct device my_device = {                .init_name = "exynos4-fimc.0",        };        clk_set_rate(clk_get(NULL,"sclk_cam0"),24000000);        cam_clk = clk_get(&my_device,"fimc");        clk_enable(clk_get(NULL,"sclk_cam0"));        if(IS_ERR(cam_clk)){                printk("%s():clk_get failed!\n",__func__);        }        else{                printk("%s():clk_get succeed!\n",__func__);                if(clk_enable(cam_clk))                        printk("clk enable failed\n");                else                        printk("clk enable succeed\n");        }        printk("cam clk rate is %lu\n",clk_get_rate(clk_get(NULL,"sclk_cam0")));

In this way, the camera interface clock is successfully configured and a 24 MHz clock is generated on the mclk pin.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.